Redis第2讲——Java三种客户端(Jedis、Lettuce和Redisson)

上篇文章介绍了Redis的9种数据类型和常命令、7种数据结构和9种编码方式。但是如果想要把它应用到项目中,我们还需要一个redis的客户端。redis的Java客户端种类还是很多的,其中使用最广泛的有三种——Jedis、lettuce和redisson,下面我们一起来学习下。

一、Redis客户端简介

介绍之前我们先来了解一下什么是客户端。客户端——即真正的使用者,比如进入redis命令操作有一个redis-cli,这其实就是redis提供的一个基于操作系统(linux、windows)的客户端,此时的使用者是电脑,电脑通过这个客户端可以连接redis并操作redis。同理,在java中如果想要要操作redis同样需要客户端来与redis建立连接。

基于redis开放的通信协议,大神们纷纷开发出了各种语言的redis客户端,包括C、C++、C#、D、java、Python、Ruby等50多种,这些客户端都是基于redis命令做了一层封装,并打包成工具,以便更方便地操作redis。

ps:SpringBoot项目用spring-data-redis的比较多,其实它主要是封装了jedis和lettuce两个客户端,相当于在它们基础上加了一层门面。

在java语言里redis官方最推荐的便是jedis、lettuce和redisson,如下图。

二、Jedis

2.1 简介

Jedis是redis老牌的Java客户端,它把Redis的所有命令封装成了Java可直接调用的方法,但它并没有替我们封装一些基于redis的特殊功能,比如分布式锁等。

官方网址:GitHub - redis/jedis: Redis Java client

2.2 基本使用

2.2.1 导入依赖

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>5.0.0</version>
</dependency>

2.2.2 建立连接

Jedis实例连接redis

