java使用jedis连接redis
在 Java 中操作 Redis 通常使用 Jedis 或 Lettuce 这两个库。Jedis简单易用,适合大部分使用场景,Lettuce支持异步和反应式编程,适合需要高并发和非阻塞操作的应用。这里使用jedis来连接redis操作。
添加依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.3</version>
</dependency>
使用jedis操作redis和使用jdbc操作数据库差不多,jedis将redis的命令行操作指令都封装到一个Jedis类里。操作方法名也基本上和redis的命令行命令相同。
获取Jedis对象
获取Jedis对象可以直接通过其构造函数获取连接
Jedis jedis = new Jedis("localhost",6379);
熟悉数据库jdbc操作的肯定知道,在实际使用中一般对应这种数据库连接使用连接池,来提高操作效率。jedis也有对应的JedisPool来创建连接池。
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(3);
config.setMinIdle(1);
config.setMaxTotal(5);
JedisPool pool = new JedisPool(config,"localhost", 6379, 3000, null);
Jedis jedis = pool.getResource();
连接池和其它连接池一样,有最大连接数、空闲数等几个固定参数配置。
基本数据类型操作
jedis封装的操作方法基本上和redis 命令行命令是一一对应,还是来简单看下基本操作
String
//设置key
jedis.set("name","hello");
//获取key
System.out.println(jedis.get("name"));
//设置一个不存在的key
Long setnx = jedis.setnx("22222", "1");
List
//添加元素
jedis.lpush("lang","java");
jedis.lpush("lang","c","python","go","php","js");
//获取元素
System.out.println(jedis.lpop("lang"));
//根据范围获取元素列表
List<String> list = jedis.lrange("lang", 0, 2);
System.out.println(list);
Hash
//设置元素
jedis.hset("user:001","name","test01");
jedis.hset("user:001","code","001");
jedis.hset("user:002",new HashMap<String, String>(){{
put("name","test02");
put("code","002");
put("date","2024-09-04");
}}
);
//获取元素
System.out.println(jedis.hget("user:002","name"));
//删除某个属性
Long hdel = jedis.hdel("user:002", "date");
Set
//添加元素
jedis.sadd("color",new String[]{"red","blue","green"});
jedis.sadd("color","yellow");
//集合中元素数量
System.out.println(jedis.scard("color"));
//元素是否在集合中
System.out.println(jedis.sismember("color","pink"));
//从集合中取指定数量的元素
List<String> color1 = jedis.srandmember("color", 1);
System.out.println(jedis.spop("color"));;
//取集合所有元素
Set<String> color = jedis.smembers("color");
System.out.println(color);
Sorted Set
//添加元素
jedis.zadd("zset",1,"one");
jedis.zadd("zset",2,"two");
jedis.zadd("zset",new HashMap<String,Double>(){{
put("three",3d);
put("four",4d);
}});
//获取元素
Set<String> zset = jedis.zrange("zset", 0, 2);
System.out.println(zset);
连接sentinel
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(3);
config.setMinIdle(1);
config.setMaxTotal(5);
String masterName = "";
Set<String> stlSet = new HashSet<>(){{
add(new HostAndPort("127.0.0.1",26379).toString()) ;
add(new HostAndPort("127.0.0.1",26380).toString()) ;
add(new HostAndPort("127.0.0.1",26381).toString()) ;
}};
JedisSentinelPool pool = new JedisSentinelPool(masterName,stlSet,config,3000,null);
Jedis jedis = pool.getResource();
使用上面的数据连接池来进行数据操作
Jedis jedis = pool.getResource();
System.out.println(jedis.get("name"));
try {
Thread.sleep(1000000);
} catch (InterruptedException exception) {
exception.printStackTrace();
}
jedis.close();
pool.close();
这里线程暂停一下,然后在服务器上查看连接信息,客户端ip是192.168.1.101
# netstat -anp|grep '192.168.1.101'
tcp 0 0 172.31.164.136:26381 192.168.1.101:4077 ESTABLISHED 336/./src/redis-sen
tcp 0 0 172.31.164.136:26380 192.168.1.101:36303 ESTABLISHED 327/./src/redis-sen
tcp 0 0 172.31.164.136:26382 192.168.1.101:2049 ESTABLISHED 345/./src/redis-sen
tcp 0 0 172.31.164.136:6381 192.168.1.101:15977 ESTABLISHED 32080/./src/redis-s
这里看到客户端程序和sentinel三个端口服务(26380,26381,26382)建立了连接,这是没问题的。然后还有一条6381的连接,6381是master节点的服务端口。也就是Sentinel 实例负责监控 Redis 集群中的 master 和 slave 节点,Sentinel 实例负责监控 Redis 集群中的 master 和 slave 节点。客户端根据 Sentinel 提供的 master 节点信息,与 master 节点建立连接。所有读写操作都会通过这个连接进行。
下面来模拟一个故障转移例子:
1、首先客户端使用循环不断从连接池中获取连接执行操作
while (true){
Jedis jedis = pool.getResource();
System.out.println(jedis.get("name"));
System.out.println("server信息:"+jedis.getClient().getHost()+":"+jedis.getClient().getPort());
try {
Thread.sleep(2000);
} catch (InterruptedException exception) {
exception.printStackTrace();
}
jedis.close();
}
2、看下此时的sentinel状态
127.0.0.1:26381> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.1.101:6382,slaves=2,sentinels=3
这个时候主节点是6382,状态是ok
3、启动客户端程序
观察日志信息
server信息:192.168.1.101:6382
server信息:192.168.1.101:6382
server信息:192.168.1.101:6382
server信息:192.168.1.101:6382
这里一切正常,连接的是master 6382。
4、停止master
./src/redis-cli -p 6382
127.0.0.1:6382> shutdown
先观察服务器sentinel状态
127.0.0.1:26381> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.1.101:6382,slaves=2,sentinels=3
127.0.0.1:26381> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=sdown,address=192.168.1.101:6382,slaves=2,sentinels=3
127.0.0.1:26381> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=odown,address=192.168.1.101:6382,slaves=2,sentinels=3
127.0.0.1:26381> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.1.101:6380,slaves=2,sentinels=3
这里看下master的状态。首先原主节点6382经历了sdown和odown两个状态。
sdown:(Subjectively Down)主观下线。当一个 Redis Sentinel 实例检测到主节点可能出现了问题时,它将主节点标记为 sdown
。这表示 Sentinel 对主节点的状态有怀疑,但还没有确认。此时,Sentinel 会继续监控主节点,并试图获取更多的确认信息。
odown:(Objectively Down)客观下线。当达到配置中的 quorum
(即至少有足够多的 Sentinel 实例)确认主节点真的不可用时,主节点会被标记为 odown
。odown
状态是经过多个 Sentinel 实例确认的客观下线状态。此时,Sentinel 会触发故障转移流程。
故障转移之后选择了6380作为master。
客户端
在停掉原master后,客户端会有一段时间无法获取连接,Connection refused。等到故障转移之后自动连接到新master 6380上。
连接cluster
使用jedis提供的JedisCluster对象来操作cluster
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(3);
config.setMinIdle(1);
config.setMaxTotal(5);
String host = "192.168.1.101";
Set<HostAndPort> clusterNodes = new HashSet<HostAndPort>(){{
add(new HostAndPort(host,7001));
add(new HostAndPort(host,7002));
add(new HostAndPort(host,7003));
}};
JedisCluster cluster = new JedisCluster(clusterNodes,5000,10000,3,null,config);
System.out.println(cluster.get("name"));