In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly introduces how to use Redis+Lua script to achieve counter interface anti-brush function, the article is very detailed, has a certain reference value, interested friends must read it!
[implementation process] I. problem analysis
If the set command is set, but the setting is not successful due to network jitter and other reasons when setting the failure time, a dead counter (similar to deadlock) will appear.
II. Solutions
Redis+Lua is a good solution, using scripts to make set commands and expire commands work together so that Redis is executed without interference, thus ensuring atomic operations to a large extent.
Why is atomic operation guaranteed to a large extent rather than completely guaranteed? Because there may be problems when executing inside Redis, but the probability is very small. Even for low probability events, there are corresponding solutions. For example, an idea to solve deadlocks is worth referring to: preventing deadlocks will save the value of the lock as a timestamp. Even if the failure time is not set, you can add to determine whether the lock is locked or not, and if so, delete and reset the lock.
III. Code modification
1. The realization of Redis+Lua lock.
Package han.zhang.utils; import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.DigestUtils;import org.springframework.data.redis.core.script.RedisScript;import java.util.Collections;import java.util.UUID;public class RedisLock {private static final LogUtils logger = LogUtils.getLogger (RedisLock.class); private final StringRedisTemplate stringRedisTemplate; private final String lockKey; private final String lockValue; private boolean locked = false / * using scripts to execute this logic on the redis server can guarantee the atomicity of this operation to a certain extent * (that is, the client will not crash or lose the connection with the server between the setNX and expire commands, resulting in expire not being executed and permanent deadlock) *
* unless the redis server crashes when the script is executed by the redis server, the lock will also fail * / private static final RedisScript SETNX_AND_EXPIRE_SCRIPT; static {StringBuilder sb = new StringBuilder (); sb.append ("if ('setnx', KEYS [1], ARGV [1]) = = 1) then\ n") Sb.append ("\ tredis.call ('expire', KEYS [1], tonumber (ARGV [2]))\ n"); sb.append ("\ treturn true\ n"); sb.append ("else\ n"); sb.append ("\ treturn false\ n"); sb.append ("end"); SETNX_AND_EXPIRE_SCRIPT = new RedisScriptImpl (sb.toString (), Boolean.class) } private static final RedisScript DEL_IF_GET_EQUALS; sb.append ("if (redis.call ('get', KEYS [1]) = = ARGV [1]) then\ n"); sb.append ("\ tredis.call (' del', KEYS [1])\ n"); DEL_IF_GET_EQUALS = new RedisScriptImpl (sb.toString (), Boolean.class); public RedisLock (StringRedisTemplate stringRedisTemplate, String lockKey) {this.stringRedisTemplate = stringRedisTemplate This.lockKey = lockKey; this.lockValue = UUID.randomUUID (). ToString () + "." + System.currentTimeMillis (); private boolean doTryLock (int lockSeconds) {if (locked) {throw new IllegalStateException ("already locked!");} locked = stringRedisTemplate.execute (SETNX_AND_EXPIRE_SCRIPT, Collections.singletonList (lockKey), lockValue, String.valueOf (lockSeconds)); return locked * an attempt was made to acquire the lock, and true was returned successfully. If it fails, the false * * @ param lockSeconds lock time (seconds) is immediately returned. After this time, the lock will automatically release public boolean tryLock (int lockSeconds) {try {return doTryLock (lockSeconds);} catch (Exception e) {logger.error ("tryLock Error", e); return false * the lock is obtained by polling, and true is returned successfully. If the number of polls is exceeded or the time in which false * @ param lockSeconds is locked is returned with an exception (in seconds), the lock will automatically release the interval between * @ param tryIntervalMillis polls (Ms) * @ param maxTryCount. The maximum number of polls public boolean tryLock (final int lockSeconds, final long tryIntervalMillis, final int maxTryCount) {int tryCount = 0 While (true) {if (+ + tryCount > = maxTryCount) {/ / acquire lock timeout return false;} try {if (doTryLock (lockSeconds)) {return true } catch (Exception e) {logger.error ("tryLock Error", e); Thread.sleep (tryIntervalMillis);} catch (InterruptedException e) {logger.error ("tryLock interrupted", e) * unlock operation public void unlock () {if (! locked) {throw new IllegalStateException ("not locked yet!"); locked = false; / / ignore the result stringRedisTemplate.execute (DEL_IF_GET_EQUALS, Collections.singletonList (lockKey), lockValue); private static class RedisScriptImpl implements RedisScript {private final String script; private final String sha1; private final Class resultType Public RedisScriptImpl (String script, Class resultType) {this.script = script; this.sha1 = DigestUtils.sha1DigestAsHex (script); this.resultType = resultType; @ Override public String getSha1 () {return sha1; public Class getResultType () {return resultType; public String getScriptAsString () {return script;}
2. Use lock for reference to realize Redis+Lua counter.
(1) tool class
Package han.zhang.utils; import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.DigestUtils;import org.springframework.data.redis.core.script.RedisScript;import java.util.Collections;public class CountUtil {private static final LogUtils logger = LogUtils.getLogger (CountUtil.class); private final StringRedisTemplate stringRedisTemplate / * using scripts to execute this logic on the redis server can guarantee the atomicity of this operation to a certain extent * (that is, the client will not crash or lose the connection with the server between the setNX and expire commands, resulting in the expire not being executed and the permanent death counter) *
* unless the redis server crashes when the script is executed by the redis server, the counter will also fail * / private static final RedisScript SET_AND_EXPIRE_SCRIPT; static {StringBuilder sb = new StringBuilder (); sb.append ("local visitTimes = redis.call ('incr', KEYS [1])\ n"); sb.append ("if (visitTimes = = 1) then\ n") Sb.append ("\ tredis.call ('expire', KEYS [1], tonumber (ARGV [1]))\ n"); sb.append ("\ treturn false\ n"); sb.append ("elseif (visitTimes > tonumber (ARGV [2])) then\ n"); sb.append ("\ treturn true\ n"); sb.append ("else\ n"); sb.append ("end") SET_AND_EXPIRE_SCRIPT = new RedisScriptImpl (sb.toString (), Boolean.class);} public CountUtil (StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate; public boolean isOverMaxVisitTimes (String key, int seconds, int maxTimes) throws Exception {try {return stringRedisTemplate.execute (SET_AND_EXPIRE_SCRIPT, Collections.singletonList (key), String.valueOf (seconds), String.valueOf (maxTimes)) } catch (Exception e) {logger.error ("RedisBusiness > isOverMaxVisitTimes; get visit times Exception; key:" + key + "result:" + e.getMessage ()); throw new Exception ("already OverMaxVisitTimes");} private static class RedisScriptImpl implements RedisScript {private final String script; private final String sha1; private final Class resultType Public RedisScriptImpl (String script, Class resultType) {this.script = script; this.sha1 = DigestUtils.sha1DigestAsHex (script); this.resultType = resultType; @ Override public String getSha1 () {return sha1; public Class getResultType () {return resultType; public String getScriptAsString () {return script;}
(2) call the test code
Public void run (String... Strings) {CountUtil countUtil = new CountUtil (SpringUtils.getStringRedisTemplate ()); try {for (int I = 0; I < 10; iTunes +) {boolean overMax = countUtil.isOverMaxVisitTimes ("zhanghantest", 600,2); if (overMax) {System.out.println ("more than I:" + I + ":" + overMax) } else {System.out.println ("not more than I:" + I + ":" + overMax);} catch (Exception e) {logger.error ("Exception {}", e.getMessage ());}}
(3) Test results
The above is all the content of this article "how to use Redis+Lua script to achieve counter interface anti-brushing function". Thank you for reading! Hope to share the content to help you, more related knowledge, welcome to follow the industry information channel!
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: 0
*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.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.