Application of Redis distributed lock in business scene

Source: Internet
Author: User
Tags aop redis throwable redis cluster redis server

Background Description:

The company is to do the Internet lending business, a period of time before docking a third-party platform for users of the platform to provide cash lending business. However, just on the line found that there is a very serious problem, that is, in a short period of time (milliseconds) with a user generated a lot of loans, our business scenario is to ask the same user for the same class of loans, can only exist a loan to be repaid. After the investigation found that the third party platform at the same time to send multiple times the same loan request (in fact, the third party platform does not control the good logic, very short time to the user of the same loan request multiple retries). When the problem is detected, the Third-party platform troubleshooting is immediately notified, preventing repeated submissions from users, and controlling the frequency of server retry mechanisms, but it is clear that the security of our platform cannot be relied upon by a third party.

In response to this problem, three solutions were thought out:

1. Implement a lock-like function in the application by caching

2. Implement distributed locks in the database

3. Implement distributed locks using Redis

The first approach, implemented through caching in an application, will consume a large cache of application servers, and it is clearly not possible to resolve the problem in a distributed deployment scenario.

The second way, the database store the loan lock, this method is feasible, but the performance of relational database is always a problem, when the request is large, will become the bottleneck of the system.

After consideration, the final choice of Redis distributed lock to achieve the serial processing of the loan logic.

Code implementation (sensitive information processing, log printing is also eliminated, simplified code):

/**

* @title user loan serial execution, Redis lock control, using spring AOP implementation

*/

@Aspect

@Order (1)

@Component

Public class Custborrowlockaspect extends writeresult{

Final Loggerlogger = Loggerfactory.getlogger (this. getclass ());

Final dateformatformat=New SimpleDateFormat ("Yyyy-mm-dd HH:mm:ss");

Final Long Lock_minutes= 5L; Loan Lock Expiration time, 5 minutes

Final Long Expire_second= Timeunit. MINUTES. Toseconds (lock_minutes);//loan Lock Expiration Time

@Resource

Private Redislockhelperlockhelper;

/**

* The interface provided to the third party to borrow, method Apploan4channel is the loan entrance (rest style)

*/

@Pointcut ("Execution (*p2p.service.loan.icreditloanservice.apploan4channel (..))")

Public void Apply4channel () {

}

/**

* The interface to provide loans to its own platform, method Apploan is the borrowing portal (rest style)

*/

@Pointcut ("Execution (*p2p.service.loan.icreditloanservice.apploan (..))")

Public void Apploan () {

}

@Around ("Apply4channel ()")

Public Objectdoborrow4channel (PROCEEDINGJOINPOINTPJP)throwsThrowable {

return Doborrow (PJP, "Doborrow4channel", Mongodbutils. cust_id,false);

}

@Around ("Apploan ()")

Public Objectdoborrow4apporh5 (PROCEEDINGJOINPOINTPJP)throwsThrowable {

return Doborrow (PJP, "Doborrow4apporh5", Mongodbutils. cust_id,false);

}

Public Objectdoborrow (PROCEEDINGJOINPOINTPJP, Stringintface,stringcustidkey,booleanisreturnstring)

throws Throwable {

jsonresultjsonresult= Newjsonresult ();

Jsonresult.setsuccess (false);

Booleanislocked=false;//whether to get a lock

Stringlockname=null;

stringcustid=null;

objectresult= null;

object[]paramarray= null;

Try {

ParamArray = Pjp.getargs ();//parameter array

Custid=getcustid (Paramarray[0],custidkey);//Get user number

Lockname=redisprekeyenum. Lock_cust_borrow_key. Getpre_key () +custid;//user number as a lock to prevent concurrent loan operations by the same user

Islocked=lockhelper.lock (lockname,intface+ ":" +format.format (new Date ()), Expire_second);

if (!islocked) {//Failed to acquire lock

Jsonresult.setmsg ("Repeat the request, please" +lock_minutes+ "minutes after" try again.) ");

Jsonresult.setmsgcode ("G0002");

return Tojsonstr (jsonresult,isreturnstring);

}

result = Pjp.proceed ();//execute Borrowing related logic

return Tojsonstr (result,false);

}catch(Throwablee) {

JSONRESULT.SETMSG ("System internal error [ERR001].") ");

return Tojsonstr (jsonresult,isreturnstring);

finally{

if (lockname!=null &&islocked) {

Lockhelper.unlock (Lockname);/release lock

}

}

}


/**

* Convert an object to a JSON string

*/

Private Object tojsonstr (Objectjsonresult,booleanisreturnstring) {

return isreturnstring writeresult (jsonresult) jsonresult): Jsonresult;

}

/**

* Resolve User ID

*/

Private String Getcustid (Objectparamobj,stringuserkey) {

Stringparam= (String) paramobj;

jsonobjectparams= jsonobject.parseobject (param);

Longcustid= Params.getlong (UserKey);

Stringcid=string.valueof (CustID);

if (cid==null) {

throw New RuntimeException ("Not Found param:cust_id");

}

CID =cid.trim ();

if (Cid.length () ==0) {

throw New RuntimeException ("Not Found param:cust_id");

}

return CID;

}

}

/**
* Redis Lock Help Class (Operations based on Springframework.data.redis)
*/
@Component ("Redislockhelper")
public class Redislockhelper {
Final Logger Logger = Loggerfactory.getlogger (This.getclass ());

@Autowired
Private Stringredistemplate Jedis;

/**
* Get a Redis lock (according to key)
* (Jedis.opsforvalue () without SETNX interface is implemented through execute)
* @param lockname Lock Name
* @param value Lock other information
* @param expire Lock Expiration time (in seconds)
* @return Whether the lock was successfully acquired: True-successfully acquired the lock, False-failed to acquire the lock
*/
Public boolean lock (final string lockname,final string value, final long expire) {
try {
Final byte[] lockbytes = Jedis.getstringserializer (). Serialize (Lockname);
Final byte[] valuebytes = Jedis.getstringserializer (). Serialize (value);
Long rs= Jedis.execute (New rediscallback<long> () {
Public Long Doinredis (redisconnection connection) {
Boolean locked = CONNECTION.SETNX (lockbytes, valuebytes);
if (locked) {
Connection.expire (lockbytes, expire);/set an expiration time
return 1L;
}
TTL check expiration (prevents setnx, expire crashes) without acquiring a lock
Long Checkexpire=connection.ttl (lockbytes);
if (Checkexpire = = 1) {//ttl:-1, if key does not expire. -2 If the key does not exist.
Connection.expire (lockbytes, expire); If the previous key does not have an expiration mechanism, set an expiration time
Logger.warn ("TTL check is valid, reset Expiration time!") [lockname={}, expire={}] ", lockname, expire);
}
Return 0l;//does not do the queuing processing, returns the failure directly
}
});
return (RS==1L);
catch (Exception e) {
Logger.error ("Get Redis Lock exception!") [Lockname= "+lockname+"] ", e);
}
return false;
}

public void Unlock (final String lockname) {
try {
Jedis.delete (Lockname);
catch (Exception e) {
Logger.error ("Release Redis lock Exception!") [Lockname= "+lockname+"] ", e);
}
}

}


Summarize:

1. To reduce code coupling, spring AOP was used to prevent the modification of existing code.

2. The use of Redis to achieve distributed locks, to achieve a distributed deployment scenario user Loan serial processing (to prevent the generation of multiple duplicate loans).


Insufficient:

When the Redis server is down, it will cause all borrowings to be blocked, so you can consider deploying the Redis cluster later.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.