SpringCloud源码研读(三):consul

自动配置

在spring-cloud-consul-discovery.jar!/META-INF/spring.factories中,有如下配置:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.consul.discovery.RibbonConsulAutoConfiguration,\
org.springframework.cloud.consul.discovery.configclient.ConsulConfigServerAutoConfiguration,\
org.springframework.cloud.consul.serviceregistry.ConsulAutoServiceRegistrationAutoConfiguration,\
org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistryAutoConfiguration,\
org.springframework.cloud.consul.discovery.ConsulDiscoveryClientConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.consul.discovery.configclient.ConsulDiscoveryClientConfigServiceBootstrapConfiguration

后面我们将逐一分析这些配置带来的效果。

服务注册

服务注册要解决一个经典的问题

  • 我是谁
  • 我在哪
  • 我要去向何方

​ 在分布式服务中,一个服务通常有多个实例,这时候需要分别申明服务和实例,以便确认主体,合并不同实例。服务声明就是告诉consul server“我是谁”,ConsulAutoRegistration定制了包括服务名称serviceName和服务唯一标识instanceID的规则。

​ 服务注册的目的是让别的服务能够使用,通常我们访问一个http服务是通过host:port这样的uri定位。host又分为ip和域名两种类型。一个springboot服务是如何知道“我在哪”呢?spring的InetUtils使用了jdk的java.net.NetworkInterface网络接口获得当前服务IP和hostname,具体原理可以参考附录中的“java中的getHostname”。而port则是通过WebServerInitializedEvent.getWebServer(). getPort()获得。

​ consul server的地址可以直接通过配置获得,spring cloud通过AgentConsulClient代理了对 consul server的请求。

服务初始注册
1. ConsulAutoServiceRegistrationListener.onApplicationEvent(ApplicationEvent) 
2. ConsulAutoServiceRegistration.start() 
3. ConsulAutoServiceRegistration.register() 
4. AbstractAutoServiceRegistration.register() 
5. ConsulServiceRegistry.register(Registration) 
6. ConsulClient.agentServiceRegister(NewService,String)
7. AgentConsulClient.agentServiceRegister(NewService,String)
8. ConsulRawClient.makePutRequest()
  1. ConsulAutoServiceRegistrationListener监听服务启动事件WebServerInitializedEvent,执行服务注册。在第一步中调用了这样一个代码:this.autoServiceRegistration.setPortIfNeeded(event.getWebServer().getPort()),此处通过jdk的CAS机制实现了防止重复设置端口和并发操作。
//ConsulAutoServiceRegistration
private AtomicInteger port = new AtomicInteger(0);
void setPortIfNeeded(int port) {
	getPort().compareAndSet(0, port);
}
  1. ConsulServiceRegistry是服务注册,销毁等业务功能的流程控制类。
//ConsulServiceRegistry.register(ConsulRegistration reg)
this.client.agentServiceRegister(reg.getService(),
	                this.properties.getAclToken());
    NewService service = reg.getService();
if (this.heartbeatProperties.isEnabled() && this.ttlScheduler != null
					&& service.getCheck() != null
					&& service.getCheck().getTtl() != null) {
	this.ttlScheduler.add(reg.getInstanceId());
}

2.1 具体的服务注册请求由ConsulClient完成。reg.getService()获得了服务注册需要的所有信息NewService。NewService.check数据中,http指定了consul server健康检查请求地址。而ttl则指定agent心跳检查的间隔时间。spring cloud consul这两个字段是互斥的,当客户端主动做心跳检测时就不做健康检查。

NewService内容如下:

{
    "id": "resource-server-1-8081",
    "name": "resource-server-1",
    "tags": [
        {
            "secure": false
        }
    ],
    "address": "172.17.0.1",
    "meta": null,
    "port": 8081,
    "enableTagOverride": null,
    "check": {
        "script": "null",
        "interval": "1s",
        "ttl": "null",
        "http": "http://172.17.0.1:8081/actuator/health",
        "method": "null",
        "header": {},
        "tcp": "null",
        "timeout": "null",
        "deregisterCriticalServiceAfter": "null",
        "tlsSkipVerify": null,
        "status": "null"
    }
}

