preface
White pixie digging gold for a long time, recently learned the redis distributed lock related knowledge, decided to write an article to share with you, one is to strengthen their memory, the other is to want to understand the relevant knowledge of friends some ideas. This article will use Nginx and two cluster microservices to show why distributed locks are used, as well as a step-by-step analysis of the problems associated with locking.
Simple demo procedures
Here is a simple demo program, which is a controller called once and operated once redis reduces an inventory
@RestController public class GoodController { @Autowired private StringRedisTemplate stringRedisTemplate; @Value("${server.port}") private String serverPort; @GetMapping("/buyGoods") public String buyGoods() { String result = stringRedisTemplate.opsForValue().get("goods"); int goodsNumber = result == null ? 0 : Integer.parseInt(result); if(goodsNumber > 0) { int realNumber = goodsNumber - 1; stringRedisTemplate.opsForValue().set("goods", String.valueOf(realNumber)); System.out.println(" + realNumber + "+ "\t service provider window" + serverPort); Return "+ realNumber +" + "\t service offering window "+ serverPort; } else {system.out. println(" product sold out/activity ended/call timeout, welcome next time "+ serverPort); } return "product sold out/activity ended/call timeout, welcome next time" + serverPort; }}Copy the code
Other configuration classes are not posted, mainly such a function. The same code is then copied into another project. Start two microservices as a cluster. The only difference is that one port is 1111 and the other is 2222
Then use nginx for forwarding and load balancing. Nginx is configured as follows
Upstream mynginx{server 127.0.0.1:1111 weight=1; Server 127.0.0.1:2222 weight = 1; } server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { proxy_pass http://mynginx; index index.html index.htm; }Copy the code
A polling load balancing is done.
Then use JMeter to create a hundred threads per second.
Compared with the results printed by the console, it is obvious that a product has been sold several times
The solution
Consider locking redis, now the production mature solution is to use Redisson directly. Let’s use redis String setnx to lock, and then analyze what the problem is, and the emergence of Redisson to solve the production of what problem
The first is to lock, the code is as follows
The results are as follows
Look, there is no one commodity has been sold more than once. The idea here is to define a constant that is the name of the lock, which is the key, and then use setIfAbsent to return true if the key is absent and false if it is present. Then delete this key when you use it.
Ok everything seems to be fine. There are no problems. But is that really the case?
1, after you have acquired the lock, if your next business process reported an exception, can not successfully delete the key, and then other threads will never be able to acquire the lock, right? Similar to file streaming, or locking. Make sure you release it when you use it. So let’s add finally to the delete key
2. In extreme cases, the program just goes to get the lock, and then the program suddenly breaks down. Finally cannot be reached at all, and there is no way to ensure the unlock. Is it possible to add an expiration date to this key? stringRedisTemplate.expire(REDIS_LOCK, 10L, TimeUnit.SECONDS); Add this code and set an expiration date for Redis so that the key is automatically deleted
3. The code above looks like this
What’s the problem here, setting up the key and setting the expiration time, that’s two lines of code, it’s not guaranteed atomicity, it’s still a problem in the case of high concurrency. So two lines of code in the diagram can be combined into one line Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK,value,10L, TimeUnit.SECONDS);
This ensures atomicity in creating keys and setting expiration times
4. In this case, we set the expiration time for this key to 10 seconds. What happens if our business takes longer than 10 seconds to process? Thread A’s own key has been deleted, and thread B has created A key. Then, after A finishes processing the business, his key has timed out. What happens when the delete key is then executed? It’s going to delete the key of thread B, and then when thread B deletes it’s going to delete the key of thread C, and all hell breaks loose. So let’s delete the key and write it this way
Compare the values to be equal to ensure that you delete your own key
5. At this point, any questions? There is. The judge and delete locks in finally above are also not atomic. How to do, the official already gave the relevant solution.
That’s right, lua scripts. Go straight to code
6. There is basically no problem at this point, except how can we ensure that the expiration time of the key is longer than the execution time of the business? Someone said: I set a large expiration time, ok? You can! Doesn’t that conflict with number two above? So the best case is that the expiration date can be automatically renewed. The other problem is that we’re all talking about stand-alone Redis so far, and in production, you’re most likely going to use cluster, master-slave sentry mode. Redis in CAP belongs to AP high availability and is not strongly consistent. If the key is written to the master server, the synchronization has not been completed, and the master server has been suspended, what will happen if the new master elected at this time does not have the key information? In a cluster environment, the self-written lock is still not safe.
7. Redisson makes his entrance. Adding configuration files
Here is a single machine on, you can also configure the cluster.
The core code is here
conclusion
Here by first writing a redis lock, step by step analysis of the problem and the final solution. As for why Redisson was able to solve these problems, there’s also the issue of automatic key renewals. Set a flag, 51 read the source code, figure it out, and share it. One last question, regarding the issue of obtaining and unlocking atomicity mentioned above, is there any other way to solve it if you don’t have to use Lua scripts?