目录
Redis 之 使用连接池管理Redis6.0以上的连接 及 消息的订阅与发布
使用连接池管理Redis连接
从Redis 6.0开始,Redis可支持使用多线程来接收、处理客户端命令,因此应用程序可使用连接池来管理Redis连接。
上一章讲的是创建单个连接来操作redis数据库,这次使用连接池来操作redis数据库
Lettuce连接池 支持需要 Apache Commons Pool2 的支持,需要添加该依赖
接下来即可在程序中通过类似如下代码片段来创建连接池了。
var conf = new GenericObjectPoolConfig<StatefulRedisConnection<String, String>>();
conf.setMaxTotal(20); // 设置连接池中允许的最大连接数
// 创建连接池对象(其中连接由redisClient的connectPubSub方法创建)
pool = ConnectionPoolSupport.createGenericObjectPool(redisClient::connect, conf);
代码演示
创建连接池对象,创建两个消息订阅者和一个消息发布者,然后操作redis数据库
1、添加依赖
Subscriper 第一个消息订阅者
启动这个消息订阅者的程序
Subscriper 第二个消息订阅者
直接拷贝第一个消息订阅者,然后修改这个消息订阅者只订阅 c2 这个channel 主题
Publisher 消息发布者
也是拷贝消息订阅者的代码,因为创建连接池对象的代码都是一样的。
这里只需要把消息订阅的方法改成消息发布的方法就可以了,其他代码一样。
测试:
测试成功
消息发布者成功发布消息
消息订阅者也能接收到各自订阅的channel的消息
用小黑窗测试也没有问题
完整代码
Subscriper
package cn.ljh.app;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.ScoredValue;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import io.lettuce.core.pubsub.RedisPubSubAdapter;
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
import io.lettuce.core.pubsub.api.async.RedisPubSubAsyncCommands;
import io.lettuce.core.pubsub.api.sync.RedisPubSubCommands;
import io.lettuce.core.support.ConnectionPoolSupport;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import java.time.Duration;
//使用 Lettuce ,这个类是消息订阅者
//通过连接池操作redis数据库
public class Subscriper
{
private RedisClient redisClient;
//连接池pool对象
private GenericObjectPool<StatefulRedisPubSubConnection<String, String>> pool;
public void init()
{
//1、定义RedisURI
RedisURI uri = RedisURI.builder()
.withHost("127.0.0.1")
.withPort(6379)
//选择redis 16个数据库中的哪个数据库
.withDatabase(0)
.withPassword(new char[]{'1', '2', '3', '4', '5', '6'})
.withTimeout(Duration.ofMinutes(5))
.build();
//2、创建 RedisClient 客户端
this.redisClient = RedisClient.create(uri);
//创建连接池的配置对象
//GenericObjectPoolConfig<StatefulRedisConnection<String, String>> conf = new GenericObjectPoolConfig<StatefulRedisConnection<String, String>>();
var conf = new GenericObjectPoolConfig<StatefulRedisPubSubConnection<String, String>>();
//设置连接池允许的最大连接数
conf.setMaxTotal(20);
//3、创建连接池对象(其中连接由 redisClient 的 connectPubSub 方法创建)
pool = ConnectionPoolSupport.createGenericObjectPool(this.redisClient::connectPubSub, conf);
}
//关闭资源
public void closeResource()
{
//关闭连接池--先开后关
this.pool.close();
//关闭RedisClient 客户端------最先开的最后关
this.redisClient.shutdown();
}
//订阅消息的方法
public void subscribe() throws Exception
{
//从连接池中取出连接
StatefulRedisPubSubConnection<String, String> conn = this.pool.borrowObject();
//4、创建 RedisPubSubCommands -- 作用相当与 RedisTemplate 这种,有各种操作redis的方法
RedisPubSubCommands cmd = conn.sync();
//监听消息:消息到来时,是通过监听器来实现的
conn.addListener(new RedisPubSubAdapter<>()
{
//匿名内部类重写这3个方法:收到消息、订阅主题、取消订阅主题
//接收来自普通的channel的消息,就用这个方法(就是没带模式的,比如那些主从、集群模式,点进RedisPubSubAdapter类里面看)
//接收消息的方法
@Override
public void message(String channel, String message)
{
System.err.printf("从 %s 收到消息 : %s\n " , channel , message);
}
//订阅普通channel激发的方法,
//订阅主题的方法--下面有这个订阅的方法cmd.subscribe("c1", "c2");
//不太清楚这个 subscribed方法 和 下面的 cmd.subscribe 方法的关联 todo
@Override
public void subscribed(String channel, long count)
{
System.err.println("完成订阅 :" + count);
}
//不订阅普通的channel所使用方法--取消订阅
//取消订阅的方法
@Override
public void unsubscribed(String channel, long count)
{
System.err.println("取消订阅");
}
});
//订阅消息------订阅了 c1 和 c2 这两个主题 channel
cmd.subscribe("c1", "c2");
}
public static void main(String[] args) throws Exception
{
Subscriper subscriper = new Subscriper();
subscriper.init();
subscriper.subscribe();
//改程序只订阅了60分钟,超过60分钟就程序就退出不订阅了
Thread.sleep(600000);
//关闭资源
subscriper.closeResource();
}
}
Subscriper2
package cn.ljh.app;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.pubsub.RedisPubSubAdapter;
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
import io.lettuce.core.pubsub.api.sync.RedisPubSubCommands;
import io.lettuce.core.support.ConnectionPoolSupport;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import java.time.Duration;
//使用 Lettuce ,这个类是消息订阅者2
//通过连接池操作redis数据库
public class Subscriper2
{
private RedisClient redisClient;
//连接池pool对象
private GenericObjectPool<StatefulRedisPubSubConnection<String, String>> pool;
public void init()
{
//1、定义RedisURI
RedisURI uri = RedisURI.builder()
.withHost("127.0.0.1")
.withPort(6379)
//选择redis 16个数据库中的哪个数据库
.withDatabase(0)
.withPassword(new char[]{'1', '2', '3', '4', '5', '6'})
.withTimeout(Duration.ofMinutes(5))
.build();
//2、创建 RedisClient 客户端
this.redisClient = RedisClient.create(uri);
//创建连接池的配置对象
//GenericObjectPoolConfig<StatefulRedisConnection<String, String>> conf = new GenericObjectPoolConfig<StatefulRedisConnection<String, String>>();
var conf = new GenericObjectPoolConfig<StatefulRedisPubSubConnection<String, String>>();
//设置连接池允许的最大连接数
conf.setMaxTotal(20);
//3、创建连接池对象(其中连接由 redisClient 的 connectPubSub 方法创建)
pool = ConnectionPoolSupport.createGenericObjectPool(this.redisClient::connectPubSub, conf);
}
//关闭资源
public void closeResource()
{
//关闭连接池--先开后关
this.pool.close();
//关闭RedisClient 客户端------最先开的最后关
this.redisClient.shutdown();
}
//订阅消息的方法
public void subscribe() throws Exception
{
//从连接池中取出连接
StatefulRedisPubSubConnection<String, String> conn = this.pool.borrowObject();
//4、创建 RedisPubSubCommands -- 作用相当与 RedisTemplate 这种,有各种操作redis的方法
RedisPubSubCommands cmd = conn.sync();
//监听消息:消息到来时,是通过监听器来实现的
conn.addListener(new RedisPubSubAdapter<>()
{
//接收来自普通的channel的消息,就用这个方法(就是没带模式的,比如那些主从、集群模式,点进RedisPubSubAdapter类里面看),
@Override
public void message(String channel, String message)
{
System.err.printf("从 %s 收到消息 : %s\n " , channel , message);
}
//订阅普通channel激发的方法,
@Override
public void subscribed(String channel, long count)
{
System.err.println("完成订阅 :" + count);
}
//不订阅普通的channel所使用方法
@Override
public void unsubscribed(String channel, long count)
{
System.err.println("取消订阅");
}
});
//订阅消息------订阅了 c2 这个主题 channel
cmd.subscribe( "c2");
}
public static void main(String[] args) throws Exception
{
Subscriper2 subscriper2 = new Subscriper2();
subscriper2.init();
subscriper2.subscribe();
//改程序只订阅了60分钟,超过60分钟就程序就退出不订阅了
Thread.sleep(600000);
//关闭资源
subscriper2.closeResource();
}
}
Publisher
package cn.ljh.app;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.pubsub.RedisPubSubAdapter;
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
import io.lettuce.core.pubsub.api.sync.RedisPubSubCommands;
import io.lettuce.core.support.ConnectionPoolSupport;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import java.time.Duration;
//消息发布者
//通过连接池操作redis数据库
public class Publisher
{
private RedisClient redisClient;
//连接池pool对象
private GenericObjectPool<StatefulRedisPubSubConnection<String, String>> pool;
public void init()
{
//1、定义RedisURI
RedisURI uri = RedisURI.builder()
.withHost("127.0.0.1")
.withPort(6379)
//选择redis 16个数据库中的哪个数据库
.withDatabase(0)
.withPassword(new char[]{'1', '2', '3', '4', '5', '6'})
.withTimeout(Duration.ofMinutes(5))
.build();
//2、创建 RedisClient 客户端
this.redisClient = RedisClient.create(uri);
//创建连接池的配置对象
//GenericObjectPoolConfig<StatefulRedisConnection<String, String>> conf = new GenericObjectPoolConfig<StatefulRedisConnection<String, String>>();
var conf = new GenericObjectPoolConfig<StatefulRedisPubSubConnection<String, String>>();
//设置连接池允许的最大连接数
conf.setMaxTotal(20);
//3、创建连接池对象(其中连接由 redisClient 的 connectPubSub 方法创建)
pool = ConnectionPoolSupport.createGenericObjectPool(this.redisClient::connectPubSub, conf);
}
//关闭资源
public void closeResource()
{
//关闭连接池--先开后关
this.pool.close();
//关闭RedisClient 客户端------最先开的最后关
this.redisClient.shutdown();
}
//订阅消息的方法
public void publish() throws Exception
{
//从连接池中取出连接
StatefulRedisPubSubConnection<String, String> conn = this.pool.borrowObject();
//4、创建 RedisPubSubCommands -- 作用相当与 RedisTemplate 这种,有各种操作redis的方法
RedisPubSubCommands cmd = conn.sync();
//向这两个channel主题各自发布了一条消息
cmd.publish("c2","c2 c2 c2 这是一条来自 c2 这个channel 里面的消息");
cmd.publish("c1","c1 c1 c1 这是一条来自 c1 这个channel 里面的消息");
//关闭资源
redisClient.shutdown();
}
//发送消息,消息发出去,程序就退出了
public static void main(String[] args) throws Exception
{
Publisher subscriper2 = new Publisher();
subscriper2.init();
subscriper2.publish();
subscriper2.closeResource();
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.ljh</groupId>
<artifactId>Lettucepool</artifactId>
<version>1.0.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 引入 Lettuce 这个操作redis的框架的依赖 -->
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.1.4.RELEASE</version>
</dependency>
<!-- 创建连接池对象的依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
</project>