聊聊redisson的RRateLimiter

2024-04-23 15:04

本文主要是介绍聊聊redisson的RRateLimiter,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文主要研究一下redisson的RRateLimiter

RRateLimiter

redisson/src/main/java/org/redisson/api/RRateLimiter.java

public interface RRateLimiter extends RRateLimiterAsync, RExpirable {/*** Initializes RateLimiter's state and stores config to Redis server.* * @param mode - rate mode* @param rate - rate* @param rateInterval - rate time interval* @param rateIntervalUnit - rate time interval unit* @return {@code true} if rate was set and {@code false}*         otherwise*/boolean trySetRate(RateType mode, long rate, long rateInterval, RateIntervalUnit rateIntervalUnit);/*** Updates RateLimiter's state and stores config to Redis server.** @param mode - rate mode* @param rate - rate* @param rateInterval - rate time interval* @param rateIntervalUnit - rate time interval unit*/void setRate(RateType mode, long rate, long rateInterval, RateIntervalUnit rateIntervalUnit);/*** Acquires a permit only if one is available at the* time of invocation.** <p>Acquires a permit, if one is available and returns immediately,* with the value {@code true},* reducing the number of available permits by one.** <p>If no permit is available then this method will return* immediately with the value {@code false}.** @return {@code true} if a permit was acquired and {@code false}*         otherwise*/boolean tryAcquire();/*** Acquires the given number of <code>permits</code> only if all are available at the* time of invocation.** <p>Acquires a permits, if all are available and returns immediately,* with the value {@code true},* reducing the number of available permits by given number of permits.** <p>If no permits are available then this method will return* immediately with the value {@code false}.** @param permits the number of permits to acquire* @return {@code true} if a permit was acquired and {@code false}*         otherwise*/boolean tryAcquire(long permits);/*** Acquires a permit from this RateLimiter, blocking until one is available.** <p>Acquires a permit, if one is available and returns immediately,* reducing the number of available permits by one.* */void acquire();/*** Acquires a specified <code>permits</code> from this RateLimiter, * blocking until one is available.** <p>Acquires the given number of permits, if they are available * and returns immediately, reducing the number of available permits * by the given amount.* * @param permits the number of permits to acquire*/void acquire(long permits);/*** Acquires a permit from this RateLimiter, if one becomes available* within the given waiting time.** <p>Acquires a permit, if one is available and returns immediately,* with the value {@code true},* reducing the number of available permits by one.** <p>If no permit is available then the current thread becomes* disabled for thread scheduling purposes and lies dormant until* specified waiting time elapses.** <p>If a permit is acquired then the value {@code true} is returned.** <p>If the specified waiting time elapses then the value {@code false}* is returned.  If the time is less than or equal to zero, the method* will not wait at all.** @param timeout the maximum time to wait for a permit* @param unit the time unit of the {@code timeout} argument* @return {@code true} if a permit was acquired and {@code false}*         if the waiting time elapsed before a permit was acquired*/boolean tryAcquire(long timeout, TimeUnit unit);/*** Acquires the given number of <code>permits</code> only if all are available* within the given waiting time.** <p>Acquires the given number of permits, if all are available and returns immediately,* with the value {@code true}, reducing the number of available permits by one.** <p>If no permit is available then the current thread becomes* disabled for thread scheduling purposes and lies dormant until* the specified waiting time elapses.** <p>If a permits is acquired then the value {@code true} is returned.** <p>If the specified waiting time elapses then the value {@code false}* is returned.  If the time is less than or equal to zero, the method* will not wait at all.** @param permits amount* @param timeout the maximum time to wait for a permit* @param unit the time unit of the {@code timeout} argument* @return {@code true} if a permit was acquired and {@code false}*         if the waiting time elapsed before a permit was acquired*/boolean tryAcquire(long permits, long timeout, TimeUnit unit);/*** Returns current configuration of this RateLimiter object.* * @return config object*/RateLimiterConfig getConfig();/*** Returns amount of available permits.** @return number of permits*/long availablePermits();}

