当前位置:   article > 正文

Java的Redis客户端,Jedis和Redisson客户端的使用详细说明;Jedis的基本使用;Redisson作为消息中间件​、​布隆过滤器​、限流器、分布式锁、分布式集合、分布式远程服务;_java redissonclient

java redissonclient

目录

一、Jedis

1)获取Jedis

2)Jedis的基本使用

3)Jedis连接池使用

4)Jedis中Pipeline使用

5)Jedis的Lua脚本使用

二、Redisson

1、在SpringBoot中快速集成

2、增删改查操作

​3、Redisson作为消息中间件​

1)实战生产端代码

2)实战消费端代码

​4、布隆过滤器​

5、限流器

6、分布式锁

1)立即获取锁

2)可重入特性

3)等待放弃获取锁机制

4)自动释放锁

5)宕机情况

7、分布式集合

1)​分布式集合列表RList​

2)分布式集合映射RMap

3)RLocalCachedMap本地缓存映射

4)MapCache映射缓存

5)MultiMap多值映射

 8、Remote Service分布式远程服务


        Java有很多优秀的Redis客户端,这里介绍使用较为广泛的客户端Jedis和Redisson:

概念:

  Jedis:是老牌的Redis的Java实现客户端,提供了比较全面的Redis命令的支持,

  Redisson:实现了分布式和可扩展的Java数据结构。

优点:

  Jedis:比较全面的提供了Redis的操作特性

  Redisson:促使使用者对Redis的关注分离,提供很多分布式相关操作服务,例如:分布式锁,分布式集合,可通过Redis支持延迟队列

可伸缩:

  Jedis:使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。Jedis客户端实例不是线程安全的,所以需要通过连接池来使用Jedis。

  Redisson:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Redisson的API是线程安全的,所以可以操作单个Redisson连接来完成各种操作;

比较:

        jedis 是直连 redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个jedis实例增加物理连接 ;

        Redisson 实现了分布式 和 可扩展的Java数据结构,和 Jedis 相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson 的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。


一、Jedis

1)获取Jedis

        Maven项目为例,在项目中加入下面的依赖即可,引入版本选择比较稳定的版本,选择更新活跃的第三方开发包,例如Redis有了Redis Cluster新特性,但是如果使用的客户端一直不支持,并且维护的人也比较少,这种就谨慎选择。

  1. <dependency>
  2. <groupId>redis.clients</groupId>
  3. <artifactId>jedis</artifactId>
  4. <version>2.8.2</version>
  5. </dependency>

2)Jedis的基本使用

         代码实现:

  1. # 1. 生成一个Jedis对象,这个对象负责和指定Redis实例进行通信
  2. Jedis jedis = new Jedis("127.0.0.1", 6379);
  3. # 2. jedis执行set操作
  4. jedis.set("hello", "world");
  5. # 3. jedis执行get操作, value="world"
  6. String value = jedis.get("hello");

        我们可以看到初始化Jedis需要两个参数:Redis实例的IP和端口,除了这两个参数外,还有一个包含了四个参数的构造函数是比较常用的:

  1. Jedis(final String host, final int port, final int connectionTimeout, final int
  2. soTimeout)
  3. ·host:Redis实例的所在机器的IP
  4. ·port:Redis实例的端口
  5. ·connectionTimeout:客户端连接超时
  6. ·soTimeout:客户端读写超时

         我们在实际项目中比较推荐使用try catch finally(或者用try catch 语法糖)的形式来进行代码的书写:一方面可以在Jedis出现异常的时候(本身是网络操作),将异常进行捕获或者抛出;另一个方面无论执行成功或者失败,将Jedis连接关闭掉,在开发中及时关闭不用的连接资源,想了解异常处理机制的可以参考:http://t.csdn.cn/Opbpc

