Skip to content

有状态高可用:粘性连接

About 2289 wordsAbout 8 min

架构新书

2026-10-28

有状态服务在JVM中为用户或者其他客户端,如设备。保留了必要的数据,不需要从外部持久化数据库加载数据,能极大降低服务延迟。无状态服务通常是从数据库或者Redis等加载数据,进行逻辑运算,有状态服务的数据保留在本地,业务逻辑部署时候提供,也可能是通过外部加载。有状态服务包含典型的应用

  • 设备会连接到固定的物联网网关节点上,此节点会保存设备的基本信息(产品类型,产品编号等)并实现属性上报,告警等业务逻辑,如携带基本信息发布到Kafka,而不需要每个Consumer去数据库中读取取出。
  • Redis可以理解为一个有状态服务,每个Redis节点都包含了若干数据,Redis支持主从部署,主节点的数据备份到从节点
  • Zookeeper可以理解为一个有状态服务,Zookeeper每个节点包含了一个Ztree的数据结构以实现分布式协调。ZK的主节点负责数据的写操作,主从节点支持数据读操作。
  • 棋牌游戏中,RoomServer是一个有状态服务节点,此节点包含了如果房间实例,同一个房间的用户持久连接到RoomServer

下图展示了一个有状态的设备网关在属性上报时候,携带基本信息发布到消息中间件

​

设备首次连接设备网关时候,上报了设备ID以及产品的基本信息,设备网关记录这些信息,当后续的属性上报,告警等业务时候,设备网关不需要查询数据库就能获得产品信息,并完成具体的业务(比如执行属性上报的规则链),并携带产品信息下发到消息中间件。其他应用无需再此从数据库中查询产品。

有状态节点能提实现系统的高性能质量属性,但如果节点宕机,则状态将消失。因此为了实现高可用,通常业务系统会把有状态节点的设计改成无状态节点而牺牲性能属性,在高可用设计原则《有状态服务Vs无状态服务中》中,介绍了4种方法,本章不再详细介绍,本章的重点是如何让要状态节点变得高可用,这些策略与Zookeeper,Redis以及数据库采用的策略没有什么区别。

即使你的系统是无状态节点,阅读本章,你仍然能获得相当大的好处,包括:

  • 了解分布式系统高可用实现最困难的部分
  • 了解Zookeeper协调服务器和Redis等NOSQL的基本原理,以方便你的分布式系统能更好的使用这些基础服务
  • 了解分布系统难点概念,如CP和AP,数据分区和复制,逻辑时钟,数据一致性,分布式事务等,有助于你设计高可用的微服务系统。
  • 作为例子:提供了一个极简的支持分片和备份的Key-Value缓存数据库,此库也支持协调服务。此库类似Redis+Zookeeper。使用者可以决定哪些Key数据需要可靠的保存。

注意:本书不是一本讲解分布式系统原理的书,如果阅读完本章好想深入学习分布式系统,可以参考《分布式系统概念与设计》、《分布式计算 原理,算法与系统》、《分布式系统原理与范型 》以及我个人推荐的《设计数据密集型应用》

粘性连接

不同于无状态服务,客户端能连接任意的节点。当实现有状态服务的时候,面对的第一个挑战是要求客户端能保持粘性连接(Sticky Connection),当存在如下情况时候,客户端仍然能连上保留有状态数据的那个服务

  • 客户端连接到固定的节点,无论是长连接和短连接。
  • 客户端重启后能连接到原来的有状态服务。
  • 有状态服务集群增加节点或者减少节点时候,客户端仍然能连接到原来的节点。
  • 服务节点压力过大,或者服务节点需要停机维护,通知客户端连接其他节点。

实现粘性连接,最简单的方式是借助代理网关,代理网关可根据HASH算法路由到固定的后端有状态节点,或者记住为每个客户端分配的有状态服务节点。下表总结了常见的代理服务实现粘性连接的配置

代理服务配置
Nginx配置ip_hash 按照源IP+端口分配后端IP。或者sticky Nginx根据配置的Cookie、Header路由到固定的后端IP
LVS在首次实现负载均衡后,LVS会记录客户端分配的后端IP。
HAProxy同Nginx一样,根据IP,Cookie或者Header Hash到后端固定的IP。也可以向LVS那样,配置stick-table,使用一张内存表,记录IP、Cookie或者Header对应的后端IP。
Spring Cloud Gateway需要开启request-based-sticky-session, 并添加Cookie 其值是后端实例ID。如果Cookie未添加,则由Gateway添加。 Gateway将根据实例ID路由

当使用Hash算法时候,如上例Nginx,如果集群增减节点,将使得代理网关将请求可能分配到一个新的节点从而使得粘性连接失效。

如果代理网关能记住为客户端每次分配的IP,则能避免此问题,如HAProxy的stick-table,但通常HAProxy也会集群部署通过(参考9.1负载均衡),如果访问了其他HAProxy,则可能路由到其他后端节点。因此HAProxy集群之间必须开启同步stick-table功能。

代理网关能借助客户端的IP,或者HTTP协议中的Header和Cookie实现粘性连接,但有些场景下,客户端链接到后端有状态节点有更多约束

  • 具体的业务逻辑路由,比如特定设备路由到特定的设备网关节点。游戏玩家需要路由到特定房间的服务端实例上
  • 根据后端负载情况,指导客户端链接到低负载的有状态节点。
  • 当后端需要维护时候,指导设客户端新链接到可用节点上。

这时候需要使用负载均衡中资源发现,客户端链接有状态服务集群前,先访问专有服务获得下一步连接的具体的IP。

用户Joel先访问分配服务,分配服务为其分配一个后端节点,Joel记录此后端节点(可能是IP+端口;也可能是后端服务逻辑名称,代理网关能识别这个逻辑名称),使用此后端节点实现粘性连接。

后端增减节点,Joel将一直使用此后端作为服务,如果节点3宕机,Joel将从分配服务获取新的一个节点。

如果节点3需要维护,节点3将向Joel发出一个重定向请求,Joel将从分配服务获得一个可用服务。

如何实现资源服务超过了本书写作范畴,下面列出了资源服务需要实现的功能

功能点描述
资源服务无状态资源服务应该本生是无状态服务,以实现高性能和高可用。
收集后端节点信息后端节点的实例名称,IP和端口,以及其他业务信息可以定时上报到分配服务。分配服务可以存储这些数据到数据库和Zookeeper。
路由分配服务缓存节点信息,并根据客户端请求计算路由地址。分配服务通常会使用HASH算法,或者根据节点的负载情况从满足业务需求的节点中选择一个,这类似代理网关的路由算法。
可视化管理分配服务通常提供UI管理界面,可视化管理有状态集群及其查看路由业务相关的信息,如每个节点的设备数量或者每个节点的房间数和用户数。

如果不存在资源发现服务,任何有状态节点也可以充当资源服务发现功能,根据复杂的业务逻辑,计算此客户端应该路由到那个有状态节点。下图Joel首先连接到节点1,询问粘性连接的地址,节点1根据业务逻辑(比如设备的类型,游戏玩家选定的房间等)返回节点3的的IP。 Joel将连接IP3。

知行合一