RRateLimiter继承了RRateLimiterAsync、RExpirable接口,它主要定义了trySetRate、setRate、tryAcquire、acquire、getConfig、availablePermits方法

RRateLimiterAsync

redisson/src/main/java/org/redisson/api/RRateLimiterAsync.java

public interface RRateLimiterAsync extends RExpirableAsync {/*** Initializes RateLimiter's state and stores config to Redis server.* * @param mode - rate mode* @param rate - rate* @param rateInterval - rate time interval* @param rateIntervalUnit - rate time interval unit* @return {@code true} if rate was set and {@code false}*         otherwise*/RFuture<Boolean> trySetRateAsync(RateType mode, long rate, long rateInterval, RateIntervalUnit rateIntervalUnit);/*** Acquires a permit only if one is available at the* time of invocation.** <p>Acquires a permit, if one is available and returns immediately,* with the value {@code true},* reducing the number of available permits by one.** <p>If no permit is available then this method will return* immediately with the value {@code false}.** @return {@code true} if a permit was acquired and {@code false}*         otherwise*/RFuture<Boolean> tryAcquireAsync();/*** Acquires the given number of <code>permits</code> only if all are available at the* time of invocation.** <p>Acquires a permits, if all are available and returns immediately,* with the value {@code true},* reducing the number of available permits by given number of permits.** <p>If no permits are available then this method will return* immediately with the value {@code false}.** @param permits the number of permits to acquire* @return {@code true} if a permit was acquired and {@code false}*         otherwise*/RFuture<Boolean> tryAcquireAsync(long permits);/*** Acquires a permit from this RateLimiter, blocking until one is available.** <p>Acquires a permit, if one is available and returns immediately,* reducing the number of available permits by one.* * @return void*/RFuture<Void> acquireAsync();/*** Acquires a specified <code>permits</code> from this RateLimiter, * blocking until one is available.** <p>Acquires the given number of permits, if they are available * and returns immediately, reducing the number of available permits * by the given amount.* * @param permits the number of permits to acquire* @return void*/RFuture<Void> acquireAsync(long permits);/*** Acquires a permit from this RateLimiter, if one becomes available* within the given waiting time.** <p>Acquires a permit, if one is available and returns immediately,* with the value {@code true},* reducing the number of available permits by one.** <p>If no permit is available then the current thread becomes* disabled for thread scheduling purposes and lies dormant until* specified waiting time elapses.** <p>If a permit is acquired then the value {@code true} is returned.** <p>If the specified waiting time elapses then the value {@code false}* is returned.  If the time is less than or equal to zero, the method* will not wait at all.** @param timeout the maximum time to wait for a permit* @param unit the time unit of the {@code timeout} argument* @return {@code true} if a permit was acquired and {@code false}*         if the waiting time elapsed before a permit was acquired*/RFuture<Boolean> tryAcquireAsync(long timeout, TimeUnit unit);/*** Acquires the given number of <code>permits</code> only if all are available* within the given waiting time.** <p>Acquires the given number of permits, if all are available and returns immediately,* with the value {@code true}, reducing the number of available permits by one.** <p>If no permit is available then the current thread becomes* disabled for thread scheduling purposes and lies dormant until* the specified waiting time elapses.** <p>If a permits is acquired then the value {@code true} is returned.** <p>If the specified waiting time elapses then the value {@code false}* is returned.  If the time is less than or equal to zero, the method* will not wait at all.** @param permits amount* @param timeout the maximum time to wait for a permit* @param unit the time unit of the {@code timeout} argument* @return {@code true} if a permit was acquired and {@code false}*         if the waiting time elapsed before a permit was acquired*/RFuture<Boolean> tryAcquireAsync(long permits, long timeout, TimeUnit unit);/*** Updates RateLimiter's state and stores config to Redis server.*** @param mode - rate mode* @param rate - rate* @param rateInterval - rate time interval* @param rateIntervalUnit - rate time interval unit* @return {@code true} if rate was set and {@code false}*         otherwise*/RFuture<Void> setRateAsync(RateType mode, long rate, long rateInterval, RateIntervalUnit rateIntervalUnit);/*** Returns current configuration of this RateLimiter object.* * @return config object*/RFuture<RateLimiterConfig> getConfigAsync();/*** Returns amount of available permits.** @return number of permits*/RFuture<Long> availablePermitsAsync();}

