MySQL :: 更改客户端代码,跳过Haproxy/LVS做OneProxy负载均衡和高可用。

最近被好几个用户询问OneProxy中间件自身的高可用应当如何做?中间件本身是无状态的,可以布署多个OneProxy,应用只要连接到任何一个实例即可,问题是如何让应用不改代码就能连接到多个地址,常见的方式有几种,比如利用动态的DNS将一个域名对应到不同的机器,或者利用F5/Haproxy/LVS之类的TCP层路由软件来做负载均衡,或者使用OneProxy自身基于VIP的高可用功能,也有用户提出直接在客户端指定多个Proxy的IP地址是最好的。

其实MySQL官方应当在客户端实现可以指定多个主机地址的功能,平民软件已经向官方提交了此项功能的补丁,希望官方能接受,成为一个标准。在架构上的确可以减少一层依赖,减少一层网络转发,就算不接入中间件,直接指定多台Slave进行负载均衡,也是一个很好的方案。代码本身的实现非常简单,不考虑权重等因素,只需要修改一个文件,将“sql-common/client.cc”中原来的连接函数“CLI_MYSQL_REAL_CONNECT”改名,然后实现一个同名函数,源代码如下:

MYSQL * STDCALL
CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
                       const char *passwd, const char *db,
                       uint port, const char *unix_socket,ulong client_flag)
{
        int rowcount = 0;
        int arr_pos1[32], arr_pos2[32];
        int pos1=0, pos2=0;
        while(host != NULL && host[pos2])
        {
                while(host[pos2] && host[pos2] != ',') pos2 ++;
                if (pos2 > pos1)
                {
                        arr_pos1[rowcount] = pos1;
                        arr_pos2[rowcount] = pos2 - pos1;
                        rowcount ++;
                }
                if (host[pos2])
                {
                        pos2 ++;
                        pos1 = pos2;
                }
		if (rowcount >= 32) break;
        }

        if (rowcount < 2)
        {
                return CLI_MYSQL_REAL_CONNECT2(mysql, host, user, passwd,
				db, port, unix_socket, client_flag);
        }
        else
        {
                MYSQL *con_mysql = NULL;
                char hostbuf[256];

                my_rand_buffer(&pos1, sizeof(pos1));
                pos1 = (pos1 >> 4);
                pos1 = pos1 % rowcount;

                for(pos2 = pos1; pos2 < pos1 + rowcount; pos2++)
                {
                        memcpy(hostbuf, host + arr_pos1[pos2 % rowcount],
				MY_MIN(arr_pos2[pos2 % rowcount],255));
			hostbuf[MY_MIN(arr_pos2[pos2 % rowcount],255)] = 0;
                            con_mysql = CLI_MYSQL_REAL_CONNECT2(mysql, 
		                   hostbuf, user, passwd, db, port, 
                                   unix_socket, client_flag);
                        if(con_mysql != NULL) return con_mysql;
                }
                return con_mysql;
        }
}

可以专门只编译客户端库,比如在编译PHP时,使用定制的客户端后,就可以指定多个IP地址,从而连接到任何一个活着的MySQL节点或OneProxy节点,不依赖其他任何组件实现负载均衡和高可用的功能。可以直接使用补丁后的MySQL的客户端工具进行测试,如下所示:

$ export DB1=172.30.12.15
$ export DB2=172.30.12.4
$ ./mysql -u test -ptest -h$DB1,$DB2 -e "select @@version"
+-----------+
| @@version |
+-----------+
| 5.7.12    |
+-----------+
$ ./mysql -u test -ptest -h$DB1,$DB2 -e "select @@version"
+-------------+
| @@version   |
+-------------+
| 5.6.26-74.0 |
+-------------+

可以看到会随机连接到一台数据库,发挥了负载均衡的功能,也可以关闭一台数据库,进行高可用功能的测试。