1.背景
Eureka作为SpringCloud全家桶中的注册中心,一旦挂了,依赖它的微服务就无法找到彼此而不可用,可谓关键组件。因此有必要探究一下它的基本原理和配置,不至于救火的时候两眼一抹黑。
其大体作用和交互可参考:eureka详解,下图即引用自该🔗。
本文主要关注:
- 高可用集群配置
- 利用tcpdump进行行为分析
spring cloud 里面的eureka这个词有点意思,eureka ~= aha moment!
2. 集群配置
2.1 Eureka Server集群启动
1
2
3
4
5
6
7
8
eureka:
client:
registerWithEureka: true
fetchRegistry: true
docker run -d --name peer1 -e SPRING_OPTS="--eureka.environment=prod --eureka.instance.appname=server-eureka --spring.application.name=server-eureka --eureka.instance.hostname=10.185.55.81 --eureka.instance.preferIpAddress=true --eureka.instance.ip-address=10.185.55.81 --eureka.client.serviceUrl.defaultZone=http://10.185.55.82:8501/eureka/,http://10.185.55.83:8501/eureka/"
docker run -d --name peer2 -e SPRING_OPTS="--eureka.environment=prod --eureka.instance.appname=server-eureka --spring.application.name=server-eureka --eureka.instance.hostname=10.185.55.82 --eureka.instance.preferIpAddress=true --eureka.instance.ip-address=10.185.55.82 --eureka.client.serviceUrl.defaultZone=http://10.185.55.81:8501/eureka/,http://10.185.55.83:8501/eureka/"
docker run -d --name peer3 -e SPRING_OPTS="--eureka.environment=prod --eureka.instance.appname=server-eureka --spring.application.name=server-eureka --eureka.instance.hostname=10.185.55.83 --eureka.instance.preferIpAddress=true --eureka.instance.ip-address=10.185.55.83 --eureka.client.serviceUrl.defaultZone=http://10.185.55.81:8501/eureka/,http://10.185.55.82:8501/eureka/"
Spring Cloud: High Availability for Eureka
High Availability, Zones and Regions
2.2 Eureka client配置
1
2
3
4
5
6
7
8
9
10
eureka:
instance:
// 不同服务可以配置不同参数,查看meta data即可知晓
lease-renewal-interval-in-seconds: 4
lease-expiration-duration-in-seconds: 12
client:
fetch-registry: true
registry-fetch-interval-seconds: 8
serviceUrl:
defaultZone: http://10.185.55.81:8501/eureka/,http://10.185.55.82:8501/eureka/,http://10.185.55.83:8501/eureka/
对于client端,第一个注册中心服务挂了,会尝试第二个,依次类推
Eureka Server: How to achieve high availability
Spring Cloud Netflix Eureka - The Hidden Manual
2.3 重要参数及其影响
2.3.1 eureka.instance.appname
eureka.instance.appname必须一致,否则会出现下面的情况(2.3.4)。也就是说,页面上”Application”列使用该参数
2.3.2 spring.application.name
spring.application.name可以不一致,但作为同一服务的不同实例,建议统一
2.3.3 eureka.instance.preferIpAddress
默认情况下,eureka client使用主机名(hostName)向注册中心注册。
当prefer-ip-address: true时 ,client使用的是ip向服务中心注册 ,但是默认获取的ip是 127.0.0.1。默认情况下当获取的ip 和 hostName 不同时 ,则产生不可用分片提示信息(unavailable-replicas),并且集群间的数据不会同步
prefer-ip-address必须为true时, 可做如下设置
1
2
3
4
5
6
7
8
9
SPRING_OPTS="
--eureka.instance.preferIpAddress=true
--eureka.instance.hostname=ip1
--eureka.instance.ip-address=ip1"
SPRING_OPTS="
--eureka.instance.preferIpAddress=true
--eureka.instance.hostname=ip2
--eureka.instance.ip-address=ip2"
2.3.4 unavailable-replicas现象
3. Eureka集群间交互准则
3.1 集群间通信(peer to peer communication)
- 等同于eureka client -> eureka server 通信
- 增量更新 delta update
- timestamp 冲突化解
- 容忍不同server间数据不一致
- self-preservation during network outages
Understanding Eureka Peer to Peer Communication
Understanding eureka client server communication
The Mystery of Eureka Self-Preservation
3.2 peer to peer VS master/slave
集群节点之间不分主从,任何节点都可以接受写数据,节点之间进行数据同步。
3.3 持久化与客户端使用
Eureka的注册信息都在内存,未持久化,比较适合中小规模应用。客户端支持负载均衡算法及失败重试
4. 行为分析
4.0 手段
4.0.1 tcpdump
在10.x.x.21,即eureka所在机器
1
tcpdump -tttt -s0 -X -vv tcp port 8501 -w captcha.cap
4.0.2 wireshark分析
4.0.3 SimpleHTTPServer文件访问
1
2
3
4
5
6
7
8
// 文件所在端执行
python -m SimpleHTTPServer 12345
// 下载端浏览器访问即可
ip:12345
// wget
wget ip:12345/file_to_download
4.1 特定实例信息(meta data)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
curl http://10.182.50.79:8501/eureka/apps/BOP-ZUUL-GATEWAY
// 三个注册实例的详情
<application>
<name>BOP-ZUUL-GATEWAY</name>
<instance>
<instanceId>cat.bop.weibo.com:bop-zuul-gateway:8800</instanceId>
<hostName>cat.bop.weibo.com</hostName>
<app>BOP-ZUUL-GATEWAY</app>
<ipAddr>10.182.50.79</ipAddr>
<status>UP</status>
<port enabled="true">8800</port>
<leaseInfo>
<renewalIntervalInSecs>4</renewalIntervalInSecs>
<durationInSecs>12</durationInSecs>
<registrationTimestamp>1683790603401</registrationTimestamp>
<lastRenewalTimestamp>1684229441428</lastRenewalTimestamp>
<evictionTimestamp>0</evictionTimestamp>
<serviceUpTimestamp>1683790603401</serviceUpTimestamp>
</leaseInfo>
...
</instance>
<instance>
<instanceId>tiger.bop.weibo.com:bop-zuul-gateway:8800</instanceId>
<hostName>tiger.bop.weibo.com</hostName>
<app>BOP-ZUUL-GATEWAY</app>
<ipAddr>10.185.55.87</ipAddr>
<status>UP</status>
<port enabled="true">8800</port>
...
</instance>
<instance>
<instanceId>lion.bop.weibo.com:bop-zuul-gateway:8800</instanceId>
<hostName>lion.bop.weibo.com</hostName>
<app>BOP-ZUUL-GATEWAY</app>
<ipAddr>10.182.50.80</ipAddr>
<status>UP</status>
<port enabled="true">8800</port>
...
</instance>
</application>
4.2 实例注册行为
post /eureka/apps/BOP-FMS-QUERY-INFO
4.3 实例心跳
PUT /eureka/apps/BOP-FMS-QUERY-INFO/donkey.bop.weibo.com:bop-fms-query-info:8857
4.4 实例下线行为
将BOP-FMS-QUERY-INFO实例停止:
4.5 注册中心节点间更新
10.185.55.87 => 10.182.50.80,可见节点间同步数据采用的是主动push行为
4.6 重启特定注册中心节点
重启10.182.50.79所在eureka节点,第79帧即位其向另一个注册中心节点注册。此后,第86帧为另一节点向重启节点增量同步注册信息。
5 实践遇到的一些问题
5.1 注册中心不更新服务状态
现象:关停服务,注册中心不再将该服务状态置为”down”,也不删除该实例
分析:
1)测试关停不同服务(spring cloud版本不同,2.1.2及以下),tcpdump抓包,发现低版本在关闭的时候会主动向注册中心发送状态更新数据,而高版本不会
2)eureka注册中心进入self-preservation状态
此时,lease expiration enabled 为false
解决及结论
最终重启了eureka注册中心,让其走出self-preservation模式,主动靠心跳检测下线已关停服务。两个结论:
a. 重启注册中心会担心注册信息丢失和恢复速度,看到有文章说服务只是在启动的时候才会注册,那就需要重启所有服务造成较大影响,其实不然,每次心跳即可作为注册信息,此次得到验证
b. 服务关停时向注册中心主动发送状态更新消息是一种方式(受spring cloud版本影响),靠注册中心的心跳探活机制是另外一种机制