我们的专业付出,值得您的永久信赖!为您量身定制,信誉第一!

订货热线:16428221892

推荐产品
  • 王芳为红歌会暴瘦20斤 被评委称为“东方芭比”-开云app登录
  • 辰亦儒十年有成发个人迷你专辑 闭关半年升级美胸C罩杯|开云app登录
  • 王矜霖新歌《再见少年》首发 用歌声献给每一个值得怀念的青春-开云app登录
当前位置:首页 > 产品中心 > 建筑模板
开云app登录-微服务架构的四大金刚利器

 


49286
本文摘要:Photo @Christopher Campbell 文 | 孔凡勇概述互联网应用生长到今天,从单体应用架构到 SOA 以及今天的微服务,随着微服务化的不停升级进化,服务和服务之间的稳定性变得越来越重要,漫衍式系统之所以庞大,主要原因是漫衍式系统需要思量到网络的延时和不行靠,微服务很重要的一个特质就是需要保证服务幂等,保证幂等性很重要的前提需要漫衍式锁控制并发,同时缓存、降级和限流是掩护微服务系统运行稳定性的三大利器。

Photo @Christopher Campbell 文 | 孔凡勇概述互联网应用生长到今天,从单体应用架构到 SOA 以及今天的微服务,随着微服务化的不停升级进化,服务和服务之间的稳定性变得越来越重要,漫衍式系统之所以庞大,主要原因是漫衍式系统需要思量到网络的延时和不行靠,微服务很重要的一个特质就是需要保证服务幂等,保证幂等性很重要的前提需要漫衍式锁控制并发,同时缓存、降级和限流是掩护微服务系统运行稳定性的三大利器。随着业务不停的生长,按业务域的划分子系统越来越多,每个业务系统都需要缓存、限流、漫衍式锁、幂等工具组件, distributed-tools 组件(暂未开源)正式包罗了上述漫衍式系统所需要的基础功效组件。distributed-tools 组件基于 tair、redis 划分提供了 2 个 springboot starter ,使用起来很是简朴。

开云app登录

以使用缓存使用 redis 为例, application.properties 添加如下设置:redis.extend.hostName=127.0.0.1redis.extend.port=6379redis.extend.password=pwdcoderedis.extend.timeout=10000redis.idempotent.enabled=true接下来的篇幅,重点会先容一下缓存、限流、漫衍式锁、幂等的使用方式。缓存缓存的使用可以说无处不在,从应用请求的会见路径来看,用户 user -> 浏览器缓存 -> 反向署理缓存-> WEB服务器缓存 -> 应用法式缓存 -> 数据库缓存等,险些每条链路都充斥着缓存的使用,缓存最直白的解释就是“用空间换时间”的算法。缓存就是把一些数据暂时存放于某些地方,可能是内存,也有可能硬盘。

总之,目的就是为了制止某些耗时的操作。我们常见的耗时的操作,好比数据库的查询、一些数据的盘算效果,或者是为了减轻服务器的压力。

其实减轻压力也是因查询或盘算,虽然短耗时,但操作很频繁,累加起来也很长,造成严重排队等情况,服务器抗不住。distributed-tools 组件提供了一个 CacheEngine 接口,基于 Tair、Redis 划分有差别的实现,详细 CacheEngine 界说如下: public String get(String key); /** * 获取指定的key对应的工具,异常也会返回null * * @param key * @param clazz * @return */ public <T> T get(String key, Class<T> clz); /** * 存储缓存数据,忽略逾期时间 * * @param key * @param value * @return */ public <T extends Serializable> booleanput(String key, T value); /** * 存储缓存数据 * * @param key * @param value * @param expiredTime * @param unit * @return */ public <T extends Serializable> booleanput(String key, T value, int expiredTime, TimeUnit unit); /** * 基于key删除缓存数据 * * @param key * @return */ publicbooleaninvalid(String key);get 方法针对 key 举行查询, put 存储缓存数据, invalid 删除缓存数据。限流在漫衍式系统中,尤其面临一些秒杀、瞬时高并发场景,都需要举行一些限流措施,保证系统的高可用。