例如:

  1. Jedis jedis = null;
  2. try {
  3. jedis = new Jedis("127.0.0.1", 6379);
  4. jedis.get("hello");
  5. } catch (Exception e) {
  6. logger.error(e.getMessage(),e);
  7. } finally {
  8. if (jedis != null) {
  9. jedis.close();
  10. }
  11. }

Jedis对于Redis五种数据结构的操作:

  1. // 1.string 输出结果:OK
  2. jedis.set("hello", "world");
  3. // 输出结果:world
  4. jedis.get("hello");
  5. // 输出结果:1
  6. jedis.incr("counter");
  7. // 2.hash
  8. jedis.hset("myhash", "f1", "v1");
  9. jedis.hset("myhash", "f2", "v2");
  10. // 输出结果:{f1=v1, f2=v2}
  11. jedis.hgetAll("myhash");
  12. // 3.list
  13. jedis.rpush("mylist", "1");
  14. jedis.rpush("mylist", "2");
  15. jedis.rpush("mylist", "3");
  16. // 输出结果:[1, 2, 3]
  17. jedis.lrange("mylist", 0, -1);
  18. // 4.set
  19. jedis.sadd("myset", "a");
  20. jedis.sadd("myset", "b");
  21. jedis.sadd("myset", "a");
  22. // 输出结果:[b, a]
  23. jedis.smembers("myset");
  24. // 5.zset
  25. jedis.zadd("myzset", 99, "tom");
  26. jedis.zadd("myzset", 66, "peter");
  27. jedis.zadd("myzset", 33, "james");
  28. // 输出结果:[[["james"],33.0], [["peter"],66.0], [["tom"],99.0]]
  29. jedis.zrangeWithScores("myzset", 0, -1);
  30. Jedis还提供了字节数组的参数:
  31. public String set(final String key, String value)
  32. public String set(final byte[] key, final byte[] value)
  33. public byte[] get(final byte[] key)
  34. public String get(final String key)

        通过这些API的支持将Java对象序列化为二进制,当应用需要获取Java对象时,使用get(final byte[]key)函数将字节数组取出, 然后反序列化为Java对象即可。Jedis本身没有提供序列化的工具开发者需要自己引入序列化的工具。序列化的工具有很多例如XML、Json;

3)Jedis连接池使用

        上面介绍的是Jedis的直连方式,所谓直连是指Jedis每次都会新建 TCP连接,使用后再断开连接,对于频繁访问Redis的场景显然不是高效的使用方式;因此生产环境中一般使用连接池的方式对Jedis连接进行管理,所有Jedis对象预先放在池子中(JedisPool),每次要连接Redis,只需要在池子中借,用完了在归还给池子。