RRateLimiterAsync继承了RExpirableAsync,它是async版本的RRateLimiter,它主要定义了trySetRateAsync、setRateAsync、tryAcquireAsync、acquireAsync、getConfigAsync、availablePermitsAsync方法

RedissonRateLimiter

redisson/src/main/java/org/redisson/RedissonRateLimiter.java

public class RedissonRateLimiter extends RedissonExpirable implements RRateLimiter {//......@Overridepublic boolean tryAcquire() {return tryAcquire(1);}@Overridepublic RFuture<Boolean> tryAcquireAsync() {return tryAcquireAsync(1L);}@Overridepublic boolean tryAcquire(long permits) {return get(tryAcquireAsync(RedisCommands.EVAL_NULL_BOOLEAN, permits));}@Overridepublic RFuture<Boolean> tryAcquireAsync(long permits) {return tryAcquireAsync(RedisCommands.EVAL_NULL_BOOLEAN, permits);}@Overridepublic void acquire() {get(acquireAsync());}@Overridepublic RFuture<Void> acquireAsync() {return acquireAsync(1);}@Overridepublic void acquire(long permits) {get(acquireAsync(permits));}@Overridepublic RFuture<Void> acquireAsync(long permits) {CompletionStage<Void> f = tryAcquireAsync(permits, -1, null).thenApply(res -> null);return new CompletableFutureWrapper<>(f);}@Overridepublic boolean tryAcquire(long timeout, TimeUnit unit) {return get(tryAcquireAsync(timeout, unit));}@Overridepublic RFuture<Boolean> tryAcquireAsync(long timeout, TimeUnit unit) {return tryAcquireAsync(1, timeout, unit);}@Overridepublic boolean tryAcquire(long permits, long timeout, TimeUnit unit) {return get(tryAcquireAsync(permits, timeout, unit));}
}

RedissonRateLimiter继承了RedissonExpirable,实现了RRateLimiter接口

trySetRate

    public boolean trySetRate(RateType type, long rate, long rateInterval, RateIntervalUnit unit) {return get(trySetRateAsync(type, rate, rateInterval, unit));}public RFuture<Boolean> trySetRateAsync(RateType type, long rate, long rateInterval, RateIntervalUnit unit) {return commandExecutor.evalWriteNoRetryAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,"redis.call('hsetnx', KEYS[1], 'rate', ARGV[1]);"+ "redis.call('hsetnx', KEYS[1], 'interval', ARGV[2]);"+ "return redis.call('hsetnx', KEYS[1], 'type', ARGV[3]);",Collections.singletonList(getRawName()), rate, unit.toMillis(rateInterval), type.ordinal());}  

trySetRate委托给了trySetRateAsync,这里主要是使用hsetnx来设置rate、interval、type三个值

setRate

    public void setRate(RateType type, long rate, long rateInterval, RateIntervalUnit unit) {get(setRateAsync(type, rate, rateInterval, unit));}public RFuture<Void> setRateAsync(RateType type, long rate, long rateInterval, RateIntervalUnit unit) {return commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,"local valueName = KEYS[2];"+ "local permitsName = KEYS[4];"+ "if ARGV[3] == '1' then "+ "    valueName = KEYS[3];"+ "    permitsName = KEYS[5];"+ "end "+"redis.call('hset', KEYS[1], 'rate', ARGV[1]);"+ "redis.call('hset', KEYS[1], 'interval', ARGV[2]);"+ "redis.call('hset', KEYS[1], 'type', ARGV[3]);"+ "redis.call('del', valueName, permitsName);",Arrays.asList(getRawName(), getValueName(), getClientValueName(), getPermitsName(), getClientPermitsName()), rate, unit.toMillis(rateInterval), type.ordinal());}  