通常来说限流的目的是通过对并发会见/请求举行限速,或者一个时间窗口内的的请求举行限速来掩护系统,一旦到达限制速率则可以 拒绝服务(定向到错误页或见告资源没有了)、排队 或 等候(好比秒杀、评论、下单)、降级(返回托底数据或默认数据,如商品详情页库存默认有货)。常见的一些限流算法包罗牢固窗口、滑动窗口、漏桶、令牌桶,distributed-tools 组件现在基于计数器只实现了牢固窗口算法,详细使用方式如下: /** * 指定逾期时间自增计数器,默认每次+1,非滑动窗口 * * @param key 计数器自增key * @param expireTime 逾期时间 * @param unit 时间单元 * @return */ publiclongincrCount(String key, int expireTime, TimeUnit unit); /** * 指定逾期时间自增计数器,单元时间内凌驾最大值rateThreshold返回true,否则返回false * * @param key 限流key * @param rateThreshold 限流阈值 * @param expireTime 牢固窗口时间 * @param unit 时间单元 * @return */ publicbooleanrateLimit(final String key, finalint rateThreshold, int expireTime, TimeUnit unit);基于 CacheEngine 的 rateLimit 方法可以实现限流, expireTime 只能设定牢固窗口时间,非滑动窗口时间。另外 distributed-tools 组件提供了模板 RateLimitTemplate 可以简化限流的易用性,可以直接挪用 RateLimitTemplate 的 execute 方法处置惩罚限流问题。/** * @param limitKey 限流KEY * @param resultSupplier 回调方法 * @param rateThreshold 限流阈值 * @param limitTime 限制时间段 * @param blockDuration 阻塞时间段 * @param unit 时间单元 * @param errCodeEnum 指定限流错误码 * @return */ public <T> T execute(String limitKey, Supplier<T> resultSupplier, long rateThreshold, long limitTime, long blockDuration, TimeUnit unit, ErrCodeEnum errCodeEnum){ boolean blocked = tryAcquire(limitKey, rateThreshold, limitTime, blockDuration, unit); if (errCodeEnum != null) { AssertUtils.assertTrue(blocked, errCodeEnum); } else { AssertUtils.assertTrue(blocked, ExceptionEnumType.ACQUIRE_LOCK_FAIL); } return resultSupplier.get(); }另外 distributed-tools 组件还提供了注解 @RateLimit 的使用方式,详细注解 RateLimit 界说如下:@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Documentedpublic @interface RateLimit { /** * 限流KEY */ String limitKey(); /** * 允许会见的次数,默认值MAX_VALUE */ longlimitCount()default Long.MAX_VALUE; /** * 时间段 */ longtimeRange(); /** * 阻塞时间段 */ longblockDuration(); /** * 时间单元,默认为秒 */ TimeUnit timeUnit()default TimeUnit.SECONDS;}基于注解的方式限流使用代码如下:@RateLimit(limitKey = "#key", limitCount = 5, timeRange = 2, blockDuration = 3, timeUnit = TimeUnit.MINUTES)public String testLimit2(String key){ .......... return key;}任何方法添加上述注解具备了一定的限流能力(详细方法需要在 spring aop 指定拦截规模内),如上代码表现以参数 key 作为限流 key ,每 2 分钟请求次数不凌驾 5 次,凌驾限制后阻塞 3 分钟。

漫衍式锁在 Java 单一历程中通过 synchronized 关键字和 ReentrantLock 可重入锁可以实现在多线程情况中控制对资源的并发会见,通常当地的加锁往往不能满足我们的需要,我们更多的面临场景是漫衍式系统跨历程的锁,简称为漫衍式锁。漫衍式锁实现手段通常是将锁标志存在内存中,只是该内存不是某个历程分配的内存而是公共内存如 Redis、Tair ,至于使用数据库、文件等做锁与单机的实现是一样的,只要保证标志能互斥就行。漫衍式锁相对单机历程的锁之所以庞大,主要原因是漫衍式系统需要思量到网络的延时和不行靠。

开云app登录

distributed-tools 组件提供的漫衍式锁要具备如下特性:互斥性:同当地锁一样具有互斥性,可是漫衍式锁需要保证在差别节点历程的差别线程的互斥。可重入性:同一个节点上的同一个线程如果获取了锁之后那么也可以再次获取这个锁。锁超时:和当地锁一样支持锁超时,防止死锁,通过异步心跳 demon 线程刷新逾期时间,防止特殊场景(如 FGC 死锁超时)下死锁。

