Spring Cloud核心组件Eureka

Eureka 是微服务架构中的注册中⼼,专⻔负责服 务的注册与发现。

 如上图所示,库存服务、仓储服务、积分服务中都有⼀个 Eureka Client 组件,这个组件专⻔负 责将这个服务的信息注册到 Eureka Server 中。说⽩了,就是告诉 Eureka Server,⾃⼰在哪台机 器上,监听着哪个端⼝。⽽ Eureka Server 是⼀个注册中⼼,⾥⾯有⼀个注册表,保存了各服务 所在的机器和端⼝号;

订单服务⾥也有⼀个 Eureka Client 组件,这个 Eureka Client 组件会找 Eureka Server 问⼀下: 库存服务在哪台机器啊?监听着哪个端⼝啊?仓储服务呢?积分服务呢?然后就可以把这些相 关信息从 Eureka Server 的注册表中拉取到⾃⼰本地缓存起来。

这时如果订单服务想要调⽤库存服务,不就可以找⾃⼰本地的 Eureka Client 问⼀下库存服务在 哪台机器?监听哪个端⼝吗?收到响应后,紧接着就可以发送⼀个请求过去,调⽤库存服务扣 减库存的那个接⼝!同理,如果订单服务要调⽤仓储服务、积分服务,也是如法炮制;

总结⼀下:

Eureka Client:负责将这个服务的信息注册到

Eureka Server 中 Eureka Server:注册中⼼,⾥⾯有⼀个注册表,保存了各个服务所在的机器和端⼝号

Eureka 作为微服务注册中⼼的核⼼原理

Eureka 注册中⼼使⽤什么样的⽅式来储存各个服务注册时发送过来的机器地址和端⼝号?

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

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

⼀个⼏百服务,部署上千台机器的⼤型分布式系统,会对 Eureka Server 造成多⼤的访问压

Eureka Server 从技术层⾯是如何抗住⽇千万级访问量的?

先给⼤家说⼀个基本的知识点,各个服务内的 Eureka Client 组件,默认情况下,每隔 30 秒会

发送⼀个请求到 Eureka Server,来拉取最近有变化的服务信息

举个列子

库存服务原本部署在 1 台机器上,现在扩容了,部署到了 3 台机器,并且均注册到了 Eureka Server 上。

然后订单服务的 Eureka Client 会每隔 30 秒去找 Eureka Server 拉取最近注册表的变化,看 看其他服务的地址有没有变化。

除此之外,Eureka 还有⼀个⼼跳机制,各个 Eureka Client 每隔 30 秒会发送⼀次⼼跳到 Eureka Server,通知⼈家说,哥们,我这个服务实例还活着! 如果某个 Eureka Client 很⻓时间没有发送⼼跳给 Eureka Server,那么就说明这个服务实例已经挂了。 光看上⾯的⽂字,⼤家可能没什么印象。⽼规矩!咱们还是来⼀张图,⼀起来直观的感受⼀下 这个过程。 

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, ⽇请求量在千万级,每秒的访问量在 150 次左右。 即使算上其他⼀些额外操作,我们姑且就算每秒钟请求 Eureka Server 在 200 次~300 次吧。 所以通过设置⼀个适当的拉取注册表以及发送⼼跳的频率,可以保证⼤规模系统⾥对 Eureka Server 的请求压⼒不会太⼤。

关键问题来了,Eureka Server 是如何保证轻松抗住这每秒数百次请求,每天千万级请求的呢? 要搞清楚这个,⾸先得清楚 Eureka Server 到底是⽤什么来存储注册表的?三个字,看源码 接下来咱们就⼀起进⼊ Eureka 源码⾥⼀探究竟:

**
 * Handles all registry requests from eureka clients.
 *
 * <p>
 * Primary operations that are performed are the
 * <em>Registers</em>, <em>Renewals</em>, <em>Cancels</em>, <em>Expirations</em>, and <em>Status Changes</em>. The
 * registry also stores only the delta operations
 * </p>
 *
 * @author Karthik Ranganathan
 *
 */
public abstract class AbstractInstanceRegistry implements InstanceRegistry {
    private static final Logger logger = LoggerFactory.getLogger(AbstractInstanceRegistry.class);

    private static final String[] EMPTY_STR_ARRAY = new String[0];
    private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
            = new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();
    //下面还有很多代码
}

如上图所示,图中的这个名字叫做 registry 的 CocurrentHashMap,就是注册表的核⼼结构。看完之后忍不住先赞叹⼀下,精妙的设计! 从代码中可以看到,Eureka Server 的注册表直接基于纯内存,即在内存⾥维护了⼀个数据 结构。 各个服务的注册、服务下线、服务故障,全部会在内存⾥维护和更新这个注册表。 各个服务每隔 30 秒拉取注册表的时候,Eureka Server 就是直接提供内存⾥存储的有变化的 注册表数据给他们就可以了。 同样,每隔 30 秒发起⼼跳时,也是在这个纯内存的 Map 数据结构⾥更新⼼跳时间。

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

搞清楚了这个,咱们再来分析⼀下 registry 这个东⻄的数据结构,⼤家千万别被它复杂的外表 唬住了,沉下⼼来,⼀层层的分析!

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

再来看看作为value的这个Map

Map<String, Lease<InstanceInfo>>

这个Map的key就是服务实例的id, value 是⼀个叫做 Lease 的类,它的泛型是⼀个叫做 InstanceInfo 的东东,你可能会问,这俩⼜是什么⻤?

⾸先说下 InstanceInfo,其实啊,我们⻅名知义,这个 InstanceInfo 就代表了服务实例的具 体信息,⽐如机器的 ip 地址、hostname 以及端⼝号。

⽽这个 Lease,⾥⾯则会维护每个服务最近⼀次发送⼼跳的时间 。

Eureka Server端优秀的多缓存机制

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

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

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

在拉取注册表的时候:

⾸先从 ReadOnlyCacheMap ⾥查缓存的注册表。

若没有,就找 ReadWriteCacheMap ⾥缓存的注册表。

如果还没有,就从内存中获取实际的注册表数据。

在注册表发⽣变更的时候:

会在内存中更新变更的注册表数据,同时过期掉 ReadWriteCacheMap。

此过程不会影响 ReadOnlyCacheMap 提供⼈家查询注册表。

⼀段时间内(默认 30 秒),各服务拉取注册表会直接读 ReadOnlyCacheMap 30 秒过后,Eureka Server 的后台线程发现 ReadWriteCacheMap 已经清空了,也会清空 ReadOnlyCacheMap 中的缓存

下次有服务拉取注册表,⼜会从内存中获取最新的数据了,同时填充各个缓存。

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

尽可能保证了内存注册表数据不会出现频繁的读写冲突问题。

并且进⼀步保证对 Eureka Server 的⼤量请求,都是快速从纯内存⾛,性能极⾼。 为⽅便⼤家更好的理解,同样来⼀张图,⼤家跟着图再来回顾⼀下这整个过程:

通过上⾯的分析可以看到,Eureka 通过设置适当的请求频率(拉取注册表 30 秒间隔,发送 ⼼跳 30 秒间隔),可以保证⼀个⼤规模的系统每秒请求 Eureka Server 的次数在⼏百次。 同时通过纯内存的注册表,保证了所有的请求都可以在内存处理,确保了极⾼的性能 另外, 多级缓存机制,确保了不会针对内存数据结构发⽣频繁的读写并发冲突操作,进⼀步 提升性能。 上述就是 Spring Cloud 架构中,Eureka 作为微服务注册中⼼可以承载⼤规模系统每天千万 级访问量的原理。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值