Просмотр исходного кода

分布式锁

release_toaliyun_real
Stormeye.Wu 7 лет назад
Родитель
Сommit
79c091e1a7
5 измененных файлов: 118 добавлений и 10 удалений
  1. +1
    -0
      mallinkAdmin/src/main/java/com/simple/config/RedisConfig.java
  2. +2
    -0
      mallinkService/src/main/java/com/simple/common/ErrorCode.java
  3. +26
    -1
      mallinkService/src/main/java/com/simple/service/impl/WxOrderServiceImpl.java
  4. +19
    -9
      mallinkService/src/main/java/com/simple/service/impl/WxPayOrderServiceImpl.java
  5. +70
    -0
      mallinkService/src/main/java/com/simple/utils/RedisLock.java

+ 1
- 0
mallinkAdmin/src/main/java/com/simple/config/RedisConfig.java Просмотреть файл

@@ -46,4 +46,5 @@ public class RedisConfig extends CachingConfigurerSupport {
return jedisPool;
}


}

+ 2
- 0
mallinkService/src/main/java/com/simple/common/ErrorCode.java Просмотреть файл

@@ -30,6 +30,8 @@ public enum ErrorCode{
LOGIN_DENIED(1014, "登录失败"),
TOKEN_INVALID(1015, "TOKEN无效"),

TOO_MANY_REQUEST(1050, "太多的请求访问"),


/**
* 业务级别


+ 26
- 1
mallinkService/src/main/java/com/simple/service/impl/WxOrderServiceImpl.java Просмотреть файл

@@ -13,6 +13,7 @@ import com.simple.exception.MallinkException;
import com.simple.mapper.WxCouponMapper;
import com.simple.mapper.WxOrderMapper;
import com.simple.service.WxOrderService;
import com.simple.utils.RedisLock;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -25,6 +26,9 @@ public class WxOrderServiceImpl implements WxOrderService {

private Logger logger = Logger.getLogger(getClass());

@Autowired
RedisLock redisLock;

@Autowired
WxCouponMapper wxCouponMapper;

@@ -49,17 +53,38 @@ public class WxOrderServiceImpl implements WxOrderService {
@Override
@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
public WxOrder saveOrder(WxOrder record) {

String couponIdStr = String.valueOf(record.getCouponId());
//加锁
long time = System.currentTimeMillis() + RedisLock.TIMEOUT;
String timeStr = String.valueOf(time);
if(!redisLock.lock(couponIdStr, timeStr)) {
logger.error("此券被锁定, couponId: " + record.getCouponId());
throw new MallinkException(ErrorCode.TOO_MANY_REQUEST);
}

try {
// 检查 优惠券 库存
WxCoupon coupon = wxCouponMapper.selectByPrimaryKey(record.getCouponId());
if (coupon.getRemainInventory() <= 0) {
logger.error("coupon not found, couponId: " + record.getCouponId());
//解锁
redisLock.unlock(couponIdStr, timeStr);

logger.error("此券库存为0, couponId: " + record.getCouponId());
throw new MallinkException(ErrorCode.REMAIN_IS_EMPTY);
}
// 减库存
coupon.setRemainInventory(coupon.getRemainInventory() - 1);
wxCouponMapper.updateByPrimaryKeySelective(coupon);
} catch (RuntimeException e) {
//解锁
redisLock.unlock(couponIdStr, timeStr);

logger.error("此券减库存失败, couponId: " + record.getCouponId());
throw new MallinkException(ErrorCode.ORDER_IS_FAIL);
}

try {
// 保存订单
final IdWorker idWorker = IdWorker.get();
record.setId(idWorker.nextId());


+ 19
- 9
mallinkService/src/main/java/com/simple/service/impl/WxPayOrderServiceImpl.java Просмотреть файл

@@ -14,7 +14,6 @@ import com.simple.domain.po.WxPayOrder;
import com.simple.enums.EnumOrderStatus;
import com.simple.enums.EnumPayStatus;
import com.simple.enums.EnumPayWay;
import com.simple.exception.BizMessageException;
import com.simple.exception.MallinkException;
import com.simple.mapper.WxOrderMapper;
import com.simple.mapper.WxPayOrderMapper;
@@ -32,7 +31,6 @@ import org.springframework.stereotype.Service;
import com.simple.common.IdWorker;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

@Service
public class WxPayOrderServiceImpl implements WxPayOrderService {
@@ -48,6 +46,11 @@ public class WxPayOrderServiceImpl implements WxPayOrderService {
@Autowired
WxOrderService wxOrderService;

String weappId = "";
String wechatMchId = "";
String partnerKey = "";
String wxpayNotifyUrl = "";

JSONObject errorMap = JSON.parseObject("{\"NOAUTH\":{\"detail\":\"商户无此接口权限\",\"reason\":\"商户未开通此接口权限\",\"resolution\":\"请商户前往申请此接口权限\"}," +
"\"NOTENOUGH\":{\"detail\":\"余额不足\",\"reason\":\"用户帐号余额不足\",\"resolution\":\"用户帐号余额不足,请用户充值或更换支付卡后再支付\"}," +
"\"ORDERPAID\":{\"detail\":\"商户订单已支付\",\"reason\":\"商户订单已支付,无需重复操作\",\"resolution\":\"商户订单已支付,无需更多操作\"}," +
@@ -66,7 +69,8 @@ public class WxPayOrderServiceImpl implements WxPayOrderService {

@Override
public ResultData createPayOrder(WxPayOrder record, EnumPayWay payWay) {
String partnerKey = null;
final IdWorker idworker = IdWorker.get();

try {
// 1. check 订单
WxOrder order = wxOrderMapper.selectByPrimaryKey(record.getOrderId());
@@ -80,9 +84,10 @@ public class WxPayOrderServiceImpl implements WxPayOrderService {
}
// TODO body
String body = "";
// 2. 创建支付订单
// 2. check 是否有支付订单
// 3. 创建支付订单
Date currentDate = new Date();
final IdWorker idworker = IdWorker.get();
Long id = idworker.nextId();
record.setId(id);
record.setCreateTime(currentDate);
@@ -99,18 +104,22 @@ public class WxPayOrderServiceImpl implements WxPayOrderService {

int sqlRow = wxPayOrderMapper.insertSelective(record);
if(sqlRow == 1) {
return new ResultData();
}
/*
{
// 统一下单
String noncestr = Utility.generate32UUID();
WxPayOrderP wxPayOrderP = new WxPayOrderP();
//wxPayOrderP.setAppid(wechatAppId);
//wxPayOrderP.setMch_id(wechatMchId);
wxPayOrderP.setAppid(weappId);
wxPayOrderP.setMch_id(wechatMchId);
wxPayOrderP.setNonce_str(noncestr);
//wxPayOrderP.setBody(body);
wxPayOrderP.setBody(body);
wxPayOrderP.setOut_trade_no(record.getPayOrderNo());
BigDecimal llp = order.getPayment().multiply(new BigDecimal(100));
wxPayOrderP.setTotal_fee(llp.intValue());
wxPayOrderP.setSpbill_create_ip(record.getIp()); // 终端IP
//wxPayOrderP.setNotify_url(wxpayNotifyUrl);
wxPayOrderP.setNotify_url(wxpayNotifyUrl);
wxPayOrderP.setTrade_type(WxPay.TradeType.APP.name()); // 终端类型
wxPayOrderP.setProduct_id(String.valueOf(order.getId())); // 订单ID
wxPayOrderP.setTime_start(Utility.getDataFormatStringYYYYMMDDHHmmss(currentTime));
@@ -139,6 +148,7 @@ public class WxPayOrderServiceImpl implements WxPayOrderService {
return new ResultData(ErrorCode.PAYORDER_ERROR.getCode(), errObj.toJSONString(), returnMap);
}
}
*/
return new ResultData();
} catch (RuntimeException e) {
throw new MallinkException(ErrorCode.PAYORDER_ERROR);


+ 70
- 0
mallinkService/src/main/java/com/simple/utils/RedisLock.java Просмотреть файл

@@ -0,0 +1,70 @@
package com.simple.utils;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

/**
* RedisLock
* User: Stormeye
* Date: 2018/8/12
* Explain:Redis分布式锁
*/
@Component
public class RedisLock {
private org.apache.log4j.Logger logger = Logger.getLogger(getClass());

@Autowired
private StringRedisTemplate stringRedisTemplate;

public static final int TIMEOUT = 10*1000;//超时时间 10s

/**
* 加锁
* @param key productId - 商品的唯一标志
* @param value 当前时间+超时时间
* @return
*/
public boolean lock(String key,String value){
if(stringRedisTemplate.opsForValue().setIfAbsent(key,value)){//对应setnx命令
//可以成功设置,也就是key不存在
return true;
}

//判断锁超时 - 防止原来的操作异常,没有运行解锁操作 防止死锁
String currentValue = stringRedisTemplate.opsForValue().get(key);
//如果锁过期
if(!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()){//currentValue不为空且小于当前时间
//获取上一个锁的时间value
String oldValue =stringRedisTemplate.opsForValue().getAndSet(key,value);//对应getset,如果key存在

//假设两个线程同时进来,key被占用了。获取的值currentValue=A(get取的旧的值肯定是一样的),两个线程的value都是B,key都是K.锁时间已经过期了。
//而这里面的getAndSet一次只会一个执行,也就是一个执行之后,上一个的value已经变成了B。只有一个线程获取的上一个值会是A,另一个线程拿到的值是B。
if(!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue) ){
//oldValue不为空且oldValue等于currentValue,也就是校验是不是上个对应的商品时间戳,也是防止并发
return true;
}
}
return false;
}


/**
* 解锁
* @param key
* @param value
*/
public void unlock(String key,String value){
try {
String currentValue = stringRedisTemplate.opsForValue().get(key);
if(!StringUtils.isEmpty(currentValue) && currentValue.equals(value) ){
stringRedisTemplate.opsForValue().getOperations().delete(key);//删除key
}
} catch (Exception e) {
logger.error("[Redis分布式锁] 解锁出现异常了,{}",e);
}
}

}

Загрузка…
Отмена
Сохранить