企业网站建设课程体会,西安网站制作定制,深圳网站建设一般多少钱,网站建设项目报价上篇文章介绍了Redis的9种数据类型和常命令、7种数据结构和9种编码方式。但是如果想要把它应用到项目中#xff0c;我们还需要一个redis的客户端。redis的Java客户端种类还是很多的#xff0c;其中使用最广泛的有三种——Jedis、lettuce和redisson#xff0c;下面我们一起来…上篇文章介绍了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。
psSpringBoot项目用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 导入依赖
dependencygroupIdredis.clients/groupIdartifactIdjedis/artifactIdversion5.0.0/version
/dependency
2.2.2 建立连接
Jedis实例连接redis
public class Test {public static void main(String[] args) {//1、构建一个Jedis对象参数为host和protJedis jedisnew 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里面也是getset对应set... 不过我们通常不用这种方式连接redis而是用连接池因为在多线程共享一个Jedis实例是线程不安全的。这里并不是说redis处理数据不安全而是Jedis向reids推数据和获取数据不安全。在单个Jedis实例中有RedisInputStream和RedisOutPutStream两个成员变量发送命令和获取返回值都是使用这两个变量显然这很容易发生并发问题。 既然多个线程使用一个实例就会产生问题那我们就给每个线程分配一个Jedis实例让他们单独取操作自己的数据这里就得使用JedisPool线程池来实现了在使用过程中我们通常会封装一个工具类
//引入common-pool线程池依赖包
dependencygroupIdorg.apache.commons/groupIdartifactIdcommons-pool2/artifactIdversion2.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:typeGenericObjectPool,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服务器列表SetString 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 {SetHostAndPort 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基本--
dependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactIdversion5.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娃哈哈哈哈哈哈哈哈, age18)}
}
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 ClusterRedis 集群的支持可以轻松地与 Redis 集群进行交互、自动故障转移和负载均衡。 缺点 Jedis 客户端的使用方式相对简单只提供了一些基本的接口方法如果需要实现自己的功能需要自己重写或者拓展 Jedis 客户端。Jedis 客户端实例不是线程安全的需要借助连接池来管理和使用 Jedis。使用阻塞的I/O且其方法调用都是同步的程序流需要等到 sockets 处理完 I/O 才能执行不支持异步 三、Lettuce
3.1 简介
Lettuce是一个高级redis客户端支持高级的redis特性比如Sentinel、集群、流水线、自动重新连接和redis数据模型等。目前已成为SpringBoot 2.0版本默认的redis客户端。
相比于Jedislettuce不仅功能丰富而且提供了很多新的功能特性比如异步操作、响应式编程等同时还解决了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 导入依赖
dependencygroupIdio.lettuce/groupIdartifactIdlettuce-core/artifactIdversion5.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 StatefulRedisConnectionString, String connection;private static RedisCommandsString, String syncCommands;//响应式编程private static RedisReactiveCommandsString,String reactiveCommands;//发布订阅private static StatefulRedisPubSubConnectionString, String pubSubConn;
public static RedisCommandsString, String getConnection() {if (syncCommands null) {getConn();syncCommands connection.sync();}return syncCommands;}
/*** 响应式编程* return*/public static RedisReactiveCommandsString, String getReactiveConn() {if (reactiveCommands null) {getConn();//响应式编程reactiveCommands connection.reactive();}return reactiveCommands;}
/*** 发布订阅* return*/public static StatefulRedisPubSubConnectionString, 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) {//获取RedisCommandsRedisCommandsString, 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、获取RedisCommandsRedisCommandsString, 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 {//建立连接RedisReactiveCommandsString, String commands LettuceSyncClient.getReactiveConnection();//操作redisMonoString setc commands.set(name, xhz);System.out.println(setc.block());MonoString getc commands.get(name);getc.subscribe(System.out::println);FluxString 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
count1OK
count22
transactionResultfalse
3.2.5 发布订阅
public class PubSubTest {public static void main(String[] args) throws InterruptedException {StatefulRedisPubSubConnectionString, String pubSubConn LettuceSyncClient.getPubSubConn();// 监听器对象用于接收订阅频道的消息pubSubConn.addListener(new RedisPubSubListenerString, 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 channelmessage 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!;// 将消息发布到聊天频道RedisCommandsString, 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);StatefulRedisMasterReplicaConnectionString, String connection MasterReplica.connect(client, StringCodec.UTF8, uri);//从节点读取数据connection.setReadFrom(ReadFrom.REPLICA);RedisCommandsString, 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) {//集群节点ListRedisURI 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();StatefulRedisMasterReplicaConnectionString, String connection MasterReplica.connect(client, StringCodec.UTF8, uris);//从节点读取数据connection.setReadFrom(ReadFrom.REPLICA);RedisCommandsString, 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) {SetRedisURI 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);StatefulRedisClusterConnectionString, String connection client.connect();RedisAdvancedClusterCommandsString, String commands connection.sync();commands.set(name, xhz);System.out.println(commands.get(name));
//选择从节点,只读NodeSelectionString, String replicas commands.replicas();NodeSelectionCommandsString, String nodeSelectionCommands replicas.commands();ExecutionsListString 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 导入依赖
dependencygroupIdorg.redisson/groupIdartifactIdredisson/artifactIdversion3.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);//哈希结构RMapObject, 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);RMapObject, 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、字符串操作RBucketObject 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 usernew User(xhz,18);RBucketObject obj redisson.getBucket(obj);obj.set(user,10,TimeUnit.SECONDS);System.out.println(redisson.getBucket(obj).get());
//3、哈希操作RMapObject, Object map redisson.getMap(map);//设置key为map的value值map.put(name,张三);map.put(age,18);//设置过期时间map.expire(10,TimeUnit.SECONDS);//打印valuefor (Map.EntryObject, Object entry : redisson.getMap(map).entrySet()) {System.out.println(key entry.getKey(): value entry.getValue());}
//4、list操作(支持对象操作列表RListObject list redisson.getList(list);User user1new User(张三,18);User user2new User(李四,20);list.expire(10,TimeUnit.SECONDS);list.add(user1);list.add(user2);System.out.println(redisson.getList(list));
//5、set操作同样支持对象操作RSetObject set redisson.getSet(set);User user3new User(王五,18);User user4new User(赵六,20);set.add(user3);set.add(user4);set.expire(10,TimeUnit.SECONDS);System.out.println(redisson.getSet(set));
//6、zset操作对象操作需要实现Comparable接口并重写比较逻辑RSortedSetObject zset redisson.getSortedSet(zset);User user5new User(王五,18);User user6new User(赵六,19);zset.add(user5);zset.add(user6);System.out.println(redisson.getSortedSet(zset));//7、关闭客户端redisson.shutdown();}
}
运行结果
xhz
User(namexhz, age18)
key name: value 张三
key age: value 18
[User(name张三, age18), User(name李四, age20)]
[User(name赵六, age20), User(name王五, age18)]
[User(name王五, age18), User(name赵六, age19)]
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);RBloomFilterObject 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: SLAVEslaveAddresses:- redis://127.0.0.1:6381- redis://127.0.0.1:6380masterAddress: redis://127.0.0.1:6379database: 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: SLAVEsentinelAddresses:- redis://127.0.0.1:26379- redis://127.0.0.1:26389masterName: mymasterdatabase: 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: SLAVEnodeAddresses:- redis://127.0.0.1:7004- redis://127.0.0.1:7001- redis://127.0.0.1:7000scanInterval: 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希望对大家有所帮助如果有纰漏或者更好的想法请您一定不要吝啬你的赐教。