Skip to content

RSocket SDK LoadBalance

linux_china edited this page Jul 22, 2020 · 5 revisions

RSocket Broker是一个集群(Cluster),我们讲到集群,通常都会涉及到负载均衡的设计。 那么在Alibaba RSocket Broker中负载均衡是如何设计的呢? 在介入Broker设计后,服务消费者不直接和服务提供者打交道,而是通过Broker来转发请求,所以服务消费者到服务提供者之间不会有连接,这个传统的服务监听+服务发现的机制是不一样的。 那么负载均衡主要发生在应用到broker集群之间的连接上,接下来我们就看一下负载均衡是如何设计的。

Share Nothing架构

Alibaba RSocket Broker采用Share Nothing的架构设计,也就是broker之间不进行请求转发,彼此都是独立的,每一个broker实例都保存网络的拓扑结构全量信息。 这个设计就要求应用和集群中的每一个broker都创建连接,这样可以保证任何服务消费方连接到任何一个broker上都能找到对应的服务提供方。 这样的设计有好处也有坏处,好处在于没有请求转发,性能是最高的,而且请求均匀分布,没有热点等问题。当然坏处就是要求应用和每一个broker都要连接,要处理集群的连接池,同时单个broker要承担更多的连接。

应用到Broker集群的负载均衡

假设一个集群有10台Broker服务器,其中三台是种子服务器,用于进行gossip广播种子节点,其他7台为工作节点。 根据前面的Share Nothing架构设计,应用启动会和三台种子节点建立连接,然后种子节点会将集群的拓扑结构,也就是10台服务器信息反向推送给应用,然后应用会再和其他7台服务器建立连接。

服务提供者

如果是服务提供者,会向每一个broker连接提交自己的服务信息,这个可以理解为服务的注册,这样服务消费者就可以在任何一台broker上进行服务调用。 服务提供者不需要考虑负载均衡,只要和每一个broker创建好连接就可以啦,Broker在收到调用请求后,会从能够提供对应服务的连接中随机挑选一个然后进行请求转发,服务提供者只需要处理请求就可以。

服务消费者

服务消费者和10台服务建立连接后,如果发起一个调用请求,会随机从10个连接中找一个进行请求发送,这样可以保证broker收到的请求都是均匀的,不会存在某一个broker的接收到的调用请求特别多,形成热点。

集群连接的管理

这里会涉及到一个集群连接管理的问题,如broker服务器上下线等。当有broker上线或者下线时,会通过gossip机制广播给其他broker服务器,然后broker会以集群变更事件的方式通知连接到自己的应用, 应用会根据新的集群结构进行重新连接,如下线的broker服务器,就不会有应用和它进行连接。当然应用侧会做一些工作,如去除重复集群变更通知等。

集群变更通知

broker会根据一定的优先级通知应用,目前的策略就是先通知服务提供者,然后是服务消费提供者+服务消费者,最后是服务消费者,这样可以保证服务优先注册,这对新上线的broker非常必要。 对于下线的broker来说,这个下线只是触发一个集群通知消息,而不是真的马上停止服务,这里有一个drain mode(泄洪模式),能够继续服务一段时间,极大限度保证处理完所有请求再下线。 关闭和broker的连接顺序是,先服务消费者断连,然后是服务消费者断连。

容错处理

尽管我们竭力处理好优雅上下线,drain mode等,还是会出现异常,下面我们看一下应用到Broker集群间的异常是如何处理的。

  • broker实例连接不了:如10台服务器中,有一台连接不上,可能因为网络或者应用的问题。 当连接错误时,当然不会进入活跃连接池,应用会尝试12次连接,每次间隔5秒,如果都失败,该Broker的URI会进入连接失败列表名单。
  • 连接失败列表名单:对进入连接失败列表名单的broker实例,应用会每隔3分钟再重试一次,这个是不会停止的。如果在深夜,可能一台服务因为一些问题,又重新恢复啦,确保能重新加入到集群。
  • 健康度检查:应用会对所有的broker发起健康度检查,这个每15秒一次健康度检查,如果健康度检查失败,会尝试进行一个1分钟内12次重连测试,如果还是失败,则进入连接失败列表名单。
  • 调用期间失败:如果在调用期间发生错误,这里主要是指connection error,而不是业务级别的错误,这个时候会尝试找下一个连接进行重新调用。而刚刚失败的连接会异步进行1分钟内12次重连测试,规则和健康度检查一样。