Jedis连接池使用方式:

        客户端连接Redis使用的是TCP协议,直连的方式每次需要建立TCP 连接,而连接池的方式是可以预先初始化好Jedis连接,所以每次只需要从Jedis连接池借用即可,而借用和归还操作是在本地进行的,只有少量的并发同步开销,远远小于新建TCP连接的开销。另外直连的方式无法限制Jedis对象的个数,在极端情况下可能会造成连接泄露,而连接池的形式可以有效的保护和控制资源的使用。

        Jedis提供了JedisPool这个类作为对Jedis的连接池,同时使用了 Apache的通用对象池工具common-pool作为资源的管理工具,下面是使用JedisPool操作Redis的代码示例:

  1. 1)Jedis连接池(通常JedisPool是单例的):
  2. // common-pool连接池配置,这里使用默认配置,后面小节会介绍具体配置说明
  3. GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
  4. // 初始化
  5. Jedis连接池
  6. JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);
  7. 2)获取Jedis对象不再是直接生成一个Jedis对象进行直连,而是从
  8. 连接池直接获取,代码如下:
  9. Jedis jedis = null;
  10. try {
  11. // 1. 从连接池获取
  12. jedis对象
  13. jedis = jedisPool.getResource();
  14. // 2. 执行操作
  15. jedis.get("hello");
  16. } catch (Exception e) {
  17. logger.error(e.getMessage(),e);
  18. } finally {
  19. if (jedis != null) {
  20. // 如果使用
  21. JedisPool,
  22. close操作不是关闭连接,代表归还连接池
  23. jedis.close();
  24. }
  25. }

        可以看到在finally中依然是jedis.close()操作,Jedis的close()实 现方式如下:

  1. public void close() {
  2. // 使用
  3. Jedis连接池
  4. if (dataSource != null) {
  5. if (client.isBroken()) {
  6. this.dataSource.returnBrokenResource(this);
  7. } else {
  8. this.dataSource.returnResource(this);
  9. }
  10. // 直连
  11. } else {
  12. client.close();
  13. }
  14. }
  15. ·dataSource!=null代表使用的是连接池,所以jedis.close()代表归
  16. 还连接给连接池,而且Jedis会判断当前连接是否已经断开。
  17. ·dataSource=null代表直连,jedis.close()代表关闭连接。

        前面GenericObjectPoolConfig使用的是默认配置,实际它提供有很 多参数,例如池子中最大连接数、最大空闲连接数、最小空闲连接数、 连接活性检测:

  1. GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
  2. // 设置最大连接数为默认值的5倍
  3. poolConfig.setMaxTotal(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL * 5);
  4. // 设置最大空闲连接数为默认值的3倍
  5. poolConfig.setMaxIdle(GenericObjectPoolConfig.DEFAULT_MAX_IDLE * 3);
  6. // 设置最小空闲连接数为默认值的2倍
  7. poolConfig.setMinIdle(GenericObjectPoolConfig.DEFAULT_MIN_IDLE * 2);
  8. // 设置开启jmx功能
  9. poolConfig.setJmxEnabled(true);
  10. // 设置连接池没有连接后客户端的最大等待时间(单位为毫秒)
  11. poolConfig.setMaxWaitMillis(3000);

4)Jedis中Pipeline使用

        Jedis支持Pipeline特性,我们知道 Redis提供了mget、mset方法,但是并没有提供mdel方法,如果想实现这 个功能,可以借助Pipeline来模拟批量删除,虽然不会像mget和mset那样 是一个原子命令,但是在绝大数场景下可以使用。下面代码是mdel删除 的实现过程。

  1. public void mdel(List<String> keys) {
  2. Jedis jedis = new Jedis("127.0.0.1");
  3. // 1)生成
  4. pipeline对象
  5. Pipeline pipeline = jedis.pipelined();
  6. // 2)pipeline执行命令,注意此时命令并未真正执行
  7. for (String key : keys) {
  8. pipeline.del(key);
  9. }
  10. // 3)执行命令
  11. pipeline.sync();
  12. }
  13. ·利用jedis对象生成一个pipeline对象,直接可以调用
  14. jedis.pipelined()。
  15. ·将del命令封装到pipeline中,可以调用pipeline.del(String key),
  16. 这个方法和jedis.del(String key)的写法是完全一致的,只不过此时不
  17. 会真正的执行命令。
  18. ·使用pipeline.sync()完成此次pipeline对象的调用。
  19. 除了pipeline.sync(),还可以使用pipeline.syncAndReturnAll()
  20. 将pipeline的命令进行返回,例如下面代码将set和incr做了一次pipeline操
  21. 作,并顺序打印了两个命令的结果:
  22. Jedis jedis = new Jedis("127.0.0.1");
  23. Pipeline pipeline = jedis.pipelined();
  24. pipeline.set("hello", "world");
  25. pipeline.incr("counter");
  26. List<Object> resultList = pipeline.syncAndReturnAll();
  27. for (Object object : resultList) {
  28. System.out.println(object);
  29. }
  30. 输出结果为:
  31. OK
  32. 1

