首页 > 代码库 > MC中间件WCCS
MC中间件WCCS
一、问题描述
在大访问量的Web服务中,MC集群作为缓解后端数据源访问压力的中间层已经成为了不可缺少的一部分,机器数量越来越大,维护成本也变得越来越高了,其中的问题有:
- 故障机器自动剔除。后端某台MC机器宕机时,需要及时发现并将该机器从集群中移除。
- 透明运维。需要能够在不用业务方做任何修改的情况下,快速的增加或迁移机器。
- 跨机房同步。MC夸多个机房,并且每个机房的数据独立时,就需要考虑MC数据的一致性问题。
面临以上问题,传统的直接将MC服务器IP写在配置文件中无法满足及时响应的需求。为了解决这些问题,huati.weibo.com曾使用了一个基于Zookeeper的中间件来自动同步配置,但或多或少存在一些问题,于是我们决定尝试WCCS.
二、WCCS介绍
WCCS是由 @laruence 带领的微博主站架构研发部发起,由 @猫爷宋Qi 同学主导开发的一个MC中间件服务,它参考Twemproxy实现了自动检查MC的存活情况并自动剔除故障节点的功能,它支持根据key的前缀做正则匹配后在集群中定制分发策略,还可对hot key进行多机复制以达到分散压力的效果等。
除此之外WCCS还根据微博的业务特点,提供了多个集群间数据同步的功能,这样就可以将机房A的数据自动同步机房B,显著的提高了可用性。在进程模型方面,WCSS采用了和apache类似的多进程模型,通过worker进程接受和分发MC请求,配置为长连接方式并启动后,每个worker进程与后端的每个MC维持固定个数的连接。并支持对同一个pool中的不同MC端口设定不同的权重,以达到一定程度上的SLA。
下面用一个配置文件来展示WCCS的功能:
server: {
host = "0.0.0.0"; //监听的IP
port = 2888; //监听的端口
daemon = true; //是否以daemon方式运行
workers_num = 10; //启动多少个子进程
pid = "/usr/local/sinasrv2/var/run/wccs.pid";
};
pool: //WCCS使用pool的方式对MC进行分组管理
(
{
name = "PoolDefault"; //pool的名字
timeout = 1000; //与MC连接的超时时间
preconnect = true; //是否使用持久连接
auto_eject_hosts = true; //是否开启自动剔除故障MC实例
backend_connections = 20; //每个worker与每个MC实例的连接数
backend_failure_limit = 500; //触发自动剔除的最大失败次数
backend_retry_timeout = 30000; //自动重连被剔除机器的时间间隔,单位为毫秒
backend: //pool对应的MC实例
(
{ ip = "10.73.19.37"; port = 7943; weight = 1 }, //weight指定MC实例权重
//....
);
},
);
keymap:
(
{
prefix = "*"; //路由前缀, WCCS支持将指定前缀的key分发到指定的pool中
pool = "PoolDefault"; //pool的名字
distribution = "ketama"; //分布策略采用一致性Hash,支持多种分发算法
hash = "md5"; //使用md5作为hash策略,支持多种hash算法
hash_tag = "{}";
need_sync = false; //是否开启跨机房同步
},
);
dir = “/var/wccsdata”, //开启跨机房同步时,用于记录MC的更新操作日志
backend : //需要同步的WCCS端口
(
{ip = “127.0.0.1”; port = 2888}, // 位于机房B的WCCS集群
{ip = “127.0.0.1”; port = 2810} // 位于机房C的WCCS集群
//....
)
三、 对WCCS的一些测试
为了了解WCCS的特性,我对它进行了一些测试,重点测试了以下几点:
- keymap机制是如何工作的?
- 是否能及时自动剔除故障机器?
- 性能如何?
- 参数如何配配置才能够达到最大性能?
压测工具采用mcperf. 支持高并发访问,并可以输出清晰的测试报告。同时使用memcache-top观察后端MC的使用状况。
测试环境准备
- 机器配置:
- 网卡:1000M/s
- CPU: 2.4GHz 12(逻辑)核心
- IP : 10.13.2.143
- 内存:48G
部署方式:
在10.13.2.143上同时部署MC和WCCS,MC端口为11211,11212,各分配1G的最大使用内
存,WCCS端口为2888,WCCS上仅配置一个默认池
下面是我的测试步骤
1. keymap机制是如何工作的
给WCCS配置两个pool, DefaultPool和UserPool,其中DefaultPool
pool:
(
{
name = "DefaultPool";
backend:
(
{ ip = "10.2.13.143"; port = 21111; weight = 1 }, //仅一个实例,端口21111
);
//...
},
{
name = "UserPool";
backend:
(
{ ip = "10.2.13.143"; port = 21112; weight = 1 }, //仅一个实例,端口21112
);
//...
},
);
keymap: (
{
prefix = "*";
pool = "DefaultPool";
distribution = "ketama";
hash = "md5";
hash_tag = "{}";
},
{
prefix = "user";
pool = "UserPool";
distribution = "ketama";
need_sync = true;
hash = "md5";
hash_tag = "{}";
}
);
尝试写入user前缀和非user前缀的key,并查看key被写入到了哪个Pool中
小结:通过上面的截图可以看到,WCCS按照预期完成了key的分发。配置keymap时,必须包含一个prefix="*"的默认规则。这里需要注意的是,key前缀的匹配是贪婪的,即如果一个key可以匹配keymap中的多个前缀,key最终会被发送到前缀匹配最长的pool中。利用WCCS的keymap机制让key在集群分发机制也对业务变的透明了,是不是很cool?
2. 自动处理故障端口功能测试
首先修改配置
backend_failure_limit = 5; //连续失败五次后触发自动剔除
backend_retry_timeout =30000; //30秒
a) set key1 为frank,发现key1落在端口11212上,get key1,成功;直接连接11211端口,并执行 将key1设置为hello
b) kill掉11212端口
c) get key1 提示SERVER_ERROR unknown, 连续5次get,仍然提示错误,等待30s后, get key1返回hello, 说明key1已经读取到了11211端口上的数据,auto_reject已经完成
d) 启动11211端口,等待30s
e) 在WCCS端口上执行get key1得到结果为空,说明已经自动重连到已经恢复服务的11212端口.
小结:auto_reject开启后,自动剔除功能工作正常,在连续访问失败backend_failure_limit次后,经过 backend_retry_timeout 秒,WCCS会自动重连。重启MC后,无需重启WCCS。
3. 性能测试
写入数据1k-2k之间的数据
mcperf -s 10.13.2.143 -p 2888 --linger=0 --timeout=2--conn-rate=6000 --call-rate=1000 --num-calls=20 --num-conns=6000 --method=set--sizes=u1024,2048
参考线上MC压力峰值,并发数选择6000
mcperf -s 10.13.2.143 -p 11211 --linger=0 --timeout=0.2--conn-rate=6000 --call-rate=1000 --num-calls=15 --num-conns=6000 --method=get
原生MC
WCCS
小结:从上面可以看出WCCS的可以充分发挥后端的MC性能,在数据的平均大小为1.5k时,get的峰值可以达到71000/s。
值得注意的是,MC中value的大小直接影响了MC的性能,我将数据的平均大小调整到了0.65k, 又进行了一次测试:
重启MC, 注入1byte-1.3k之间的数据:
mcperf -s 10.13.2.143 -p2888 --linger=0 --timeout=2 --conn-rate=1000 --call-rate=100--num-calls=2000 --num-conns=1000--method=set --sizes=u1, 1331
MC的资源占用情况:
测试WCCS性能:
mcperf -s 10.13.2.143 -p 2888 --linger=0 --timeout=0.2--conn-rate=6000 --call-rate=1000 --num-calls=29 --num-conns=6000 --method=get
从上面的数据可以看出,当平均数据大小在0.65k左右时,WCCS的get操作的TPS可以达到16w/s,这个结果已经非常理想了。
小结:测试环境下,并发6000/s,平均数据大小1.5k(1k-2k之间)时,get可以达到的TPS为7w/s,当平均数据大小为0.65k时(0-1.3k之间)时,get可以达到的TPS为16w/s。这说明WCCS的性能是足够高的。另外通过综合测试,还发现WCCS挂载两个MC端口时,综合性能无法达到2台MC的处理性能, 跟单个MC端口性能相近。
4. 调整配置文件中的参数值,比较不同的配置对性能的影响
调整workers_num, backend_connections的值,并测试性能变化。具体过程略。
调整workers_num和backend_connections的值后发现影响WCCS的性能的主要因素是WCCS于后端MC维持的长连接数量,这个数值等于
workers_num*backend_connections*pool中MC端口数
线上配置时需要注意总连接数不要超过MC设置的最大值。测试后发现当这个值维持在300左右时,就可以足以达到上面16w/s的TPS了。
backend_failure_limit是所有的worker共享的,考虑到push情况下MC失败也会比较多,启用auto_reject时,为了防止误判,可以将这个值设置的稍微大一些(如500)。
5.同步功能测试
启动两个WCCS端口2888和2889,并将2888 need_sync设置为true, 2888端口上的相关配置如下:
sync: { //WCCS同步相关配置
dir = “/var/wccsdata”, //开启跨机房同步时,用于记录MC的更新操作日志
backend : //需要同步的WCCS端口
(
{ip = “127.0.0.1”; port = 2888}
{ip = “127.0.0.1”; port = 2889}
)
}
keymap:
(
{
prefix = "*"; //路由前缀, WCCS支持将指定前缀的key分发到指定的pool中
pool = "PoolDefault";
distribution = "ketama"; //分布策略采用一致性Hash
hash = "md5"; //使用md5作为hash策略
hash_tag = "{}";
need_sync = true; //开启同步
},
);
在2888端口上写入数据,并尝试从2889端口上读取。通过终端操作时,发现数据可以被正确同步。
提示:MC数据同步功能开启后,数据可以立即被同步,但是使用PHP客户端测试时发现,同步到2889端口上的数据丢失flag了字段,使用PHP MC客户端写入一个数组后,读取出的类型为字符串,没有对象进行自动解析,这一点使用的时候需要注意。
6. 其他注意问题:
a. WCSS会维持比较多的连接,需要通过ulimit放宽系统支持的文件描述符的数量
b. /O是影响WCCS性能的主要因素,需要通过Irqbalance打开中断优化,防止所有的I/O都阻塞在同一个CPU上。
c. WCCS在实例之间自动同步数据的功能实现方式为,首先记录接受到的MC数据更新命令到发送到本地的日志文件中,WCCS使用单独的进程对日志进行扫描,并发将幂等命令直接发送到需要被同步的机房中,非幂等的add、incr、decr、cas操作都会被转换为delete操作。
7. WCCS一些可以改进的方面
目前WCCS没有提供使用情况监控功能,使用中间件后,将无法通过监控后端MC的连接数评估连接的情况,只能通过netstat查看socket的连接数。
WCCS没有提供通过一个key获取key所在的机器的功能,不过如果知道MC的 IP列表,可以自行遍历个个实例获得。
MC中间件WCCS