public class Test {public static void main(String[] args) {//1、构建一个Jedis对象,参数为host和protJedis jedis=new Jedis("127.0.0.1",6379);//2、密码验证(没设置密码的请忽略)//jedis.auth("password");//3、返回PONG说明连成功String ping = jedis.ping();System.out.println(ping);//PONG//4、释放资源jedis.close();}
}

对于Jedis而言,一旦连接上了redis服务器,剩下的操作就非常容易了,因为Jedis提供的API和redis的命令基本相同,比如get命令Jedis里面也是get,set对应set...

不过我们通常不用这种方式连接redis,而是用连接池,因为在多线程共享一个Jedis实例是线程不安全的。这里并不是说redis处理数据不安全,而是Jedis向reids推数据和获取数据不安全。在单个Jedis实例中有RedisInputStreamRedisOutPutStream两个成员变量,发送命令和获取返回值都是使用这两个变量,显然这很容易发生并发问题。

既然多个线程使用一个实例就会产生问题,那我们就给每个线程分配一个Jedis实例,让他们单独取操作自己的数据,这里就得使用JedisPool线程池来实现了,在使用过程中,我们通常会封装一个工具类:

//引入common-pool线程池依赖包
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.11.1</version>
</dependency>
public class JedisPoolFactory {private static JedisPool jedisPool = null;//地址private static String addr = "127.0.0.1";//端口private static int port = 6379;//密码private static String auth = "";
​static{try {JedisPoolConfig config = new JedisPoolConfig();//连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认trueconfig.setBlockWhenExhausted(true);//设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)config.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy");//是否启用pool的jmx管理功能, 默认trueconfig.setJmxEnabled(true);//MBean ObjectName = new ObjectName("org.apache.commons.pool2:type=GenericObjectPool,name=" + "pool" + i); 默认为"pool", JMX不熟,具体不知道是干啥的...默认就好.config.setJmxNamePrefix("pool");//是否启用后进先出, 默认trueconfig.setLifo(true);//最大空闲连接数, 默认8个config.setMaxIdle(8);//最大连接数, 默认8个config.setMaxTotal(8);//获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1config.setMaxWaitMillis(-1);//逐出连接的最小空闲时间 默认1800000毫秒(30分钟)config.setMinEvictableIdleTimeMillis(1800000);//最小空闲连接数, 默认0config.setMinIdle(0);//每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3config.setNumTestsPerEvictionRun(3);//对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断  (默认逐出策略)config.setSoftMinEvictableIdleTimeMillis(1800000);//在获取连接的时候检查有效性, 默认falseconfig.setTestOnBorrow(false);//在空闲时检查有效性, 默认falseconfig.setTestWhileIdle(false);//逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1config.setTimeBetweenEvictionRunsMillis(-1);jedisPool = new JedisPool(config, addr, port, 3000, auth);} catch (Exception e) {e.printStackTrace();}}
​/*** 获取 Jedis 资源* @return*/public static Jedis getJedis() {if (jedisPool != null) {return jedisPool.getResource();}return null;}
​/*** 释放Jedis资源*/public static void close(final Jedis jedis) {if (jedis != null) {jedis.close();}}
}

2.2.3 操作redis

本次就演示String数据类型的操作。

public class Test {public static void main(String[] args) throws InterruptedException {//1、建立连接Jedis jedis = JedisPoolFactory.getJedis();//2、操作redisSystem.out.println("清空数据:"+jedis.flushDB());System.out.println("判断某个键是否存在:"+jedis.exists("xhz"));System.out.println("新增<xhz,ctr>键:"+jedis.set("xhz","ctr"));System.out.println("xhz键是否存在:"+jedis.exists("xhz"));System.out.println("所有键:"+jedis.keys("*"));System.out.println("给xhz键设置生存时间:"+jedis.expire("xhz",100L));//sleep1秒TimeUnit.SECONDS.sleep(1);System.out.println("查看xhz键剩余生存时间:"+jedis.ttl("xhz"));System.out.println("查看xhz键的编码方式:"+jedis.objectEncoding("xhz"));System.out.println("查看xhz键的类型:"+jedis.type("xhz"));System.out.println("获取xhz键:"+jedis.get("xhz"));System.out.println("删除xhz键:"+jedis.del("xhz"));//关闭连接JedisPoolFactory.close(jedis);}
}

测试结果:

清空数据:OK
判断某个键是否存在:false
新增<xhz,ctr>键:OK
xhz键是否存在:true
所有键:[xhz]
给xhz键设置生存时间:1
查看xhz键剩余生存时间:99
查看xhz键的编码方式:embstr
查看xhz键的类型:string
获取xhz键:ctr
删除xhz键:1

2.3 集群配置

redis通常是通过集群配置,来保证服务的高可用。常用的搭建的方式有2种:

  • 哨兵模式:在主从复制的基础上,增加一个节点对redis服务进行监控,如果master宕机,就从slave节点选一个作为master,实现自动切换。

  • Cluster模式:将数据进行分片存储,避免全部节点数据一样,浪费空间。

ps:这里就简单介绍一下,后续会专门有一篇介绍redis集群的文章。

2.3.1 哨兵模式

哨兵模式简单来说就是一台主机、一台或多台备机、外加一台监控节点(哨兵节点),当主机宕机,监控节点就会将备用节点自动切换成主机,以便继续提供服务。

public class SentinePoolUtil {private static Jedis jedis;private static JedisSentinelPool jedisSentinelPool;static{try {JedisPoolConfig config = new JedisPoolConfig();//最大空闲连接数, 默认8个config.setMaxIdle(8);//最大连接数, 默认8个config.setMaxTotal(8);//最小空闲连接数, 默认0config.setMinIdle(0);//获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1config.setMaxWaitMillis(3000);//在获取连接的时候检查有效性,表示取出的redis对象可用, 默认falseconfig.setTestOnBorrow(true);//redis服务器列表Set<String> sentinels = new HashSet<>();sentinels.add("host:port1");sentinels.add("host:port2");sentinels.add("host:port3");//初始化连接池jedisSentinelPool = new JedisSentinelPool("mymaster", sentinels, config, "password");// 从池中获取一个Jedis对象jedis = jedisSentinelPool.getResource();} catch (Exception e) {e.printStackTrace();}}
}

2.3.2 Cluster集群配置

Cluster模式是一种高级集群模式,它通过数据分片和分布式存储实现了负载均衡和高可用。在Cluster模式下,redis将所有键值对数据分散在多个节点上。每个节点负责一部分数据(slot槽),简而言之,Cluster模式突破了单节点的内存限制,实现了更大规模的数据存储。

public class ClusterUtil {private static JedisCluster jedisCluster;static{try {Set<HostAndPort> nodes = new HashSet<>();nodes.add(new HostAndPort("host", 2222));nodes.add(new HostAndPort("host", 3333));nodes.add(new HostAndPort("host", 4444));nodes.add(new HostAndPort("host", 5555));nodes.add(new HostAndPort("host", 6666));nodes.add(new HostAndPort("host", 7777));jedisCluster = new JedisCluster(nodes);jedisCluster.set("key", "hello world");jedisCluster.close();} catch (Exception e) {e.printStackTrace();}}
}

2.4 byte[]方式操作

使用的时候不难发现,除了String方式,还支持byte[]方式操作。Spring提供了序列化byte[]的操作

  • 导入依赖

<!--        spring基本-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.10.RELEASE</version>
</dependency>
  • 测试

public class ByteTest {public static void main(String[] args) {Jedis jedis = JedisPoolFactory.getJedis();//2.1 准备对象-注意:要实现Serializable接口User user = new User();user.setAge(18);user.setName("娃哈哈哈哈哈哈哈哈");//2.2 通过Spring提供的工具类将user对象转为byte[]byte[] value = SerializationUtils.serialize(user);byte[] key = SerializationUtils.serialize("user");//2.3 存储jedis.setex(key,888888,value);//2.5 获取byte[] value1 = jedis.get(key);//2.4 反序列化byte[]User user1 = (User) SerializationUtils.deserialize(value1);System.out.println(user1);//User(name=娃哈哈哈哈哈哈哈哈, age=18)}
}

2.5 Jedis管道操作

Redis更多的使用来做缓存。

应当在项目启动时,就从传统的MySQL、Oracle数据库中将作为缓存的数据查询出来并且同步到Redis服务中。

可能需要在项目启动时,将数10W甚至上百万的数据同步到Redis中,会在客户端和Redis服务交互时,网络传输数据所带来的性能损耗是很大的,采用管道来解决这个问题。

管道可以实现将大量的请求任务在客户端封装好,一次性的发送给Redis服务,从而减少网络请求带来的损耗

  • 现测试不用管道存储10w条数据(测试4.06秒)

public class Test {public static void main(String[] args) throws InterruptedException {Jedis jedis = JedisPoolFactory.getJedis();long start = System.currentTimeMillis();// 不采用管道,向Redis存储10W条数据for (int i = 0; i < 100000; i++) {jedis.setex("key" + i,500, UUID.randomUUID().toString());}System.out.println((System.currentTimeMillis()-start)+"ms");//4060ms//返还连接对象jedis.close();}
}
  • 采用管道(测试0.64秒)

public class PipelineTest {public static void main(String[] args) {Jedis jedis = JedisPoolFactory.getJedis();long start = System.currentTimeMillis();// 采用管道,向Redis存储10W条数据Pipeline pipelined = jedis.pipelined();for (int i = 0; i < 100000; i++) {pipelined.setex("key" + i,500, UUID.randomUUID().toString());}pipelined.sync();System.out.println((System.currentTimeMillis()-start)+"ms");//649ms//返还连接对象jedis.close();}
}

2.6 优缺点

优点:

  • Jedis 提供了简单直观的API,它的API与Redis命令一一对应,易于学习和使用。
  • Jedis 客户端使用高性能的连接池,支持连接复用,可有效地减少频繁创建和关闭连接对性能的影响。同时支持 pipelining 等批量操作,能够有效地提升 Redis 的性能,减少网络开销。并且使用高效的序列化机制(如使用对象池和二进制序列化)来提供快速的数据访问和传输。
  • Jedis 客户端提供了对 Redis Cluster(Redis 集群)的支持,可以轻松地与 Redis 集群进行交互、自动故障转移和负载均衡。

缺点:

  • Jedis 客户端的使用方式相对简单,只提供了一些基本的接口方法,如果需要实现自己的功能,需要自己重写或者拓展 Jedis 客户端。
  • Jedis 客户端实例不是线程安全的,需要借助连接池来管理和使用 Jedis。
  • 使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到 sockets 处理完 I/O 才能执行,不支持异步

三、Lettuce

3.1 简介

Lettuce是一个高级redis客户端,支持高级的redis特性,比如Sentinel、集群、流水线、自动重新连接和redis数据模型等。目前已成为SpringBoot 2.0版本默认的redis客户端。

相比于Jedis,lettuce不仅功能丰富,而且提供了很多新的功能特性,比如异步操作、响应式编程等,同时还解决了Jedis线程不安全的问题。

官方地址:GitHub - lettuce-io/lettuce-core: Advanced Java Redis client for thread-safe sync, async, and reactive usage. Supports Cluster, Sentinel, Pipelining, and codecs.

3.2 基本使用

3.2.1 导入依赖

<dependency><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId><version>5.3.3.RELEASE</version>
</dependency>

3.2.2 建立连接

Lettuce连接设计的时候,就是线程安全的,所以一个连接可以被多个线程共享,同时lettuce连接默认是自动重连的,使用单连接基本可以满足业务需求,大多数情况下不需要配置线程池,多连接并不会给操作带来性能上的提升。

工具类:

public class LettuceSyncClient {private static final String HOST = "127.0.0.1";private static final int PORT = 6379;
​private static RedisClient redisClient;private static StatefulRedisConnection<String, String> connection;private static RedisCommands<String, String> syncCommands;//响应式编程private static RedisReactiveCommands<String,String> reactiveCommands;//发布订阅private static StatefulRedisPubSubConnection<String, String> pubSubConn;
​public static RedisCommands<String, String> getConnection() {if (syncCommands == null) {getConn();syncCommands = connection.sync();}return syncCommands;}
​/*** 响应式编程* @return*/public static RedisReactiveCommands<String, String> getReactiveConn() {if (reactiveCommands == null) {getConn();//响应式编程reactiveCommands = connection.reactive();}return reactiveCommands;}
​/*** 发布订阅* @return*/public static StatefulRedisPubSubConnection<String, String> getPubSubConn(){if (pubSubConn == null) {getConn();//发布订阅pubSubConn = redisClient.connectPubSub();}return pubSubConn;}
​public static void getConn(){RedisURI redisUri = RedisURI.builder().withHost(HOST).withPort(PORT)
//                .withPassword("password").withTimeout(Duration.of(10, ChronoUnit.SECONDS)).build();redisClient = RedisClient.create(redisUri);connection = redisClient.connect();}
​public static void close() {if (connection != null && syncCommands != null) {connection.close();redisClient.shutdown();}}
}

测试: 

public class Test {public static void main(String[] args) {//获取RedisCommandsRedisCommands<String, String> commands = LettuceClient.getConnection();//测试连接System.out.println(commands.ping());//PONG//关闭连接LettuceClient.close();}
}

3.2.3 操作redis

其实和Jedis操作大差不差,这里就不纠结了:

public class Test {
​public static void main(String[] args) throws InterruptedException {//1、获取RedisCommandsRedisCommands<String, String> commands = LettuceClient.getConnection();//2、操作redisSystem.out.println("清空数据:"+commands.flushdb());System.out.println("判断某个键是否存在:"+commands.exists("xhz"));System.out.println("新增<xhz,ctr>键:"+commands.set("xhz","ctr"));System.out.println("是否存在:"+commands.exists("xhz"));System.out.println("所有键:"+commands.keys("*"));System.out.println("给xhz键设置生存时间:"+commands.expire("xhz",100L));//sleep1秒TimeUnit.SECONDS.sleep(1);System.out.println("查看xhz键剩余生存时间:"+commands.ttl("xhz"));System.out.println("查看xhz键的编码方式:"+commands.objectEncoding("xhz"));System.out.println("查看xhz键的类型:"+commands.type("xhz"));System.out.println("获取xhz键:"+commands.get("xhz"));System.out.println("删除xhz键:"+commands.del("xhz"));//3、关闭连接LettuceClient.close();}
}

3.2.4 响应式编程

Lettuce引入响应式编程框架时Project Reactor,通过使用Lettuce的响应式API,可以以流式方式处理redis:

public class ReactiveTest {public static void main(String[] args) throws InterruptedException {//建立连接RedisReactiveCommands<String, String> commands = LettuceSyncClient.getReactiveConnection();//操作redisMono<String> setc = commands.set("name", "xhz");System.out.println(setc.block());Mono<String> getc = commands.get("name");getc.subscribe(System.out::println);Flux<String> keys = commands.keys("*");keys.subscribe(System.out::println);
​//开启一个事务,先把count设置为1,再将count自增1commands.multi().doOnSuccess(r -> {commands.set("count", "1").doOnNext(value -> System.out.println("count1:" +  value)).subscribe();commands.incr("count").doOnNext(value -> System.out.println("count2:" +  value)).subscribe();}).flatMap(s -> commands.exec()).doOnNext(transactionResult -> System.out.println("transactionResult:" + transactionResult.wasDiscarded())).subscribe();
​Thread.sleep(1000 * 5);//关闭连接LettuceSyncClient.close();}
}

运行结果:

OK
xhz
name
count1:OK
count2:2
transactionResult:false

3.2.5 发布订阅

​
public class PubSubTest {public static void main(String[] args) throws InterruptedException {StatefulRedisPubSubConnection<String, String> pubSubConn = LettuceSyncClient.getPubSubConn();// 监听器对象,用于接收订阅频道的消息pubSubConn.addListener(new RedisPubSubListener<String, String>() {@Overridepublic void message(String channel, String message) {System.out.println("Received new message - Channel: " + channel + ", Message: " + message);}@Overridepublic void message(String pattern, String channel, String message) {System.out.println("pattern = " + pattern + ",channel = "+channel+",message = "+message);}@Overridepublic void subscribed(String channel, long count) {System.out.println("channel = " + channel);}@Overridepublic void psubscribed(String pattern, long count) {System.out.println("pattern = " + pattern);}@Overridepublic void unsubscribed(String channel, long count) {System.out.println("channel = " + channel);}@Overridepublic void punsubscribed(String pattern, long count) {System.out.println("pattern = " + pattern);}});// 订阅聊天频道pubSubConn.sync().subscribe("chat");// 模拟用户发送消息String user = "User1";String message = "Hello, world!";// 将消息发布到聊天频道RedisCommands<String, String> connection = LettuceSyncClient.getConnection();Long messagesSent = connection.publish("chat", "[" + user + "]: " + message);System.out.println("Messages sent: " + messagesSent);//关闭连接LettuceSyncClient.close();}
}

测试结果: 

channel = chat
Messages sent: 1
Received new message - Channel: chat, Message: [User1]: Hello, world!

3.3 集群配置

3.3.1 主从模式

Lettuce支持自动发现主从模式下的节点信息,然后保存到本地,具体如下:

public class MsTest {
​public static void main(String[] args) {//这里只需要配置一个节点的连接信息,不一定需要是主节点的信息,从节点也可以;可以自动发现主从节点RedisURI uri = RedisURI.builder().withHost("127.0.0.1").withPort(6379)//               .withPassword("123456").build();RedisClient client = RedisClient.create(uri);StatefulRedisMasterReplicaConnection<String, String> connection = MasterReplica.connect(client, StringCodec.UTF8, uri);//从节点读取数据connection.setReadFrom(ReadFrom.REPLICA);RedisCommands<String, String> commands = connection.sync();commands.set("name", "xhz");System.out.println(commands.get("name"));connection.close();client.shutdown();}
}

3.3.2 哨兵模式

public class SentinalTest {
​public static void main(String[] args) {//集群节点List<RedisURI> uris = new ArrayList();uris.add(RedisURI.builder().withSentinel("host", 2222).withSentinelMasterId("mymaster").withPassword("123456").build());uris.add(RedisURI.builder().withSentinel("host", 3333).withSentinelMasterId("mymaster").withPassword("123456").build());uris.add(RedisURI.builder().withSentinel("host", 4444).withSentinelMasterId("mymaster").withPassword("123456").build());
​RedisClient client = RedisClient.create();StatefulRedisMasterReplicaConnection<String, String> connection = MasterReplica.connect(client, StringCodec.UTF8, uris);//从节点读取数据connection.setReadFrom(ReadFrom.REPLICA);RedisCommands<String, String> commands = connection.sync();commands.set("name", "xhz");System.out.println(commands.get("name"));connection.close();client.shutdown();}
}

3.3.3 Cluster模式

public class ClusterTest {public static void main(String[] args) {Set<RedisURI> uris = new HashSet<>();uris.add(RedisURI.builder().withHost("host").withPort(1111).withPassword("123456").build());uris.add(RedisURI.builder().withHost("host").withPort(2222).withPassword("123456").build());uris.add(RedisURI.builder().withHost("host").withPort(3333).withPassword("123456").build());uris.add(RedisURI.builder().withHost("host").withPort(4444).withPassword("123456").build());uris.add(RedisURI.builder().withHost("host").withPort(5555).withPassword("123456").build());uris.add(RedisURI.builder().withHost("host").withPort(6666).withPassword("123456").build());
​RedisClusterClient client = RedisClusterClient.create(uris);StatefulRedisClusterConnection<String, String> connection = client.connect();RedisAdvancedClusterCommands<String, String> commands = connection.sync();commands.set("name", "xhz");System.out.println(commands.get("name"));
​//选择从节点,只读NodeSelection<String, String> replicas = commands.replicas();NodeSelectionCommands<String, String> nodeSelectionCommands = replicas.commands();Executions<List<String>> keys = nodeSelectionCommands.keys("*");keys.forEach(key -> System.out.println(key));
​connection.close();client.shutdown();}
}
​

3.4 优缺点

优点:

  • 异步和非阻塞:Lettuce 客户端使用异步和非阻塞的方式与 Redis 交互,可以处理并行的请求和高并发的场景,提供更高的吞吐量和响应速度。
  • 响应式编程模型:Lettuce 客户端支持 Reactive 编程模型,可以通过使用 Reactive Streams、Flux 或 Mono 这样的响应式类型来处理异步操作和流式数据处理。
  • 完整的特性支持:Lettuce 客户端支持 Redis 的所有高级特性,如事务、流水线操作、发布/订阅、Lua 脚本等,可以满足复杂的应用需求。
  • 集群支持:Lettuce 客户端提供了对 Redis Cluster 的支持,可以轻松地与 Redis 集群进行交互,并进行自动的故障转移和节点发现。
  • 可扩展性:Lettuce 客户端使用模块化的设计,可以通过插件机制进行功能扩展,可以根据需求选择所需的模块,减小依赖的大小。

缺点:

  • 和其他 Redis 客户端相比,Lettuce 的使用可能稍微复杂,需要更多的学习和了解。

四、Redisson

Redis官方置顶推荐的Java客户端Redisson。

4.1 简介

Redisson是架设再redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。它不仅将原生的redis Hash、List、Set、String等数据结构封装为Java里大家熟悉的Map、List、Set、Object Bukcket等数结构,并在此基础上还提供了许多分布式服务,比如分布式锁、分布式对象、分布式集合、分布式调度任务等。

相比于Jedis、Lettuce等基于redis基础命令封装的客户端,Redisson提供的功能更加高端和抽象。

官方地址:GitHub - redisson/redisson: Redisson - Easy Redis Java client with features of In-Memory Data Grid. Sync/Async/RxJava/Reactive API. Over 50 Redis based Java objects and services: Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Spring Cache, Tomcat, Scheduler, JCache API, Hibernate, RPC, local cache ...

4.2 基本使用

4.2.1 导入依赖

<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.21.3</version>
</dependency>

4.2.2 建立连接

单机模式连接如下:

public class Test {public static void main(String[] args) throws IOException {Config config = new Config();config.useSingleServer().setAddress("redis://127.0.0.1:6379")
//                .setPassword("123456").setDatabase(0);//获取客户端RedissonClient redisson = Redisson.create(config);//哈希结构RMap<Object, Object> hash = redisson.getMap("hash");hash.put("name","xhz");String name = hash.get("name").toString();System.out.println(name);//xhz//关闭客户端redisson.shutdown();}
}

当然也可以用配置文件的方式:

redisson.yml:

singleServerConfig:idleConnectionTimeout: 10000 # 空闲连接超时,单位:毫秒connectTimeout: 10000 # 连接超时,单位:毫秒timeout: 3000 # 命令等待超时,单位:毫秒retryAttempts: 3 # 命令失败重试次数retryInterval: 1500 # 命令重试发送间隔,单位:毫秒password: null # Redis 服务器密码subscriptionsPerConnection: 5 # 每个连接的最大订阅数量clientName: null # Redis 客户端名称address: "redis://127.0.0.1:6379" # Redis 服务器地址subscriptionConnectionMinimumIdleSize: 1 # 订阅连接的最小空闲数量subscriptionConnectionPoolSize: 50 # 订阅连接的最大连接数量connectionMinimumIdleSize: 10 # 正常连接的最小空闲数量,至少保持10个空闲连接connectionPoolSize: 50 # 正常连接的最大连接数量,最多可以创建50个连接database: 0 # 连接的数据库编号,默认是0dnsMonitoringInterval: 5000 # DNS监控间隔,单位:毫秒

测试连接:

public class Test {public static void main(String[] args) throws IOException {Config config = Config.fromYAML(Test.class.getClassLoader().getResource("redisson.yml"));RedissonClient redisson = Redisson.create(config);RMap<Object, Object> hash = redisson.getMap("hash");hash.put("name","xhz");String name = hash.get("name").toString();System.out.println(name);//xhzredisson.shutdown();}
}

4.2.3 操作redis

下面我们介绍一下5中基本类型的操作:

public class OperTest {
​public static void main(String[] args) throws IOException {Config config = Config.fromYAML(Test.class.getClassLoader().getResource("redisson.yml"));RedissonClient redisson = Redisson.create(config);
​//1、字符串操作RBucket<Object> name = redisson.getBucket("string");//设置value和key的过期时间name.set("xhz",10, TimeUnit.SECONDS);//获取key为name的valueSystem.out.println(redisson.getBucket("string").get().toString());
​//2、对象操作(切记要实现Serializable接口User user=new User("xhz",18);RBucket<Object> obj = redisson.getBucket("obj");obj.set(user,10,TimeUnit.SECONDS);System.out.println(redisson.getBucket("obj").get());
​//3、哈希操作RMap<Object, Object> map = redisson.getMap("map");//设置key为map的value值map.put("name","张三");map.put("age","18");//设置过期时间map.expire(10,TimeUnit.SECONDS);//打印valuefor (Map.Entry<Object, Object> entry : redisson.getMap("map").entrySet()) {System.out.println("key = "+entry.getKey()+": value = "+entry.getValue());}
​//4、list操作(支持对象操作列表)RList<Object> list = redisson.getList("list");User user1=new User("张三",18);User user2=new User("李四",20);list.expire(10,TimeUnit.SECONDS);list.add(user1);list.add(user2);System.out.println(redisson.getList("list"));
​//5、set操作(同样支持对象操作)RSet<Object> set = redisson.getSet("set");User user3=new User("王五",18);User user4=new User("赵六",20);set.add(user3);set.add(user4);set.expire(10,TimeUnit.SECONDS);System.out.println(redisson.getSet("set"));
​//6、zset操作(对象操作需要实现Comparable接口并重写比较逻辑)RSortedSet<Object> zset = redisson.getSortedSet("zset");User user5=new User("王五",18);User user6=new User("赵六",19);zset.add(user5);zset.add(user6);System.out.println(redisson.getSortedSet("zset"));//7、关闭客户端redisson.shutdown();}
}

运行结果:

xhz
User(name=xhz, age=18)
key = name: value = 张三
key = age: value = 18
[User(name=张三, age=18), User(name=李四, age=20)]
[User(name=赵六, age=20), User(name=王五, age=18)]
[User(name=王五, age=18), User(name=赵六, age=19)]

4.2.4 布隆过滤器

布隆过滤器是由布隆在1970年提出的。它实际上是一个很长的二进制向量和一系列的随机映射函数(哈希函数)两部分组成的结构,用于快速检索一个元素是否可能存在于一个集合(bit数组中)。

实现方式有很多,比如Guava、Apache Commons、Jedis和Redisson等,我们今天就介绍一下Redisson的实现方式:

public class BloomTest {public static void main(String[] args) throws IOException {Config config = Config.fromYAML(Test.class.getClassLoader().getResource("redisson.yml"));RedissonClient redisson = Redisson.create(config);RBloomFilter<Object> bloom = redisson.getBloomFilter("bloom");//初始化预期插入的数据量为100和期望误差率为0.01bloom.tryInit(100,0.01);//插入数据bloom.add("哈哈");bloom.add("嘻嘻");bloom.add("嘿嘿");//判断数据是否存在System.out.println(bloom.contains("哈哈"));//trueSystem.out.println(bloom.contains("呵呵"));//falseredisson.shutdown();}
}

4.2.5 分布式锁

Redisson最大的亮点,也是使用最多的功能,就是分布式锁,使用起来还是挺简单的:

public class LockTest {private static final String KEY = "xhz";public static void main(String[] args) throws IOException {Config config = Config.fromYAML(Test.class.getClassLoader().getResource("redisson.yml"));RedissonClient redisson = Redisson.create(config);RLock lock = redisson.getLock(KEY);if (!lock.tryLock()) {//没获取到锁,提前结束return;}try {//处理业务逻辑System.out.println("获取锁成功");} catch (Exception e) {System.out.println("发生异常");throw new RuntimeException(e);}finally {//释放锁if(lock.isLocked() && lock.isHeldByCurrentThread()){lock.unlock();}}}
}

ps:另外,Redisson还支持公平锁、联锁、红锁、读写锁、信号量、闭锁等,后续会专门总结一篇分布式锁的文章。

4.3 集群配置

上面已经简单介绍过了,这次就简单说了。

4.3.1 主从模式

public class MsTest {public static void main(String[] args) {Config config = new Config();config.useMasterSlaveServers()//可以用"rediss://"来启用SSL连接.setMasterAddress("redis://127.0.0.1:6379").addSlaveAddress("redis://127.0.0.1:6389", "redis://127.0.0.1:6332", "redis://127.0.0.1:6419").addSlaveAddress("redis://127.0.0.1:6399");
​RedissonClient redisson = Redisson.create(config);}
}

yaml格式:

---
masterSlaveServersConfig:idleConnectionTimeout: 10000connectTimeout: 10000timeout: 3000retryAttempts: 3retryInterval: 1500failedAttempts: 3password: nullsubscriptionsPerConnection: 5clientName: nullloadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}slaveSubscriptionConnectionMinimumIdleSize: 1slaveSubscriptionConnectionPoolSize: 50slaveConnectionMinimumIdleSize: 32slaveConnectionPoolSize: 64masterConnectionMinimumIdleSize: 32masterConnectionPoolSize: 64readMode: "SLAVE"slaveAddresses:- "redis://127.0.0.1:6381"- "redis://127.0.0.1:6380"masterAddress: "redis://127.0.0.1:6379"database: 0
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
"transportMode":"NIO"

4.3.2 哨兵模式

public class SentinalTest {public static void main(String[] args) {Config config = new Config();config.useSentinelServers().setMasterName("mymaster")//可以用"rediss://"来启用SSL连接.addSentinelAddress("127.0.0.1:26389", "127.0.0.1:26379").addSentinelAddress("127.0.0.1:26319");
​RedissonClient redisson = Redisson.create(config);}
}

yaml格式:

---
sentinelServersConfig:idleConnectionTimeout: 10000connectTimeout: 10000timeout: 3000retryAttempts: 3retryInterval: 1500password: nullsubscriptionsPerConnection: 5clientName: nullloadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}slaveSubscriptionConnectionMinimumIdleSize: 1slaveSubscriptionConnectionPoolSize: 50slaveConnectionMinimumIdleSize: 32slaveConnectionPoolSize: 64masterConnectionMinimumIdleSize: 32masterConnectionPoolSize: 64readMode: "SLAVE"sentinelAddresses:- "redis://127.0.0.1:26379"- "redis://127.0.0.1:26389"masterName: "mymaster"database: 0
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
"transportMode":"NIO"

4.3.3 Cluster模式

public class Cluster {public static void main(String[] args) {Config config = new Config();config.useClusterServers().setScanInterval(2000) // 集群状态扫描间隔时间,单位是毫秒//可以用"rediss://"来启用SSL连接.addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001").addNodeAddress("redis://127.0.0.1:7002");
​RedissonClient redisson = Redisson.create(config);}
}

yaml格式:

---
clusterServersConfig:idleConnectionTimeout: 10000connectTimeout: 10000timeout: 3000retryAttempts: 3retryInterval: 1500password: nullsubscriptionsPerConnection: 5clientName: nullloadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}slaveSubscriptionConnectionMinimumIdleSize: 1slaveSubscriptionConnectionPoolSize: 50slaveConnectionMinimumIdleSize: 32slaveConnectionPoolSize: 64masterConnectionMinimumIdleSize: 32masterConnectionPoolSize: 64readMode: "SLAVE"nodeAddresses:- "redis://127.0.0.1:7004"- "redis://127.0.0.1:7001"- "redis://127.0.0.1:7000"scanInterval: 1000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
"transportMode":"NIO"

4.4 优缺点

优点:

  • 实现了分布式特性和可扩展的 Java 数据结构,例如分布式锁,分布式集合,分布式对象,分布式远程调度等等高级功能,适合分布式开发
  • 与 Lettuce 一样,基于 Netty 框架的事件驱动与 redis 通信,支持异步调用,性能高
  • Redisson 的 API 是线程安全的,所以可以使用单个 Redisson 连接来完成各种操作。
  • 支持读写分离,支持读负载均衡,在主从复制和 Redis Cluster 架构下都可以使用
  • 内建 Tomcat Session Manager,为 Tomcat 6/7/8 提供了会话共享功能,可以与 Spring Session 集成,实现基于 Redis 的会话共享
  • 相比于 Jedis、Lettuce 等基于 redis 命令封装的客户端,Redisson 提供的功能更加高端和抽象,Redisson 可以类比 Spring 框架,这些框架搭建了应用程序的基础框架和功能,可以显著提升开发效率,让开发者有更多的时间来关注业务逻辑

缺点:

  • 和 Jedis、Lettuce 客户端相比,功能较为简单,对字符串的支持比较差,不支持排序、事务、管道、分区等 Redis 特性
  • API 更加抽象,学习使用成本高

 End:希望对大家有所帮助,如果有纰漏或者更好的想法,请您一定不要吝啬你的赐教🙋。  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/235553.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

web前端游戏项目-雷霆战机飞机大战游戏【附源码】

文章目录 一&#xff1a;雷霆战机HTML源码&#xff1a;JS文件&#xff1a;&#xff08;1&#xff09;function.js&#xff08;2&#xff09;impact.js&#xff08;3&#xff09;move.1.1.js&#xff08;4&#xff09;script.js 二&#xff1a;飞机大战HTML源码&#xff1a;CSS源…

性能压力测试--确保企业数字化业务稳健运行

随着企业的数字化转型和依赖云计算的普及&#xff0c;软件系统的性能已经成为企业成功运营的关键因素之一。性能压力测试作为确保系统在各种条件下都能高效运行的关键步骤&#xff0c;对企业的重要性不可忽视。以下是性能压力测试对企业的几个重要方面的影响和作用&#xff1a;…

最新AI创作系统ChatGPT系统源码+DALL-E3文生图+AI绘画+GPT语音对话功能

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Ch…

ubuntu 20.04安装一系列软件

1&#xff09;安装下载的包的指令&#xff1a; sudo dpkg -i xxx.deb 2&#xff09;通用指令&#xff1a; sudo apt-get install xxxx 3&#xff09;更新和升级软件包&#xff08;遇到问题先尝试这个指令&#xff09;&#xff1a; sudo apt-get update sudo apt-get install…

Java_集合进阶(Collection和List系列)

一、集合概述和分类 1.1 集合的分类 已经学习过了ArrayList集合&#xff0c;但是除了ArrayList集合&#xff0c;Java还提供了很多种其他的集合&#xff0c;如下图所示&#xff1a; 我想你的第一感觉是这些集合好多呀&#xff01;但是&#xff0c;我们学习时会对这些集合进行…

基于alibaba druid的血缘解析工具

基于alibaba druid的血缘解析 1、前言 仅仅对mysql数据库的select查询语句进行了血缘解析&#xff0c;该血缘解析包含了原始表字段、临时表字段和目标表字段的关联关系。 2、涉及到技术 主要使用了druid的如下接口对语法树进行解析&#xff1a; &#xff08;1&#xff09;…

JavaWeb笔记之前端开发CSS

一 、引言 1.1 CSS概念 层叠样式表(英文全称&#xff1a;Cascading Style Sheets)是一种用来表现HTML&#xff08;标准通用标记语言的一个应用&#xff09;或XML&#xff08;标准通用标记语言的一个子集&#xff09;等文件样式的计算机语言。CSS不仅可以静态地修饰网页&…

美颜技术详解:深入了解视频美颜SDK的工作机制

本文将深入探讨视频美颜SDK的工作机制&#xff0c;揭示其背后的科技奥秘和算法原理。 1.引言 视频美颜SDK作为一种集成到应用程序中的技术工具&#xff0c;通过先进的算法和图像处理技术&#xff0c;为用户提供令人印象深刻的实时美颜效果。 2.视频美颜SDK的基本工作原理 首…

SVN小白常见操作流程

SVN小白常见操作流程 一、什么是Subversion&#xff1f;二、TortoiseSVN客户端安装教程三、SVN 操作3.1 SVN Ckeckout(检出)3.2 Add(新增文件)3.3 SVN Commit(提交)3.4 SVN Update(更新操作)3.5SVN Delete(删除操作)3.6 SVN Revert to a revision(版本回溯)3.7 不同版本内容之间…

Jenkins 执行远程脚本的插件—SSH2 Easy

SSH2 Easy 是什么&#xff1f; SSH2 Easy 是一个 Jenkins 插件&#xff0c;它用于在 Jenkins 构建过程中通过 SSH2 协议与远程服务器进行交互。通过该插件&#xff0c;用户可以在 Jenkins 的构建过程中执行远程命令、上传或下载文件、管理远程服务器等操作。 以下是 SSH2 Eas…

vue3使用mock模拟后端接口

安装mock axios yarn add mock yarn add axios 新建在src/mockdata/automenu.js 模拟后端的json数据格式 import Mock from mockjs Mock.mock(/menu,get,{status: 200,menuList: [{id : 1,iconCls: "fa fa-window",name: 系统管理,url: /},{id: 2,icon: icon-j…

YOLOv8改进 | 主干篇 | 利用MobileNetV1替换Backbone(轻量化网络结构)

一、本文介绍 本文给大家带来的改进机制是MobileNetV1&#xff0c;其是专为移动和嵌入式视觉应用设计的轻量化网络结构。这些模型基于简化的架构&#xff0c;并利用深度可分离卷积构建轻量级深度神经网络&#xff0c;其引入了两个简单的全局超参数&#xff0c;用于在延迟和准确…

【C语言】指针详解(一)

目录 1.内存和地址 1.1内存 1.2如何理解编址 2.指针变量和地址 2.1取地址操作符&#xff08;&&#xff09; 2.2指针变量和解引用操作符&#xff08;*&#xff09; 2.2.1指针变量 2.2.2拆解指针类型 2.2.3解引用操作符 2.3指针变量大小 1.内存和地址 1.1内存 在讲内…

《数据分析-JiMuReport》积木报表详细入门教程

积木报表详细入门教程 一、JimuReport部署入门介绍 积木报表可以通过源码部署、SpringBoot集成、Docker部署以及各种成熟框架部署&#xff0c;具体可查看积木官方文档 当前采用源码部署&#xff0c;首先下载Jimureport-example-1.5.6 1 jimureport-example目录查看 使用ID…

自动气象监测站助力生活生产

随着科技的发展&#xff0c;我们的生活和生产方式正在发生着日新月异的变化。其中&#xff0c;WX-CQ12 自动气象监测站作为一项气象监测设备&#xff0c;正在发挥着越来越重要的作用。它不仅为我们提供了更加准确、实时的天气信息&#xff0c;还为农业、交通、旅游等领域提供了…

python flask+vue实现前后端图片上传

python flaskvue实现前后端图片上传 vue代码如下&#xff1a; <template><div><input type"file" change"handleFileChange"/><button click"uploadFile">上传</button><br><img :src"imageUrl&…

uniapp如何原生app-云打包

首先第一步&#xff0c;需要大家在HBuilder X中找到一个项目&#xff0c;然后呢在找到上面的发行选项 发行->原生App-云打包 选择完该选中的直接大包就ok。 大包完毕后呢&#xff0c;会出现一个apk包&#xff0c;这是后将这个包拖动发给随便一个人就行了。 然后接收到的那…

2-高可用-负载均衡、反向代理

负载均衡、反向代理 upstream server即上游服务器&#xff0c;指Nginx负载均衡到的处理业务的服务器&#xff0c;也可以称之为real server,即真实处理业务的服务器。 对于负载均衡我们要关心的几个方面如下&#xff1a; 上游服务器配置&#xff1a;使用upstream server配置上…

摄像头画面作为电脑桌面背景

1. 创建文件main.pyw&#xff0c;文件内容 import base64 import io import os import threading import tkinter as tkimport cv2 import pystray import win32api import win32con import win32gui from PIL import Image, ImageTk from pystray import MenuItem, Menuclass…

Linux之yum管理器

目录 yum管理器 yum相关指令 yum list yum list | grep yum install yum remove 拓展 1.yum install -y man-pages 2.切换yum源 3.yum install -y epel-release 4. yum install -y lrzsz rz指令 sz指令 在window系统上&#xff0c;我们会在电脑自带的应用商…