setRate委托给了setRateAsync,这里使用hset来写入rate、interval、type三个值,如果存在则覆盖;另外这里删除了valueName、permitsName这两个key

tryAcquire

    public boolean tryAcquire(long permits) {return get(tryAcquireAsync(RedisCommands.EVAL_NULL_BOOLEAN, permits));}private <T> RFuture<T> tryAcquireAsync(RedisCommand<T> command, Long value) {byte[] random = getServiceManager().generateIdArray();return commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, command,"local rate = redis.call('hget', KEYS[1], 'rate');"+ "local interval = redis.call('hget', KEYS[1], 'interval');"+ "local type = redis.call('hget', KEYS[1], 'type');"+ "assert(rate ~= false and interval ~= false and type ~= false, 'RateLimiter is not initialized')"+ "local valueName = KEYS[2];"+ "local permitsName = KEYS[4];"+ "if type == '1' then "+ "valueName = KEYS[3];"+ "permitsName = KEYS[5];"+ "end;"+ "assert(tonumber(rate) >= tonumber(ARGV[1]), 'Requested permits amount could not exceed defined rate'); "+ "local currentValue = redis.call('get', valueName); "+ "local res;"+ "if currentValue ~= false then "+ "local expiredValues = redis.call('zrangebyscore', permitsName, 0, tonumber(ARGV[2]) - interval); "+ "local released = 0; "+ "for i, v in ipairs(expiredValues) do "+ "local random, permits = struct.unpack('Bc0I', v);"+ "released = released + permits;"+ "end; "+ "if released > 0 then "+ "redis.call('zremrangebyscore', permitsName, 0, tonumber(ARGV[2]) - interval); "+ "if tonumber(currentValue) + released > tonumber(rate) then "+ "currentValue = tonumber(rate) - redis.call('zcard', permitsName); "+ "else "+ "currentValue = tonumber(currentValue) + released; "+ "end; "+ "redis.call('set', valueName, currentValue);"+ "end;"+ "if tonumber(currentValue) < tonumber(ARGV[1]) then "+ "local firstValue = redis.call('zrange', permitsName, 0, 0, 'withscores'); "+ "res = 3 + interval - (tonumber(ARGV[2]) - tonumber(firstValue[2]));"+ "else "+ "redis.call('zadd', permitsName, ARGV[2], struct.pack('Bc0I', string.len(ARGV[3]), ARGV[3], ARGV[1])); "+ "redis.call('decrby', valueName, ARGV[1]); "+ "res = nil; "+ "end; "+ "else "+ "redis.call('set', valueName, rate); "+ "redis.call('zadd', permitsName, ARGV[2], struct.pack('Bc0I', string.len(ARGV[3]), ARGV[3], ARGV[1])); "+ "redis.call('decrby', valueName, ARGV[1]); "+ "res = nil; "+ "end;"+ "local ttl = redis.call('pttl', KEYS[1]); "+ "if ttl > 0 then "+ "redis.call('pexpire', valueName, ttl); "+ "redis.call('pexpire', permitsName, ttl); "+ "end; "+ "return res;",Arrays.asList(getRawName(), getValueName(), getClientValueName(), getPermitsName(), getClientPermitsName()),value, System.currentTimeMillis(), random);}    

