微服务注册中心如何承载大型系统的千万级访问

EurekaServer通过设置30秒的拉取和心跳频率,有效控制大规模系统对注册中心的请求压力。利用内存中的ConcurrentHashMap作为注册表,保证高效性能。同时采用多级缓存机制,包括ReadOnlyCacheMap和ReadWriteCacheMap,减少并发冲突并加速响应。在面临每秒数百次请求及每天千万级访问量时,EurekaServer通过内存存储和缓存策略轻松应对。
摘要由CSDN通过智能技术生成

问题起源

1 Eureka Server设计精妙的注册表存储结构
2 Eureka Server端优秀的多级缓存机制

常见问题

1 Eureka Server到底要部署几台机器?

2 我们的系统那么多服务,到底会对Eureka Server产生多大的访问压力?

3 Eureka Server能不能抗住一个大型系统的访问压力?

4 Eureka注册中心使用什么样的方式来储存各个服务注册时发送过来的机器地址和端口号?

5 各个服务找Eureka Server拉取注册表的时候,是什么样的频率?

6 各个服务是如何拉取注册表的?

7 一个几百服务,部署上千台机器的大型分布式系统,会对Eureka Server造成多大的访问压力?

8 Eureka Server从技术层面是如何抗住日千万级访问量的?

问题解答

Eureka 过程
在这里插入图片描述
Eureka Server 设计精妙的注册表存储结构
现在咱们假设手头有一套大型的分布式系统,一共100个服务,每个服务部署在20台机器上,机器是4核8G的标准配置。

也就是说,相当于你一共部署了100 * 20 = 2000个服务实例,有2000台机器。

每台机器上的服务实例内部都有一个Eureka Client组件,它会每隔30秒请求一次Eureka Server,拉取变化的注册表。

此外,每个服务实例上的Eureka Client都会每隔30秒发送一次心跳请求给Eureka Server。

那么大家算算,Eureka Server作为一个微服务注册中心,每秒钟要被请求多少次?一天要被请求多少次?

按标准的算法,每个服务实例每分钟请求2次拉取注册表,每分钟请求2次发送心跳
这样一个服务实例每分钟会请求4次,2000个服务实例每分钟请求8000次
换算到每秒,则是8000 / 60 = 133次左右,我们就大概估算为Eureka Server每秒会被请求150次
那一天的话,就是8000 * 60 * 24 = 1152万,也就是每天千万级访问量

首先,对于微服务注册中心这种组件,在一开始设计它的拉取频率以及心跳发送频率时,就已经考虑到了一个大型系统的各个服务请求时的压力,每秒会承载多大的请求量。

所以各服务实例每隔30秒发起请求拉取变化的注册表,以及每隔30秒发送心跳给Eureka Server,其实这个时间安排是有其用意的

即使算上其他一些额外操作,我们姑且就算每秒钟请求Eureka Server在200次~300次吧。

所以通过设置一个适当的拉取注册表以及发送心跳的频率,可以保证大规模系统里对Eureka Server的请求压力不会太大

Eureka Server是如何保证轻松抗住这每秒数百次请求,每天千万级请求的呢
在这里插入图片描述
图中的这个名字叫做registry的CocurrentHashMap,就是注册表的核心结构。看完之后忍不住先赞叹一下,精妙的设计!

从代码中可以看到,Eureka Server的注册表直接基于纯内存,即在内存里维护了一个数据结构。

各个服务的注册、服务下线、服务故障,全部会在内存里维护和更新这个注册表。

各个服务每隔30秒拉取注册表的时候,Eureka Server就是直接提供内存里存储的有变化的注册表数据给他们就可以了。

同样,每隔30秒发起心跳时,也是在这个纯内存的Map数据结构里更新心跳时间。

一句话概括:维护注册表、拉取注册表、更新心跳时间,全部发生在内存里!这是Eureka Server非常核心的一个点

  1. 首先,这个ConcurrentHashMap的key就是服务名称,比如“inventory-service”,就是一个服务名称。
  2. value则代表了一个服务的多个服务实例。
  3. 举例:比如“inventory-service”是可以有3个服务实例的,每个服务实例部署在一台机器上。

再来看看作为 value的 这个Map: Map<String, Lease<InstanceInfo****>>

  1. 这个Map的key就是服务实例的id
  2. value是一个叫做Lease的类,它的泛型是一个叫做InstanceInfo的东东,你可能会问,这俩又是什么鬼?
  3. 首先说下InstanceInfo,其实啊,我们见名知义,这个InstanceInfo就代表了服务实例的具体信息,比如机器的ip地址、hostname以及端口号
  4. 而这个Lease,里面则会维护每个服务最近一次发送心跳的时间

Eureka Server端优秀的多级缓存机制

假设Eureka Server部署在4核8G的普通机器上,那么基于内存来承载各个服务的请求,每秒钟最多可以处理多少请求呢?

