背景描述:
公司是做互联网借贷业务的,前段时间对接了一个第三方平台,为该平台的用户提供现金借贷业务。但是,刚上线便发现存在一个很严重的问题,就是在短时间内(毫秒级)同一个用户生成了多笔借款,我们的业务场景是要求同一个用户针对同一类借款,只可以存在一笔待还借款。经过排查发现第三方平台每次都会在同一时间发送多次相同的借款请求(其实就是第三方平台没有控制好逻辑,极短时间内对用户的同一个借款请求多次重试引起的)。查出问题后便立即通知第三方平台排查问题,防止用户的重复提交,并控制好服务器重试机制的频率,但是显然我们平台的安全性不能够依赖第三方去实现。
针对该问题,想到了三个解决办法:
1. 在应用中通过缓存实现一个类似锁的功能
2. 在数据库中实现分布式锁
3. 使用redis实现分布式锁
第一种方式,在应用中通过缓存来实现,将会占用应用服务器的大量缓存,而且在分布式部署的场景下,显然无法解决该问题。
第二种方式,数据库中存储借款锁,此方法可行,但是关系型数据库的性能始终是个问题,当请求量大了之后将会成为系统的瓶颈。
经过考量,最终选择redis分布式锁来实现借款逻辑的串行处理。
代码实现(敏感信息处理过,日志打印也剔除,简化代码):
/**
* @title用户借款串行执行,redis锁控制,利用spring AOP 实现
*/
@Aspect
@Order(1)
@Component
public classCustBorrowLockAspect extends WriteResult{
final Loggerlogger = LoggerFactory.getLogger(this.getClass());
final DateFormatformat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
final long LOCK_MINUTES= 5L; //借款锁过期时间,5分钟
final long EXPIRE_SECOND= TimeUnit.MINUTES.toSeconds(LOCK_MINUTES);//借款锁过期时间
@Resource
private RedisLockHelperlockHelper;
/**
* 提供给第三方借款的接口,方法appLoan4Channel是借款入口(rest风格)
*/
@Pointcut("execution(*p2p.service.loan.ICreditLoanService.appLoan4Channel(..))")
public void apply4Channel() {
}
/**
* 提供给自身平台借款的接口,方法appLoan是借款入口(rest风格)