reids集群实战
1.Redis压力测试
./redis-benchmark -h 127.0.0.1
-c Number of parallel connections (default 50)
-n Total number of requests (default 100000)
-d Data size of SET/GET value in bytes (default 2)
2.Redis实现分表分库
2.1Redis数据压力
如果mysql压力不够,使用mycat
如果tomcat压力不够,使用nginx
如果redis内存不够呢?
实现分表分库
2.2分库思路
不管数据库还是客户的缓存都找代理(网关)
对Key进行路由(这里是通过Key的长度取模)
把数据存到相应Redis服务器
2.3代码解析
2.3.1服务器配置
2.3.2初始化
static {
servers.add("127.0.0.1:6379");
servers.add("127.0.0.1:6380");
servers.add("127.0.0.1:6381");
}
2.3.3主体方法
// 最简单的代理实现负载均衡
public static void main(String[] args) throws Exception {
// 监听端口
ServerSocket serverSocket = new ServerSocket(19000);
Socket socket;
while ((socket = serverSocket.accept()) != null) {
try {
while(true) {
System.out.println("一个链接....");
InputStream inputStream = socket.getInputStream();
byte[] request = new byte[1024];
inputStream.read(request);
// 解析请求 RESP
String req = new String(request);
System.out.println("收到请求:");
System.out.println(req);
String[] params = req.split("\r\n");
// 获取key的长度
int keyLenth = Integer.parseInt(params[3].split("\\$")[1]);
// 根据key长度取模
int mod = keyLenth % servers.size();
// 根据取模结果获取地址
System.out.println("根据算法选择服务器:" + servers.get(mod));
//127.0.0.1:6380
String[] serverInfo = servers.get(mod).split(":");
// 处理请求
Socket client = new Socket(serverInfo[0], Integer.parseInt(serverInfo[1]));
client.getOutputStream().write(request);
// 返回结果
byte[] response = new byte[1024];
client.getInputStream().read(response);
client.close();
socket.getOutputStream().write(response);
System.out.println("##############打印结束");
System.out.println();
}
}catch (Exception e) {
}
}
}
3.Redis实现读写分离
3.1读写分离概述
主节点负责写数据
从节点负责读数据
3.2主从同步配置
slave-read-only yes
配置
slaveof 127.0.0.1 6379
分别在两台服务器 使用 info replication
新增第三台服务器,实现水平扩容
3.3代码解析
private static void open () throws Exception {
// 监听端口
ServerSocket serverSocket = new ServerSocket(19000);
Socket socket;
while ((socket = serverSocket.accept()) != null) {
try {
while(true) {
System.out.println("一个链接....");
InputStream inputStream = socket.getInputStream();
byte[] request = new byte[1024];
inputStream.read(request);
// 解析请求
String req = new String(request);
System.out.println("收到请求:");
System.out.println(req);
String[] params = req.split("\r\n");
String param = params[2];
String[] serverInfo = null;
if("SET".equals(param)) {
//127.0.0.1:6379
serverInfo = master.split(":");
}else {
int idx = new Random().nextInt(slaveofServers.size());
serverInfo = slaveofServers.get(idx).split(":");
}
// 处理请求
Socket client = new Socket(serverInfo[0], Integer.parseInt(serverInfo[1]));
client.getOutputStream().write(request);
//使用的主机是
System.out.println("使用的主机:"+Arrays.toString(serverInfo));
// 返回结果
byte[] response = new byte[1024];
client.getInputStream().read(response);
client.close();
socket.getOutputStream().write(response);
System.out.println();
}
}catch (Exception e) {
}
}
}
4.哨兵
4.1概念
sentinal,中文名是哨兵
哨兵是redis集群架构中非常重要的一个组件,主要功能如下
集群监控,负责监控redis master和slave进程是否正常工作
消息通知,如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
故障转移,如果master node挂掉了,会自动转移到slave node上
配置中心,如果故障转移发生了,通知client客户端新的master地址
4.2Redis哨兵原理
- 哨兵至少需要3个实例,来保证自己的健壮性。
- 哨兵 + redis主从的部署架构,是不会保证数据零丢失的,只能保证redis集群的高可用性。
- 对于哨兵 + redis主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。
4.3哨兵配置
4.3.1创建并修改sentinel.conf
port 26379 // 当前Sentinel服务运行的端口
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 15000
protected-mode no
1.port :当前Sentinel服务运行的端口
2.sentinel monitor mymaster 127.0.0.1 6379 2:Sentinel监视一个名为mymaster的主redis实例这个主实例的IP地址为本机地址127.0.0.1 端口号为6379, 失效至少需要2个 Sentinel进程的同意
3.sentinel down-after-milliseconds mymaster 5000,指定了Sentinel认为Redis实例已经失效所需的毫秒数。
4.sentinel parallel-syncs mymaster 1
指定了在执行故障转移时,最多可以有多少个从Redis实例在同步新的主实例,在从Redis实例较多的情况下这个数字越小,同步的时间越长,完成故障转移所需的时间就越长
5.sentinel failover-timeout mymaster 15000
如果在该时间(ms)内未能完成failover操作,则认为该failover失败
4.3.2启动
启动sentinel服务:
./redis-sentinel ../etc/sentinel_26379.conf &
./redis-sentinel ../etc/sentinel_26380.conf &
./redis-sentinel ../etc/sentinel_26381.conf &
./redis-server ../etc/redis6379.conf &
./redis-server ../etc/redis6380.conf &
./redis-server ../etc/redis6381.conf &
./redis-cli -p 26379
4.4Redis哨兵代码
@Test
public void testSentinel() {
String masterName = "mymaster";
String password = "12345678";
// 设置参数
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(10);
jedisPoolConfig.setMaxIdle(5);
// 哨兵信息,注意填写哨兵的地址
Set<String> sentinels = new HashSet<String>();
sentinels.add("192.168.244.10:26379");
sentinels.add("192.168.244.10:26380");
sentinels.add("192.168.244.10:26381");
// 创建连接池
JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels, jedisPoolConfig,password);
//JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels,jedisPoolConfig, password);
// 获取客户端
Jedis jedis = pool.getResource();
// 执行两个命令
jedis.set("name", "sentinel-name");
String value = jedis.get("name");
System.out.println(value);
}