架构图
机器说明
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是在同一机器上,通过不同的端口来启动
安装目录
/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还不能应用到生产环境:
配置
keepalived+twemproxy
vim /etc/keepalived/keepalived.conf
vrrp_script chk_nutcraker {
script "</dev/tcp/127.0.0.1/63790" #监测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 + nopreempt,表示不抢占,避免VIP不必要的漂移;为了使得初始时VIP绑定在10.75.201.67上,配置10.75.201.67的优先级为101,10.75.201.66为100
vim /home/redis/nutcracker/conf/nutcracker.yml
nutcrakerB:
listen: 0.0.0.0:63790 #nutcraker在端口63790启动。keepalived应该监控该端口
hash: one_at_a_time
hash_tag: "{}"
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: "{}"
distribution: modula
这三行配置在测试时可采用,可以准确地知道数据将会保存在哪台机器:
distribution: modula表示根据key值的hash值取模,根据取模的结果选择对应的服务器
hash_tag: "{}"表示计算hash值时,只取key中包含在{}里面的那部分来计算
one_at_a_time计算hash值的,java版本的实现:
private static int oneAtATime (String k) {
int hash = 0;
try {
for (byte bt : k.getBytes("utf-8")) {
hash += (bt & 0xFF);
hash += (hash << 10);
hash ^= (hash >>> 6);
}
hash += (hash << 3);
hash ^= (hash >>> 11);
hash += (hash << 15);
} catch (Exception e) {
e.printStackTrace();
}
return hash;
}
测试可得:
oneAtATime("a") % 2得到0
oneAtATime("b") % 2得到1
因此,zzz{a}xxx=yyy这样的键值对会保存在10.75.201.26,而xxx{b}yyy=zzz则保存在10.75.201.68
生产环境可采用:
hash: fnv1a_64
distribution: ketama
Redis Cluster
目录结构:
|--/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+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
"a1"
注意客户端有可能:
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 greater than server_failure_limit: value, which ensures that the original request has a chance to make it to a live server.
因此代码里可以这样写:
int retryTimes = 2;
boolean done = false;
while (!done && retryTimes > 0) {
try {
bean.getRedisTemplate().opsForHash().put("{a}4", "a4".hashCode(),"a4");
done = true;
} catch (Exception e) {
e.printStackTrace();
} finally {
retryTimes--;
}
}
代码略显丑陋,不知为什么RedisTemplate没有类似retryTimes这样的参数
部署说明就到这里

- 大小: 35.5 KB

- 大小: 103.6 KB
分享到:
相关推荐
2. Codis和Twemproxy:适用于大规模Redis集群,提供分片和负载均衡功能, Codis是在Redis Sentinel推出前的开源解决方案之一。 3. Redis Sentinel:官方推荐的高可用性工具,能够监控Redis服务器状态,自动进行故障...
Redis高性能集群之Twemproxy of Redis 数据存储 MongoDB NOSQL简介及MongoDB支持的数据类型分析 MongoDB可视化客户端及JavaApi实践 手写基于MongoDB的ORM框架 MongoDB企业级集解决方案 MongoDB聚合、索引及...
在集群模式下,Redis还支持通过Twemproxy等方式来实现集群部署。 当在Java环境下使用Redis时,首先需要准备开发环境,并熟悉如何使用Jedis等客户端库与Redis服务器交互。开发者可以通过操作字符串、集合、哈希表、...
Redis集群方案设计与实现 ...总的来说,构建Redis集群是一项复杂但必要的任务,需要综合考虑扩展性、高可用性和数据安全性等多个因素。通过合理选择和配置集群方案,可以有效地支撑大规模的推荐系统和其他业务需求。
7、Redis集群的批量数据查询性能优化:对于分布式的Redis集群,数据在多个实例中分布式存储,如果要优化大批量数据的批量查询性能,就需要采用hash tag分片路由+mget单分批大批量读取的优化设计。 8、高可用架构...