tryAcquire委托给了tryAcquireAsync,它通过一个lua脚本来执行,首先通过hget获取rate、interval、type的值,然后根据type来确定valueName、permitsName,如果type为0则valueName是getValueName(),permitsName是getPermitsName(),如果type=1则valueName是getClientValueName(),permitsName是getClientPermitsName();之后获取valueName的值,若为false则直接用设置rate、permits,并递减valueName;若为true则获取expiredValues计算released值,再计算出currentValue,若不够扣则计算返回值,若够扣则通过zadd添加当前permit(System.currentTimeMillis()),然后递减valueName

acquire

    public void acquire() {get(acquireAsync());}public RFuture<Void> acquireAsync() {return acquireAsync(1);}public RFuture<Void> acquireAsync(long permits) {CompletionStage<Void> f = tryAcquireAsync(permits, -1, null).thenApply(res -> null);return new CompletableFutureWrapper<>(f);}public RFuture<Boolean> tryAcquireAsync(long permits, long timeout, TimeUnit unit) {long timeoutInMillis = -1;if (timeout >= 0) {timeoutInMillis = unit.toMillis(timeout);}CompletableFuture<Boolean> f = tryAcquireAsync(permits, timeoutInMillis);return new CompletableFutureWrapper<>(f);}private CompletableFuture<Boolean> tryAcquireAsync(long permits, long timeoutInMillis) {long s = System.currentTimeMillis();RFuture<Long> future = tryAcquireAsync(RedisCommands.EVAL_LONG, permits);return future.thenCompose(delay -> {if (delay == null) {return CompletableFuture.completedFuture(true);}if (timeoutInMillis == -1) {CompletableFuture<Boolean> f = new CompletableFuture<>();getServiceManager().getGroup().schedule(() -> {CompletableFuture<Boolean> r = tryAcquireAsync(permits, timeoutInMillis);commandExecutor.transfer(r, f);}, delay, TimeUnit.MILLISECONDS);return f;}long el = System.currentTimeMillis() - s;long remains = timeoutInMillis - el;if (remains <= 0) {return CompletableFuture.completedFuture(false);}CompletableFuture<Boolean> f = new CompletableFuture<>();if (remains < delay) {getServiceManager().getGroup().schedule(() -> {f.complete(false);}, remains, TimeUnit.MILLISECONDS);} else {long start = System.currentTimeMillis();getServiceManager().getGroup().schedule(() -> {long elapsed = System.currentTimeMillis() - start;if (remains <= elapsed) {f.complete(false);return;}CompletableFuture<Boolean> r = tryAcquireAsync(permits, remains - elapsed);commandExecutor.transfer(r, f);}, delay, TimeUnit.MILLISECONDS);}return f;}).toCompletableFuture();}                

acquire也是复用了tryAcquireAsync方法,只获取不到时会根据返回的delay进行重新调度,若timeoutInMillis不为-1则会根据超时时间进行计算和重新调度

availablePermits

	public long availablePermits() {return get(availablePermitsAsync());}public RFuture<Long> availablePermitsAsync() {return commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_LONG,"local rate = redis.call('hget', KEYS[1], 'rate');"+ "local interval = redis.call('hget', KEYS[1], 'interval');"+ "local type = redis.call('hget', KEYS[1], 'type');"+ "assert(rate ~= false and interval ~= false and type ~= false, 'RateLimiter is not initialized')"+ "local valueName = KEYS[2];"+ "local permitsName = KEYS[4];"+ "if type == '1' then "+ "valueName = KEYS[3];"+ "permitsName = KEYS[5];"+ "end;"+ "local currentValue = redis.call('get', valueName); "+ "if currentValue == false then "+ "redis.call('set', valueName, rate); "+ "return rate; "+ "else "+ "local expiredValues = redis.call('zrangebyscore', permitsName, 0, tonumber(ARGV[1]) - interval); "+ "local released = 0; "+ "for i, v in ipairs(expiredValues) do "+ "local random, permits = struct.unpack('Bc0I', v);"+ "released = released + permits;"+ "end; "+ "if released > 0 then "+ "redis.call('zremrangebyscore', permitsName, 0, tonumber(ARGV[1]) - interval); "+ "currentValue = tonumber(currentValue) + released; "+ "redis.call('set', valueName, currentValue);"+ "end;"+ "return currentValue; "+ "end;",Arrays.asList(getRawName(), getValueName(), getClientValueName(), getPermitsName(), getClientPermitsName()),System.currentTimeMillis());}

