redis哨兵模式-sentinel ,java客户端动态切换master

redis 的搭建模式

  1. 单机模式 : 单机服务
  2. 主从模式: 有备机,备机提供备份,和 读写分离的功能
  3. 哨兵模式: master宕机后,哨兵会选举备机替换master
  4. 集群模式: 集群能够做分布式,master宕机,备机会被升级为 master

redis哨兵模式的使用

单机模式 和 主备模式其实差不多,
哨兵模式的服务架构: master 一个,多个 slave, 多个哨兵。

部署好了redis 后,开启master
./redis-4.0/src/redis-server …/conf/redis-master-6379.conf

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#绑定的ip 地址,可多个
bind 192.168.23.11 127.0.0.1

#端口号
port 6379

tcp-backlog 511

timeout 0

tcp-keepalive 300

# 守护进程运行
daemonize yes



#pid存放路径
pidfile "/var/run/redis_6379.pid"

loglevel notice
#日志存放路径
logfile "/home/smkapp/redislog/log6379.log"

#数据库开启数量
databases 2

always-show-logo yes

开启slave
./redis-4.0/src/redis-server …/conf/redis-slave-6380.conf

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#绑定的ip 地址,可多个
bind 192.168.23.11 127.0.0.1

#端口号
port 6380

tcp-backlog 511

timeout 0

tcp-keepalive 300

# 守护进程运行
daemonize yes

#pid存放路径
pidfile "/var/run/redis_6380.pid"

loglevel notice
#日志存放路径
logfile "/home/smkapp/redislog/log6380.log"

#数据库开启数量
databases 2

slaveof  192.168.23.11 6379
always-show-logo yes

开启 哨兵
./redis-4.0/src/redis-sentinel …/conf/redis-sentinel-26379.conf

#绑定的ip 地址,可多个
bind 192.168.23.11 127.0.0.1

#端口号
port 26380

tcp-backlog 511

timeout 0

tcp-keepalive 300

# 守护进程运行
daemonize yes

#pid存放路径
pidfile "/var/run/redis_6380.pid"

loglevel notice
#日志存放路径
logfile "/home/smkapp/redislog/log6380.log"

#监控master
sentinel monitor luoyangmaster 192.168.23.11 6381 1

#指定超时时间 5秒
sentinel down-after-milliseconds luoyangmaster 5000

java 客户端

网上找了好些 哨兵模式的 JAVA客户端,没找到合适的。
自己写了一个样例,算是基本可以跑,在切换master 的时候,还是有有一定量的请求报错

package com.redis;

import java.util.HashSet;
import java.util.Set;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;

public class RedisClientSentinel {

	public static final int default_seconds = 300;

	private static RedisClientSentinel client = null;

	private JedisSentinelPool poolConfig = null;

	private RedisClientSentinel() {
		try {
			getPool();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static synchronized RedisClientSentinel getInstance() {
		if (null == client) {
			client = new RedisClientSentinel();
		}
		return client;
	}

	/**
	 * 单例模式
	 * 
	 * @return
	 */
	private  JedisSentinelPool getPool() {
		if (poolConfig == null || poolConfig.isClosed()) {
			Set<String> sentinels = new HashSet<>();
			sentinels.add("192.168.23.11:26379");
			sentinels.add("192.168.23.11:26380");

			JedisPoolConfig ab = new JedisPoolConfig();
			ab.setMinIdle(Integer.parseInt("5"));
			ab.setMaxTotal(5);
			ab.setMaxIdle(5);
			poolConfig = new JedisSentinelPool("luoyangmaster", sentinels, ab);
		}
		return poolConfig;
	}

	private Jedis getJedis() {
		//查看当前获得的 master 的ip和 端口
		System.out.println(poolConfig.getCurrentHostMaster());
		// 从哨兵里面 获取链接
		return getPool().getResource();
	}

	/**
	 * 返回值
	 * 
	 * @param key
	 * @return
	 */
	public String getStringValue(String key) {
		Jedis j = getJedis();
		String d = null;
		try {
			d = j.get(key);
			// 连接是OK的 返回到连接池里面
			poolConfig.returnResourceObject(j);
		} catch (Exception e) {
			//链接出现异常
			System.out.println("异常");
			//异常要关闭
			j.close();
		}
//		j.close();
		return d;
	}

	public static void main(String[] args) {
		
		new Thread(new Tre()).start();
		new Thread(new Tre()).start();
		new Thread(new Tre()).start();
		new Thread(new Tre()).start();
		new Thread(new Tre()).start();
		new Thread(new Tre()).start();
	}
	
	public static int i = 0;
	
	public  synchronized static  void add() {
		i++;
	}
	
	
}

class Tre implements Runnable{

	@Override
	public void run() {
		RedisClientSentinel s = RedisClientSentinel.getInstance();
		while (true) {
			try {
				String d = s.getStringValue("username");
				if(d == null) {
					RedisClientSentinel.add();
				}
				System.out.println(RedisClientSentinel.i);
			} catch (Exception e) {
				e.printStackTrace();
			}

			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}
	
}

在哨兵模式里面,获取的连接客户端 Jedis ,这个客户端连接是对接的master,
但是如果这个master 宕机了,哨兵会选举一个新的master 出来。这个时候原来的 jedis 客户端就不可用了。

针对这种情况,JAVA客户端要获取新的master 来链接。

所以在每次使用的时候,都需要从 哨兵连接池里面获取新的 客户端getPool().getResource();

在使用的时候,如果报错,就需要关闭客户端jedis.close();

连接池里面的客户端是有数量限制的,如果取出来了不还回去,那么就只能操作几次,后续都操作都阻塞了。
所以每次使用了 jedis 客户端之后,如果连接正常,就需要将连接客户端还回去poolConfig.returnResourceObject(jedis);

我这是大致研究了一下这个哨兵模式的用法,但是总觉得还有更优秀的使用方式,如有说的不对的地方,欢迎拍砖

猜你喜欢

转载自blog.csdn.net/walle167/article/details/88575568
今日推荐