• 【Redis】Jedis使用


    参考: 

    1、https://xiaomi-info.github.io/2019/12/17/redis-distributed-lock/    基于jedis

    2、https://www.baeldung.com/tag/redis/

    对接访问

     JedisPool -> JedisPoolAbstrace -> Pool<Jedis>  { GenericObjectPool  来自apache.commons.pool2   }  

    // 1. Redis单节点模式
    Jedis jedis = new Jedis("127.0.0.1", 6379);
    System.out.println(jedis.ping());
    
    // 2. 通过Pool方式
    public class JedisUtils {
      private static JedisPool jedisPool;
      static {
        // 配置连接池
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(5);
        config.setMaxIdle(3);
        config.setMinIdle(2);
           config.testOnBorrow(true); 
    
        // 创建连接池
        jedisPool = new JedisPool(config, "localhost", 6379);
      }
    
      public static Jedis getJedis() {
        return jedisPool.getResource();
      }
    }
    
    // 使用方,通过调用close()完成申请的Jedis被pool回收
    try (Jedis jedis = JedisUtils.getJedis()) {  
        jedis.xxxx
    catch(JedisExecption e)
    {
    }
    
    // 3. Redis集群部署
    Set<HostAndPort> clusterNodes = new HashSet<HostAndPort>();
    clusterNodes.add(new HostAndPort("192.168.248.128", 7001));
    clusterNodes.add(new HostAndPort("192.168.248.128", 7002));
    JedisCluster jc = new JedisCluster(clusterNodes);
    jc.set("address", "深圳");
    String address = jc.get("address");
    System.out.println(address);

    分布式锁 

    关注: 1、加锁解锁的操作原子性;2、指定过期时间,避免加锁节点异常退出而锁永远无法释放;3、不能释放别人加的锁

     参考: https://www.cnblogs.com/linjiqin/p/8003838.html

    1、加锁:

       错误实现:setnx(set if not exist 成功返回1,否则返回0)+ expire(设置过期时间):非原子操作,当执行expire时程序宕机,则导致锁永远不会被释放

      正确实现:根据Jedis版本API定义有差异     

    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    
    /**
         * 尝试获取分布式锁
        * @param jedis Redis客户端
        * @param lockKey 锁
        * @param requestId 请求标识  常 UUID.randomUUID().toString()
        * @param expireTime 超期时间
        * @return 是否获取成功
    */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
    
        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
    
        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

      基于3.5.2样例(Jedis API有调整)     

    SetParams params = new SetParams();
    params.ex(100); // 设置超时时间
    params.nx(); // 若锁不存在才进行写操作
    
    jedis.set(key, requestId, params); // 成功返回OK

      解决方案:通过lua脚本 参考https://xiaomi-info.github.io/2019/12/17/redis-distributed-lock/

    2、解锁 (通过lua脚本实现原子操作)

    private static final Long RELEASE_SUCCESS = 1L;
    
    /**
    * 释放分布式锁
    * @param jedis Redis客户端
    * @param lockKey 锁
    * @param requestId 请求标识
    * @return 是否释放成功
    */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
    
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
    
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

     错误的实现:多线程场景,可能if判断成功后,锁已被别的客户端获取,此时执行删除的是别人创建的锁

    // 判断加锁与解锁是不是同一个客户端
    if (requestId.equals(jedis.get(lockKey))) {
        // 若在此时,这把锁突然不是这个客户端的,则会误删除别人获取的分布式锁
        jedis.del(lockKey);
    }

    连通性检测 

    定时检测redis可用性,当Redis恢复时重新初始化Jedis

     1 public class RedisDetector {
     2 
     3     private static Jedis jedis;
     4 
     5     static boolean statusFlag = false;
     6 
     7     static {
     8 
     9         new Thread(() -> {
    10             while (true) {
    11                 if (jedis == null) {
    12                     initJedis();
    13                 }
    14 
    15                 if (jedis != null) {
    16                     try {
    17                         jedis.ping();
    18                         statusFlag = true;
    19                     } catch (Exception e) {
    20                         statusFlag = false;
    21                         jedis = null;
    22                     }
    23                 }
    24 
    25                 try {
    26                     Thread.sleep(5000);
    27                     System.out.println("Sleep 5s");
    28                 } catch (InterruptedException e) {
    29                     e.printStackTrace();
    30                 }
    31             }
    32         }).start();
    33     }
    34 
    35     private static void initJedis() {
    36         jedis = new Jedis("127.0.0.1", 6379);
    37         System.out.println("call initJedis....");
    38     }
    39 }
    View Code

    使用方,使用前首先判断Redis可用标记

  • 相关阅读:
    Linux下的ping命令
    git stash
    ansiable
    「疫期集训day4」硝烟
    「线段树」「单点修改」洛谷P1198 [JSOI2008]最大数
    「状压DP」「暴力搜索」排列perm
    「疫期集训day3」要塞
    「疫期集训day2」高地
    「疫期集训day1」无言
    「区间DP」「洛谷P1043」数字游戏
  • 原文地址:https://www.cnblogs.com/clarino/p/15243681.html
Copyright © 2020-2023  润新知