当前位置: 首页 > 科技观察

程序员的修真之路——分布式系统中最重要的枢纽可能是

时间:2023-03-19 17:29:01 科技观察

为什么分布式系统需要注册中心?分布式系统注册中心有哪些坑?使用现成的组件是否容易实施该中心?看到标题你可能会鄙视,注册中心谈什么。注册中心作为当前架构中的一个组件,确实很常见。微服务作为分布式系统最典型的形态,是近年来最热门的概念之一。注册中心在每篇关于微服务的文章中都会或多或少的提到,但只是一个路过。作为分布式系统或者微服务架构中最重要的部分,我觉得有必要单独写一篇文章。文章要详细介绍,这也是写这篇文章的原因。从架构上看,分布式系统的痛点注册中心其实是一个集体的概念,不是现在流行的微服务所拥有的。很久以前用Nginx做负载均衡(反向代理)的时候,Nginx会根据配置来使用。该文件根据配置的策略将每个请求定向到特定的后端处理程序。在这个过程中,从客户端的角度来看,Nginx就像一个网关,而从后端处理程序的角度来看,Nginx更像是一个服务管理中心,管理所有可以提供服务的后端处理程序信息,也可以通过一些手段实现服务健康检查、服务的自动注册和移除等操作。当然现在微服务流行,网关和注册中心是两个平行的概念和组件。就重要性而言,我觉得注册中心比网关更重要。现在很流行拆分单服务的操作,但是这里要强调一下,有没有必要拆分你的单服务,要看很多情况。毕竟,拆分成小的微服务并不是免费的。很久以前,如果客户端需要请求多个后端服务,很多时候后端服务信息是写在请求方的配置文件中的,类似这样{"ServiceA":["http://192.168.100.100""http://192.168.100.101","http://192.168.100.102"]}这种方法固然是一种解决方法,但是随着系统的不断升级,也会遇到很多问题:当系统需要扩展时后端服务器,需要手动修改客户端的配置文件,大多数情况下需要重启客户端进程。当后端某个服务节点出现故障时,需要手动删除客户端配置文件中对应的节点,并且大多数情况下需要重启客户端进程。每次新增或删除节点都需要人工干预,大大增加了维护成本。鉴于以上原因,注册中心应运而生。注册中心的作用注册中心不仅解决了服务节点的增删改查问题,还修改了寻找服务可用节点的整个过程。匹配服务健康检查的手段后,可以实现自动化。目前业界有很多注册中心可供选择,比如ZooKeeper、ETCD、阿里的微服务注册中心Nacos、SpringCloud的Eureka等。在之前菜菜的文章中,写过使用ETCD实现一个配置中心服务注册发现服务注册发现是注册中心提供的最基本、最主要的功能:当有新的服务节点上线时,可以通过注册中心的接口进行注册。当某个服务节点出现故障时,注册中心会自动删除当注册中心内的服务节点发生变化时,服务节点可以及时通知调用者,服务调用者可以近实时地更新可用的服务节点信息。负载均衡当客户端在注册中心获取到可用的服务节点后,可以根据轮训或权重等策略访问服务。在这种场景下,注册中心更像是一个负载均衡器,将流量导向多个不同的节点。既然是负载均衡,从某种意义上说,可以实现服务的水平扩展。说实话,真的没什么问题。原因类似于Nginx做负载均衡。虽然那些坑服务中心在整体架构模型上解决了很多问题,但是我们在使用过程中不得不面对它带来的一些副作用,而这些副作用有时会成为整个系统瘫痪的导火索。数据一致性问题数据一致性似乎是所有系统都必须面对的问题,注册中心也不例外。这里的一致性指的是注册中心存储的可用节点数据与后端真实可用节点和客户端存储的可用节点之间的差异。举个例子:如果注册中心存储了三个服务节点ABC的信息,此时节点A因为某种原因下线了,注册中心必须及时移除节点A,并通知客户端也移除节点Aremove。从理论上讲,上述过程跨越了注册中心、主叫方和被叫方之间的交互过程,属于分布式事务问题,即分布式事务问题。菜菜之前的文章中提到,分布式事务如果要保证严格的一致性,必然会影响可用性。分布式下,我要的是一致性,从目前主流的注册中心技术来看,注册中心与双方的通信过程是一个异步过程,无法满足实时事务的要求。目前注册中心在通知客户端变更时可以做到近实时(其实不是实时),但是在后台监控是否有变化的过程中很难做到近实时。服务节点可用。原因之一是由于网络的不可靠性,网络通信失败并不意味着下一次网络通信也会失败,其次是监控后端服务可用性的方式不真实-时间。目前流行的两种检测后端服务的方式是:注册中心主动检测,很多注册中心组件都支持这种方式,这种方式需要后台的每个服务提供一个可以检测到的接口或者端口,注册中心调用该接口或者根据配置每隔一段时间服务的端口。如果恢复正常,则认为服务正常运行,否则认为服务不可用。如果不可用,注册中心将主动移除当前服务列表并通知客户端。这种方法虽然看起来很完美,但还是有坑:注册中心在检测过程中可能会因为网络问题而出错,但实际上服务是正常运行的,这就意味着会产生误判。对于这种问题,我们可以设置为通过多次检测结果来判断,而不是通过一次检测结果就草草判断。如果服务节点较多,注册中心就相当于承担了一个比较重的检测任务,这会对注册中心的性能造成一定的损失,影响其可用性。如果服务以端口的形式开放检测接口,当服务较多时,可能会出现端口抢占的情况。毕竟,这些服务可能是由不同的团队开发的。相对于注册中心的主动检测方式,后台服务的主动心跳更倾向于使用服务主动上报心跳的方式。使用心跳模式的一般流程是:后端各服务节点根据配置(可修改此配置)定时主动向注册中心发送心跳包。至于心跳包的内容,可以协商约定,比如有的系统只发送ping命令,有的会发送更详细的服务状态,比如cpu使用率,内存使用率等信息,然后注册中心可以根据这些信息做更准确的流量分配,比如让资源丰富的服务节点承载更多的流量。注册中心收到服务节点的心跳包后,可以以滑动窗口的形式为服务节点续约时间(生存时间)。只要服务节点不断发送心跳包,注册中心就可以判断该节点已经正常运行。当然,这个过程中也会出现意想不到的情况。例如,由于网络状况,服务节点上报心跳失败,但服务运行正常。这种场景下,最直接的解决方案是:注册中心判断服务是否存活的时间窗口可以大于上报时间间隔。例如心跳上报时间为10秒,则注册中心判断服务不可用。当然,以上只是注册中心的一个假设。事实上,系统可以使用主动检测来确定服务是否可用。在这种情况下,结果的准确性会更高。也就是说:当服务的某个节点在配置的N个心跳时间后没有上报心跳数据时,注册中心可以通过主动检测重新判断服务是否正常运行。当然,这增加了设计的一定程度的复杂性,需要编写更多的代码。我们还需要考虑一种不太常见的情况。如果所有服务节点因为网络异常导致心跳上报超时,主动检测失败,按照约定,注册中心会逐步清除所有节点信息,这样的后果是系统肯定会出问题。有时在系统设计时可以考虑一些保护措施,例如:当删除的节点信息数量大于一定比例时,停止删除操作并发出告警信息。这在一定程度上可以避免注册中心没有节点数据的情况。当然客户端也可以有这样的保护策略。NotificationStorm虽然这个问题在大多数情况下不是问题,但还是值得一提。当注册中心随着项目升级承接越来越多的服务节点时,服务间调用链的复杂度也随之增加,随之而来的一个新节点可能要通知几十个客户端,移除时也会出现类似情况一个节点。如果多个服务同时添加或删除节点,注册中心将推送更多消息。在这样的场景下,系统设计者需要控制注册服务节点的数量以避免网络风暴。具体数量可以根据服务器的峰值带宽来确定。本文转载自微信公众号《建筑师实践之路》,可通过以下二维码关注。转载本文请联系架构师修炼之路公众号。