为MySQL MGR实现简单的负载均衡代理

在多写(多节点写入)数据库(例如MySQL MGR的multi-primary mode)与应用之间,往往会加一层代理组件,通过算法调节不同节点负载,分发高并发读写请求。

要求代理工具需要具有请求转发、负载均衡、故障转移的功能。

在后端节点故障发生或者连接因为客户端异常、网络问题断开时,需要及时将故障节点及时踢出负载均衡队列或者关闭异常连接,做到故障转移。

这就是接下来介绍的主要内容,使用golang简单编写一个这样的工具,来深入学习一下负载均衡代理的实现。

1、功能一览

负载均衡

将应用端的连接请求(负载)按照既定的均衡算法转发到不同的后端节点,服务程序建立应用(客户端)与数据库节点之间的通信并保持至客户端断开连接。

故障转移

在后端节点出现故障时,能及时的检测到故障,并将故障节点踢出负载均衡队列,不再将应用请求路由到故障节点,做到应用无感知。在故障恢复后,能够检测到节点状态恢复,将其再次加入到负载均衡队列。

2、实现细节

核心功能

请求转发

代理需要做到将请求分发到不同的后端节点上去,并保持应用与对应节点的通信,直至其中一端退出(故障或者主动)。

负载均衡

对应用的负载,均衡的分发的不同的节点,需要对应的算法支持。目前通用的负载均衡算法有随机、轮询、加权轮询,代码实现了这三种算法。

此外还有动态判断后端节点负载情况,根据负载情况动态调整负载分发,这需要额外的负载监控工作,这里没有实现。

故障检测

负载均衡代理需要避免向失效的节点分发请求。故障类型无疑是很多的,如果面面俱到的对每个故障类型都照顾到,无疑增加了实现难度。

例如在分布式中,不可靠的网络增加了检测故障难度,对于数据库实例,在分布式中很难判断节点到底是crash了还是网络中断导致的。

并且节点因为负载较高无法及时响应请求,这时也是很难判断节点状态,此时进行重试可能会加剧节点的负载。

在这里并不是要模糊这种判断,而是实际情况实在是太复杂了,我并不是相关领域专家,所以在实现故障检测时,只考虑了几种确定性较高或者容易判断的情况。

过程实现

其中, 转发 实现过程是在接收到请求后,定义一个后端节点的地址,并建立一个和这个地址的连接。

在开启两个协程,一个负责将应用(客户端)发送的数据包传递给后端的连接,另一个是将后端的返回的数据传递给应用,这样就在应用与后端节点之间搭建起了通信,使之像直接通信一样交换数据,核心的步骤可以参考下面代码的实现。

sConn, err := l.Accept() dTcpAddr, _ := net.ResolveTCPAddr("tcp4", addr) dConn, err := net.DialTCP("tcp", nil, dTcpAddr) go io.Copy(sConn, dConn) go io.Copy(dConn, sConn)