接触SpringCloud后由于好奇做了一个测试,测试内容如下:
1.新建一个服务消费者A注册进Eureka;
其中A中有一个任务一直在循环调用服务提供者B的一个接口,但是这时候服务B未启动,因此服务消费者A未能调用到指定借口,一直调用失败;
2.启动服务提供者B;
从启动服务B开始计算服务A调用服务B成功的时间。
以下是测试的不同springboot版本(对应不同的springcloud版本)下需要的时间,每种版本测试了20次。
微信截图_20200413114606.png
时间区间是在0-90s之间,关于这个的原因是因为springcloud在设计时候,很多地方用了缓存。
A服务调用B服务,底层通过ribbon实现(feign底层也是基于ribbon)。
ribbon组件其实自己维护了一个当前可调用的服务实例列表(暂叫ribbon instance map),
这个列表是通过定时任务(30s)从服务调用者A获取的;
A也有一个缓存列表(A instance map),这个列表也是通过定时任务(30s)从注册中心获取;
注册中心也有一个缓存列表(源码中对应ReadOnlyMap),这个列表是通过定时任务(30s)从注册中心中的ReadWriteMap中获取。
ReadWriteMap-->ReadOnlyMap-->A instance map-->ribbon instance map,所以当新启动一个服务时候,理论最长时间是30*3s,ribbon本地的服务实例列表才能保证是最新的。
为什么要这么设计?
注册中心:注册中心维护的两个ReadWriteMap和ReadOnlyMap,服务从注册中心获取可用服务列表是从ReadOnlyMap获取的,ReadWriteMap是通过服务心跳、服务注册、服务下线等动作维护最新的可用服务列表,ReadOnlyMap再按定时任务从ReadWriteMap中复制实例列表,这样可以减少读写锁占用,增大并发量。
A服务:从注册中心的ReadOnlyMap获取服务实例列表到A服务本地。
ribbon: 从A服务A instance map中获取服务实例列表到ribbon组件本地。
这样ribbon在调用服务时候会直接根据调用的服务名称从自己本地列表获取对应服务的信息(如根据负载均衡策略,负载到的服务的真实ip),不需要再从注册中心去获取,减少了注册中心的负载。
反过来思考,既然服务发现需要一定的时间,那么服务下线时,ribbon本地也是有延迟的。注册中心的ReadWriteMap中可能B服务已经下线,但是也需要理论最大时间30*3s才能发现。
以上总结其实不难发现,springcloud在设计时是偏向CAP原理中的AP。
以上纯属个人理解,如有错误,欢迎指正。
网友评论