根据之前的测试,单台4核8G的机器,处理纯内存操作,哪怕加上一些网络的开销,每秒处理几百请求也是轻松加愉快的。

而且Eureka Server为了避免同时读写内存数据结构造成的并发冲突问题,还采用了多级缓存机制来进一步提升服务请求的响应速度

在拉取注册表的时候:
	1 首先从ReadOnlyCacheMap里查缓存的注册表。
	2 若没有,就找ReadWriteCacheMap里缓存的注册表。
	3 如果还没有,就从内存中获取实际的注册表数据。
 在注册表发生变更的时候:
	1会在内存中更新变更的注册表数据,同时过期掉ReadWriteCacheMap2此过程不会影响ReadOnlyCacheMap提供人家查询注册表。
	3一段时间内(默认30秒),各服务拉取注册表会直接读ReadOnlyCacheMap
	4 30秒过后,Eureka Server的后台线程发现ReadWriteCacheMap已经清空了,也会清空ReadOnlyCacheMap中的缓存
	下次有服务拉取注册表,又会从内存中获取最新的数据了,同时填充各个缓存。

多级缓存机制的有优点是什么?

  1. 尽可能保证了内存注册表数据不会出现频繁的读写冲突问题。
  2. 并且进一步保证对Eureka Server的大量请求,都是快速从纯内存走,性能极高。

在这里插入图片描述

总结

  1. Eureka 通过设置 适当的请求频率(拉取注册表30s间隔,发送心跳30s间隔),可以保证一个大规模的系统每秒请求Server1的次数在几百次
  2. 同时通过存内存的注册表,保证了所有的请求都可以在内存处理,确保了极高的性能
  3. 多级缓存机制,确保了不会针对内存数据结构发生频繁的读写并发冲突操作,进一步提升性能

题外话

每秒上万并发下的Spring Cloud参数优化实战

扬汤止沸,饮鸩止渴
在这里插入图片描述

所以设置超时一般设置两个地方,feign和ribbon那块的超时,还有hystrix那块的超时。其中后者那块的超时一般必须大于前者

问题爆发,洪水猛兽
在这个过程中,先是紧张的各种扩容服务,一台变两台,两台变四台。

然后数据库主从架构挂上去,读写分离是必须的,否则单个数据库服务器哪能承载那么大的请求!多搞几个从库,扛一下大量的读请求,这样基本就扛住了。

追本溯源,治标治本
第一步:
关键点,优化图中核心服务B的性能。互联网公司,核心业务逻辑,面向C端用户高并发的请求,不要用上百行的大SQL,多表关联,那样单表几百万行数据量的话,会导致一下执行好几秒。

其实最佳的方式,就是对数据库就执行简单的单表查询和更新,然后复杂的业务逻辑全部放在java系统中来执行,比如一些关联,或者是计算之类的工作。

第二步:
那个超时的时间,也就是上面那段ribbon和hystrix的超时时间设置。

奉劝各位同学,不要因为系统接口的性能过差而懒惰,搞成几秒甚至几十秒的超时,一般****超时定义在1秒以内,是比较通用以及合理的

因为一个接口,理论上 最佳响应速度应该在200ms以内,或者慢点的接口就是几百毫秒
如果一个接口响应时间达到1s以上,建议考虑用缓存,索引,NoSql 等各种你能想到的技术收到,优化一下性能。
否则你要是胡乱设置超时时间是几秒,甚至几十秒,万一下游服务偶然出了点问题响应时间长了点呢?那你这个线程池里的线程立马全部卡死!

第三步:
如果你要是超时时间设置成了1秒,如果就是因为偶然发生的网络抖动,导致接口某次调用就是在1.5秒呢?这个是经常发生的,因为网络的问题,接口调用偶然超时。

所以此时配合着超时时间,一般都会设置一个合理的重试,如下所示:

在这里插入图片描述
设置这段重试之后,Spring Cloud中的Feign + Ribbon的组合,在进行服务调用的时候,如果发现某台机器超时请求失败,会自动重试这台机器,如果还是不行会换另外一台机器重试。

第四步:
你的系统架构中,只要涉及到了重试,那么必须上接口的幂等性保障机制

否则的话,试想一下,你要是对一个接口重试了好几次,结果人家重复插入了多条数据,该怎么办呢?

其实幂等性保证本身并不复杂,根据业务来,常见的方案:

1 可以在数据库里建一个唯一索引,插入数据的时候如果唯一索引冲突了就不会插入重复数据
2 或者是通过redis里放一个唯一id值,然后每次要插入数据,都通过redis判断一下,那个值如果已经存在了,那么就不要插入重复数据了。
类似这样的方案还有一些。总之,要****保证一个接口被多次调用的时候,不能插入重复的数据。

总结:
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值