springcloud sidecar

本文详细介绍了Spring Cloud Sidecar的使用场景、配置方法及工作原理。Sidecar作为非JVM应用与Spring Cloud生态间的桥梁,允许非JVM应用利用Eureka、Ribbon等组件。文章深入解析了Sidecar的配置步骤,包括依赖引入、启动配置、健康检查定制等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.官网

https://cloud.spring.io/spring-cloud-static/Finchley.SR2/multi/multi__polyglot_support_with_sidecar.html

2.什么时候用

您是否拥有想要利用Eureka,Ribbon和Config Server的非JVM语言?

3.怎么用

1.在与非JVM应用程序相同的主机上运行生成的应用程序。
2.要在项目中包含Sidecar,请使用具有组ID org.springframework.cloud 和工件ID或的依赖项spring-cloud-netflix-sidecar。
3.要启用Sidecar,请创建一个Spring Boot应用程序@EnableSidecar
4.要配置的Sidecar,在application.yml中加sidecar.port和sidecar.health-uri
该sidecar.port属性是非JVM应用程序侦听的端口。这样Sidecar可以正确地向Eureka注册应用程序。
这sidecar.health-uri是一个可在非JVM应用程序上访问的URI,它模仿Spring Boot运行状况指示器。它应该返回类似于以下内容的JSON文档:

{
  "status":"UP"
}

以下application.yml示例显示了Sidecar应用程序的示例配置:

server:
  port: 5678
spring:
  application:
    name: sidecar

sidecar:
  port: 8000
  health-uri: http://localhost:8000/health.json

4.调用

1.内部通过ribbon调用
2.外部通过zuul调用

5.源码

**5.1自定义健康检查指标LocalApplicationHealthIndicator **

package org.springframework.cloud.netflix.sidecar;

import java.net.URI;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.web.client.RestTemplate;

/**
 * @author Spencer Gibb
 */
public class LocalApplicationHealthIndicator extends AbstractHealthIndicator {

	@Autowired
	private SidecarProperties properties;

	@SuppressWarnings("unchecked")
	@Override
	protected void doHealthCheck(Health.Builder builder) throws Exception {
		//获取yml中配置的sidecar.health-uri
		URI uri = this.properties.getHealthUri();
		if (uri == null) {
			builder.up();
			return;
		}
		//restTemplate请求yml中配置的sidecar.health-uri,获取健康状态
		Map<String, Object> map = new RestTemplate().getForObject(uri, Map.class);
		Object status = map.get("status");
		if (status != null && status instanceof String) {
			builder.status(status.toString());
		}
		else if (status != null && status instanceof Map) {
			Map<String, Object> statusMap = (Map<String, Object>) status;
			Object code = statusMap.get("code");
			if (code != null) {
				builder.status(code.toString());
			}
			else {
				getWarning(builder);
			}
		}
		else {
			getWarning(builder);
		}
	}

	private Health.Builder getWarning(Health.Builder builder) {
		return builder.unknown().withDetail("warning", "no status field in response");
	}

}

5.2自定义健康检查调度器LocalApplicationHealthCheckHandler

package org.springframework.cloud.netflix.sidecar;

import static com.netflix.appinfo.InstanceInfo.InstanceStatus.DOWN;
import static com.netflix.appinfo.InstanceInfo.InstanceStatus.OUT_OF_SERVICE;
import static com.netflix.appinfo.InstanceInfo.InstanceStatus.UNKNOWN;
import static com.netflix.appinfo.InstanceInfo.InstanceStatus.UP;

import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;

/**
 * Eureka HealthCheckHandler that translates boot health status to
 * InstanceStatus so the proper status of the non-JVM app is sent to Eureka.
* @author Spencer Gibb
*/
class LocalApplicationHealthCheckHandler implements HealthCheckHandler {

	private final HealthIndicator healthIndicator;

	public LocalApplicationHealthCheckHandler(HealthIndicator healthIndicator) {
		this.healthIndicator = healthIndicator;
	}

	@Override
	public InstanceStatus getStatus(InstanceStatus currentStatus) {
	    //获取健康检查状态,并返回
		Status status = healthIndicator.health().getStatus();
		if (status.equals(Status.UP)) {
			return UP;
		} else if (status.equals(Status.OUT_OF_SERVICE)) {
			return OUT_OF_SERVICE;
		} else if (status.equals(Status.DOWN)) {
			return DOWN;
		}
		return UNKNOWN;
	}
}

5.3将健康检查指标和健康检查处理器注册到instance中

package org.springframework.cloud.netflix.sidecar;

import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceId;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
import org.springframework.cloud.netflix.eureka.metadata.DefaultManagementMetadataProvider;
import org.springframework.cloud.netflix.eureka.metadata.ManagementMetadata;
import org.springframework.cloud.netflix.eureka.metadata.ManagementMetadataProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.StringUtils;

import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.discovery.EurekaClientConfig;

import java.util.Map;

