Java 操作 Redis 的库有两个,Jedis 和 Lettuce,目前 SpringBoot 2.x 中已经将 Jedis 换成了 Lettuce,让我们一起来看看这个东西。
Redis介绍
Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。相比Memcached它支持存储的类型相对更多(字符、哈希、集合、有序集合、列表、GEO),同时Redis是线程安全的。2010年3月15日起,Redis的开发工作由VMware主持,2013年5月开始,Redis的开发由Pivotal赞助。
Lettuce
Lettuce和Jedis的都是连接Redis Server的客户端程序。Jedis在实现上是直连redis server,多线程环境下非线程安全,除非使用连接池,为每个Jedis实例增加物理连接。Lettuce基于Netty的连接实例(StatefulRedisConnection),可以在多个线程间并发访问,且线程安全,满足多线程环境下的并发访问,同时它是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例
导入依赖
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>5.0.4.RELEASE</version>
</dependency>
1、入门
@Test
public void test1() {
// client
RedisClient client = RedisClient.create("redis://106.12.180.180");
// connection, 线程安全的长连接,连接丢失时会自动重连,直到调用 close 关闭连接。
StatefulRedisConnection<String, String> connection = client.connect();
// sync, 默认超时时间为 60s.
RedisCommands<String, String> redisCommands = connection.sync();
redisCommands.set("name", "老五");
String value = redisCommands.get("name");
System.out.println("姓名:"+value);
// close connection
connection.close();
// shutdown
client.shutdown();
}
2、集群模式
@Test
public void test2() {
//创建RedisURI的两种方式
RedisURI redisURI1 = RedisURI.create("redis://106.12.180.180:6380");
RedisURI redisURI2 = RedisURI.Builder.redis("106.12.180.180").withPort(6381).withDatabase(0).build();
List<RedisURI> list = Arrays.asList( redisURI1,redisURI2);
RedisClusterClient client = RedisClusterClient.create(list);
//超时时间
client.setDefaultTimeout(Duration.ofSeconds(20));
StatefulRedisClusterConnection<String, String> connect = client.connect();
/* 同步执行的命令 */
RedisAdvancedClusterCommands<String, String> commands = connect.sync();
commands.set("name", "老五");
String value = commands.get("name");
System.out.println("姓名:" + value);
/* 异步执行的命令 */
// RedisAdvancedClusterAsyncCommands<String, String> commands= connect.async();
// RedisFuture<String> future = commands.get("test2");
// try {
// String str = future.get();
// System.out.println(str);
// } catch (InterruptedException e) {
// e.printStackTrace();
// } catch (ExecutionException e) {
// e.printStackTrace();
// }
connect.close();
client.shutdown();
}
3、异步使用
异步调用,可以避免将 CPU 浪费在等待网络 IO 和磁盘 IO 时上,实现提高资源使用率。
基本例子
@Test
public void test3() throws Exception {
// client
RedisClient client = RedisClient.create("redis://106.12.180.184:6380");
StatefulRedisConnection<String, String> connection = client.connect();
// async
RedisStringAsyncCommands<String, String> asyncCommands = connection.async();
asyncCommands.set("name", "老五");
RedisFuture<String> future = asyncCommands.get("name");
future.thenAccept((str) -> {
System.out.println("姓名:" + str);
});
System.out.println("END");
Thread.sleep(10*1000);
connection.close();
client.shutdown();
}
END
姓名:老五
4、Reactive 调用
@Test
public void test4() throws Exception {
// client
RedisClient client = RedisClient.create("redis://106.12.180.184:6380");
// connect
StatefulRedisConnection<String, String> connection = client.connect();
// reactive
RedisStringReactiveCommands<String, String> reactiveCommands = connection.reactive();
reactiveCommands.set("name", "老五");
reactiveCommands.get("name").subscribe((str) -> {
System.out.println("姓名:" + str);
});
System.out.println("END");
Thread.sleep(10 * 1000);
connection.close();
client.shutdown();
}
END
姓名:老五
5、Pub/Sub
@Test
public void test5() throws Exception {
ExecutorService threadPool = Executors.newFixedThreadPool(2);
//发布Pub
threadPool.submit(() -> {
RedisClient client = RedisClient.create("redis://106.12.180.184:6380");
StatefulRedisPubSubConnection<String, String> pubSubConnection = client.connectPubSub();
RedisPubSubCommands<String, String> pubSubCommands = pubSubConnection.sync();
while (true) {
pubSubCommands.publish("CCTV1", "新文联播" + System.currentTimeMillis());
pubSubCommands.publish("CBA", "篮球" + System.currentTimeMillis());
pubSubCommands.publish("BCC", "动物世界" + System.currentTimeMillis());
Thread.sleep(2000);
}
});
//订阅Sub
threadPool.submit(() -> {
RedisClient client = RedisClient.create("redis://106.12.180.184:6380");
StatefulRedisPubSubConnection<String, String> pubSubConnection = client.connectPubSub();
pubSubConnection.addListener(new MyRedisPubSubListener());
RedisPubSubCommands<String, String> pubSubCommands = pubSubConnection.sync();
pubSubCommands.subscribe( "BCC");
pubSubCommands.psubscribe("CCTV*");
});
while (true){
Thread.sleep(1000);
}
}
class MyRedisPubSubListener implements RedisPubSubListener<String, String> {
//===============普通频道====================
@Override
public void subscribed(String channel, long count) {
System.out.println(toJson(new HashMap() {{
put("methodName", "订阅频道");
put("channel", channel);
put("count", count);
}}));
}
@Override
public void message(String channel, String message) {
System.out.println(toJson(new HashMap() {{
put("methodName", "从频道订阅中接收消息");
put("channel", channel);
put("message", message);
}}));
}
@Override
public void unsubscribed(String channel, long count) {
System.out.println(toJson(new HashMap() {{
put("methodName", "取消订阅频道");
put("channel", channel);
put("count", count);
}}));
}
//================给定模式频道===================
@Override
public void psubscribed(String pattern, long count) {
System.out.println(toJson(new HashMap() {{
put("methodName", "订阅给定模式频道");
put("pattern", pattern);
put("count", count);
}}));
}
@Override
public void message(String pattern, String channel, String message) {
System.out.println(toJson(new HashMap() {{
put("methodName", "从给定模式频道订阅中接收消息");
put("pattern", pattern);
put("channel", channel);
put("message", message);
}}));
}
@Override
public void punsubscribed(String pattern, long count) {
System.out.println(toJson(new HashMap() {{
put("methodName", "取消订阅给定模式频道");
put("pattern", pattern);
put("count", count);
}}));
}
private String toJson(Object obj) {
return JSONObject.toJSONString(obj);
}
}
{"channel":"BCC","count":1,"methodName":"订阅频道"}
{"pattern":"CCTV*","count":2,"methodName":"订阅给定模式频道"}
{"channel":"BCC","methodName":"从频道订阅中接收消息","message":"动物世界1577021706587"}
{"pattern":"CCTV*","channel":"CCTV1","methodName":"从给定模式频道订阅中接收消息","message":"新文联播1577021708616"}
……