Nacos、Eureka都是微服务领域内熟知、常用的注册中心组件。只不过呢,Nacos还多了个功能身份就是配置中心。从目前流行与随着Spring Cloud Alibaba发展来看,Nacos使用得更加多,也是趋势所在。
注册中心原理其实很简单,基本思想都是差不多的。就是生产者客户端在启动时,将当前服务实例信息封装上报给服务端。服务端进行解析处理存起来。这里的存储可以是在内存存储,也可以持久化存储(文件、数据库都行)。生产者与服务端维持心跳,达到服务健康状态检查。最后消费者客户端从服务端拉取服务信息。从而达到服务的注册与发现效果。网络通信这块可以用netty 也可以用http来做。最好netty性能会更加好些。
一个好的注册中心必须要有很好的读写并发性能,不然在成千上万的服务,再加之服务集群部署时,注册中心怎么扛得住?
注册中心的注册表的服务实例信息,会随着生产者的改变发生写操作,而消费者会从注册中心拉取注册表信息,这是读操作。当读写同时进行时,就会产生读写并发问题了。常见的解决方案有:
接下来我们细细了解Nacos与Eureka的在架构设计是如何提高其读写并发性能的
Nacos客户端在向服务端注册时,将注册信息封装成instance对象,服务端接受后,存入一个内部阻塞队列,就返回成功响应给客户端完成注册了。这样可以极大提高客户端的注册速度以及启动速度,不会对客户端所属服务带来影响。
Nacos服务端起了一个只有一个线程的线程池定时调度执行一个任务,那就是异步从阻塞队列里拉取注册信息进行处理,完成注册信息的注册。在注册时,是利用COW写时复制,拷贝一个副本出来进行写处理,之后再回写回原注册表,来提高读写并发性能。
还有一个亮点就是 ,我们都知道 这么多服务集群实例,肯定会存在多个同时写并发问题,难道一个实例要完成写入注册,就COW拷贝一份,那这么多实例节点,Nacos服务端怎么能够扛得住呢?
显然Nacos设计者考虑到了这一点,阿里中间件设计有这么一句话,解决问题的最好办法不是去解决他,而是规避他。所以Nacos在实现上,并没有去花精力在怎么解决同时写并发问题,而是选择了直接一个线程池里只有一个线程去解决处理。这样单个线程从阻塞队列里拉取一个处理一个。因为都是基于内存操作,这个处理速度是相当快的。所以不用担心一个线程消费能不能跟得上的问题。
但是这样设计也不是十全十美,会存在一个问题 :虽然提高了Nacos注册表读写性能,但是也带来客户端(消费者)拉取到的注册表信息不是最实时或者说不是最新的。但是由于客户端会定时向Nacos拉取注册表信息,可以解决这个问题。顶多就是服务没那么及时被发现,并不影响整个系统的可用性。
Eureka读写并发架构设计比较简单暴力,它是利用多级缓存来提高读写并发性能的。Eureka里的服务注册表发生变更时,同步readwrite缓存时,不是做相应条目的变更更新,而是直接把readwrite缓存清空。这点跟我们平时做业务缓存架构比较像,不去比较条目变更更新,而是直接清空它这个缓存。这样后台线程判断比较readwrite缓存与readonly缓存不一致时,就会同步更新空的给readonly缓存。这样客户端拉取时,发现readonly缓存也为空,就会从服务注册表拉取内容,再填充给这两个缓存。
但是带来的弊端也是比较明显,那就是Eureka一直被吐槽的 服务发现太慢,太不及时了!检查同步都是30秒级别,三个一加就得1分半钟。所以Eureka部署时必须得调优,调低检查时间。但是调太低了 又会导致线程检查过于频繁,浪费CPU。要是注册表没啥变动情况下,就是“徒劳”在那里检查个寂寞。
好了 ,本文也要结束啦~ Nacos与Eureka读写并发设计上,都是抛弃了锁来实现的。两者各有千秋,都是优秀的,都是值得我们学习的架构思想。