在【Redis Jedis实战(含Spring Boot)】基础上做如下修改:
1、子pom.xml
去掉
redis.clients
jedis
和
io.lettuce
lettuce-core
springboot2的spring-boot-starter-data-redis默认就引入lettuce。
2、Lettuce+SpringBoot实战
【--------------------------------单机--------------------------------】
application.yml
spring:
redis:
host: 192.168.78.169
port: 6379
# password: 123456
database: 0
timeout: 60000
lettuce:
pool:
maxActive: 20
maxIdle: 20
minIdle: 0
maxWait: 60000
【--------------------------------sentinel哨兵模式--------------------------------】
application.yml
spring:
redis:
database: 0
timeout: 60000
# password: 123456
lettuce:
pool:
maxActive: 15
maxIdle: 15
minIdle: 0
maxWait: 60000
sentinel:
master: mymaster
nodes: 192.168.78.169:26379,192.168.78.169:26380,192.168.78.169:26381
【--------------------------------cluster集群模式--------------------------------】
application.yml
spring:
redis:
database: 0
timeout: 60000
# password: 123456
lettuce:
pool:
maxActive: 15
maxIdle: 15
minIdle: 0
maxWait: 60000
cluster:
nodes:
- 192.168.78.169:6379
- 192.168.78.169:6380
- 192.168.78.169:6381
- 192.168.78.169:6382
- 192.168.78.169:6383
- 192.168.78.169:6384
总结:
1、可以使用springboot默认的RedisConnectionFactory为LettuceConnectionFactory,debug方式查看connectionFactory的默认实现类(其他雷同);
2、自定义@Bean(LettuceConnectionFactory),其中构造函数的参数也可以使用自定义bean覆盖springboot默认提供的bean;
例如:
package com.java.ashare.redis.config;
import java.time.Duration;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Value("${spring.redis.sentinel.master}")
private String master;
@Value("${spring.redis.sentinel.nodes}")
private String nodes;
@Value("${spring.redis.database}")
private int database;
@Value("${spring.redis.timeout}")
private long timeout;
//@Value("${spring.redis.password}")
//private String password;
@Bean
@ConfigurationProperties(prefix = "spring.redis.lettuce.pool")
public GenericObjectPoolConfig GenericObjectPoolConfig() {
GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
return genericObjectPoolConfig;
}
@Bean
public LettuceClientConfiguration lettuceClientConfiguration(GenericObjectPoolConfig genericObjectPoolConfig){
LettucePoolingClientConfiguration lettucePoolingClientConfiguration = LettucePoolingClientConfiguration.builder()
.poolConfig(genericObjectPoolConfig)
.commandTimeout(Duration.ofMillis(timeout))
.build();
return lettucePoolingClientConfiguration;
}
@Bean
public RedisSentinelConfiguration redisSentinelConfiguration() {
RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();
redisSentinelConfiguration.setMaster(master);
// redisSentinelConfiguration.setPassword(RedisPassword.of(password.toCharArray()));
redisSentinelConfiguration.setDatabase(database);
String[] addrArr = nodes.split(",");
for(String addr : addrArr) {
String[] item = addr.split(":");
String ip = item[0];
String port = item[1];
redisSentinelConfiguration.addSentinel(new RedisNode(ip, Integer.parseInt(port)));
}
return redisSentinelConfiguration;
}
@Bean
public LettuceConnectionFactory lettuceConnectionFactory(RedisSentinelConfiguration redisSentinelConfiguration,
LettuceClientConfiguration lettuceClientConfiguration) {
LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisSentinelConfiguration, lettuceClientConfiguration);
return lettuceConnectionFactory;
}
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
// key使用字符串序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// value使用fastjson序列化,fastjson比jackson速度快
FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer();
redisTemplate.setValueSerializer(fastJsonRedisSerializer);
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
3、Lettuce单独实战
Lettuce入门
package com.java.ashare.redis.clients.lettuce;
import java.time.Duration;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.async.RedisAsyncCommands;
import io.lettuce.core.api.reactive.RedisReactiveCommands;
import io.lettuce.core.api.sync.RedisCommands;
public class LettuceRumen {
public static void main(String[] args) {
RedisURI redisURI = null;
RedisClient redisClient = null;
StatefulRedisConnection connection = null;
RedisCommands redisCommands = null;
RedisAsyncCommands redisAsyncCommands = null;
RedisReactiveCommands redisReactiveCommands = null;
try {
// redis连接信息---单机
redisURI = RedisURI.builder()
.withHost("192.168.78.169")
.withPort(6379)
//.withPassword("123456")
.withDatabase(0)
.withTimeout(Duration.ofSeconds(60))
.build();
// redis客户端
redisClient = RedisClient.create(redisURI);
// redis连接
connection = redisClient.connect();
// redis命令API接口
// sync:同步阻塞等待服务器返回结果 *******************************************************************************
/*
redisCommands = connection.sync();
// 测试服务是否正常
String pong = redisCommands.ping();
if("PONG".equals(pong)) {
System.out.println("redis服务正常......");
}
// 基本操作
redisCommands.del("key", "key1");
redisCommands.expire("key", 60);
redisCommands.expireat("key", new Date());
redisCommands.pexpire("key", 60000);
redisCommands.persist("key");
redisCommands.exists("key", "key1");
redisCommands.dbsize();
redisCommands.flushdb();
redisCommands.flushdbAsync();
redisCommands.flushall();
redisCommands.flushallAsync();
redisCommands.select(0);
redisCommands.keys("pattern");
redisCommands.scan();
// 字符串操作
redisCommands.set("key", "value");
redisCommands.mset(new HashMap());
redisCommands.setnx("key", "value");
redisCommands.setex("key", 60, "value");
SetArgs setArgs = SetArgs.Builder.nx().ex(60);
redisCommands.set("key", "value", setArgs);
redisCommands.get("key");
redisCommands.mget("key", "key1");
redisCommands.incr("key");
redisCommands.incrby("key", 10);
redisCommands.decr("key");
redisCommands.decrby("key", 10);
// 哈希操作
redisCommands.hset("key", "field", "value");
redisCommands.hsetnx("key", "field", "value");
redisCommands.hmset("key", new HashMap());
redisCommands.hget("key", "field");
redisCommands.hmget("key", "field", "field1");
redisCommands.hgetall("key");
redisCommands.hexists("key", "field");
redisCommands.hdel("key", "field", "field1");
redisCommands.hlen("key");
redisCommands.hincrby("key", "field", 1);
redisCommands.hincrbyfloat("key", "field", 1.1);
// 列表操作
redisCommands.lpush("key", "value", "value1");
redisCommands.rpush("key", "value", "value1");
redisCommands.lpop("key");
redisCommands.rpop("key");
redisCommands.blpop(60, "key", "key1");
redisCommands.brpop(60, "key", "key1");
redisCommands.lrange("key", 0, 10);
redisCommands.llen("key");
// 集合操作
redisCommands.sadd("key", "member", "member1");
redisCommands.srem("key", "member", "member1");
redisCommands.smembers("key");
redisCommands.scard("key");
redisCommands.sismember("key", "member");
redisCommands.srandmember("key");
redisCommands.srandmember("key", 10);
redisCommands.spop("key");
redisCommands.spop("key", 10);
redisCommands.sinter("key", "key1");
redisCommands.sinterstore("destination", "key", "key1");
redisCommands.sunion("key", "key1");
redisCommands.sunionstore("destination", "key", "key1");
redisCommands.sdiff("key", "key1");
redisCommands.sdiffstore("destination", "key", "key1");
// 有序集合操作
redisCommands.zadd("key", 80, "member");
redisCommands.zadd("key", 80, "member", 70, "member1");
redisCommands.zcard("key");
Range range = Range.create(0, 100);
redisCommands.zcount("key", range);
redisCommands.zrank("key", "member");
redisCommands.zrevrank("key", "member");
redisCommands.zrange("key", 0, 10);
redisCommands.zrevrange("key", 0, 10);
redisCommands.zscore("key", "member");
redisCommands.zinterstore("destination", "key", "key1");
redisCommands.zunionstore("destination", "key", "key1");
// ...等等
*/
// async:异步非阻塞 ********************************************************************************************
// 操作类似sync,只不过返回结果都是RedisFuture
/*
redisAsyncCommands = connection.async();
RedisFuture redisFuture = redisAsyncCommands.ping(); // 此处不会阻塞
System.out.println("Ping result: " + redisFuture.get()); // get方法获取结果时,此处会阻塞
// redisFuture.get(60, TimeUnit.SECONDS);
// redisFuture.getError();
*/
// reactive:反应式 ********************************************************************************
// 操作类似sync,只不过返回结果如果只包含0或1个元素,那么返回值类型是Mono,如果返回的结果包含0到N(N大于0)个元素,那么返回值是Flux
// Reactor编程可以自己了解下,这里省略了,本人也不太了解
/*
redisReactiveCommands = connection.reactive();
redisReactiveCommands.set("key", "value").block();
redisReactiveCommands.get("key").subscribe(value -> System.out.println("结果:" + value));
*/
} catch(Exception e) {
e.printStackTrace();
} finally {
if(connection != null) {
connection.close();
}
if(redisClient != null) {
redisClient.shutdown();
}
}
}
}
Lettuce sentinel哨兵模式
package com.java.ashare.redis.clients.lettuce;
import java.time.Duration;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.sync.RedisCommands;
import io.lettuce.core.codec.Utf8StringCodec;
import io.lettuce.core.masterslave.MasterSlave;
import io.lettuce.core.masterslave.StatefulRedisMasterSlaveConnection;
public class LettuceSentinel {
public static void main(String[] args) {
RedisURI redisURI = null;
RedisClient redisClient = null;
StatefulRedisMasterSlaveConnection connection = null;
RedisCommands redisCommands = null;
try {
// redis连接信息---哨兵模式(可以提供部分sentinel地址信息,lettuce自动拓扑发现其他节点信息并返回redis主实例)
redisURI = RedisURI.builder()
//.withPassword("123456")
.withSentinel("192.168.78.169", 26379)
.withSentinel("192.168.78.169", 26380)
.withSentinel("192.168.78.169", 26381)
.withSentinelMasterId("mymaster") // sentinel monitor
.withDatabase(0)
.withTimeout(Duration.ofSeconds(60))
.build();
// redis客户端
redisClient = RedisClient.create();
// redis连接
connection = MasterSlave.connect(redisClient, new Utf8StringCodec(), redisURI);
// connection.setReadFrom(ReadFrom.SLAVE_PREFERRED); // 读写分离,spring-data-redis2.1之后支持
redisCommands = connection.sync();
//redisCommands.set("k11", "v11");
System.out.println("k11: " + redisCommands.get("k11"));
} catch(Exception e) {
e.printStackTrace();
} finally {
if(connection != null) {
connection.close();
}
if(redisClient != null) {
redisClient.shutdown();
}
}
}
}
Lettuce cluster集群模式
package com.java.ashare.redis.clients.lettuce;
import java.time.Duration;
import java.util.Arrays;
import io.lettuce.core.RedisURI;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
public class LettuceCluster {
public static void main(String[] args) {
RedisClusterClient redisClusterClient = null;
StatefulRedisClusterConnection connection = null;
RedisAdvancedClusterCommands redisCommands = null;
try {
// redis连接信息---集群模式(可以提供部分节点地址信息,lettuce自动拓扑发现集群中其他节点信息)
RedisURI redisURI1 = RedisURI.builder()
//.withPassword("123456")
.withHost("192.168.78.169")
.withPort(6379)
.withDatabase(0)
.withTimeout(Duration.ofSeconds(60))
.build();
RedisURI redisURI2 = RedisURI.builder()
//.withPassword("123456")
.withHost("192.168.78.169")
.withPort(6380)
.withDatabase(0)
.withTimeout(Duration.ofSeconds(60))
.build();
RedisURI redisURI3 = RedisURI.builder()
//.withPassword("123456")
.withHost("192.168.78.169")
.withPort(6381)
.withDatabase(0)
.withTimeout(Duration.ofSeconds(60))
.build();
RedisURI redisURI4 = RedisURI.builder()
//.withPassword("123456")
.withHost("192.168.78.169")
.withPort(6382)
.withDatabase(0)
.withTimeout(Duration.ofSeconds(60))
.build();
RedisURI redisURI5 = RedisURI.builder()
//.withPassword("123456")
.withHost("192.168.78.169")
.withPort(6383)
.withDatabase(0)
.withTimeout(Duration.ofSeconds(60))
.build();
RedisURI redisURI6 = RedisURI.builder()
//.withPassword("123456")
.withHost("192.168.78.169")
.withPort(6384)
.withDatabase(0)
.withTimeout(Duration.ofSeconds(60))
.build();
// redis客户端
redisClusterClient = RedisClusterClient.create(Arrays.asList(redisURI1, redisURI2, redisURI3, redisURI4, redisURI5, redisURI6));
// redis连接
connection = redisClusterClient.connect();
redisCommands = connection.sync();
redisCommands.set("k13", "v13");
System.out.println("k13: " + redisCommands.get("k13"));
} catch(Exception e) {
e.printStackTrace();
} finally {
if(connection != null) {
connection.close();
}
if(redisClusterClient != null) {
redisClusterClient.shutdown();
}
}
}
}
Lettuce pipe流水线
package com.java.ashare.redis.clients.lettuce;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import io.lettuce.core.LettuceFutures;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisFuture;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.async.RedisAsyncCommands;
/**
* lettuce redis pipeline流水线功能
*
* 在正常情况下,lettuce会在API客户端调用命令后立即执行命令,这是大多数普通应用程序所需要的,特别是如果它们依赖于串行接收命令结果。
* 但是,如果应用程序不立即需要结果或批量上传大量数据,则此行为效率不高。异步应用程序可以覆盖此行为。
*/
public class LettuceBatch {
public static void main(String[] args) {
RedisURI redisURI = null;
RedisClient redisClient = null;
StatefulRedisConnection connection = null;
RedisAsyncCommands redisAsyncCommands = null;
try {
// redis连接信息---单机
redisURI = RedisURI.builder()
.withHost("192.168.78.169")
.withPort(6379)
//.withPassword("123456")
.withDatabase(0)
.withTimeout(Duration.ofSeconds(60))
.build();
// redis客户端
redisClient = RedisClient.create(redisURI);
// redis连接
connection = redisClient.connect();
// redis命令API接口
redisAsyncCommands = connection.async();
// 流水线-批处理
redisAsyncCommands.setAutoFlushCommands(false); // false:命令不会立即发送到redis服务器执行
List> futures = new ArrayList>();
for(int i = 0; i < 10; i++) {
futures.add(redisAsyncCommands.set("key" + i, "value" + i));
}
redisAsyncCommands.flushCommands(); // 发送所有命令到redis服务器执行
// 等待执行结果
LettuceFutures.awaitAll(60, TimeUnit.SECONDS, futures.toArray(new RedisFuture[futures.size()]));
} catch(Exception e) {
e.printStackTrace();
} finally {
if(connection != null) {
connection.close();
}
if(redisClient != null) {
redisClient.shutdown();
}
}
}
}
Lettuce 连接池
package com.java.ashare.redis.clients.lettuce;
import java.time.Duration;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import io.lettuce.core.support.ConnectionPoolSupport;
/**
* lettuce连接池
* lettuce使用netty,线程安全可以共享连接,一般情况下无需连接池
*
* 有如下几种情况你不能在线程之间复用连接:
* 1、请求批量下发,即禁止调用命令后立即flush
* 2、使用blpop这种阻塞命令
* 3、事务操作
* 4、有多个数据库的情况
*/
public class LettuceConnectionPool {
public static void main(String[] args) {
RedisURI redisURI = null;
RedisClient redisClient = null;
GenericObjectPool> pool = null;
StatefulRedisConnection connection = null;
RedisCommands redisCommands = null;
try {
// redis连接信息---单机
redisURI = RedisURI.builder()
.withHost("192.168.78.169")
.withPort(6379)
//.withPassword("123456")
.withDatabase(0)
.withTimeout(Duration.ofSeconds(60))
.build();
// redis客户端
redisClient = RedisClient.create(redisURI);
// redis连接池
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(20);
poolConfig.setMaxIdle(20);
poolConfig.setMinIdle(0);
poolConfig.setMaxWaitMillis(60 * 1000);
// ::是java8提供的关键字,用于访问对象方法、静态方法
// AsyncConnectionPoolSupport: 支持异步连接的池化
// ConnectionPoolSupport: 支持同步连接的池化
pool = ConnectionPoolSupport.createGenericObjectPool(redisClient::connect, poolConfig);
// 获取redis连接
connection = pool.borrowObject();
// redis命令API接口
redisCommands = connection.sync();
String pong = redisCommands.ping();
if("PONG".equals(pong)) {
System.out.println("redis服务正常......");
}
} catch(Exception e) {
e.printStackTrace();
} finally {
if(connection != null) {
pool.returnObject(connection);
}
if(pool != null) {
pool.close();
}
if(redisClient != null) {
redisClient.shutdown();
}
}
}
}
Lettuce pub/sub发布订阅
package com.java.ashare.redis.clients.lettuce;
import java.time.Duration;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.pubsub.RedisPubSubListener;
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
import io.lettuce.core.pubsub.api.sync.RedisPubSubCommands;
/**
* lettuce redis pub/sub发布订阅
*/
public class LettucePubSub {
public static void main(String[] args) {
RedisURI redisURI = null;
RedisClient redisClient = null;
StatefulRedisPubSubConnection connection = null;
RedisPubSubCommands redisPubSubCommands = null;
try {
// redis连接信息---单机
redisURI = RedisURI.builder()
.withHost("192.168.78.169")
.withPort(6379)
//.withPassword("123456")
.withDatabase(0)
.withTimeout(Duration.ofSeconds(60))
.build();
// redis客户端
redisClient = RedisClient.create(redisURI);
// redis连接
connection = redisClient.connectPubSub();
// redis命令API接口
redisPubSubCommands = connection.sync();
redisPubSubCommands.publish("channel", "message"); // 向频道channel发布消息
} catch(Exception e) {
e.printStackTrace();
} finally {
if(connection != null) {
connection.close();
}
if(redisClient != null) {
redisClient.shutdown();
}
}
}
}
class Subscriber {
public static void main(String[] args) {
RedisURI redisURI = null;
RedisClient redisClient = null;
StatefulRedisPubSubConnection connection = null;
RedisPubSubCommands redisPubSubCommands = null;
try {
// redis连接信息---单机
redisURI = RedisURI.builder()
.withHost("192.168.78.169")
.withPort(6379)
//.withPassword("123456")
.withDatabase(0)
.withTimeout(Duration.ofSeconds(60))
.build();
// redis客户端
redisClient = RedisClient.create(redisURI);
// redis连接
connection = redisClient.connectPubSub();
// RedisPubSubListener定义接收channel消息的监听器
connection.addListener(new RedisPubSubListener() {
@Override
public void unsubscribed(String channel, long count) {
}
@Override
public void subscribed(String channel, long count) {
}
@Override
public void punsubscribed(String pattern, long count) {
}
@Override
public void psubscribed(String pattern, long count) {
}
@Override
public void message(String pattern, String channel, String message) {
}
@Override
public void message(String channel, String message) {
System.out.println("频道:" + channel + ",消息:" + message);
}
});
// redis命令API接口
redisPubSubCommands = connection.sync();
redisPubSubCommands.subscribe("channel"); // 订阅频道channel,接收消息
} catch(Exception e) {
e.printStackTrace();
} finally {
if(connection != null) {
connection.close();
}
if(redisClient != null) {
redisClient.shutdown();
}
}
}
}
Lettuce 事务
package com.java.ashare.redis.clients.lettuce;
import java.time.Duration;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.TransactionResult;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
/**
* lettuce redis事务
*/
public class LettuceTransaction {
public static void main(String[] args) {
RedisURI redisURI = null;
RedisClient redisClient = null;
StatefulRedisConnection connection = null;
RedisCommands redisCommands = null;
try {
// redis连接信息---单机
redisURI = RedisURI.builder()
.withHost("192.168.78.169")
.withPort(6379)
//.withPassword("123456")
.withDatabase(0)
.withTimeout(Duration.ofSeconds(60))
.build();
// redis客户端
redisClient = RedisClient.create(redisURI);
// redis连接
connection = redisClient.connect();
// redis命令API接口
redisCommands = connection.sync();
try {
redisCommands.watch("key");
redisCommands.multi(); // 开启事务
redisCommands.set("key", "value"); // exec执行之前返回null
redisCommands.set("key1", "value1");
TransactionResult transactionResult = redisCommands.exec(); // 提交事务
String firstResult = transactionResult.get(0); // 第1条命令执行结果
String secondResult = transactionResult.get(1); // 第2条命令执行结果
System.out.println("命令执行结果:" + firstResult + ", " + secondResult);
} catch(Exception e) {
redisCommands.discard(); // 回滚事务
throw e;
}
} catch(Exception e) {
e.printStackTrace();
} finally {
if(connection != null) {
connection.close();
}
if(redisClient != null) {
redisClient.shutdown();
}
}
}
}