Jedis的使用及配置优化

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jedis的使用及配置优化相关的知识,希望对你有一定的参考价值。

参考技术A jedis就是基于java语言的redis客户端,集成了redis的命令操作,提供了连接池管理。
redis-cli是redis官方提供的客户端,可以看作一个shell程序,它可以发送命令对redis进行操作。
对于jedis同理是使用java语言操作redis,双方都遵循redis提供的协议,按照协议开发对应的客户端。

jedis直连,本质是定义一个tcp连接,然后使用socket技术进行通信

每次操作创建一个jedis对象,执行完毕后关闭连接,对应的就是一次Tcp连接。

预先生成一批jedis连接对象放入连接池中,当需要对redis进行操作时从连接池中借用jedis对象,操作完成后归还。这样jedis对象可以重复使用,避免了频繁创建socket连接,节省了连接开销。

这里只是对连接池进行一个简单使用,实际开发通常会对JedisPool进行封装,进行一些参数配置和方法定义等,在使用Jedis API时,也会对常用API进行封装,方便程序调用

对于企业级开发来说,连接池的合理使用是非常重要的,如果设置不当会引起很多不必要的麻烦,容易造成线上的故障。
其实关于配置是一个比较难或者说没有确定答案的部分,这里只能给出一些思路和解决一些异常的方法。

为了方便使用,Jedis提供了 JedisPoolConfig ,它本身继承了 GenericObjectPoolConfig 设置了一些空闲监测设置

其实这个参数是比较难确定的,举个例子:

对于适合的maxTotal而言,我们需要考虑

无法从资源池获取到资源,原因是获取空闲连接超时了。

无法从资源池获取到资源,原因是池子中资源已经耗尽了。

Redis项目实战---应用及理论---Jedis使用

Jedis即redis java客户端,源码地址:https://github.com/xetorthio/jedis

pom配置:

<dependency>
   <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.0.1</version>
    <type>jar</type>
    <scope>compile</scope>
</dependency>
 
 
1)连接单机版
     public void testJedis() throws Exception 
          // 第一步:创建一个Jedis对象。需要指定服务端的ip及端口。
          Jedis jedis = new Jedis("192.168.**.**", 6379);
          // 第二步:使用Jedis对象操作数据库,每个redis命令对应一个方法。
          String result = jedis.get("hello");
          // 第三步:打印结果。
          System.out.println(result);
          // 第四步:关闭Jedis
          jedis.close();
     

2) 连接单机版使用连接池

     public void testJedisPool() throws Exception 
          // 第一步:创建一个JedisPool对象。需要指定服务端的ip及端口。
          JedisPool jedisPool = new JedisPool("192.168.**.**", 6379);
          // 第二步:从JedisPool中获得Jedis对象。
          Jedis jedis = jedisPool.getResource();
          // 第三步:使用Jedis操作redis服务器。
          jedis.set("jedis", "test");
          String result = jedis.get("jedis");
          System.out.println(result);
          // 第四步:操作完毕后关闭jedis对象,连接池回收资源。
          jedis.close();
          // 第五步:关闭JedisPool对象。
          jedisPool.close();
     
3) 连接集群版
     public void testJedisCluster() throws Exception 
          // 第一步:使用JedisCluster对象。需要一个Set<HostAndPort>参数。Redis节点的列表。
          Set<HostAndPort> nodes = new HashSet<>();
          nodes.add(new HostAndPort("192.168.**.**", 7001));
          nodes.add(new HostAndPort("192.168.**.**", 7002));
          nodes.add(new HostAndPort("192.168.**.**", 7003));
          nodes.add(new HostAndPort("192.168.**.**", 7004));
          nodes.add(new HostAndPort("192.168.**.**", 7005));
          nodes.add(new HostAndPort("192.168.**.**", 7006));
          JedisCluster jedisCluster = new JedisCluster(nodes);
          // 第二步:直接使用JedisCluster对象操作redis。在系统中单例存在。
          jedisCluster.set("hello", "100");
          String result = jedisCluster.get("hello");
          // 第三步:打印结果
          System.out.println(result);
          // 第四步:系统关闭前,关闭JedisCluster对象。
          jedisCluster.close();
     
 