5)Jedis的Lua脚本使用

        Jedis中执行Lua脚本和redis-cli十分类似,Jedis提供了三个重要的函 数实现Lua脚本的执行:

  1. Object eval(String script, int keyCount, String... params)
  2. Object evalsha(String sha1, int keyCount, String... params)
  3. String scriptLoad(String script)

 eval函数有三个参数,分别是:

  • ·script:Lua脚本内容。
  • ·keyCount:键的个数。
  • ·params:相关参数KEYS和ARGV。
  1. //以一个最简单的Lua脚本为例子进行说明:
  2. return redis.call('get',KEYS[1])
  3. //在redis-cli中执行上面的Lua脚本,方法如下:
  4. 127.0.0.1:6379> eval "return redis.call('get',KEYS[1])" 1 hello "world"
  5. //在Jedis中执行,方法如下:
  6. String key = "hello";
  7. String script = "return redis.call('get',KEYS[1])";
  8. Object result = jedis.eval(script, 1, key);
  9. // 打印结果为 world
  10. System.out.println(result)
  11. //scriptLoad和evalsha函数要一起使用,首先使用scriptLoad将脚本加载到Redis中,代码如下:
  12. String scriptSha = jedis.scriptLoad(script);

evalsha函数用来执行脚本的SHA1校验和,它需要三个参数:

  • ·scriptSha:脚本的SHA1。
  • ·keyCount:键的个数。
  • ·params:相关参数KEYS和ARGV。
  1. Stirng key = "hello";
  2. Object result = jedis.evalsha(scriptSha, 1, key);
  3. // 打印结果为 world
  4. System.out.println(result);

 redis客户端使用总结:

1)Jedis操作放在try catch finally里更加合理。

2)区分直连和连接池两种实现方式优缺点。

3)jedis.close()方法的两种实现方式。

4)Jedis依赖了common-pool,有关common-pool的参数需要根据不 同的使用场景,各不相同,需要具体问题具体分析。

5)如果key和value涉及了字节数组,需要自己选择适合的序列化方法。


二、Redisson

        Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

1、在SpringBoot中快速集成

(1)导入依赖

  1. <dependency>
  2. <groupId>org.redisson</groupId>
  3. <artifactId>redisson</artifactId>
  4. <version>3.8.2</version>
  5. </dependency>

(2)在SpringBoot中增加RedisClient配置类。我这里是单机部署的方法,集群配置方式以及更多属性的配置

  1. @Configuration
  2. @Component
  3. public class RedissonConfig {
  4. private static final Logger LOGGER = LoggerFactory.getLogger(RedissonConfig.class);
  5. /**
  6. * https://github.com/redisson/redisson/wiki/
  7. */
  8. @Bean
  9. public RedissonClient redissonClient() {
  10. Config config = new Config();
  11. config.useSingleServer().setAddress("yourRedisUrl:port");
  12. //.setPassword("yourRedisPwd");
  13. RedissonClient redisson = Redisson.create(config);
  14. return redisson;
  15. }
  16. }

2、增删改查操作

        创建一个比较简单的Controller。User对象以及UserMapper自己生成一下,User有两三个简单的属性,UserMapper就是对User对象对应的数据库表进行的增删改查操作。

  1. @RestController
  2. @RequestMapping(value = "redisson")
  3. public class BaseController {
  4. private static final Logger LOGGER = LoggerFactory.getLogger(BaseController.class);
  5. private static final String USER_BUCKET_KEY = "USER_BUCKET_KEY::";
  6. @Autowired
  7. private MUserMapper userMapper;
  8. @Autowired
  9. private RedissonClient redisson;
  10. }

        每个Redisson对象实例都会有一个与之对应的Redis数据实例。Redisson的分布式RBucket对象是一种通用对象桶可以用来存放任类型的对象。

         先来写新增和查询。先通过redisson对象通过getBucket方法

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/寸_铁/article/detail/852818
推荐阅读
相关标签
  

闽ICP备14008679号