设为首页 收藏本站
查看: 2680|回复: 0

[经验分享] keepalived+twemproxy部署redis高可用集群

[复制链接]

尚未签到

发表于 2015-11-19 15:31:07 | 显示全部楼层 |阅读模式
转载自:http://bylijinnan.iyunv.com/blog/2175191
keepalived+twemproxy部署redis集群高可用
    博客分类:
  • redis
redis集群高可用 架构图


DSC0000.png


机器说明

Java代码   DSC0001.png

  • 10.75.201.67:keepalived + twemproxy  
  • 10.75.201.66:keepalived + twemproxy  
  • 初始化时,VIP绑定在10.75.201.67  
  • 10.75.201.26:ClusterA(redis master A + redis slave A1)  
  • 10.75.201.68:ClusterB(redis master B + redis slave B1)  


如果机器充足的话,redis master A与redis slave A1部署在两台机器上(redis master B + redis slave B1也一样)
为实验方便,目前redis master与redis slave是在同一机器上,通过不同的端口来启动

安装目录
Java代码  

  • /home/redis:  
  • |-- nutcracker  
  • |   |-- conf  
  • |   |-- sbin  
  • |-- redis  
  • |   |-- bin  
  • |   |-- conf  
  • |   `-- logs  

版本
redis-2.8.19
nutcracker-0.4.0
keepalived-1.2.12

各框架作用:
1.keepalived提供VIP漂移,避免twemproxy的单点故障
2. twemproxy作为redis代理,可以提供一致性哈希;当它代理的某个Cluster挂掉了,它会把该Cluster移除,并把原本属于该Cluster的读写请求按哈希算法重新分派给另外的Cluster
3.ClusterA,ClusterB,ClusterC各有一主一从。可以横向扩展,增加ClusterD、ClusterE等

说明:
上述方案有个瑕疵:
当ClusterX中的redis master挂掉后,整个ClusterX就被twemproxy移除了(即使redis slave还正常)。可以通过keepalived或sentinel来使得slave可以在master挂掉时升级为master并绑定VIP。但这样意义不大,配置相对复杂(使用sentinel的例子见http://blog.youyo.info/blog/2014/05/24/redis-cluster/)
一个更完美的方案是:
https://blog.recurly.com/2014/05/clustering-redis-maximize-uptime-scale
使用了keepalived+twemproxy+smitty+sentinel
sentinel可以使得redis slave升级为master,而smitty可以监测到该变化并更新twemproxy的配置文件
但到smitty的github上看,smitty还不能应用到生产环境:


DSC0002.png


配置

keepalived+twemproxy

vim /etc/keepalived/keepalived.conf
Java代码  

  • vrrp_script chk_nutcraker {  
  •                 script &quot;</dev/tcp/127.0.0.1/63790&quot; #监测nutcraker是否正常  
  •                 interval 2  
  • }  
  • vrrp_instance VI_2 {  
  •         state BACKUP        #both BACKUP  
  •         interface eth1  
  •         virtual_router_id 12  
  •         priority 101    #101 on master, 100 on backup  
  •         nopreempt       #both nopreempt  
  •         track_script {  
  •                 chk_nutcraker  
  •         }  
  •         virtual_ipaddress {  
  •              10.75.201.3  
  •         }  
  • }  

两台keepalived都配置为BACKUP &#43; nopreempt,表示不抢占,避免VIP不必要的漂移;为了使得初始时VIP绑定在10.75.201.67上,配置10.75.201.67的优先级为101,10.75.201.66为100

vim  /home/redis/nutcracker/conf/nutcracker.yml
Java代码  

  • nutcrakerB:  
  •   listen: 0.0.0.0:63790    #nutcraker在端口63790启动。keepalived应该监控该端口  
  • hash: one_at_a_time  
  •   hash_tag: &quot;{}&quot;  
  •   distribution: modula  
  •   auto_eject_hosts: true  
  •   redis: true  
  •   server_retry_timeout: 2000  
  •   server_failure_limit: 1  
  • timeout: 400  
  •   servers:  
  •    - 10.75.201.26:6379:1    #这里只需要写Cluster中redis master的IP和端口  
  •    - 10.75.201.68:6379:1    #同上  

说明:
hash: one_at_a_time
hash_tag: &quot;{}&quot;
distribution: modula
这三行配置在测试时可采用,可以准确地知道数据将会保存在哪台机器:
distribution: modula表示根据key&#20540;的hash&#20540;取模,根据取模的结果选择对应的服务器
hash_tag: &quot;{}&quot;表示计算hash&#20540;时,只取key中包含在{}里面的那部分来计算
one_at_a_time计算hash&#20540;的,java版本的实现:
Java代码  

  • private static int oneAtATime (String k) {  
  •         int hash = 0;  
  •         try {  
  •             for (byte bt : k.getBytes(&quot;utf-8&quot;)) {  
  •                 hash &#43;= (bt & 0xFF);  
  •                 hash &#43;= (hash << 10);  
  •                 hash ^= (hash >>> 6);  
  •             }  
  •             hash &#43;= (hash << 3);  
  •             hash ^= (hash >>> 11);  
  •             hash &#43;= (hash << 15);  
  •         } catch (Exception e) {  
  •             e.printStackTrace();  
  •         }  
  •         return hash;  
  •     }  

测试可得:
oneAtATime(&quot;a&quot;) % 2得到0
oneAtATime(&quot;b&quot;) % 2得到1
因此,zzz{a}xxx=yyy这样的键&#20540;对会保存在10.75.201.26,而xxx{b}yyy=zzz则保存在10.75.201.68

生产环境可采用:
hash: fnv1a_64
distribution: ketama


Redis Cluster

目录结构:
Java代码  

  • |--/home/redis/redis  
  •    |--bin  
  •    |--6379  
  •       |--redis.conf  
  •       |--redis.pid  
  •    |--63791  
  •       |--redis.conf  
  •       |--redis.pid  


6379为redis master,63791为redis slave
需要修改redis.conf中对应的配置:

vim /home/redis/redis/6379/redis.conf
daemonize yes
pidfile /home/redis/redis/6379/redis.pid
port 6379

在63791/redis.conf中还要配置:
slaveof 127.0.0.1 6379

启动

1.启动redis
在10.75.201.26和10.75.201.68上启动:
redis-server /home/redis/redis/6379/redis.conf
redis-server /home/redis/redis/63791/redis.conf
2.启动twemproxy&#43;keepalived
先启动10.75.201.67:
nutcracker -d -c /home/redis/nutcracker/conf/nutcracker.yml
service keepalived start
再启动10.75.201.66,重复上述操作

测试验证

1.正常情况下
查看10.75.201.26的redis,6379为master,63791为slave
查看10.75.201.68的redis,6379为master,63791为slave

客户端连接并写入:

redis-cli -h 10.75.201.3 -p 63790
10.75.201.3:63790> set {a}1 a1
10.75.201.3:63790> set {b}1 b1
则{a}1=a1写到10.75.201.26,{b}1=b1写入10.75.201.68

在10.75.201.26上(:6379以及:63791):
get {a}1得到a1,get {b}1得到nil

在10.75.201.68上(:6379以及:63791)
get {a}1得到nil,get {b}1得到b1

2.把10.75.201.67上的twemproxy或keepalived进程kill掉
则VIP转移到10.75.201.66:
在10.75.201.66上执行ip add | grep eth1,输出:
eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UNKNOWN qlen 1000
    inet 10.75.201.66/24 brd 10.75.201.255 scope global eth1
    inet 10.75.201.3/32 scope global eth1
此时客户端仍可连接redis-cli -h 10.75.201.3 -p 63790并进行读写,与正常情况下没什么区别

3.把10.75.201.26的redis master进程kill掉:
lsof -i:6379
kill -9 <pid>
则客户端取不到之前写入ClusterA的数据了:
10.75.201.3:63790> get {a}1
(nil)

但ClusterA上的数据还在ClusterA-redis-slave上:
10.75.201.26:63791> get {a}1
&quot;a1&quot;

注意客户端有可能:
10.75.201.3:63790> get {a}1
(error) ERR Connection refused
10.75.201.3:63790> get {a}1
(nil)
第一次表明没有连接上,第二次表明连接上了但查询不到数据
这时需要注意客户端的重连和失败次数设置,官方文档说:

To ensure that requests always succeed in the face of server ejections (auto_eject_hosts: is enabled), some form of retry must be implemented at the client layer since nutcracker itself does not retry a request. This client-side retry count must be greaterthan server_failure_limit: value, which ensures that the original request has a chance to make it to a live server.

因此代码里可以这样写:
Java代码  

  • int retryTimes = 2;  
  • boolean done = false;  
  • while (!done && retryTimes > 0) {  
  •     try {  
  •               bean.getRedisTemplate().opsForHash().put(&quot;{a}4&quot;, &quot;a4&quot;.hashCode(),&quot;a4&quot;);  
  •               done = true;  
  •           } catch (Exception e) {  
  •               e.printStackTrace();  
  •           } finally {  
  •               retryTimes--;  
  •           }  
  • }  

代码略显丑陋,不知为什么RedisTemplate没有类&#20284;retryTimes这样的参数

部署说明就到这里

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-141262-1-1.html 上篇帖子: Nginx+keepalived双主负载均衡架构 下篇帖子: Vagrant体验之一nginx+keepalived高可用测试
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表