springboot下使用Redisson(redlock, redis鎖, 分佈式鎖)

pom依賴:

		<dependency>
			<groupId>org.redisson</groupId>
			<artifactId>redisson</artifactId>
			<version>3.11.5</version>
		</dependency>

bean配置(這邊用單例配置配置redisson, 用spring.redis作爲redis配置):

package com.wjj.application.config;

import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.redisson.config.TransportMode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
 * @author hank
 * @date 2019/11/5 0005 下午 15:01
 * redisson 配置
 * https://github.com/redisson/redisson/wiki/2.-配置方法#23-常用設置
 */
@Configuration
 public class RedissonSpringDataConfig {
    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private String port;

    @Value("${spring.redis.password}")
    private String password;

//    @Bean(destroyMethod = "shutdown")
//    public RedissonClient redisson(@Value("classpath:/redisson.yaml") Resource configFile) throws IOException {
//        Config config = Config.fromYAML(configFile.getInputStream());
//        return Redisson.create(config);
//    }
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient(){
        Config config = new Config();
        SingleServerConfig singleServerConfig = config.useSingleServer().setAddress("redis://" + host + ":" + port);
        if(StringUtils.isNotBlank(password)){
            singleServerConfig.setPassword(password);
        }
        System.out.println("------------- redisson -----------------------");
        System.out.println(config.getTransportMode());;
//        config.setTransportMode(TransportMode.EPOLL);
        return Redisson.create(config);
    }

 }

TransportMode.EPOLL 只能在linux下使用,原則上應該比默認的nio快 

 

鎖工具接口定義:

package com.wjj.application.utils;

/**
 * @author hank
 * @date 2019/11/5 0005 下午 15:01
 */
public interface RedLockUtils {
    /**
     * 可重入,必須手動解鎖!線程不主動解鎖將會永遠存在! 慎用
     * Acquires the lock.
     *
     * <p>If the lock is not available then the current thread becomes
     * disabled for thread scheduling purposes and lies dormant until the
     * lock has been acquired.
     *
     * <p><b>Implementation Considerations</b>
     *
     * <p>A {@code Lock} implementation may be able to detect erroneous use
     * of the lock, such as an invocation that would cause deadlock, and
     * may throw an (unchecked) exception in such circumstances.  The
     * circumstances and the exception type must be documented by that
     * {@code Lock} implementation.
     */
    void lock(String lockKey);

    /**
     * 可重入,必須手動解鎖或等待leaseTime超時
     * Acquires the lock.
     *
     * <p>If the lock is not available then the current thread becomes
     * disabled for thread scheduling purposes and lies dormant until the
     * lock has been acquired.
     *
     * If the lock is acquired, it is held until <code>unlock</code> is invoked,
     * or until leaseTime milliseconds have passed
     * since the lock was granted - whichever comes first.
     *
     * @param leaseTime MILLISECONDS the maximum time to hold the lock after granting it,
     *        before automatically releasing it if it hasn't already been released by invoking <code>unlock</code>.
     *        If leaseTime is -1, hold the lock until explicitly unlocked.
     *
     */
    void lock(String lockKey, long leaseTime);

    /**
     * Returns <code>true</code> as soon as the lock is acquired.
     * If the lock is currently held by another thread in this or any
     * other process in the distributed system this method keeps trying
     * to acquire the lock for up to <code>waitTime</code> before
     * giving up and returning <code>false</code>. If the lock is acquired,
     * it is held until <code>unlock</code> is invoked, or until <code>leaseTime</code>
     * have passed since the lock was granted - whichever comes first.
     *
     * @param waitTime the maximum time to aquire the lock  MILLISECONDS
     * @param leaseTime lease time MILLISECONDS
     * @return <code>true</code> if lock has been successfully acquired
     * @throws InterruptedException - if the thread is interrupted before or during this method.
     */
    boolean tryLockTimeout(String lockKey,long waitTime, long leaseTime) throws InterruptedException;

    /**
     * Releases the lock.
     *
     * <p><b>Implementation Considerations</b>
     *
     * <p>A {@code Lock} implementation will usually impose
     * restrictions on which thread can release a lock (typically only the
     * holder of the lock can release it) and may throw
     * an (unchecked) exception if the restriction is violated.
     * Any restrictions and the exception
     * type must be documented by that {@code Lock} implementation.
     */
    void unLock(String lockKey);




}

 

鎖實現類:

 

package com.wjj.application.utils;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @author hank
 * @date 2019/11/5 0005 下午 15:01
 */
