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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章