Spring Cloud菜鸟学习笔记

最近接到了一个新的项目,将上一次上线失败的网关重新上线。我们使用的网关系统是基于SpringCloud+Zuul搭建的,在上一次上线后出现了异常,切换回了原先的nginx。但是Zuul网关集成了熔断和限流的功能,相对于nginx来说更符合业务的需要,于是我们计划重新评估项目并计划再次上线。

请注意:熊熊对SpringCloud的了解有限,本文仅作为个人学习笔记,不保证内容的可靠性,请谨慎“食用”。

任务目标

  1. 重新评估项目
  2. 测试重新配置后的网关系统
  3. 制定灰度上线和验证、监控方案

服务注册与发现 - Eureka

Eureka Server自我保护机制

Eureka自我保护模式警告

上图中红字的释义是:警告!Eureka可能正在错误地维护包含实际已断开实例的实例列表。Renews值小于Threshold值,因此当前实例列表不执行过期操作以保证安全。
在本地实验的过程中,偶然发现服务端页面显示了上述红字,原因分析如下:

  1. Eureka Server在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%,如果出现低于的情况(在单机调试的时候很容易满足,实际在生产环境上通常是由于网络不稳定导致),Eureka Server会将当前的实例注册信息保护起来,同时提示这个警告。
  2. 进入自我保护机制后,server不会删除注册信息,这就有可能导致在调用微服务时,实际上服务并不存在。
  3. 这种保护状态实际上是考虑了client和server之间的心跳是因为网络问题,而非服务本身问题,不能简单的删除注册信息

相关配置:

  1. eureka.server.enable-self-preservation:自我保护机制的开关,设为false即为不再自我保护
  2. eureka.server.renewal-percent-threshold:renews和renews threshold的比值,低于就启动自我保护机制,默认值为0.85

参考文章:

  1. SpringCloud警告(Eureka)

Eureka Server高可用hostname不能重复

本地搭建eureka server高可用集群时,为了配置容易,使用了localhost,发现页面上显示集群其他分片都处于unavailable的状态。
经研究后判断可能是因为将hostname作为分片的标识符,导致无法认出其他分片,在使用hosts建立其他hostname后集群生效。
查阅官方文档后发现,除了使用hostname,也可以使用ip地址作为分片区分的标识。

网关 - Zuul

熔断降级配置

  • circuitBreaker.requestVolumeThreshold // 滑动窗口的大小,超过该次数则熔断,默认为20
  • circuitBreaker.sleepWindowInMilliseconds // 熔断器恢复时间,默认为5000ms
  • circuitBreaker.errorThresholdPercentage // 错误率,错误次数/请求次数超过该比值测熔断,默认50%

参考文章:

  1. Hystrix使用说明,配置参数说明

分布式追踪系统 - Zipkin

从什么角度可以查询到完整链路

在整个系统中共有三个角度,分别是对外的网关、内部使用的消费者以及生产者,在试用Zipkin的过程中,我发现并不是所有的角度都可以查询到完整链路。
理想状况下,从任何角度(此处为Service Name)查询都可以查询到完整的链路,但实际上结果如下:

  • Gateway:可以查询到完整链路,即Gateway+Producer
  • Consumer:无法查询到完整链路,只能查询到Consumer本身的情况,理想情况下应该可以查询到Consumer+Producer
  • Producer:可以查询到完整链路,即Gateway+Producer以及Consumer+Producer

这个现象让我感觉很迷惑,为什么Consumer角度无法查询到完整链路呢?
因为在Span Name中出现了名为rxjava的选项排在前面,导致无法看到Consumer+Producer的请求,在筛选后发现时可以正确查到的。
至于为什么会有rxjava排在前面,就需要进一步了解了。

Spring Cloud集群启用安全配置

1
management.security.enabled=false

网上很多教程都提到需要将安全关闭,在测试后发现,如果启用安全配置,将导致非常多的问题:

  1. 客户端如果启用安全,那么将导致无法访问监控需要的endpoint
  2. 对于eureka服务端来说,如果启用了安全配置,那么eureka客户端将无法注册,将地址修改为http://${security.user.name}:${security.user.password}localhost:8761/eureka/可以解决无法注册的问题,但会导致第3点的问题
  3. 打开spring boot admin页面,几乎所有页面都在反复弹出basic auth认证的页面,无法关闭,猜测是因为客户端信息需要basic auth,加载时需要认证
    在一顿操作后居然又可以在开启安全配置的情况下正常使用了,不再反复弹窗,初步判断可能是因为eureka集群之间未正确配置basic验证导致的

于是,使用eureka注册中心正确安全配置的方法如下:

  • 在Spring Boot Admin服务端和客户端都需要在eureka地址中添加basic auth用户名和密码,同时暴露自身的basic auth用户名和密码给注册中心,如下:
1
2
3
4
5
6
7
8
eureka:
client:
service-url:
defaultZone: http://admin:admin@peer1:8802/eureka/
instance:
metadata-map:
user.name: admin
user.password: admin
  • 此外,尤其需要注意的是,eureka服务端如果是集群配置,那么每一个服务端地址都需要填写正确
1
2
3
4
eureka:
client:
service-url:
defaultZone: http://admin:admin@peer2:8802/eureka/,http://admin:admin@peer3:8803/eureka/

