Redisson客户端连接Redis(Java)
本章节介绍使用Redisson客户端连接Redis实例的方法。更多的客户端的使用方法请参考Redis客户端。
在springboot类型的项目中,spring-data-redis中提供了对jedis、lettuce的适配,但没有提供对redisson组件的适配。为了能够在springboot中集成redisson,redisson侧主动提供了适配springboot的组件:redisson-spring-boot-starter(请参考:https://mvnrepository.com/artifact/org.redisson/redisson)。
注意:在springboot1.x中默认集成的是jedis,springboot2.x中改为了lettuce。
- 如果创建Redis实例时设置了密码,使用Redisson客户端连接Redis时,需要配置密码进行连接,建议不要将明文密码硬编码在代码中。
- 连接单机、读写分离、Proxy集群实例需要使用Redisson的SingleServerConfig配置对象中的useSingleServer方法,连接主备实例需要使用Redisson的MasterSlaveServersConfig配置对象中的useMasterSlaveServers方法,Cluster集群实例需要使用ClusterServersConfig对象中的useClusterServers方法。
- Springboot版本不得低于2.3.12.RELEASE,Redisson版本不得低于3.37.0。
前提条件
- 已成功创建Redis实例,且状态为“运行中”。创建Redis实例的操作请参考购买Redis实例。
- 查看并获取待连接Redis实例的IP地址/域名和端口。具体步骤请参见查看和修改DCS实例基本信息。
Pom配置
<!-- 引入spring-data-redis组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <!-- 因springboot2.x中默认集成了lettuce,因此需要排掉该依赖 --> <exclusion> <artifactId>lettuce-core</artifactId> <groupId>io.lettuce</groupId> </exclusion> </exclusions> </dependency> <!-- 引入redisson对springboot的集成适配包 --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>${redisson.version}</version> </dependency>
基于Bean方式配置
因springboot中没有提供对redisson的适配,在application.properties配置文件自然也没有对应的配置项,只能通过基于Bean的方式注入。
- 单机、读写分离、Proxy集群实例配置
import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.codec.JsonJacksonCodec; import org.redisson.config.Config; import org.redisson.config.SingleServerConfig; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SingleConfig { @Value("${redis.address:}") private String redisAddress; @Value("${redis.password:}") private String redisPassword; @Value("${redis.database:0}") private Integer redisDatabase = 0; @Value("${redis.connect.timeout:3000}") private Integer redisConnectTimeout = 3000; @Value("${redis.connection.idle.timeout:10000}") private Integer redisConnectionIdleTimeout = 10000; @Value("${redis.connection.ping.interval:1000}") private Integer redisConnectionPingInterval = 1000; @Value("${redis.timeout:2000}") private Integer timeout = 2000; @Value("${redis.connection.pool.min.size:50}") private Integer redisConnectionPoolMinSize; @Value("${redis.connection.pool.max.size:200}") private Integer redisConnectionPoolMaxSize; @Value("${redis.retry.attempts:3}") private Integer redisRetryAttempts = 3; @Value("${redis.retry.interval:200}") private Integer redisRetryInterval = 200; @Bean public RedissonClient redissonClient(){ Config redissonConfig = new Config(); SingleServerConfig serverConfig = redissonConfig.useSingleServer(); serverConfig.setAddress(redisAddress); serverConfig.setConnectionMinimumIdleSize(redisConnectionPoolMinSize); serverConfig.setConnectionPoolSize(redisConnectionPoolMaxSize); serverConfig.setDatabase(redisDatabase); serverConfig.setPassword(redisPassword); serverConfig.setConnectTimeout(redisConnectTimeout); serverConfig.setIdleConnectionTimeout(redisConnectionIdleTimeout); serverConfig.setPingConnectionInterval(redisConnectionPingInterval); serverConfig.setTimeout(timeout); serverConfig.setRetryAttempts(redisRetryAttempts); serverConfig.setRetryInterval(redisRetryInterval); redissonConfig.setCodec(new JsonJacksonCodec()); return Redisson.create(redissonConfig); } }
- 主备实例配置
import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.codec.JsonJacksonCodec; import org.redisson.config.Config; import org.redisson.config.MasterSlaveServersConfig; import org.redisson.config.ReadMode; import org.redisson.config.SubscriptionMode; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashSet; @Configuration public class MasterStandbyConfig { @Value("${redis.master.address}") private String redisMasterAddress; @Value("${redis.slave.address}") private String redisSlaveAddress; @Value("${redis.database:0}") private Integer redisDatabase = 0; @Value("${redis.password:}") private String redisPassword; @Value("${redis.connect.timeout:3000}") private Integer redisConnectTimeout = 3000; @Value("${redis.connection.idle.timeout:10000}") private Integer redisConnectionIdleTimeout = 10000; @Value("${redis.connection.ping.interval:1000}") private Integer redisConnectionPingInterval = 1000; @Value("${redis.timeout:2000}") private Integer timeout = 2000; @Value("${redis.master.connection.pool.min.size:50}") private Integer redisMasterConnectionPoolMinSize = 50; @Value("${redis.master.connection.pool.max.size:200}") private Integer redisMasterConnectionPoolMaxSize = 200; @Value("${redis.retry.attempts:3}") private Integer redisRetryAttempts = 3; @Value("${redis.retry.interval:200}") private Integer redisRetryInterval = 200; @Bean public RedissonClient redissonClient() { Config redissonConfig = new Config(); MasterSlaveServersConfig serverConfig = redissonConfig.useMasterSlaveServers(); serverConfig.setMasterAddress(redisMasterAddress); HashSet<String> slaveSet = new HashSet<>(); slaveSet.add(redisSlaveAddress); serverConfig.setSlaveAddresses(slaveSet); serverConfig.setDatabase(redisDatabase); serverConfig.setPassword(redisPassword); serverConfig.setMasterConnectionMinimumIdleSize(redisMasterConnectionPoolMinSize); serverConfig.setMasterConnectionPoolSize(redisMasterConnectionPoolMaxSize); serverConfig.setReadMode(ReadMode.MASTER); serverConfig.setSubscriptionMode(SubscriptionMode.MASTER); serverConfig.setConnectTimeout(redisConnectTimeout); serverConfig.setIdleConnectionTimeout(redisConnectionIdleTimeout); serverConfig.setPingConnectionInterval(redisConnectionPingInterval); serverConfig.setTimeout(timeout); serverConfig.setRetryAttempts(redisRetryAttempts); serverConfig.setRetryInterval(redisRetryInterval); redissonConfig.setCodec(new JsonJacksonCodec()); return Redisson.create(redissonConfig); } }
- Cluster集群实例配置
import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.codec.JsonJacksonCodec; import org.redisson.config.ClusterServersConfig; import org.redisson.config.Config; import org.redisson.config.ReadMode; import org.redisson.config.SubscriptionMode; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.List; @Configuration public class ClusterConfig { @Value("${redis.cluster.address}") private List<String> redisClusterAddress; @Value("${redis.cluster.scan.interval:5000}") private Integer redisClusterScanInterval = 5000; @Value("${redis.password:}") private String redisPassword; @Value("${redis.connect.timeout:3000}") private Integer redisConnectTimeout = 3000; @Value("${redis.connection.idle.timeout:10000}") private Integer redisConnectionIdleTimeout = 10000; @Value("${redis.connection.ping.interval:1000}") private Integer redisConnectionPingInterval = 1000; @Value("${redis.timeout:2000}") private Integer timeout = 2000; @Value("${redis.retry.attempts:3}") private Integer redisRetryAttempts = 3; @Value("${redis.retry.interval:200}") private Integer redisRetryInterval = 200; @Value("${redis.master.connection.pool.min.size:50}") private Integer redisMasterConnectionPoolMinSize = 50; @Value("${redis.master.connection.pool.max.size:200}") private Integer redisMasterConnectionPoolMaxSize = 200; @Bean public RedissonClient redissonClient() { Config redissonConfig = new Config(); ClusterServersConfig serverConfig = redissonConfig.useClusterServers(); serverConfig.setNodeAddresses(redisClusterAddress); serverConfig.setScanInterval(redisClusterScanInterval); serverConfig.setPassword(redisPassword); serverConfig.setMasterConnectionMinimumIdleSize(redisMasterConnectionPoolMinSize); serverConfig.setMasterConnectionPoolSize(redisMasterConnectionPoolMaxSize); serverConfig.setReadMode(ReadMode.MASTER); serverConfig.setSubscriptionMode(SubscriptionMode.MASTER); serverConfig.setConnectTimeout(redisConnectTimeout); serverConfig.setIdleConnectionTimeout(redisConnectionIdleTimeout); serverConfig.setPingConnectionInterval(redisConnectionPingInterval); serverConfig.setTimeout(timeout); serverConfig.setRetryAttempts(redisRetryAttempts); serverConfig.setRetryInterval(redisRetryInterval); redissonConfig.setCodec(new JsonJacksonCodec()); return Redisson.create(redissonConfig); } }
SSL连接配置(可选配置)
当实例开启了SSL,通过SSL连接实例时,请将基于Bean方式配置中的RedissonClient构造方法clientConfiguration()中添加如下configRedissonSSL(serverConfig)逻辑,同时将redis的连接地址从redis://ip:port改为rediss://ip:port格式。Redis实例支持SSL的情况请参考配置Redis SSL数据加密传输。
private void configRedissonSSL(BaseConfig serverConfig) { TrustManagerFactory trustManagerFactory = null; try { //加载自定义路径下的ca证书,可结合具体业务配置 CertificateFactory cf = CertificateFactory.getInstance("X.509"); Certificate ca; try (InputStream is = new FileInputStream(certificationPath)) { ca = cf.generateCertificate(is); } //创建keystore String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); //创建TrustManager trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); } catch (CertificateException | IOException | KeyStoreException | NoSuchAlgorithmException e) { e.printStackTrace(); return; } serverConfig.setSslTrustManagerFactory(trustManagerFactory); }
参数明细
参数 |
默认值 |
说明 |
---|---|---|
codec |
org.redisson.codec.JsonJacksonCodec |
编码格式,内置了JSON/Avro/Smile/CBOR/MsgPack等编码格式 |
threads |
cpu核数 * 2 |
RTopic Listener、RRemoteService和RExecutorService执行使用的线程池 |
executor |
null |
功能同上,不设置该参数时,会根据threads参数初始化一个线程池 |
nettyThreads |
cpu核数 * 2 |
连接redis-server的tcp channel使用的线程池,所有channel共享该连接池,映射到netty即Bootstrap.group(...) |
eventLoopGroup |
null |
功能同上,不设置该参数时,会根据nettyThreads参数初始化一个EventLoopGroup,用于底层tcpchannel使用 |
transportMode |
TransportMode.NIO |
传输模式,可选有NIO、EPOLL(需额外引包)、KQUEUE(需额外引包) |
lockWatchdogTimeout |
30000 |
监控锁的看门狗超时时间,单位:毫秒。用于分布式锁场景下未指定leaseTimeout参数时,采用该值为默认值 |
keepPubSubOrder |
true |
是否按照订阅发布消息的顺序来接收,如能接受并行处理消息,建议设置为false |
参数 |
默认值 |
说明 |
---|---|---|
address |
- |
节点连接信息,redis://ip:port |
database |
0 |
选择使用的数据库编号 |
connectionMinimumIdleSize |
32 |
连接每个分片主节点的最小连接数 |
connectionPoolSize |
64 |
连接每个分片主节点的最大连接数 |
subscriptionConnectionMinimumIdleSize |
1 |
连接目标节点的用于发布订阅的最小连接数 |
subscriptionConnectionPoolSize |
50 |
连接目标节点的用于发布订阅的最大连接数 |
subcriptionPerConnection |
5 |
每个订阅连接上的最大订阅数量 |
connectionTimeout |
10000 |
连接超时时间,单位:毫秒 |
idleConnectionTimeout |
10000 |
空闲连接的最大回收时间,单位:毫秒 |
pingConnectionInterval |
30000 |
检测连接可用心跳,单位:毫秒,建议值:3000ms |
timeout |
3000 |
请求等待响应的超时时间,单位:毫秒 |
retryAttempts |
3 |
发送失败的最大重试次数 |
retryInterval |
1500 |
每次重试的时间间隔,单位:毫秒,建议值:200ms |
clientName |
null |
客户端名称 |
参数 |
默认值 |
说明 |
---|---|---|
masterAddress |
- |
主节点连接信息,redis://ip:port。 |
slaveAddresses |
- |
从节点连接信息列表,Set<redis://ip:port>。 |
readMode |
SLAVE |
读取模式,默认读流量分发到从节点,可选值:MASTER、SLAVE、MASTER_SLAVE;建议MASTER,其余配置在故障切换场景下,均存在访问失败风险。 |
loadBalancer |
RoundRobinLoadBalancer |
负载均衡算法,在readMode为SLAVE、MASTER_SLAVE时生效,均衡读流量分发。 |
masterConnectionMinimumIdleSize |
32 |
连接每个分片主节点的最小连接数。 |
masterConnectionPoolSize |
64 |
连接每个分片主节点的最大连接数。 |
slaveConnectionMinimumIdleSize |
32 |
连接每个分片每个从节点的最小连接数,如readMode=MASTER,该配置值将失效。 |
slaveConnectionPoolSize |
64 |
连接每个分片每个从节点的最大连接数,如readMode=MASTER,该配置值将失效。 |
subscriptionMode |
SLAVE |
订阅模式,默认只在从节点订阅,可选值:SLAVE、MASTER;建议采用MASTER。 |
subscriptionConnectionMinimumIdleSize |
1 |
连接目标节点的用于发布订阅的最小连接数。 |
subscriptionConnectionPoolSize |
50 |
连接目标节点的用于发布订阅的最大连接数。 |
subcriptionPerConnection |
5 |
每个订阅连接上的最大订阅数量。 |
connectionTimeout |
10000 |
连接超时时间,单位:毫秒。 |
idleConnectionTimeout |
10000 |
空闲连接的最大回收时间,单位:毫秒。 |
pingConnectionInterval |
30000 |
检测连接可用心跳,单位:毫秒,建议值:3000ms。 |
timeout |
3000 |
请求等待响应的超时时间,单位:毫秒。 |
retryAttempts |
3 |
发送失败的最大重试次数。 |
retryInterval |
1500 |
每次重试的时间间隔,单位:毫秒,建议值:200ms。 |
clientName |
null |
客户端名称。 |
参数 |
默认值 |
说明 |
---|---|---|
nodeAddress |
- |
集群节点的地址连接信息,每个节点采用redis://ip:port方式,多个节点连接信息用英文逗号隔开。 |
password |
null |
集群登录密码。 |
scanInterval |
1000 |
定时检测集群节点状态的时间间隔,单位:毫秒。 |
readMode |
SLAVE |
读取模式,默认读流量分发到从节点,可选值:MASTER、SLAVE、MASTER_SLAVE;建议修改为MASTER,其余配置在故障切换场景下,均存在访问失败风险。 |
loadBalancer |
RoundRobinLoadBalancer |
负载均衡算法,在readMode为SLAVE、MASTER_SLAVE时生效,均衡读流量分发。 |
masterConnectionMinimumIdleSize |
32 |
连接每个分片主节点的最小连接数。 |
masterConnectionPoolSize |
64 |
连接每个分片主节点的最大连接数。 |
slaveConnectionMinimumIdleSize |
32 |
连接每个分片每个从节点的最小连接数,如readMode=MASTER,该配置值将失效。 |
slaveConnectionPoolSize |
64 |
连接每个分片每个从节点的最大连接数,如readMode=MASTER,该配置值将失效。 |
subscriptionMode |
SLAVE |
订阅模式,默认只在从节点订阅,可选值:SLAVE、MASTER;建议采用MASTER。 |
subscriptionConnectionMinimumIdleSize |
1 |
连接目标节点的用于发布订阅的最小连接数。 |
subscriptionConnectionPoolSize |
50 |
连接目标节点的用于发布订阅的最大连接数。 |
subcriptionPerConnection |
5 |
每个订阅连接上的最大订阅数量。 |
connectionTimeout |
10000 |
连接超时时间,单位:毫秒。 |
idleConnectionTimeout |
10000 |
空闲连接的最大回收时间,单位:毫秒。 |
pingConnectionInterval |
30000 |
检测连接可用心跳,单位:毫秒,建议值:3000。 |
timeout |
3000 |
请求等待响应的超时时间,单位:毫秒。 |
retryAttempts |
3 |
发送失败的最大重试次数。 |
retryInterval |
1500 |
每次重试的时间间隔,单位:毫秒,建议值:200。 |
clientName |
null |
客户端名称。 |
DCS实例配置建议
- 读取模式(readMode)
建议采用MASTER,即Master节点承担所有的读写流量,一方面避免数据因主从同步时延带来的一致性问题;另一方面,如果从节点故障,配置值=SLAVE,所有读请求会触发报错;配置值=MASTER_SLAVE,部分读请求会触发异常。读报错会持续failedSlaveCheckInterval(默认180s)时间,直至从可用节点列表中摘除。
如需读写流量分流处理,DCS服务提供了针对读写流量分流的读写分离实例类型,通过在中间架设代理节点实现读写流量分发,遇到从节点故障时,自动切流至主节点,对业务应用无感知,且故障感知时间窗口远小于redisson内部的时间窗口。
- 订阅模式(subscriptionMode)
建议采用MASTER,原理同读取模式(readMode)。
- 连接池配置
以下计算方式只适用于一般业务场景,建议根据业务情况做适当调整适配。
连接池的大小没有绝对的标准,建议根据业务流量进行合理配置,一般连接池大小的参数计算公式如下:
- 最小连接数 =(单机访问Redis QPS)/(1000ms / 单命令平均耗时)
- 最大连接数 =(单机访问Redis QPS)/(1000ms / 单命令平均耗时)* 150%
举例:某个业务应用的QPS为10000左右,每个请求需访问Redis10次,即每秒对Redis的访问次数为100000次,同时该业务应用有10台机器,计算如下:
单机访问Redis QPS = 100000 / 10 = 10000
单命令平均耗时 = 20ms(Redis处理单命令耗时为5~10ms,遇到网络抖动按照15~20ms来估算)
最小连接数 =(10000)/(1000ms / 20ms)= 200
最大连接数 =(10000)/(1000ms / 20ms)* 150% = 300
- 重试配置
redisson中支持重试配置,主要是如下两个参数,建议根据业务情况配置合理值,一般重试次数为3,重试间隔为200ms左右。
- retryAttempts:配置重试次数
- retryInterval:配置重试时间间隔
在redisson中,部分API通过借助LUA的方式实现,性能表现上偏低,建议使用jedis客户端替换redisson。