@Component
public class RedLockUtilsImpl implements RedLockUtils{

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 可重入,!線程不主動解鎖將會永遠存在! 慎用
     * Acquires the lock.
     *
     * <p>If the lock is not available then the current thread becomes
     * disabled for thread scheduling purposes and lies dormant until the
     * lock has been acquired.
     *
     * <p><b>Implementation Considerations</b>
     *
     * <p>A {@code Lock} implementation may be able to detect erroneous use
     * of the lock, such as an invocation that would cause deadlock, and
     * may throw an (unchecked) exception in such circumstances.  The
     * circumstances and the exception type must be documented by that
     * {@code Lock} implementation.
     */
    public void lock(String lockKey){
        RLock lock1 = redissonClient.getLock(lockKey);
        redissonClient.getRedLock(lock1).lock();
    }

    @Override
    public void lock(String lockKey, long leaseTime) {
        RLock lock1 = redissonClient.getLock(lockKey);
        redissonClient.getRedLock(lock1).lock(leaseTime, TimeUnit.MILLISECONDS);
    }

    /**
     * Returns <code>true</code> as soon as the lock is acquired.
     * If the lock is currently held by another thread in this or any
     * other process in the distributed system this method keeps trying
     * to acquire the lock for up to <code>waitTime</code> before
     * giving up and returning <code>false</code>. If the lock is acquired,
     * it is held until <code>unlock</code> is invoked, or until <code>leaseTime</code>
     * have passed since the lock was granted - whichever comes first.
     *
     * @param waitTime the maximum time to aquire the lock  MILLISECONDS
     * @param leaseTime lease time MILLISECONDS
     * @return <code>true</code> if lock has been successfully acquired
     * @throws InterruptedException - if the thread is interrupted before or during this method.
     */
    public boolean tryLockTimeout(String lockKey,long waitTime, long leaseTime) throws InterruptedException {
        RLock lock1 = redissonClient.getLock(lockKey);
        return redissonClient.getRedLock(lock1).tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS);
    }
    /**
     * Releases the lock.
     *
     * <p><b>Implementation Considerations</b>
     *
     * <p>A {@code Lock} implementation will usually impose
     * restrictions on which thread can release a lock (typically only the
     * holder of the lock can release it) and may throw
     * an (unchecked) exception if the restriction is violated.
     * Any restrictions and the exception
     * type must be documented by that {@code Lock} implementation.
     */
    public void unLock(String lockKey) {
        RLock lock1 = redissonClient.getLock(lockKey);
        redissonClient.getRedLock(lock1).unlock();
    }




}

 

使用例子:

public class TotalController {

	@Autowired
	private RedLockUtils redLockUtils;
    
	private String accountUnifiedorderLockKey = "ACCOUNT_PAY_UNIFIEDORDER_LOCK_KEY_TRADE_NO";

	/**
	 * 統一入口
	 **/
	@ApiOperation(value = "統一入口", notes = "統一入口")
	@RequestMapping(value = "unifiedorder")
	public Response unifiedorder(@RequestBody GMPaymentDTO paymentDTO) {
		// 校驗
		if (paymentDTO == null) {
			return Response.returnCode(ReturnCode.PARAM_INVALID);
		}
		String accountNo = paymentDTO.getAccountNo();
		String platformTradeNo = paymentDTO.getPlatformTradeNo();
		if (StringUtils.isBlank(accountNo) || StringUtils.isBlank(platformTradeNo)) {
			return Response.returnCode(ReturnCode.PARAM_INVALID);
		}
		String lockKey = accountUnifiedorderLockKey + "unifiedorder" + accountNo + platformTradeNo;
		boolean isLock = false;
		try {
			//獲得鎖 注意鎖的力度,只需要鎖定需要防止併發的業務,鎖的力度越低性能越好!
			isLock = redLockUtils.tryLockTimeout(lockKey, 5000, 10000);
			//超時未獲得鎖
			if(!isLock ){
				return Response.returnCode(ReturnCode.BIZ_FAIL.getCode(), "操作太頻繁,請稍後重試");
			}
			//執行業務(需要鎖定的部分)
			Response response = this.totalService.unifiedorder(paymentDTO);
			return response;
		} catch (Exception e) {
			LOGGER.error("TotalController.unifiedorder.error:", e);
			return Response.fail();
		}finally {
			//解鎖
			if(isLock) {
				redLockUtils.unLock(lockKey);
			}
		}
	}
}

注意鎖的力度,只需要鎖定需要防止併發的業務,鎖的力度越低性能越好!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章