高性能、高可用:加锁息争锁需要高性能,同时也需要保证高可用防止漫衍式锁失效,可以增加降级。支持阻塞和非阻塞:同 ReentrantLock 一样支持 lock 和 trylock 以及 tryLock ( long timeOut )。

公正锁和非公正锁(不支持):公正锁是根据请求加锁的顺序获得锁,非公正锁就相反是无序的,现在 distributed-tools 组件提供的漫衍式锁不支持该特性。distributed-tools 组件提供的漫衍式锁,使用起来很是简朴,提供了一个漫衍式锁模板:DistributedLockTemplate ,可以直接挪用模板提供的静态方法(如下): /** * 漫衍式锁处置惩罚模板执行器 * * @param lockKey 漫衍式锁key * @param resultSupplier 漫衍式锁处置惩罚回调 * @param waitTime 锁等候时间 * @param unit 时间单元 * @param errCodeEnum 指定特殊错误码返回 * @return */ public static <T> T execute(String lockKey, Supplier<T> resultSupplier, long waitTime, TimeUnit unit, ErrCodeEnum errCodeEnum){ AssertUtils.assertTrue(StringUtils.isNotBlank(lockKey), ExceptionEnumType.PARAMETER_ILLEGALL); boolean locked = false; Lock lock = DistributedReentrantLock.newLock(lockKey); try { locked = waitTime > 0 ? lock.tryLock(waitTime, unit) : lock.tryLock(); } catch (InterruptedException e) { throw new RuntimeException(String.format("lock error,lockResource:%s", lockKey), e); } if (errCodeEnum != null) { AssertUtils.assertTrue(locked, errCodeEnum); } else { AssertUtils.assertTrue(locked, ExceptionEnumType.ACQUIRE_LOCK_FAIL); } try { return resultSupplier.get(); } finally { lock.unlock(); } }幂等 在漫衍式系统设计中幂等性设计中十分重要的,尤其在庞大的微服务中一套系统中包罗了多个子系统服务,而一个子系统服务往往会去挪用另一个服务,而服务挪用服务无非就是使用 RPC 通信或者 restful ,漫衍式系统中的网络延时或中断是制止不了的,通常会导致服务的挪用层触发重试。具有这一性质的接口在设计时总是秉持这样的一种理念:挪用接口发生异常而且重复实验时,总是会造成系统所无法蒙受的损失,所以必须阻止这种现象的发生。幂等通常会有两个维度:1. 空间维度上的幂等,即幂等工具的规模,是小我私家还是机构,是某一次生意业务还是某种类型的生意业务。

2. 时间维度上的幂等,即幂等的保证时间,是几个小时、几天还是永久性的。在实际系统中有许多操作,不管操作几多次,都应该发生一样的效果或返回相同的效果。

以下这些应用场景也是通常比力常见的应用场景:1. 前端重复提交请求,且请求数据相同时,后台需要返回对应这个请求的相同效果。2. 提倡一次支付请求,支付中心应该只扣用户账户一次钱,当遇到网络中断或系统异常时,也应该只扣一次钱。3. 发送消息,同样内容的短信发给用户只发一次。

4. 建立业务订单,一次业务请求只能建立一个,重试请求建立多个就会出大问题。5. 基于 msgId 的消息幂等处置惩罚。在正式使用 distributed-tools 组件提供的幂等之前,我们先看下 distributed-tools 幂等组件的设计。

幂等 key 提取能力:获取唯一幂等 key幂等 key 的提取支持 2 中注解:IdempotentTxId、IdempotentTxIdGetter,任意方法添加以上 2 注解,即可提取到相关幂等 key ,前提条件是需要将 Idempotent 注解添加相关需要幂等的方法上。如果单纯使用幂等模板举行业务处置惩罚,需要自己设置相关幂等key,且要保证其唯一性。漫衍式锁服务能力:提供全局加锁、解锁的能力distributed-tools 幂等组件需要使用自身提供的漫衍式锁功效,保证其并发唯一性, distributed-tools 提供的漫衍式锁能够提供其可靠、稳定的加锁、解锁能力。高性能的写入、查询能力:针对幂等效果查询与存储distributed-tools 幂等组件提供了基于 tair 、 redis 的存储实现,同时支持自界说一级、二级存储通过 spring 依赖注入到 IdempotentService ,建议 distributed-tools 幂等存储效果一级存储 tair mdb ,二级存储ldb或者 tablestore ,一级存储保证其高性能,二级存储保证其可靠性。

