Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to understand the idempotency of Java and distributed locks

2025-03-30 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/01 Report--

This article focuses on "how to understand Java idempotency and distributed locks". Interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn how to understand Java idempotency and distributed locks.

1. What is idempotency?

Idempotency means that the effect of an idempotent operation is the same as that of a single execution. In mathematical terms, it is expressed as follows: F (f (x)) = f (x). Just like nx1 = n, x1 is an idempotent operation. The result is the same no matter how many times it is multiplied.

two。 Common idempotent problems

Idempotent problems are often caused by network problems and repetitive operations.

Scenario 1: for example, in the like function, a user can only like the same article once, and the like prompt has already been liked.

Sample code:

Public void like (Article article,User user) {/ / check whether if (checkIsLike (article,user)) {/ / overlikes throw new ApiException (CodeEnums.SYSTEM_ERR);} else {/ / Save like saveLike (article,user);}}

There seems to be no problem. I have checked whether I like it before I save it. In theory, the same person will not like the same article repeatedly. But that's not the case. Because network requests are not queued in, but swarmed in.

Sometimes, the user's network is not good and may be clicked many times in a very short period of time. Due to network transmission problems, these requests may come to our server at the same time.

The first request, checkIsLike (), returns false, and the saveLike () operation is being performed. Transactions that have not yet come and committed are still in progress.

The second request came, and checkIsLike () returned false, too, and performed the saveLike () operation.

In this way, a user likes an article many times at the same time.

This is a typical idempotent problem, the result of one operation is different from that of two operations, because you click one more like, according to the idempotent principle, no matter how many times you click, the result is the same, only one like.

Many scenarios are caused by this, such as users repeatedly place orders, repeat comments, repeatedly submit forms, and so on.

Then how to solve it? Assuming that requests from the network are queued in, this problem will not occur.

So we can change it like this:

Public synchronized void like (Article article,User user) {/ / check whether if (checkIsLike (article,user)) {/ / overlikes throw new ApiException (CodeEnums.SYSTEM_ERR);} else {/ / Save like saveLike (article,user);}}

Synchronized synchronization lock so that our requests will obediently queue in.

PS: this is a relatively inefficient approach. It is not recommended, it is just an example, and synchronized is not suitable for distributed cluster scenarios.

Scenario 2: third-party callback

Our system often needs to deal with third-party systems, such as Wechat recharge, Wechat recharge and so on. Wechat and Alipay will often call back your interface to inform you of the payment result. In order to ensure that you can receive a callback, it is often possible to call back multiple times.

Sometimes in order to ensure the accuracy of the data, we have a timer to query the pipeline where the payment result is unknown, and perform the response processing.

If the timer rotation and callback happen to be carried out at the same time, there may be another BUG and two more repeated operations.

Then the problem comes: suppose I am a recharge operation, when I call back, I will do business processing and successfully add money to the user's account. This is after to ensure idempotence, suppose Wechat the same transaction to you back twice, if you recharge the user twice, this is obviously unreasonable (I am the boss must deduct your salary), so make sure that no matter how many times Wechat callback you, you can only give users money once for the same transaction. This is idempotency.

The solution to the idempotent problem

Synchronized is suitable for stand-alone applications, with no pursuit of performance or concurrency.

Distributed locks but often our applications are distributed clusters, and pay attention to performance, concurrency, so we need to use distributed locks to solve this problem.

Redis distributed Lock:

/ * setNx** @ param key* @ param value* @ return*/public Boolean setNx (String key,Object value) {return redisTemplate.opsForValue (). SetIfAbsent (key,value);} / * * @ param key lock * @ param waitTime wait milliseconds * @ param expireTime timeout milliseconds * @ return*/public Boolean lock (String key,long waitTime,long expireTime) {String vlaue = UUIDUtil.mongoObjectId (); Boolean flag = setNx (key,vlaue) / / successful attempt to acquire lock returned if (flag) {redisTemplate.expire (key,expireTime,TimeUnit.MILLISECONDS); return flag;} else {/ / failed / / now time long newTime = System.currentTimeMillis (); / / waiting for expiration time long loseTime = newTime + waitTime / / continuous attempts to acquire locks successfully returned while (System.currentTimeMillis () < loseTime) {Boolean testFlag = setNx (key,vlaue); if (testFlag) {redisTemplate.expire (key,expireTime,TimeUnit.MILLISECONDS); return testFlag } / / sleep 100ms try {Thread.sleep;} catch (InterruptedException e) {e.printStackTrace ();} return false / * * @ param key* @ return*/public Boolean lock (String key) {return lock (key,1000L,60 * 1000L);} / * * @ param key*/public void unLock (String key) {remove (key);}

With Redis distributed locks, our code can be changed like this:

Public void like (Article article,User user) {String key = "key:like" + article.getId () + ":" + user.getUserId (); / / time to wait for the lock is 0, expiration time is one minute to prevent deadlock Boolean flag = redisService.lock (key,0,60 * 1000L) If (! flag) {/ / failed to acquire lock indicates that the previous request has acquired lock throw new ApiException (CodeEnums.SYSTEM_ERR);} / / check whether if (checkIsLike (article,user)) {/ / like throw new ApiException (CodeEnums.SYSTEM_ERR);} else {/ / save like saveLike (article,user) } / / remove lock redisService.unLock (key);}

The design of key is also exquisite:

For the two business scenarios where the data does not conflict, the key cannot conflict, the key of different people is different, and the Key of different articles is also different.

According to the business setting of the scene.

One principle: narrow the scope of key as much as possible. Only in this way can we enhance our concurrency.

First of all, we acquire the lock, acquire the lock successfully, save the data, and delete the lock. Failed to get lock return. The expiration time is set to prevent "deadlock". For example, the machine acquires the lock and does not set the expiration time, but it crashes and does not delete the release lock.

At this point, I believe you have a deeper understanding of "how to understand Java idempotency and distributed locks". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 292

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report