为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)