部分资料
引用自各路大神,具体见“99.参考资料”,
感谢各路大神的鼎力支持!!!
上一篇:Spring Cloud第01讲:Eureka服务注册中心
概念:当eureka client往eureka server注册时,提供自身的metadata(元数据),譬如 ip、port 等。
服务提供者必须设置为true,以把自身的服务注册到eureka server上。
相关配置:# 表示是否将自己注册到Eureka Server,默认为即为true。 # 具体配置参见spring-cloud-netflix-eureka-client.jar/org.springframework.cloud.netflix.eureka.EurekaClientConfigBean eureka.client.register-with-eureka=true
概念:eureka client每隔30秒发送一次心跳来续约,也就是心跳时间。通过续约来告诉eureka server,我这个eureka client还存在,没有任何问题。正常情况下,eureka server在90秒内没有收到eureka client的续约,如果eureka server关闭了自我保护机制,那么会将该实例instance从注册列表中干掉。
相关配置:# 具体配置参见spring-cloud-netflix-eureka-client.jar/org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean # 租约续订间隔(与eureka server的心跳时间,默认30秒) eureka.instance.lease-renewal-interval-in-seconds=30 # 租约到期时间(当eureka server在一定时间内没有收到实例的心跳,便会把该实例从注册表中删除[默认是90秒]) eureka.instance.lease-expiration-duration-in-seconds=90
概念:
eureka client 从 eureka server 上获取注册列表信息,并将其缓存在本地
。客户端将使用该信息查找其他服务,进行远程调用。该注册列表信息定期(默认30秒)更新一次
。每次返回的注册列表信息可能与eureka client的缓存信息不同,eureka client会自动处理。如果由于某种原因导致注册列表信息不能及时匹配,eureka client则会重新获取整个注册表信息。eureka server和eureka client之间可以使用json/xml格式进行通讯
。默认情况下,eureka client使用压缩json格式来获取注册列表的信息。
相关配置:# 表示是否从Eureka Server获取注册信息,默认为true。 具体配置参见spring-cloud-netflix-eureka-client.jar/org.springframework.cloud.netflix.eureka.EurekaClientConfigBean eureka.client.fetch-registry=true
概念:eureka client在程序关闭时向eureka server发送取消请求。发送请求后,该客户端实例信息将从服务器的实例注册列表中删除。该下线请求不会自动完成,可通过以下方式完成:
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.beans.factory.annotation.Autowired; import com.netflix.discovery.DiscoveryClient; @RestController public class Controller { @Autowired private DiscoveryClient discoveryClient; @RequestMapping("/eureka/client/shutdown", method = RequestMethod.GET) public String shutDown() { discoveryClient.shutdown(); return "eureka client shutdown!!!"; } }
概念:默认情况下,当eureka client连续90秒没有向eureka server发送服务续约(即,心跳)。eureka server在关闭
自我保护机制[参见-3.自我保护机制]
的情况下,会将该服务实例从服务注册列表中剔除。
相关配置:# 续约到期时间 # 具体配置参见spring-cloud-netflix-eureka-client.jar/org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean # 租约到期时间(当eureka server在一定时间内没有收到实例的心跳,便会把该实例从注册表中删除[默认是90秒]) eureka.instance.lease-expiration-duration-in-seconds=90
启动eureka server工程,参见
Spring Cloud第01讲:Eureka服务注册中心 的3.1章节。
【注意】
:Eureka Server首页请求的是一个controller,具体参见
spring-cloud-netflix-eureka-server-2.1.0.jar
包中的org.springframework.cloud.netflix.eureka.server.EurekaController
的status
,从@RequestMapping()可以看出默认请求路径就是 “/”。
- Environment:环境
- Data center:数据中心
- Current time:当前系统时间,默认格式"yyyy-MM-dd’T’HH:mm:ss Z"(参见eureka-core-1.9.8.jar/com.netflix.eureka.resources.StatusResource),小编通过拦截器对格式进行了修改,链接【Spring Cloud第01讲:Eureka服务注册中心-页面】
- Uptime:系统已经运行了多长时间
- Lease expiration enabled:租约到期是否启用(自我保护机制关闭时为true, 自我保护机制开启之后为false,默认关闭自我保护机制)
- Renews threshold:每分钟最少续约数,即心跳阈值,也就是eureka server期望client每分钟最少要给自己发送几次心跳。
心跳阈值 = 1 + 2*n(n为微服务eureka client注册到服务端的实例个数)
。- Renews (last min):最后一分钟收到的续约数量(1分钟更新一次),EurekaServer最后一分钟收到来自EurekaClient的心跳数量,
EurekaClient可通过配置 “租约续订间隔(心跳间隔,默认30秒) eureka.instance.lease-renewal-interval-in-seconds=15”来完成
。
【注意】
:具体实际操作参见[3.自我保护机制],第3章节会对部分参数进行说明。
DS Replicas:展示Eureka Server相邻节点,也就是一个集群,毕竟要支持高可用。图中显示的节点名称在yml中可配置,
# 实例主机名称 eureka.instance.hostname=eurekaServer1
【注意】
:实例主机名称需要我们在hosts文件中进行配置方可用。
- total-avail-memory : 总共可用的内存
- environment : 环境名称
- num-of-cpus : CPU的个数
- current-memory-usage : 当前已经使用内存的百分比
- server-uptime : 服务启动时间
- registered-replicas : 相邻集群复制节点,图中可以看出相邻集群的服务
- unavailable-replicas :不可用的集群复制节点
- available-replicas :可用的相邻集群复制节点
展示了注册到eureka server上的微服务实例信息。
- Application:应用名,各个微服务可在yml文件中配置[spring.application.name]
- Status:状态,可以看出当前微服务实例的个数、访问地址以及状态[UP或者DOWN]
【注意】
:这里显示的状态只是eureka client注册到eureka server上的状态,不是我微服务是否可用的状态。
当状态为UP时的含义就是说,客户端告诉服务端我是正常可用的,其他微服务可以通过你这个服务端来调用我。
当状态为DOWN时的含义就是说,客户端告诉服务器我要下线了,不要再把请求发给我了,但是微服务本身是正常的,只是别的服务消费者不可以再调用了。
Last 1000 newly registered leases:最近 1000 份新登记的租约。从中可以看出续约的微服务实例名称、ip和port等。
Last 1000 cancelled leases:最近 1000 份取消的租约
概念:
当Eureka Server在一定时间内没有收到微服务实例instance的心跳
(默认微服务实例心跳时间间隔是30秒,eureka.instance.lease-renewal-interval-in-seconds=30),就会把该实例从注册表中剔除(默认为90秒)
。but,如果eureka server丢失大量实例心跳,就会触发自我保护机制。但是,这样会有一个问题,就是在开发的过程中,往往需要频繁重启我们的业务微服务实例,然而我们一般不会把eureka server跟着一起重启。
我们可以在Eureka Server管理界面看到 Renews threshold[心跳阈值] 和 Renews(last min)[最后一分钟收到的续约数量],当最后一分钟收到的续约数量小于心跳阈值时,就会触发保护机制,出现下面的红色警告:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
【自我保护机制的目的】
:当发生网络分区故障时,微服务与EurekaServer之间无法正常通信。但是微服务本身是健康的
,所以不应该注销该微服务
。如果,eureka server因为网络故障把微服务删除了,那么即使网络恢复了,该微服务实例instance也不会注册到EurekaServer了,因为微服务只有在启动的时候才会发起注册请求,启动成功后只会发送心跳检测和服务实例列表的请求。这样,虽说该instance是正常运行着的,但永远也不会被其他服务所感知。所以,EurekaServer在短时间内丢失过多的客户端心跳时,就会进入自我保护,在该模式下,会保护注册列表中的信息,不再注销微服务,当网络故障恢复后,EurekaServer会自动退出保护模式。
【自我保护机制的时机】
:
- 当Renews(last min)[最后一分钟收到的心跳数]小于Renews threshold[心跳阈值]时,eureka server就进入了自我保护机制;
- 当心跳数重新恢复到阈值的时候,eureka server会自动退出保护机制。
Eureka Client A配置
# 租约续订间隔(与eureka server的心跳时间,默认30秒)
eureka.instance.lease-renewal-interval-in-seconds=15
从配置中可以看出心跳时间是30秒,也就是一分钟发送4次心跳到eureka server
。
# 自我保护机制是否开启
eureka.server.enable-self-preservation=true
当Renews(last min)[最后一分钟收到的心跳数]小于Renews threshold[心跳阈值]时,eureka server就进入了自我保护机制
,Instances currently registered with Eureka可以看出微服务实例还在的。# 自我保护机制是否开启
eureka.server.enable-self-preservation=false
THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.
提示,自动保存模式关闭。这可能无法在出现网络/其他问题时保护实例到期。RENEWALS ARE LESSER THAN THE THRESHOLD. THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.
提示,续约低于阈值。自动保存模式关闭。这可能无法在出现网络/其他问题时保护实例到期。【注意】
:开发环境我们就关闭自我保护机制,生产环境我们就开启自我保护机制
。通常我们通过Linux命令kill -9 pid来停止服务的。但是针对Spring Cloud服务直接关闭可能会造成各个微服务之间调用失败
。当然,一般情况下,生产环境必然是集群部署的,我们一台接一台的重启也是阔以的。但是,我们的接口必须支持幂等性(当然不管什么情况下,接口都是要支持幂等性的,幂等性简说就是针对同一事务多次执行结果都一样),服务消费者也要有自己的重试策略,譬如,通过feign声明式调用时可以通过配置ribbon客户端的软负载均衡器来实现重试策略。
这里我们只介绍通过SpringBoot Actuator监控endpoint(端点)的方式来实现优雅上下线,我们要用到的端点是“/service-registry
”。
针对我们的业务微服务做如下准备
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
# ######################################################################
# 【SpringBoot Actuator监控中心配置】 #
# ######################################################################
# spring boot2.0后,默认的监控endpoint(端点)加了上下文路径/actuator[即spring-boot-actuator-autoconfigure.jar/org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties类basePath属性],可通过下面的进行修改
management.endpoints.web.base-path=/shactuator
# spring boot2.0之后默认只开启了info和health(spring-boot-actuator-autoconfigure.jar/META-INF/spring-configuration-metadata.json中进行了配置)
# 这里我们开启所有端点,允许监控所有接口
management.endpoints.web.exposure.include=*
# 显示 health 端点的详细信息
management.endpoint.health.show-details=always
【注意】:具体的API接口可以参考 Spring Cloud第01讲:Eureka服务注册中心
这里省略API接口、Feign客户端实现、服务降级等代码,具体参考 Spring Cloud第01讲:Eureka服务注册中心
为了方便测试,我们引入HTTPClient,我们在服务消费者中使用代码的方式循环调用服务提供者,来看看日志打印调用的服务提供者的ip。
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>${httpclient.version}version>
<exclusions>
<exclusion>
<artifactId>commons-loggingartifactId>
<groupId>commons-logginggroupId>
exclusion>
exclusions>
dependency>
package com.tong.hao.fw.service;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
/**
* @ClassName TestHttpClient
* @Author 友野浩二
* @Description http测试
* @Version 1.0
*/
@Slf4j
public class TestHttpClient {
public static void main(String[] args) throws IOException {
// 创建默认的httpclient
CloseableHttpClient httpClient = HttpClients.createDefault();
// 调用6次服务并输出结果
for (int i = 0; i < 3; i++) {
// 调用 GET 方法请求服务
HttpGet httpget = new HttpGet("http://127.0.0.1:9113/tong-fw/test/info");
// 获取响应
HttpResponse response = httpClient.execute(httpget);
// 根据 响应解析出字符串
log.info(EntityUtils.toString(response.getEntity()));
}
}
}
第一次请求
20:43:44.051 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection [id: 0][route: {}->http://127.0.0.1:9113] can be kept alive indefinitely
20:43:44.051 [main] DEBUG org.apache.http.impl.conn.DefaultManagedHttpClientConnection - http-outgoing-0: set socket timeout to 0
20:43:44.052 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection released: [id: 0][route: {}->http://127.0.0.1:9113][total kept alive: 1; route allocated: 1 of 2; total allocated: 1 of 20]
20:43:44.052 [main] INFO com.tong.hao.fw.service.TestHttpClient - http://192.168.1.6:9103/tong-ac/test/hehe, fw测试
第二次请求
20:43:44.096 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection [id: 0][route: {}->http://127.0.0.1:9113] can be kept alive indefinitely
20:43:44.096 [main] DEBUG org.apache.http.impl.conn.DefaultManagedHttpClientConnection - http-outgoing-0: set socket timeout to 0
20:43:44.096 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection released: [id: 0][route: {}->http://127.0.0.1:9113][total kept alive: 1; route allocated: 1 of 2; total allocated: 1 of 20]
20:43:44.096 [main] INFO com.tong.hao.fw.service.TestHttpClient - http://192.168.1.6:9102/tong-ac/test/hehe, fw测试
第三次请求
20:43:44.164 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection [id: 0][route: {}->http://127.0.0.1:9113] can be kept alive indefinitely
20:43:44.164 [main] DEBUG org.apache.http.impl.conn.DefaultManagedHttpClientConnection - http-outgoing-0: set socket timeout to 0
20:43:44.164 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection released: [id: 0][route: {}->http://127.0.0.1:9113][total kept alive: 1; route allocated: 1 of 2; total allocated: 1 of 20]
20:43:44.164 [main] INFO com.tong.hao.fw.service.TestHttpClient - http://192.168.1.6:9103/tong-ac/test/hehe, fw测试
从请求日志我们可以看出在3次调用中,服务消费者有2次调用了服务提供者端口为9103上的服务,有1次调用端口为9102提供的服务。
/service-registry
POST http://127.0.0.1:9102/tong-ac/shactuator/service-registry
{
"status": "DOWN"
}
查看eureka server管理界面,可以看到服务提供者9102的服务已经下线。这里需要注意的是 该应用只是在Eureka Server上的状已被标记为DOWN ,但是应用本身其实依然是可以正常对外服务的(我们可以直接POST http://127.0.0.1:9102/tong-ac/test/hehe来验证是否可用,小编验证可用)
。
此时,我们再来通过TestHttpClient来测试一下,小编测试了5次,每次测试的3次请求中在日志中可以看到都是调用的服务提供者的9103上的服务。
- Eureka控制台相关介绍及自我保护机制解说
- eureka 心跳机制 源码解析
- Eureka自我保护机制
- Eureka 优雅下线,上线
歇会儿,哈哈哈,各位大佬勿喷,有点小事要走开。么么哒(*  ̄3)(ε ̄ *)