首页 > 技术文章 > 【Redis】Jedis客户端对接

clarino 2021-09-08 17:19 原文

参考: 

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

public class RedisDetector {

    private static Jedis jedis;

    static boolean statusFlag = false;

    static {

        new Thread(() -> {
            while (true) {
                if (jedis == null) {
                    initJedis();
                }

                if (jedis != null) {
                    try {
                        jedis.ping();
                        statusFlag = true;
                    } catch (Exception e) {
                        statusFlag = false;
                        jedis = null;
                    }
                }

                try {
                    Thread.sleep(5000);
                    System.out.println("Sleep 5s");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private static void initJedis() {
        jedis = new Jedis("127.0.0.1", 6379);
        System.out.println("call initJedis....");
    }
}

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

推荐阅读