/**
 * Sidecar Configuration that setting up {@link com.netflix.appinfo.EurekaInstanceConfig}.
 * <p>
 * Depends on {@link SidecarProperties} and {@code eureka.instance.hostname} property. Since there is two way to
 * configure hostname:
 * <ol>
 *   <li>{@code eureka.instance.hostname} property</li>
 *   <li>{@link SidecarProperties#hostname}</li>
 * </ol>
 * {@code eureka.instance.hostname} will always win against {@link SidecarProperties#hostname} due to
 * {@code @ConfigurationProperties("eureka.instance")} on {@link EurekaInstanceConfigBeanConfiguration}.
 *
 * @author Spencer Gibb
 * @author Ryan Baxter
 *
 * @see EurekaInstanceConfigBeanConfiguration
 */
@Configuration
@EnableConfigurationProperties
@ConditionalOnProperty(value = "spring.cloud.netflix.sidecar.enabled", matchIfMissing = true)
public class SidecarConfiguration {

	@Bean
	public HasFeatures Feature() {
		return HasFeatures.namedFeature("Netflix Sidecar", SidecarConfiguration.class);
	}

	@Bean
	public SidecarProperties sidecarProperties() {
		return new SidecarProperties();
	}

	@Configuration
	@ConditionalOnClass(EurekaClientConfig.class)
	protected static class EurekaInstanceConfigBeanConfiguration {
		@Autowired
		private SidecarProperties sidecarProperties;

		@Autowired
		private InetUtils inetUtils;

		@Value(value = "${management.port:${MANAGEMENT_PORT:#{null}}}")
		private Integer managementPort;

		@Value("${server.port:${SERVER_PORT:${PORT:8080}}}")
		private int serverPort = 8080;

		@Value("${management.context-path:${MANAGEMENT_CONTEXT_PATH:#{null}}}")
		private String managementContextPath;

		@Value("${server.context-path:${SERVER_CONTEXT_PATH:/}}")
		private String serverContextPath = "/";

		@Value("${eureka.instance.hostname:${EUREKA_INSTANCE_HOSTNAME:}}")
		private String hostname;

		@Autowired
		private ConfigurableEnvironment env;

		@Bean
		@ConditionalOnMissingBean
		public ManagementMetadataProvider serviceManagementMetadataProvider() {
			return new DefaultManagementMetadataProvider();
		}

		@Bean
		public EurekaInstanceConfigBean eurekaInstanceConfigBean(ManagementMetadataProvider managementMetadataProvider) {
			EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils);
			String springAppName = this.env.getProperty("spring.application.name", "");
			//获取yml中配置的sidecar.port,获取健康状态
			int port = this.sidecarProperties.getPort();
			//设置实例的端口号
			config.setNonSecurePort(port);
			
			config.setInstanceId(getDefaultInstanceId(this.env));
			if (StringUtils.hasText(springAppName)) {
				config.setAppname(springAppName);
				config.setVirtualHostName(springAppName);
				config.setSecureVirtualHostName(springAppName);
			}
			String hostname = this.sidecarProperties.getHostname();
			String ipAddress = this.sidecarProperties.getIpAddress();
			if (!StringUtils.hasText(hostname) && StringUtils.hasText(this.hostname)) {
				hostname = this.hostname;
			}
			if (StringUtils.hasText(hostname)) {
				config.setHostname(hostname);
			}
			if (StringUtils.hasText(ipAddress)) {
				config.setIpAddress(ipAddress);
			}
			String scheme = config.getSecurePortEnabled() ? "https" : "http";
			ManagementMetadata metadata = managementMetadataProvider.get(config, serverPort,
					serverContextPath, managementContextPath, managementPort);

			if(metadata != null) {
				config.setStatusPageUrl(metadata.getStatusPageUrl());
				config.setHealthCheckUrl(metadata.getHealthCheckUrl());
				if(config.isSecurePortEnabled()) {
					config.setSecureHealthCheckUrl(metadata.getSecureHealthCheckUrl());
				}
				Map<String, String> metadataMap = config.getMetadataMap();
				if (metadataMap.get("management.port") == null) {
					metadataMap.put("management.port", String.valueOf(metadata.getManagementPort()));
				}
			}
			config.setHomePageUrl(scheme + "://" + config.getHostname() + ":" + port
					+ config.getHomePageUrlPath());
			return config;
		}
        // 重写实例的 HealthCheckHandler,并传入LocalApplicationHealthIndicator注册到EurekaInstanceConfigBean 
		@Bean
		public HealthCheckHandler healthCheckHandler(
				final LocalApplicationHealthIndicator healthIndicator) {
			return new LocalApplicationHealthCheckHandler(healthIndicator);
		}

	}
    //注册 LocalApplicationHealthIndicator 被spring管理
	@Bean
	public LocalApplicationHealthIndicator localApplicationHealthIndicator() {
		return new LocalApplicationHealthIndicator();
	}

	@Bean
	public SidecarController sidecarController() {
		return new SidecarController();
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值