ConsulAutoRegistration创建NewService.Check的代码如下:

	public static NewService.Check createCheck(Integer port,
			HeartbeatProperties ttlConfig, ConsulDiscoveryProperties properties) {
		NewService.Check check = new NewService.Check();
		if (StringUtils.hasText(properties.getHealthCheckCriticalTimeout())) {
			check.setDeregisterCriticalServiceAfter(
					properties.getHealthCheckCriticalTimeout());
		}
 // 如果启用心跳检测,则不做consul server健康检查
		if (ttlConfig.isEnabled()) {
			check.setTtl(ttlConfig.getTtl());
			return check;
		}

		Assert.notNull(port, "createCheck port must not be null");
		Assert.isTrue(port > 0, "createCheck port must be greater than 0");
//健康检查地址默认使用hostname+端口,也可以通过配置
		if (properties.getHealthCheckUrl() != null) {
			check.setHttp(properties.getHealthCheckUrl());
		}
		else {
			check.setHttp(String.format("%s://%s:%s%s", properties.getScheme(),
					properties.getHostname(), port, properties.getHealthCheckPath()));
		}
		check.setHeader(properties.getHealthCheckHeaders());
		check.setInterval(properties.getHealthCheckInterval());
		check.setTimeout(properties.getHealthCheckTimeout());
		check.setTlsSkipVerify(properties.getHealthCheckTlsSkipVerify());
		return check;
	}
心跳检测

心跳检测是agent主动向server汇报自身健康状况的机制。当超过健康检查ttl时间没有汇报自身状态时,consul server认为应用进入了critical状态。

健康检查

spring cloud consul client响应健康检查是一个非常独特的请求链路,当consul server请求client的“/actuator/health”时,client又请求了consul server获得所有服务列表。只有在获得了所有服务列表时才认为服务是正常启动的。

1. HealthEndpointWebExtension.health(SecurityContext) 
2. HealthEndpoint.health() 
3. CompositeHealthIndicator.health()
4. DiscoveryCompositeHealthIndicator$Holder.health() 
5. DiscoveryClientHealthIndicator.health()
6. CompositeDiscoveryClient.getServices()
7. ConsulClient.getCatalogServices(QueryParams)
8. CatalogConsulClient.getCatalogServices(QueryParams,String)

服务发现

consul agent通过http请求获得所有的可用服务。具体如果使用交由服务调用方。

在应用启动时创建了ConsulCatalogWatch,并创建了一个固定周期的线程。ConsulCatalogWatch.catalogServicesWatch()调用ConsulClient获得所有service,并发出HeartbeatEvent通知相关监听者更新服务内容。

//ConsulCatalogWatch
@Override
	public void start() {
		if (this.running.compareAndSet(false, true)) {
			this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(
					this::catalogServicesWatch,
					this.properties.getCatalogServicesWatchDelay());
		}
	}

consul原理学习: https://www.xuejiayuan.net/blog/b060671aa2a64092b78535765289d068
Consul实现原理系列文章2: 用Gossip来做集群成员管理和消息广播:https://blog.csdn.net/u012422829/article/details/77828870
现有系统如何集成Consul服务发现:https://www.jianshu.com/p/28c6bd590ca0
Consul - 简介、安装、常用命令的使用: https://juejin.im/entry/58c754138ac2470720133ab2
Spring Cloud Consul官方教程: https://springcloud.cc/spring-cloud-consul.html
Spring Cloud Consul 之Greenwich版本全攻略:https://blog.csdn.net/forezp/article/details/87273153
Spring Cloud Consul 从入门到精通: https://gitbook.cn/books/5b38d875d00fdd680bb774e8/index.html
java中的getHostname: https://xhao.io/2016/04/host-ip/
spring cloud集成 consul源码分析: https://www.cnblogs.com/davidwang456/p/6734995.html
SpringCloud使用Consul时,服务注销的操作方式: https://juejin.im/post/5c6a7232f265da2daa314447
springcloud(十三):注册中心 Consul 使用详解: http://www.ityouknow.com/springcloud/2018/07/20/spring-cloud-consul.html
consul配置参数大全、详解、总结: https://www.cnblogs.com/sunsky303/p/9209024.html
Consul官方文档【译文】4-1、API-Agent: https://my.oschina.net/percylee/blog/1524600

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值