availablePermits委托给了availablePermitsAsync,它执行lua脚本,先通过hget获取rate、interval、type的值,然后根据type来确定valueName、permitsName,如果type为0则valueName是getValueName(),permitsName是getPermitsName(),如果type=1则valueName是getClientValueName(),permitsName是getClientPermitsName();之后获取valueName对应的值currentValue,若值为false则重新设置rate,否则通过expiredValues重新计算released,若released大于0则更新到currentValue,最后返回currentValue

小结

redisson的RRateLimiter提供了trySetRate、setRate、tryAcquire、acquire、getConfig、availablePermits方法

  • 其RateType有OVERALL(值为0)、PER_CLIENT(值为1)两个类型,如果type为0则valueName是getValueName(),permitsName是getPermitsName(),如果type=1则valueName是getClientValueName(),permitsName是getClientPermitsName()
  • 它主要定义了几个key,一个是getRawName,类型为hash,其key有rate、interval、type;一个是key为valueName,存储了当前的permits;一个是key为permitsName,类型是sorted set,其score为System.currentTimeMillis(),value通过struct.pack了随机数长度、随机数、此次permit的value
  • trySetRate委托给了trySetRateAsync,这里主要是使用hsetnx来设置rate、interval、type三个值;setRate委托给了setRateAsync,这里使用hset来写入rate、interval、type三个值,如果存在则覆盖;另外这里删除了valueName、permitsName这两个key
  • tryAcquire委托给了tryAcquireAsync,它通过一个lua脚本来执行,首先通过hget获取rate、interval、type的值,之后获取valueName的值,若为false则直接用设置rate、permits,并递减valueName;若为true则获取expiredValues计算released值,再计算出currentValue,若不够扣则计算返回值(告诉调用方可以延时多长时间再重试),若够扣则通过zadd添加当前permit(System.currentTimeMillis()),然后递减valueName
  • acquire是也是复用了tryAcquireAsync方法,只获取不到时会根据返回的delay进行重新调度,若timeoutInMillis不为-1则会根据超时时间进行计算和重新调度

这篇关于聊聊redisson的RRateLimiter的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/929122

相关文章

聊聊说话的习惯

1 在日常生活中,每个人都有固定的说话习惯。心理学研究表明,通过一个人的说话习惯,也可以分析出他的性格特点。对于每一个人来讲,说话习惯已经融为他们生活中的一部分。在社交活动中,一些不良的说话习惯很可能会给他们带来麻烦。因此,了解说话习惯对心理活动的影响是十分有必要的。 2 具有顺畅的说话习惯的人,大多思路清晰、语速适中、用词准确并且声声人耳,是典型的顺畅型说话方式这种类型的人要么不说话,要么

聊聊分布式,再讨论分布式解决方案

前言 最近很久没有写博客了,一方面是因为公司事情最近比较忙,另外一方面是因为在进行 CAP 的下一阶段的开发工作,不过目前已经告一段落了。 接下来还是开始我们今天的话题,说说分布式事务,或者说是我眼中的分布式事务,因为每个人可能对其的理解都不一样。 分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,特别是在微服务架构中,几乎可以说是无法避免,本文就分布式事

聊聊资源调度

资源调度 般分为两个阶段: 是实现物理资源的虚拟化(即资源的抽象)于当前机器的性能越来越好,硬件配置越来越高,直接用物理机跑业务比较浪费,所以将物理机分割成更小单位的虚拟机,这样可以显著提升机器的利用效率,在公司内部一般采用容器技术来隔离资源 是将资源虚拟化后进 步在时间和空间上实现更细粒度的编排 ,优化资源的使用。 1 .一些数据 如果公司的几万台机器都是物理机,那么资源的使用率稍低: CP

