Redisベースの分散ロック---redisTemplate

2531 ワード

分散ロックアプリケーションシーンの多くは、高同時、大流量シーンで使用されます.複数のプロセスが同じシステムにない場合、分散ロックで複数のプロセスのリソースへのアクセスを制御する必要があります.本編ではRedisベースの分散ロックについて説明する.まず2つのRedisのコマンドを見てみましょう.
SETNX key value

キーをvalueに設定し、キーが存在しない場合はSETコマンドと同等です.keyが存在するとき、何もしません.SETNXは「SET if Not eXists」の略です.
GETSET key value

keyをvalueに自動的に対応させ、元のkeyに対応するvalueを返します.keyが存在しますが、対応するvalueが文字列ではない場合は、エラーが返されます.
秒殺を例にとると、Redisに基づいて実現される分散ロックは、実は秒殺の方法の前後でロックを加え、ロックを解除する操作を行う.
public void orderProductMockDiffUser(String productId) {
    //   
    // ...
    //   
}

次に、Redis分散ロックの処理を新規作成し、ロックとロック解除を書きます.
@Component
@Slf4j
public class RedisLock {
    @Resource
    private StringRedisTemplate redisTemplate;
    /**
     *   
     * @param key
     * @param value     +    
     * @return
     */
    public boolean lock(String key, String value) {
        //SETNX  ,       true,      false
        if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
            return true;
        }
        String currentValue = redisTemplate.opsForValue().get(key);
        //     
        if (StringUtils.isEmpty(currentValue) && (Long.parseLong(currentValue) < System.currentTimeMillis())) {
            //GETSET  ,          
            String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
            if (!StringUtils.isEmpty(oldValue) && oldValue.equals(value)) {
                return true;
            }
        }
        return false;
    }
    /**
     *   
     */
    public void unLock(String key, String value) {
        try {
            String currentValue = redisTemplate.opsForValue().get(key);
            if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
                redisTemplate.opsForValue().getOperations().delete(key);
            }
        } catch (Throwable e) {
            log.error("[redis    ]     , {}", e.getMessage(), e);
        }
    }
}

使うのは簡単です
private static final int TIMEOUT = 10 * 1000; //    10 
@Autowired
private RedisLock redisLock;
/**
 *      
 */
public void orderProductMockDiffUser(String productId) {
    //  
    long time = System.currentTimeMillis() + TIMEOUT;
    if(!redisLock.lock(productId, String.valueOf(time))) {
        throw new SellException(101, "     ,        ~");
    };
    //...
    //  
    redisLock.unLock(productId, String.valueOf(time));
}

この方法で実装される分散ロックには欠点がありますが、詳細は次のブログをクリックしてください.