二级存储并行查询会返回查询最快的幂等效果。二级存储并行异步写入,进一步提高性能。

开云app登录

高可用的幂等写入、查询能力:幂等存储泛起异常,不影响业务正常流程,增加容错distributed-tools 幂等组件支持二级存储,为了保证其高可用,究竟二级存储泛起故障的概率太低,不会导致业务上不行用,如果二级存储同时泛起故障,业务上做了一定的容错,针对不确定性的异常接纳重试计谋,会执行详细幂等方法。一级存储与二级存储的写入与查询处置惩罚举行隔离,任何一级存储的异常不会影响整体业务执行。在相识了 distributed-tools 组件幂等之后,接下来我们来看下如何去使用幂等组件,首先相识下 common-api 提供的幂等注解,详细幂等注解使用方式如下:幂等拦截器获取幂等 ID 的优先级:首先判断 Idempotent 的 spelKey 的属性是否为空,如果不为空会凭据 spelKey 界说的 spring 表达式生成幂等 ID。

其次判断参数是否包罗 IdempotentTxId 注解,如果有 IdempotentTxId ,会直接获取参数值生成幂等 ID。再次通过反射获取参数工具属性是否包罗 IdempotentTxId 注解,如果工具属性包罗 IdempotentTxId 注解会获取该参数工具属性生成幂等 ID。

最后以上三种情况仍未获取到幂等 ID ,会进一步通过反射获取参数工具的 Method 是否认义 IdempotentTxIdGetter 注解,如果包罗该注解则通过反射生成幂等 ID。代码使用示例: @Idempotent(spelKey = "#request.requestId", firstLevelExpireDate = 7,secondLevelExpireDate = 30) publicvoidexecute(BizFlowRequest request){ .................. }如上述代码表现从 request 获取 requestId 作为幂等 key ,一级存储有效期 7 天,二级存储有效期 30 天。distributed-tools 除了可以使用幂等注解外,幂等组件还提供了一个通用幂等模板 IdempotentTemplate ,使用幂等模板的前提必须设置 tair.idempotent.enabled=true或者redis.idempotent.enabled=true ,默认为 false ,同时需要指定幂等效果一级存储,幂等效果存储为可选项设置。详细使用幂等模板 IdempotentTemplate 的方法如下:/** * 幂等模板处置惩罚器 * * @param request 幂等Request信息 * @param executeSupplier 幂等处置惩罚回调function * @param resultPreprocessConsumer 幂等效果回调function 可以对效果做些预处置惩罚 * @param ifResultNeedIdempotence 除了凭据异常还需要凭据效果判断是否需要幂等性的场景可以提供此参数 * @return */ public R execute(IdempotentRequest<P> request, Supplier<R> executeSupplier, Consumer<IdempotentResult<P, R>> resultPreprocessConsumer, Predicate<R> ifResultNeedIdempotence){ ........ }request:幂等参数 IdempotentRequest 组装,可以设置幂等参数和幂等唯一 ID。

executeSupplier:详细幂等的方法逻辑,好比针对支付、下单接口,可以通过 JDK8 函数式接口 Supplier Callback 举行处置惩罚。resultBiConsumer:幂等返回效果的处置惩罚,该参数可以为空,如果为空接纳默认的处置惩罚,凭据幂等效果,如果乐成、不行重试的异常错误码,直接返回效果,如果失败可重试异常错误码,会举行重试处置惩罚。如果该参数值不为空,可以针对返回幂等效果举行特殊逻辑处置惩罚设置 ResultStatus(ResultStatus 包罗三种状态包罗乐成、失败可重试、失败不行重试)。作者信息:孔凡勇,混名云狄,阿里云-开放平台高级技术家,对高并发、高性能、高可用、可伸缩的漫衍式系统架构设计有富厚履历,Cloud Native坚定拥护者,坚守开发一线打磨匠艺的架构师。


本文关键词:开云app登录

本文来源:开云app登录-www.israelemergingtech.com