环境准备

一个订单服务,两个物流服务,订单通过restTemplate调用物流服务,物流服务简单的返回当前服务的IP和端口。

测试下线方式

注册中心里的服务列表
在这里插入图片描述

请求返回信息
在这里插入图片描述

1、idea界面通过命令窗口直接ctrl+c

注册中心可以快速感知下线,但是实际访问时任然能访问到已经下线的服务。

Completed shut down of DiscoveryClient表明注册中心已完成下线。
在这里插入图片描述
订单服务依旧会访问到已经下线的服务,导致服务报错。
在这里插入图片描述

产生这个现象的主要原因是因为订单服务本地缓存了一份物流服务的列表,所以即使注册中心能够及时发现服务下线,但客户端却因为是从自己的本地缓存中拉取的服务列表,而不能及时发现已经下线的服务。

解决方式:

修改中个参数的值,默认为30秒

/**
	 * Indicates how often(in seconds) to fetch the registry information from the eureka
	 * server.
	 * 表示从eureka服务器获取注册表信息的频率(秒)
	 */
	private int registryFetchIntervalSeconds = 30;

2、使用delete方式

delete请求
http://注册中心IP:端口/eureka/apps/注册中心应用名/服务IP:端口
在这里插入图片描述
下线一台服务
在这里插入图片描述
这种方式大家也可以自己试验,发送delete请求后,注册中心服务列表立刻就下线了,但是由于客户端续约心跳的原因,如果不真正把服务停掉,服务端会因为存在心跳再次上线服务。

通过后台日志可以看出,服务再次被注册了
在这里插入图片描述

服务续约心跳,默认是30秒一次。

/**
	 * Indicates how often (in seconds) the eureka client needs to send heartbeats to
	 * eureka server to indicate that it is still alive. If the heartbeats are not
	 * received for the period specified in leaseExpirationDurationInSeconds, eureka
	 * server will remove the instance from its view, there by disallowing traffic to this
	 * instance.
	 *
	 * Note that the instance could still not take traffic if it implements
	 * HealthCheckCallback and then decides to make itself unavailable.
	 */
	private int leaseRenewalIntervalInSeconds = 30;

假设不考虑各服务的本地缓存服务列表的存在,那么在服务续约的时间内调用被delete的服务是请求不到的, 但是如果超过服务续约的时间,被delete的服务任然没有被真正的关闭,那么这个服务就会通过心跳又被注册中心上线,其他服务也就又可以从注册中心拉取到delete的服务列表。

3、使用OUT_OF_SERVICE、DOWN方式

put请求:

http://注册中心IP:端口/eureka/apps/注册中心应用名/服务IP:端口/status?value=OUT_OF_SERVICE

http://注册中心IP:端口/eureka/apps/注册中心应用名/服务IP:端口/status?value=DOWN

OUT_OF_SERVICE和DOWN都可以,两种方式实现的效率一样,我也不清楚具体有什么区别,个人觉得DOWN可能比OUT_OF_SERVICE看起来更加严重,可能会记录更多的一些日志等信息。
在这里插入图片描述

这种方式注册中心也是可以立刻感知服务下线的。

后台日志输出
在这里插入图片描述
注册中心列表
在这里插入图片描述
虽然注册中心能立刻感知到状态的变化了,但是同样存在由于订单服务本地缓存了物流服务地址列表的原因,导致在一定时间内订单服务依旧可以访问到被OUT_OF_SERVICE的物流服务,但是与第一种方式不同的地方在于,第一种方式由于服务停止了,所以访问到会报错,这种方式服务并没有真正的停止,所以虽然可以访问到但也不会报错。

4、调用客户端下线接口

同样注册中心可以立刻感知,与第三种方式一样存在本地缓存的问题,在本地缓存更新前也可以访问到,但是也不会报错,因为服务并没有真正停止,只是通知注册中心下线了而已。

    @RequestMapping("/offline")
    public void offline(){
        DiscoveryManager.getInstance().shutdownComponent();
    }

在这里插入图片描述

5、异常下线

比如直接点击IDEA的STOP来关闭物流服务。
在这里插入图片描述
异常下线的方式是最不安全的,注册中心和客户端都不能立刻感知,并且会导致服务访问报错。

订单服务真正能感知到物流服务下线的时间要考虑下面3个参数配置:

客户端续约到期时间,默认90秒
eureka.instance.lease-expiration-duration-in-seconds

服务端定时检查时间,默认60秒
eureka.server.eviction-interval-timer-in-ms

客户端从服务端拉取服务列表的间隔时间,默认30秒
eureka.client.registryFetchIntervalSeconds

由于是异常下线,所以服务端只能通过心跳检测的方式来发现客户端下线,而当服务端发现客户端下线后就会更新服务列表,客户端又要等到下一次从服务端拉取服务列表之后才能真正的发现目标服务下线了。

总结:
由于erueka在CAP中选择的是AP模式,所以可以看出无论使用上述哪一种下线方式,客户端都不能及时的发现,所以客户端一定要做好服务重试、熔断等措施进行保护。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