发现的问题

Eureka Server集群并未生效

当前eureka-server各节点之间通过域名互相注册,现象为:

  1. eureka-server页面上显示分片不可用
  2. 注册到其中一个eureka-server节点后,可能需要较长时间才能同步至另一个eureka-server节点上,正常情况下应快速同步

通过阅读eureka源码找到以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
com.netflix.eureka.cluster.PeerEurekaNode:209
...
@Override
public void handleFailure(int statusCode, Object responseEntity) throws Throwable {
super.handleFailure(statusCode, responseEntity);
if (statusCode == 404) {
logger.warn("{}: missing entry.", getTaskName());
if (info != null) {
logger.warn("{}: cannot find instance id {} and hence replicating the instance with status {}",
getTaskName(), info.getId(), info.getStatus());
register(info);
}
} else if (config.shouldSyncWhenTimestampDiffers()) {
InstanceInfo peerInstanceInfo = (InstanceInfo) responseEntity;
if (peerInstanceInfo != null) {
syncInstancesIfTimestampDiffers(appName, id, info, peerInstanceInfo);
}
}
}
...

上述代码可以解释,写域名时,复制心跳,发现404,则走注册流程,所以也就可以解释为什么一段时间后,被注册上了。

1
2
3
2020-06-03 08:51:01.938  WARN 10946 --- [TaskBatchingWorker-target_guba-eureka-test.em-19] c.n.eureka.cluster.ReplicationTask       : The replication of task ZUUL-PERFORMANCE-TESTING/10.205.201.67:zuul-performance-testing:33333:Heartbeat@guba-eureka-test.em failed with response code 404
2020-06-03 08:51:01.938 WARN 10946 --- [TaskBatchingWorker-target_guba-eureka-test.em-19] c.netflix.eureka.cluster.PeerEurekaNode : ZUUL-PERFORMANCE-TESTING/10.205.201.67:zuul-performance-testing:33333:Heartbeat@guba-eureka-test.em: missing entry.
2020-06-03 08:51:01.938 WARN 10946 --- [TaskBatchingWorker-target_guba-eureka-test.em-19] c.netflix.eureka.cluster.PeerEurekaNode : ZUUL-PERFORMANCE-TESTING/10.205.201.67:zuul-performance-testing:33333:Heartbeat@guba-eureka-test.em: cannot find instance id 10.205.201.67:zuul-performance-testing:33333 and hence replicating the instance with status UP

上述日志在日志文件中反复出现,是因为该服务在复制的过程中反复出现问题导致,这在eureka官方文档中也有提到High Availability, Zones and Regions
所以现在修改为通过ip进行集群的连接,即将配置中的eureka地址修改为类似如下格式:

1
2
3
4
eureka:
client:
service-url:
defaultZone: http://1.1.1.1:8761/eureka/,http://2.2.2.2:8761/eureka/

在修改为以上配置后,复制功能正常,日志不再反复出现WARN级别报错。

客户端中可能存在通过ip而非域名连接注册中心的情况

这可能会导致所连接注册节点故障而导致整个服务不可用的情况
建议:
检查所有服务的连接配置,将地址全部修改为通过域名连接

可能存在请求时间超过5秒的接口

现在网关只有全局超时配置(execution.isolation.thread.timeoutInMilliseconds),设置为5s,但是据了解可能有请求时间超过5s的接口,如访问MongoDB的接口,这将导致该服务全不可用
建议:
验证所有接口的请求时间,后续应可针对单个服务单独配置超时时间

Eureka Server自我保护机制被关闭

这可能会在出现网络问题时,服务端未能正确接收客户端的心跳,导致该客户端被注册中心注销,而很有可能该微服务是正常工作的。详细分析见Eureka Server自我保护机制
建议:
是否需要考虑网络环境对注册中心的影响,需要结合线上实际情况判断是否需要开启或调整自我保护机制的相关配置

Spring Cloud集群自监控

当前我们通过zabbix对java服务进行进行监控,报警方面都可以正常使用,但是信息可能仅仅是定位到机器层面,无法定位到实际的服务,对整个集群的健康性掌握也不够全面。下图中展示了当前报警的信息:

Zabbix报警界面

建议:
已经搭建了一套SpringCloud的常用监控工具——Spring Boot Admin,可以对整个集群进行监控,同时,该工具中集成了turbine和hystrix dashboard,可以更方便地对实时流量进行监控。后续可以配合zabbix报警一起使用,对整个集群的健康状况有更清楚的了解。

重新上线前需要考虑的问题

是否需要限流功能

当前并没有限流的功能,是否需要添加?
Zuul中可以集成RateLimiter+Redis达到集群服务限流的功能。但是由于限流配置会对线上服务的正常工作产生较大影响,可以在网关上线稳定后,测算出各个服务的合理流量再添加限流功能。

是否需要压测网关本身的承压极限

在网关系统完成配置更新、扩容后,除了常规的测试外,是否需要进行一次极限的压测,测试网关本身的承压能力?
扩容后,我们将部署20台Zuul服务,但是常规测试可能是针对单个服务进行测试,整个网关集群的承压能力如何可能还不太清楚。

坚持原创!欢迎各位客官给我打赏买🍪小饼干吖!✿✿ヽ(°▽°)ノ✿