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);
}
}
}
}
注意鎖的力度,只需要鎖定需要防止併發的業務,鎖的力度越低性能越好!