4)JedisPool初始化配置:
private static final int taskCount = 50; // 并发任务 
private static final int batchSize = 10; // pipeline大小 
private static final int cmdCount = 1000;// 每个任务处理命令数 
private static final boolean usePipeline = true;
 
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxActive(200);
poolConfig.setMaxIdle(100);
poolConfig.setMaxWait(2000);
poolConfig.setTestOnBorrow(false);
poolConfig.setTestOnReturn(false);
jedisPool = new JedisPool(poolConfig, host, port);

5)Jedis 消息发布与订阅API

   5.1)消息订阅

   

   public void subscribeChannal()
//从jedisPool中获取一个jedis对象 Jedis jds
= RedisInsUtil.getJedis(); // 方式1 订阅得到信息在lister的onPMessage(...)方法中进行处理 jds.psubscribe(this, new String[]"channal01","channal02");
   // 方式2 订阅得到信息在lister的onMessage(...)方法中进行处理 (普通订阅方式)    //jedis.subscribe(listener, "foo", "watson");
// 初始化订阅时候的处理 @Override public void onSubscribe(String channel, int subscribedChannels) // 初始化按表达式的方式订阅时候的处理 @Override public void onPSubscribe(String pattern, int subscribedChannels) @Override // 取得按表达式的方式订阅的消息后的处理 public void onPMessage(String pattern, String channel, String message) LOG.info("onPMessage()," + pattern + "=" + channel + ",msg="+ message); // 取得订阅的消息后的处理 public void onMessage(String channel, String message)

     5.2)消息发布

             发布消息只用调用Jedis的publish(...)方法即可。

    Jedis jedis = ru.getConnection(); //获取一个jedis对象,自行封装工具类 
    jedis.publish("hello_foo", "bar123");  

 

6) 管道

原理:
    Redis客户端与Redis服务器之间使用TCP协议进行连接,管道(pipeline)可以一次性发送多条命令并在执行完后一次性将结果返回,pipeline通过减少客户端与redis的通信次数来实现降低往返延时时间,而且Pipeline 实现的原理是队列,而队列的原理是时先进先出,这样就保证数据的顺序性。 
    缺陷:需要注意到是用 pipeline方式打包命令发送,redis必须在处理完所有命令前先缓存起所有命令的处理结果。打包的命令越多,缓存消耗内存也越多。所以并不是打包的命令越多越好。具体多少合适需要根据具体情况测试。
   备注:Pipeline 的默认的同步的个数为53个,也就是说arges中累加到53条数据时会把数据提交;
  
7)扩展:
    由于redis-cluster的hash分片,JedisCluster对象原生并不支持Pipline管道和keys方法。然而,实际项目中在追求性能以及考虑到Redis集群版和单机版兼容或切换问题,往往需要支持这些方法,通过整理网上的部分资料,现给出以下思路,具体方法可参考gitHub示例
   
  管道Pipline实现思路:
 
  Redis集群规范: Redis 集群的键空间被分割为 16384 个槽(slot), 集群的最大节点数量也是 16384 个。每个主节点都负责处理 16384 个哈希槽的其中一部分。当一个集群处于“稳定”(stable)状态时(群没有在执行重配置(reconfiguration)操作), 每个哈希槽都只由一个节点进行处理(hash Slot的分配是由CRC16算法计算)。
 故, 

  1.根据要插入的key知道这个key所对应的槽的号码(JedisClusterCRC16.getSlot(key)), 再通过这个槽的号码从集群中找到对应Jedis(通过每个节点的slot分布,就知道了哪些key应该在哪些节点上)。

  2.相同槽位的key,使用同一个jedis.pipeline去执行命令。

  3.合并此次pipeline所有的response返回。

 

  keys实现思路:

     循环集群中所有的节点(分别获取j对应的client对象),然后每个节点做keys,最后再加到一块返回。

   

  备注:每次执行前需要刷新以获取最新的slot分布。

 

 

 

以上是关于Jedis的使用及配置优化的主要内容,如果未能解决你的问题,请参考以下文章

Jedis连接池配置参数

Redis进阶学习10---redis最佳实践

nginx反向代理配置及优化

编译安装nginx及简单优化配置

HTTPS配置优化及注意点

Jedis连接池优化