聊聊Spark中的宽依赖和窄依赖

开门见山,本文就针对一个点,谈谈Spark中的宽依赖和窄依赖,这是Spark计算引擎划分Stage的根源所在,遇到宽依赖,则划分为多个stage,针对每个Stage,提交一个TaskSet: 上图:一张网上的图: 基于此图,分析下这里为什么前面的流程都是窄依赖,而后面的却是宽依赖: 我们仔细看看,map和filter算子中,对于父RDD来说,一个分区内的数据,有且仅有一个子RDD的分区来

Redisson的几种锁的通俗说明

Redisson的几种锁 Redisson 提供了多种分布式锁,每种锁都有其特定的使用场景,常见的包括: RLock(可重入锁):允许同一线程多次获取锁。RReadWriteLock(读写锁):多个线程可以同时读取数据,写操作是独占的。RSemaphore(信号量):限制同一时刻能够访问共享资源的线程数量。RCountDownLatch(闭锁):一个或多个线程等待其它线程完成任务。RFairL

聊聊灰度发布

有没有在北京面试java的小伙伴,每家公司面试问的问题都不一样,昨天面试官问到了灰度发布,一脸懵,好像在哪儿听说过,毕竟我都没发布过,之前都是项目组长在干这些事儿,所以聊聊,了解一下 什么是灰度发布 全量发布:把旧服务kill掉,把新服务启动,这个过程就可以理解为全量发布 回滚周期长 如果我们更新完应用之后,我们做线上回归测试的时候发现有BUG,这个时候就要做回滚,过程就是把新服

Rides实现分布式锁,保障数据一致性,Redisson分布式事务处理

分布式环境下分布式锁有三种方式: 基于数据库分布式锁 基于Redis分布式锁 基于zk分布式锁 本帖只介绍Redis分布式锁 为什么需要用到分布式锁?         在单机环境下一个服务中多个线程对同一个事物或数据资源进行操作时,可以通过添加加锁方式(synchronized和lock)来解决数据一致性的问题。         但是如果出现多个服务的情况下,这时候我们在通过s

聊聊随机测试和猴子测试

目录 随机测试的特点 1.不可预测性 2.缺乏针对性 3.自动化 4.资源密集型 猴子测试 随机测试 (Random Testing) 猴子测试 (Monkey Testing) 特点: 区别 1.控制程度 2.目标差异 3.实现方式 在我们测试的过程中,通常会使用到随机测试和猴子测试,其中随机测试侧重于人工测试,猴子测试侧重于借助工具执行命令进行测试。 随机测试

【聊聊经济社会】论阶级跨越

为什么要在市场中寻求自由,在市场中寻求洒脱,原因不胜其数,其中便有一条,现实生活中多是xx,可能社会属性本身就具备党同伐异,像是一股意志,平庸一切不平庸,中和一切特立独行,最终以达到一种变态的稳定. 消其意志,断其未来,耗其钱财 ,而我称之为阶级壁垒 阶级之所以难以跨越,主要也在于这三点 一:没有这样的志向,像那种羡慕有钱,或者羡慕有权,权当做梦。这样的志向,正常人只停留于羡慕的层次,而一旦受到丁

聊聊PC端页面适配

聊聊PC端页面适配  目也pc端有适配的需求:目前我们pc项目的设计稿尺寸是宽度1920,高度最小是1080。 适配目标: 1.在不同分辨率的电脑上,网页可以正常显示 2.放大或者缩小屏幕,网页可以正常显示 对于宽度的适配   对于宽度适配: 首先设置html,body{width:100%;overflow-x:hidden;} 然后我们可以把页面分解为背景层(