通过容错处理,我们希望极大程度上保证集群发生一定的故障时,对应用影响最小。

种子服务器的作用

种子服务一方面是保证基于Gossip集群管理的,另外一方面还承担一定的广播通知功能。 如做配置推送时,我们都是基于种子服务器进行的。当然这里只是建议:

  • 种子服务器数量要在两台或两台以上
  • 配置略好于其他broker服务器
  • 在集群的例行发布更新时,种子服务器要分批发布
  • 种子服务器承担的额外功能:配置推送、事件广播等

监听端口号

对于一个集群来说,通常都会涉及到两个端口号,一个是接受请求端口号,在RSocket Broker集群中为9999,另外一个是集群管理端口号,也就是集群内部服务器节点之间相互通讯的端口号, RSocket的Gossip管理方式中,该端口号为42254,确保各个节点之间的gossip通讯。整体如下:

  • tcp request port: 9999,负责接收RSocket请求
  • cluster gossip port: 42254,完成集群内部服务器间的Gossip通讯
  • Spring Boot management server port: 9997, Spring Boot的actuator端口号
  • http web request port: 9998,通过该Web监听端口号,可以以REST API方式访问RSocket服务,同时是Broker集群的Web管理控制台。

SDK端Load Balance架构

  • 集群中多个服务器的地址: 这个是可以静态的,也可以是动态的

  • 连接(Connection):是指能够连接到目标服务器上,且能完成对应的RSocket健康度检查操作,也就是RSocketServiceHealth的check()调用成功,如果健康度检查失败(包括15秒超时),仍然为连接不成功。

  • SDK连接到多个服务器上,但是要保存一些全局的信息,如安全验证信息,这个主要是服务重连的需要

  • 活跃连接列表维护:这个列表主要是处理接下来的网络请求,主要包括一下部分

    • 负载均衡算法: Round Robin或者Random,确保请求均匀落在不同的节点上
    • 定期的监控度检查(Health Check): 排查不可用的连接节点,然后从活跃列表中排除,保证通讯的稳定性
    • 请求错误拦截机制(Interceptor): 在发生特定错误时,如连接中断时,能够快速将该连接从列表中删除,同时切换到下一个可用连接。 对于Reactive来说,可以通过onErrorResume钩子完成错误后的重试,起到错误拦截的作用
  • 不可用连接列表维护: 维护所有不可用的连接列表,主要用于稍后重试和运维需要,包括以下部分:

    • 重试(retry)策略: 当连接发生错误后,连接被放入到不可用连接列表后,马上执行重试,目前的策略为间隔为5秒的12次尝试
    • 定期(schedule)重连:对不可用的连接进行每隔5分钟的尝试连接(仅一次),如果连接成功则会加入到活跃连接列表,进入到服务列表
  • 集群服务器列表刷新:在实际的开发,如集群服务器重新发布,上下线等,这个时候就会牵涉到SDK的端的Load Balance重连,主要有以下几个问题:

    • 连接刷新规则: 新推送的服务器列表,如果已经在连接列表中,则复用;如果是新加入的节点,则新建连接并加入到可用列表;如果包含一些要下线的服务器,也就是活跃列表一些连接不存在与新的连接列表中,则要进行下线处理。
    • 集群服务列表缓冲: 服务器上下线时间有一定的间隔,如果是每次增量推送的服务器列表只包含一个列表更新,且存在多次推送的情况下,我们建议使用window(duration)操作对更新列表进行批量操作,如5秒内的上线和下线的服务器统一处理。 请注意:时间间隔要设置好,尤其是下线操作,window时间不能太长,不然可能导致通知不及时,节点下线等待时间过长,导致部分请求突发中断的情况。
  • 运维要求: 如将连接和不可用连接的信息以Metrics形式进行暴露,然后方便进行一些alert操作,如不可用连接数占比总连接数大于10%,可能要启动预警处理。 当然在实际的开发中,如果没有对应的监控系统,可以考虑采用错误订阅的方式进行处理,也就是Reactive的subscribe。