Twemproxy的介绍
Twitter的Twemproxy (https://github.com/twitter/twemproxy)是目前市面上用的最广的使用做多的用来做redis集群服务。由于redis是单线程,而且官方的cluster 还不是很稳定和广泛使用。Twemproxy是一种代理分片机制,Twemproxy作为代理,可接受来自多个程序的访问,按照路由规则,转发给后台的各个Redis服务器,再原路返回。该方案很好的解决了单个Redis实例承载能力的问题。当然,Twemproxy本身也是单点,需要用Keepalived做高可用方案(或者LVS)。通过Twemproxy可以使用多台服务器来水平扩张redis服务,可以有效的避免单点故障问题。虽然使用Twemproxy需要更多的硬件资源和在redis性能有一定的损失(twitter测试约20%),但是能够提高整个系统的HA也是相当划算的。其实twemproxy不光实现了redis协议,还实现了memcached协议,什么意思?换句话说,twemproxy不光可以代理redis,还可以代理memcached。
Twemproxy的优点:
1)对外暴露一个访问节点,减少程序复杂度。
2)支持失败节点自动删除,可以设置重新连接该节点的时间,可以设置连接多少次之后删除该节点,该方式适合作为cache存储,不然会丢失Key;
3)支持设置HashTag,通过HashTag可以自己设定将两个KEYhash到同一个实例上去。
4)多种hash算法,并且可以设置后端实例的权重。
5)减少与redis的直接连接数:保持与redis的长连接,可设置代理与后台每个redis连接的数目,自动分片到后端多个redis实例上。
6)避免单点问题:可以平行部署多个代理层,客户端自动选择可用的一个。
7)高吞吐量:连接复用,内存复用,将多个连接请求,组成redis pipelining统一向redis请求。
Twemproxy的缺点:
1)不支持针对多个值的操作,比如取sets的子交并补等。
2)不支持Redis的事务操作。
3)对于已申请的内存不会释放,所有机器内存要大,且需要定期重启,不然就会出现客户端连接错误。
4)不支持动态增删节点,修改完配置需重启。
5)改变节点时,系统不会对已有数据重分配,不自己写脚本做数据迁移的话,会造成部分key丢失(key本身存在某redis上,只是key被哈希到了其他节点,造成“丢失”)。
6)权重直接影响key的哈希结果,改变节点权重会造成部分key丢失。
7)默认Twemproxy是单线程运行,但是大部分使用Twemproxy的公司都会自行进行二次开发一下,改成多线程。
总体来说,twemproxy还是非常的靠谱,虽然性能有损失,但是相对来说还是很值得的,而且久经考验,使用非常广泛。关于更多更加详细的资料请参考官方文档。另外twemproxy只适合静态集群,不适合需要动态增删节点,手动调整负载的场景,如果我们直接来用,需要做开发改进工作。https://github.com/wandoulabs/codis这个系统基于twemproxy,增加了动态数据迁移等功能,具体使用方法需要进一步测试。
Twemproxy使用架构
第一种:单节点Twemproxy
ps:节省硬件资源,但容易有单点故障。
第二种:高可用Twemproxy
PS:浪费二分之一的资源,但是节点高可用。
第三种:负载均衡Twemproxy
PS:如果你是大规模Redis或Memcached应用场景,就可以做Twemproxy的负载军和场景,也就是在高可用Twemproxy的基础上加LVS节点,利用LVS(Linux virtual server)做Twemproxy的负载均衡,LVS是四层负载均衡技术,有很强大的代理能力,具体可以看本博客的LVS章节介绍。但是当你使用LVS之后,又出现了Twemproxy的问题,单点故障故障问题,这个时候又要跟给LVS做高可用了。但是LVS也支持做负载均衡,利用OSPF路由技术就可以做负载均衡了。而这个架构也就是我目前工作中正在使用的架构方式。
另外不管使用以上哪种架构方式,都无法避免Redis的单点故障问题,Redis持久化也无法避免硬件故障问题。如果必须要保证Redis数据访问的不可中断性,那你还是使用Redis集群模式吧,集群模式目前对JAVA支持还不错,工作中也有大量的使用。
安装Twemproxy
1、下载Twemproxy
1 |
git clone <a href="https://github.com/twitter/twemproxy.git">https://github.com/twitter/twemproxy.git</a> |
2、安装Twemproxy
Twemproxy需要使用Autoconf进行编译配置。 GNU Autoconf是一个在Bourne shell下制作供编译、安装和打包软件的配置脚本的工具。Autoconf并不受程序语言限制,常用于C、C++、Erlang和Objective-C。配置脚本控制了一个软件包在特定系统上的安装。在进行一系列测试后,配置脚本从模板中生成makefile与头文件进而调整软件包,使之适应某一种系统。Autoconf与Automake、Libtool等软件组成了GNU构建系统。Autoconf由戴维·麦肯思于1991年夏天编写用于支持他在自由软件基金会的编程工作。此后,Autoconf包含了多人编写的改进代码并成为了使用最广泛的自由编译配置软件。
下面开始使用autoreconf对twemproxy编译配置:
1 2 3 4 5 6 7 8 |
[root@www twemproxy]# autoreconf configure.ac:8: error: Autoconf version 2.64 or higher is required configure.ac:8: the top level autom4te: /usr/bin/m4 failed with exit status: 63 aclocal: autom4te failed with exit status: 63 autoreconf: aclocal failed with exit status: 63 [root@www twemproxy]# autoconf --version autoconf (GNU Autoconf) 2.63 |
提示autoreconf 的版本过低,上面使用的是autoconf 2.63版本的,所以下面下载autoconf 2.69版本进行编译安装。注意如果你是CentOS6,那么你的默认版本就是2.63,如果你是CentOS7,那么你的默认版本应该是2.69,如果你是Debian8或Ubuntu16,那么你的默认版本应该也是2.69。反正如果执行autoreconf报错就说明版本低了,需要编译安装了。
编译安装Autoconf
1 2 3 4 5 6 7 |
[root@www ~]# wget http://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz [root@www ~]# tar xvf autoconf-2.69.tar.gz [root@www ~]# cd autoconf-2.69 [root@www autoconf-2.69]# ./configure --prefix=/usr [root@www autoconf-2.69]# make && make install [root@www autoconf-2.69]# autoconf --version autoconf (GNU Autoconf) 2.69 |
编译安装Twemproxy
1 2 3 4 |
[root@www ~]# cd /root/twemproxy/ [root@www twemproxy]# autoreconf -fvi [root@www twemproxy]# ./configure --prefix=/etc/twemproxy CFLAGS="-DGRACEFUL -g -O2" --enable-debug=full [root@www twemproxy]# make && make install |
如果autoreconf -fvi时报如下错误,就是要安装libtool工具,需要依赖libtool(如果是CentOS直接使用yum install libtool即可,如果是Debian直接使用apt-get install libtool即可)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
autoreconf: Entering directory `.' autoreconf: configure.ac: not using Gettext autoreconf: running: aclocal --force -I m4 autoreconf: configure.ac: tracing autoreconf: configure.ac: adding subdirectory contrib/yaml-0.1.4 to autoreconf autoreconf: Entering directory `contrib/yaml-0.1.4' autoreconf: configure.ac: not using Autoconf autoreconf: Leaving directory `contrib/yaml-0.1.4' autoreconf: configure.ac: not using Libtool autoreconf: running: /usr/bin/autoconf --force configure.ac:36: error: possibly undefined macro: AC_PROG_LIBTOOL If this token and others are legitimate, please use m4_pattern_allow. See the Autoconf documentation. autoreconf: /usr/bin/autoconf failed with exit status: 1 |
Twemproxy添加配置文件
1 2 |
[root@www twemproxy]# mkdir /etc/twemproxy/conf [root@www twemproxy]# cat /etc/twemproxy/conf/nutcracker.yml |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
redis-cluster: listen: 0.0.0.0:22122 hash: fnv1a_64 distribution: ketama timeout: 400 backlog: 65535 preconnect: true redis: true server_connections: 1 auto_eject_hosts: true server_retry_timeout: 60000 server_failure_limit: 3 servers: - 172.16.0.172:6546:1 redis01 - 172.16.0.172:6547:1 redis02 |
配置选项介绍:
redis-cluster:给这个配置段取一个名字,可以有多个配置段;
listen:设置监控IP和端口端口;
hash:具体的hash函数,支持md5,crc16,crc32,finv1a_32,fnv1a_64,hsieh,murmur,jenkins等十多种,一般选用fnv1a_64可以了,默认也是fnv1a_64;
hash_tag:hash_tag允许根据key的一个部分来计算key的hash值。hash_tag由两个字符组成,一个是hash_tag的开始,另外一个是hash_tag的结束,在hash_tag的开始和结束之间,是将用于计算key的hash值的部分,计算的结果会用于选择服务器。例如:如果hash_tag被定义为”{}”,那么key值为”user:{user1}:ids”和”user:{user1}:tweets”的hash值都是基于”user1”,最终会被映射到相同的服务器。而”user:user1:ids”将会使用整个key来计算hash,可能会被映射到不同的服务器。
distribution:指定哈希算法,这个哈希算法决定通过上面hash后的key如何分布在多个server上,默认是”ketama“一致性哈希。ketama:ketama一致性hash算法,会根据服务器构造出一个hash ring,并为ring上的节点分配hash范围。ketama的优势在于单个节点添加、删除之后,会最大程度上保持整个群集中缓存的key值可以被重用。modula:modula非常简单,就是根据key值的hash值取模,根据取模的结果选择对应的服务器。random:random是无论key值的hash是什么,都随机的选择一个服务器作为key值操作的目标。
timeout:设置twemproxy的超时时间,当timeout被设置后,如果在timeout的时间过后还没有从服务端得到回应,这时会将超时错误信息SERVER_ERROR Connection time out发送给客户端
backlog:监听TCP的backlog(连接等待队列)的长度,默认是512。
preconnect:指定是否在系统启动时,twemproxy就建立和所有redis的连接,默认是false,一个布尔值;
redis:指定此配置段否作为Redis做代理,如果不加redis为true的话,就可以为memcached集群做代理(这就是Twemproxy作为redis或memcached集群代理的唯一区别);
redis_auth: 如果你的后端Redis开启了认证,那么就需要redis_auth指定认证的密码了;
server_connections:twemproxy与每台redis服务器的连接数,默认就是1,如果大于1,用户命令可能发到不同的连接上,可能造成命令的实际执行顺序和用户指定的不一致(类似并发);
auto_eject_hosts:是否在节点无法响应的时候剔除,默认为true,但是需要注意,节点剔除后,因为机器数减少,机器哈希位置变化,会造成部分key无法命中,但是不剔除程序连接就会报错;
server_retry_timeout:控制服务器连接的时间间隔,单位是毫秒,在auto_eject_host被设置为true的时候产生作用,默认是30000毫秒;
server_failure_limit:Redis连续超时的次数,超过这个次数就视其为无法连接,如果auto_eject_hosts设置为true,那么此Redis会被移除;
servers:一个pool中的服务器的地址、端口和权重的列表,包括一个可选的服务器的名字,如果提供服务器的名字,将会使用它决定server的次序,从而提供对应的一致性hash的hash ring。否则,将使用server被定义的次序,可以通过两种字符串格式指定’host:port:weight’或者’host:port:weight name’。一般都是使用第二种别名的方式,这样当其中某个Redis节点出现问题时,可以直接添加一个新的Redis节点但服务器名字不要改变,这样twemproxy还是使用相同的服务器名称进行hash ring,所以其他数据节点的数据不会出现问题(只有挂点的机器数据丢失)。
PS:要严格按照Twemproxy配置文件的格式来,不然就会有语法错误;另外,在Twemproxy的配置文件中可以同时设置代理Redis集群或Memcached集群,只需要定义不同的配置段即可。
启动twemproxy (nutcracker)
刚已经加好了配置文件,现在测试下配置文件:
1 2 |
[root@www twemproxy]# /etc/twemproxy/sbin/nutcracker -t nutcracker: configuration file 'conf/nutcracker.yml' syntax is ok |
说明配置文件已经成功,现在开始运行nutcracker:
1 |
[root@www ~]# /etc/twemproxy/sbin/nutcracker -c /etc/twemproxy/conf/nutcracker.yml -p /var/run/nutcracker.pid -o /var/log/nutcracker.log -d |
选项说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
-h, –help #查看帮助文档,显示命令选项; -V, –version #查看nutcracker版本; -c, –conf-file=S #指定配置文件路径 (default: conf/nutcracker.yml); -p, –pid-file=S #指定进程pid文件路径,默认关闭 (default: off); -o, –output=S #设置日志输出路径,默认为标准错误输出 (default: stderr); -d, –daemonize #以守护进程运行; -t, –test-conf #测试配置脚本的正确性; -D, –describe-stats #打印状态描述; -v, –verbosity=N #设置日志级别 (default: 5, min: 0, max: 11); -s, –stats-port=N #设置状态监控端口,默认22222 (default: 22222); -a, –stats-addr=S #设置状态监控IP,默认0.0.0.0 (default: 0.0.0.0); -i, –stats-interval=N #设置状态聚合间隔 (default: 30000 msec); -m, –mbuf-size=N #设置mbuf块大小,以bytes单位 (default: 16384 bytes); |
PS:一般在生产环境中,都是使用进程管理工具来进行twemproxy的启动管理,如supervisor或pm2工具,避免当进程挂掉的时候能够自动拉起进程。
验证是否正常启动
1 2 3 4 5 |
[root@www ~]# ps aux | grep nutcracker root 20002 0.0 0.0 19312 916 ? Sl 18:48 0:00 /etc/twemproxy/sbin/nutcracker -c /etc/twemproxy/conf/nutcracker.yml -p /var/run/nutcracker.pid -o /var/log/nutcracker.log -d root 20006 0.0 0.0 103252 832 pts/0 S+ 18:48 0:00 grep nutcracker [root@www ~]# netstat -nplt | grep 22122 tcp 0 0 0.0.0.0:22122 0.0.0.0:* LISTEN 20002/nutcracker |
Twemproxy代理Redis集群
这里我们使用第一种方案在同一台主机上测试Twemproxy代理Redis集群,一个twemproxy和两个Redis节点(想添加更多的也可以)。Twemproxy就是用上面的配置了,下面只需要增加两个Redis节点。
安装配置Redis
在安装Redis之前,需要安装Redis的依赖程序tcl,如果不安装tcl在Redis执行make test的时候就会报错的哦。
1 |
[root@www ~]# yum install -y tcl |
1 |
[root@www ~]# wget https://github.com/antirez/redis/archive/3.2.0.tar.gz |
1 2 3 4 5 6 7 |
[root@www ~]# tar xvf 3.2.0.tar.gz -C /usr/local [root@www ~]# cd /usr/local/ [root@www local]# mv redis-3.2.0 redis [root@www local]# cd redis [root@www redis]# make [root@www redis]# make test [root@www redis]# make install |
配置两个Redis节点
1 2 |
[root@www ~]# mkdir /data/redis-6546 [root@www ~]# mkdir /data/redis-6547 |
1 2 3 4 5 6 7 |
[root@www ~]# cat /data/redis-6546/redis.conf daemonize yes pidfile /var/run/redis/redis-server.pid port 6546 bind 0.0.0.0 loglevel notice logfile /var/log/redis/redis-6546.log |
1 2 3 4 5 6 7 |
[root@www ~]# cat /data/redis-6547/redis.conf daemonize yes pidfile /var/run/redis/redis-server.pid port 6547 bind 0.0.0.0 loglevel notice logfile /var/log/redis/redis-6547.log |
PS:简单提供两个Redis配置文件,如果开启了Redis认证,那么在twemproxy中也需要填写Redis密码。
启动两个Redis节点
1 2 |
[root@www ~]# /usr/local/redis/src/redis-server /data/redis-6546/redis.conf [root@www ~]# /usr/local/redis/src/redis-server /data/redis-6547/redis.conf |
1 2 3 |
[root@www ~]# ps aux | grep redis root 23656 0.0 0.0 40204 3332 ? Ssl 20:14 0:00 redis-server 0.0.0.0:6546 root 24263 0.0 0.0 40204 3332 ? Ssl 20:16 0:00 redis-server 0.0.0.0:6547 |
验证Twemproxy读写数据
首先twemproxy配置项中servers的主机要配置正确,然后连接Twemproxy的22122端口即可测试。
1 2 3 4 5 6 7 8 |
[root@www ~]# redis-cli -p 22122 127.0.0.1:22122> set key vlaue OK 127.0.0.1:22122> get key "vlaue" 127.0.0.1:22122> FLUSHALL Error: Server closed the connection 127.0.0.1:22122> quit |
上面我们set一个key,然后通过twemproxy也可以获取到数据,一切正常。但是在twemproxy中使用flushall命令就不行了,不支持。
然后我们去找分别连接两个redis节点,看看数据是否出现在某一个节点上了,如果有,就说明twemproxy正常运行了。
1 2 3 4 |
[root@www ~]# redis-cli -p 6546 127.0.0.1:6546> get key (nil) 127.0.0.1:6546> |
1 2 3 4 |
[root@www ~]# redis-cli -p 6547 127.0.0.1:6547> get key "vlaue" 127.0.0.1:6547> |
由上面的结果我们可以看到,数据存储到6547节点上了。目前没有很好的办法明确知道某个key存储到某个后端节点了。
如何Reload twemproxy?
由于twemproxy没有提供启动脚本,都是命令行参数启动的。所以,无法使用对twemproxy进行reload的操作,在生产环境中,一个应用无法reload(重载配置文件)是一个灾难。当你对twemproxy进行增删节点时如果直接使用restart的话势必会影响线上的业务。所以最好的办法还是reload,既然twemproxy没有提供,那么可以使用kill命令带一个信号,然后跟上twemproxy主进程的进行号即可。
1 |
kill -SIGHUP PID |
注意,PID就是twemproxy master进程。
下一篇来说说,Twemproxy增加或剔除Redis节点后对数据的影响范围,哈希算法使用一致性哈希(ketama)。