/images/avatar.png

Spring Cloud

Spring 以 Bean(对象) 为中心,提供 IOC、AOP 等功能。 Spring Boot 以 Application(应用) 为中心,提供自动配置、监控等功能,专注于快速方便的开发单个微服务。 Spring Cloud 以 Service(服务) 为中心,关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务SpringBoot可以离开SpringCloud独立使用开发项目, 但是SpringCloud离不开SpringBoot ,属于依赖的关系。 Spring Cloud是目前最常用的微服务开发框架,Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、熔断保护、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。 版本对应 Spring Cloud Version Spring Boot Version Spring Cloud Alibaba Version Spring Cloud 2020.0.1 2.4.x 2021.1 Spring Cloud Hoxton 2.2.x, 2.3.x 2.2.x Spring Cloud Greenwich 2.1.x 2.1.x Spring Cloud Finchley 2.0.x 2.0.x(停止维护,建议升级) Spring Cloud Edgware 1.5.x 1.5.x(停止维护,建议升级) Spring Cloud Dalston 1.5.x 1.5.x(停止维护,建议升级) SpringCloud的基础功能 服务治理: Spring Cloud Eureka 服务容错保护: Spring Cloud Hystrix 客户端负载均衡: Spring Cloud Ribbon 基于Ribbon和Hystrix的声明式服务调用组件: Spring Cloud OpenFeign 整合了ribbon,具有负载均衡的能力。 整合了Hystrix,具有熔断的能力。 API网关服务:Spring Cloud Zuul 分布式配置中心: Spring Cloud Config Spring Cloud 服务治理-Eureka 为了解决微服务架构中的服务实例维护问题(ip地址), 产生了大量的服务治理框架和产品。 这些框架和产品的实现都围绕着服务注册与服务发现机制来完成对微服务应用实例的自动化管理。

Dubbo总结

Apache Dubbo 是一款微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力。这意味着,使用 Dubbo 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 Dubbo 是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求。 服务是 Dubbo 中的核心概念,一个服务代表一组 RPC 方法的集合,服务是面向用户编程、服务发现机制等的基本单位。 Dubbo 开发的基本流程是:用户定义 RPC 服务,通过约定的配置 方式将 RPC 声明为 Dubbo 服务,然后就可以基于服务 API 进行编程了。对服务提供者来说是提供 RPC 服务的具体实现,而对服务消费者来说则是使用特定数据发起服务调用。 服务发现 服务发现,即消费端自动发现服务地址列表的能力,是微服务框架需要具备的关键能力,借助于自动化的服务发现,微服务之间可以在无需感知对端部署位置与 IP 地址的情况下实现通信。 实现服务发现的方式有很多种,Dubbo 提供的是一种 Client-Based 的服务发现机制,通常还需要部署额外的第三方注册中心组件来协调服务发现过程,如常用的 Nacos、Consul、Zookeeper 等,Dubbo 自身也提供了对多种注册中心组件的对接,用户可以灵活选择。 服务发现 服务发现的一个核心组件是注册中心,Provider 注册地址到注册中心,Consumer 从注册中心读取和订阅 Provider 地址列表。 因此,要启用服务发现,需要为 Dubbo 增加注册中心配置: 以 dubbo-spring-boot-starter 使用方式为例,增加 registry 配置 1 2 3 4 # application.properties dubbo registry address: zookeeper://127.0.0.1:2181 服务流量管理 流量管理的本质是将请求根据制定好的路由规则分发到应用服务上,如下图所示: 流量管理 Dubbo提供了支持mesh方式的流量管理策略,可以很容易实现 A/B测试、金丝雀发布、蓝绿发布等能力。

幂等

幂等就是:一个操作不论执行多少次,产生的效果和返回的结果都是一样的。 业务场景 查询操作 查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select是天然的幂等操作。 删除操作 删除操作也是幂等的,删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果多个) 新增操作 唯一索引或唯一组合索引来防止新增数据存在脏数据(当表存在唯一索引,并发时新增报错时,再查询一次就可以了,数据应该已经存在了,返回结果即可) 防止页面重复提交(token机制) 业务要求:页面的数据只能被点击提交一次 发生原因:由于重复点击或者网络重发,或者nginx重发等情况会导致数据被重复提交 处理流程: 用户访问页面时,浏览器自动发起获取token请求。 服务端生成token,保存到redis中,然后返回给浏览器。 用户通过浏览器发起请求时,携带该token。 在redis中查询该token是否存在,如果不存在,说明是第一次请求,做则后续的数据操作。 如果存在,说明是重复请求,则直接返回成功。 在redis中token会在过期时间之后,被自动删除。 解决办法: 集群环境:采用token加redis(redis单线程的,处理需要排队) 单JVM环境:采用token加redis或token加jvm内存 token特点:要申请,一次有效性,可以限流。 redis要用删除操作来判断token,删除成功代表token校验通过,如果用select+delete来校验token,存在并发问题,不建议使用。 对外提供接口的api保证幂等 如银联提供的付款接口:需要接入商户提交付款请求时附带:source来源,seq序列号,source+seq在数据库里面做唯一索引,防止多次付款,(并发时,只能处理一个请求)。 对外提供接口为了支持幂等调用,接口有两个字段必须传,一个是来源source,一个是来源方序列号seq,这个两个字段在服务提供方系统里面做联合唯一索引,这样当第三方调用时,先在本方系统里面查询一下,是否已经处理过,返回相应处理结果;没有处理过,进行相应处理,返回结果。注意,为了幂等友好,一定要先查询一下,是否处理过该笔业务,不查询直接插入业务系统,会报错,但实际已经处理了。 解决方案 悲观锁 获取数据的时候加锁获取 1 select * from table_xxx where id='xxx' for update; 注意:id字段一定是主键或者唯一索引,不然是锁表,会死人的,悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用。 乐观锁 乐观锁只是在更新数据那一刻锁表,其他时间不锁表,所以相对于悲观锁,效率更高。 乐观锁的实现方式多种多样可以通过version或者其他状态条件: 通过版本号实现 1 2 3 update table_xxx set name=#name#,version=version+1 where version=#version#; -- 优化后的 update table_xxx set name=#name#,version=version+1 where id=#id# and version=#version#; 通过条件限制 1 2 3 update table_xxx set avai_amount=avai_amount-#subAmount# where avai_amount-#subAmount# >= 0; -- 优化后的 update table_xxx set avai_amount=avai_amount-#subAmount# where id=#id# and avai_amount-#subAmount# >= 0 要求:quality-#subQuality# >= ,这个情景适合不用版本号,只更新是做数据安全校验,适合库存模型,扣份额和回滚份额,性能更高

分布式ID

在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识。 概括下来,那业务系统对ID号的要求有哪些呢? 全局唯一:不能出现重复的ID号,既然是唯一标识,这是最基本的要求。 趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。 单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、IM增量消息、排序等特殊需求。 信息安全:如果ID是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下,会需要ID无规则、不规则。 高可用:平均延迟和TP999延迟都要尽可能低;可用性5个9; 高QPS。 UUID UUID是通用唯一识别码(Universally Unique Identifier)的缩写,开放软件基金会(OSF)规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素。利用这些元素来生成UUID。 UUID是由128位二进制组成,一般转换成十六进制,然后用String表示。在java中有个UUID类,在他的注释中我们看见这里有4种不同的UUID的生成策略: randomly 基于随机数生成UUID,由于Java中的随机数是伪随机数,其重复的概率是可以被计算出来的。这个一般我们用下面的代码获取基于随机数的UUID: time-based 基于时间的UUID,这个一般是通过当前时间,随机数,和本地Mac地址来计算出来,自带的JDK包并没有这个算法的我们在一些UUIDUtil中,比如我们的log4j.core.util,会重新定义UUID的高位和低位。 DCE security:DCE安全的UUID。 name-based:基于名字的UUID,通过计算名字和名字空间的MD5来计算UUID。 UUID的优点 通过本地生成,没有经过网络I/O,性能较快 无序,无法预测他的生成顺序。(当然这个也是他的缺点之一) UUID的缺点 128位二进制一般转换成36位的16进制,太长了只能用String存储,空间占用较多。 不能生成递增有序的数字 适用场景 UUID的适用场景为不担心过多的空间占用,以及不需要生成有递增趋势的数字。在Log4j里面他在UuidPatternConverter中加入了UUID来标识每一条日志。 数据库主键自增 大家对于唯一标识最容易想到的就是主键自增,这个也是我们最常用的方法。例如我们有个订单服务,那么把订单id设置为主键自增即可。 优点 简单方便,有序递增,方便排序和分页 缺点 分库分表会带来问题,需要进行改造。 并发性能不高,受限于数据库的性能。 简单递增容易被其他人猜测利用,比如你有一个用户服务用的递增,那么其他人可以根据分析注册的用户ID来得到当天你的服务有多少人注册,从而就能猜测出你这个服务当前的一个大概状况。 数据库宕机服务不可用。 适用场景 根据上面可以总结出来,当数据量不多,并发性能不高的时候这个很适合,比如一些to B的业务,商家注册这些,商家注册和用户注册不是一个数量级的,所以可以数据库主键递增。如果对顺序递增强依赖,那么也可以使用数据库主键自增。 Redis Redis中有两个命令Incr,IncrBy,因为Redis是单线程的所以能保证原子性。 优点 性能比数据库好,能满足有序递增。 缺点 由于redis是内存的KV数据库,即使有AOF和RDB,但是依然会存在数据丢失,有可能会造成ID重复。 依赖于redis,redis要是不稳定,会影响ID生成。 适用 由于其性能比数据库好,但是有可能会出现ID重复和不稳定,这一块如果可以接受那么就可以使用。也适用于到了某个时间,比如每天都刷新ID,那么这个ID就需要重置,通过(Incr Today),每天都会从0开始加。 雪花算法-Snowflake Snowflake是Twitter提出来的一个算法,其目的是生成一个64bit的整数 Snowflake 1bit:一般是符号位,不做处理 41bit:用来记录时间戳,这里可以记录69年,如果设置好起始时间比如今年是2018年,那么可以用到2089年,到时候怎么办?要是这个系统能用69年,我相信这个系统早都重构了好多次了。 10bit:10bit用来记录机器ID,总共可以记录1024台机器,一般用前5位代表数据中心,后面5位是某个数据中心的机器ID 12bit:循环位,用来对同一个毫秒之内产生不同的ID,12位可以最多记录4095个,也就是在同一个机器同一毫秒最多记录4095个,多余的需要进行等待下毫秒。 上面只是一个将64bit划分的标准,当然也不一定这么做,可以根据不同业务的具体场景来划分,比如下面给出一个业务场景: 服务目前QPS10万,预计几年之内会发展到百万。 当前机器三地部署,上海,北京,深圳都有。 当前机器10台左右,预计未来会增加至百台。 这个时候我们根据上面的场景可以再次合理的划分62bit,QPS几年之内会发展到百万,那么每毫秒就是千级的请求,目前10台机器那么每台机器承担百级的请求,为了保证扩展,后面的循环位可以限制到1024,也就是2^10,那么循环位10位就足够了。 机器三地部署我们可以用3bit总共8来表示机房位置,当前的机器10台,为了保证扩展到百台那么可以用7bit 128来表示,时间位依然是41bit,那么还剩下64-10-3-7-41-1 = 2bit,还剩下2bit可以用来进行扩展。 适用场景 当我们需要无序不能被猜测的ID,并且需要一定高性能,且需要long型,那么就可以使用我们雪花算法。比如常见的订单ID,用雪花算法别人就发猜测你每天的订单量是多少。

分布式事务

数据库事务-本地事务 传统的单服务器,单关系型数据库下的事务,就是本地事务。本地事务由资源管理器管理,JDBC事务就是一个非常典型的本地事务。 事务ACID特性的实现思想 原子性:是使用 undo log来实现的,如果事务执行过程中出错或者用户执行了rollback,系统通过undo log日志返回事务开始的状态。 持久性:使用 redo log来实现,只要redo log日志持久化了,当系统崩溃,即可通过redo log把数据恢复。 隔离性:通过锁以及MVCC,使事务相互隔离开。 一致性:通过回滚、恢复,以及并发情况下的隔离性,从而实现一致性。 分布式事务 分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。 微服务架构下的分布式事务 用户下单购买礼物,礼物数据库、金币数据库、订单数据库在不同节点上,用本地事务是不可以的,那么如何保证不同数据库(节点)上的数据一致性呢?这就需要分布式事务啦 分库分表下的分布式事务 随着业务的发展,数据库的数据日益庞大,超过千万级别的数据,我们就需要对它分库分表(以前公司是用mycat分库分表,后来用sharding-jdbc)。一分库,数据又分布在不同节点上啦,比如有的在深圳机房,有的在北京机房~你再想用本地事务去保证,已经无动于衷啦~还是需要分布式事务啦。 比如A转10块给B,A的账户数据是在北京机房,B的账户数据是在深圳机房。流程如下: 分布式事务理论(CAP和BASE) 如果说到事务,ACID是传统数据库常用的设计理念,追求强一致性模型,关系数据库的ACID模型拥有高一致性+可用性,所以很难进行分区,所以在微服务中ACID已经是无法支持,我们还是回到CAP去寻求解决方案,不过根据上面的讨论,CAP定理中,要么只能CP,要么只能AP,如果我们追求数据的一致性而忽略可用性这个在微服务中肯定是行不通的,如果我们追求可用性而忽略一致性,那么在一些重要的数据(例如支付,金额)肯定出现漏洞百出,这个也是无法接受。所以我们既要一致性,也要可用性。 都要是无法实现的,但我们能不能在一致性上作出一些妥协,不追求强一致性,转而追求最终一致性,所以引入BASE理论,在分布式事务中,BASE最重要是为CAP提出了最终一致性的解决方案,BASE强调牺牲高一致性,从而获取肯用性,数据允许在一段时间内不一致,只要保证最终一致性就可以了。 这就是分布式事务等理论基础,即实现最终一致性。 分布式事务的几种解决方案 2PC(二阶段提交)方案/XA 事务的提交分为两个阶段:准备阶段和提交执行方案。 二阶段提交成功的情况 准备阶段,事务管理器向每个资源管理器发送准备消息,如果资源管理器的本地事务操作执行成功,则返回成功。 提交执行阶段,如果事务管理器收到了所有资源管理器回复的成功消息,则向每个资源管理器发送提交消息,RM 根据 TM 的指令执行提交。 二阶段提交失败的情况 准备阶段,事务管理器向每个资源管理器发送准备消息,如果资源管理器的本地事务操作执行成功,则返回成功,如果执行失败,则返回失败。 提交执行阶段,如果事务管理器收到了任何一个资源管理器失败的消息,则向每个资源管理器发送回滚消息。资源管理器根据事务管理器的指令回滚本地事务操作,释放所有事务处理过程中使用的锁资源。 二阶段提交优缺点 单点问题:如果事务管理器出现故障,资源管理器将一直处于锁定状态。 性能问题:所有资源管理器在事务提交阶段处于同步阻塞状态,占用系统资源,一直到提交完成,才释放资源,容易导致性能瓶颈。 数据一致性问题:如果有的资源管理器收到提交的消息,有的没收到,那么会导致数据不一致问题。 TCC(Try、Confirm、Cancel) TCC 采用了补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。 TCC(Try-Confirm-Cancel)模型 TCC(Try-Confirm-Cancel)是通过对业务逻辑的分解来实现分布式事务。针对一个具体的业务服务,TCC 分布式事务模型需要业务系统都实现一下三段逻辑: try阶段: 尝试去执行,完成所有业务的一致性检查,预留必须的业务资源。 Confirm阶段: 该阶段对业务进行确认提交,不做任何检查,因为try阶段已经检查过了,默认Confirm阶段是不会出错的。 Cancel 阶段: 若业务执行失败,则进入该阶段,它会释放try阶段占用的所有业务资源,并回滚Confirm阶段执行的所有操作。 TCC优缺点 TCC方案让应用可以自定义数据库操作的粒度,降低了锁冲突,可以提升性能,但是也有以下缺点: 应用侵入性强,try、confirm、cancel三个阶段都需要业务逻辑实现。 需要根据网络、系统故障等不同失败原因实现不同的回滚策略,实现难度大,一般借助TCC开源框架,ByteTCC,TCC-transaction,Himly。 在 Try 阶段,是对业务系统进行检查及资源预览,比如订单和存储操作,需要检查库存剩余数量是否够用,并进行预留,预留操作的话就是新建一个可用库存数量字段,Try 阶段操作是对这个可用库存数量进行操作。 基于 TCC 实现分布式事务,会将原来只需要一个接口就可以实现的逻辑拆分为 Try、Confirm、Cancel 三个接口,所以代码实现复杂度相对较高。 TCC 需要事务接口提供 try, confirm, cancel 三个接口,提高了编程的复杂性。依赖于业务方来配合提供这样的接口,推行难度大,所以一般不推荐使用这种方式。

分布式锁

基于数据库 基于数据库表数据记录做唯一约束 上面这种简单的实现有以下几个问题: 这把锁强依赖数据库的可用性,数据库是一个单点,一旦数据库挂掉,会导致业务系统不可用。 这把锁没有失效时间,一旦解锁操作失败,就会导致锁记录一直在数据库中,其他线程无法再获得到锁。 这把锁只能是非阻塞的,因为数据的insert操作,一旦插入失败就会直接报错。没有获得锁的线程并不会进入排队队列,要想再次获得锁就要再次触发获得锁操作。 这把锁是非重入的,同一个线程在没有释放锁之前无法再次获得该锁。因为数据中数据已经存在了。 不过这种方式对于单主却无法自动切换主从的mysql来说,基本就无法现实P分区容错性,(Mysql自动主从切换在目前并没有十分完美的解决方案)。可以说这种方式强依赖于数据库的可用性,数据库写操作是一个单点,一旦数据库挂掉,就导致锁的不可用。这种方式基本不在CAP的一个讨论范围。 基于Redis 使用redis的setNX()用于分布式锁。(原子性) 1 2 3 4 setnx key value Expire_time 获取到锁 返回 1 , 获取失败 返回 0 * 返回1,说明该进程获得锁,SETNX将键 lock.id 的值设置为锁的超时时间,当前时间 +加上锁的有效时间。 * 返回0,说明其他进程已经获得了锁,进程不能进入临界区。进程可以在一个循环中不断地尝试 SETNX 操作,以获得锁。 1 2 3 4 5 6 7 8 9 10 11 12 protected boolean getLock(String lockKey, int time,TimeUnit timeUnit) { // 使用redis的setNX命令实现分布式锁 boolean isSuccess = redisTemplate.opsForValue().setIfAbsent(lockKey, "lock"); // 防止进程中断导致redis分布锁死锁,检查是否设置了超时时间。 Long timeLeft = redisTemplate.getExpire(lockKey); if (isSuccess || timeLeft < 0) { // 设置失效时间(最好与该任务执行频率时间一致):如果执行期间宕机,5分钟后也能被另一机器获得lock redisTemplate.

分布式概览

分布式理论基础 CAP CAP 理论是分布式中基础理论,有三个重要指标:一致性、可用性、分区容错性。 一致性(Consistency):一致性意思就是写操作之后进行读操作无论在哪个节点都需要返回写操作的值 可用性(Availability):非故障的节点在合理的时间内返回合理的响应 分区容错性(Partition Tolerance):当网络出现分区后,系统依然能够继续履行职责 在分布式的环境下,网络无法做到100%可靠,有可能出现故障,因此分区是一个必须的选项,如果选择了CA而放弃了P,若发生分区现象,为了保证C,系统需要禁止写入,此时就与A发生冲突,如果是为了保证A,则会出现正常的分区可以写入数据,有故障的分区不能写入数据,则与C就冲突了。因此分布式系统理论上不可能选择CA架构,而必须选择CP或AP架构。 BASE(最终一致性) BASE是对CAP 理论中强一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于CAP定理逐步演化而来的,其核心思想是即使无法做到强一致性(Strong consistency),每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。 BASE理论是对CAP的延伸和补充,是对CAP中的AP方案的一个补充,即在选择AP方案的情况下,如何更好的最终达到C。 BASE是基本可用,柔性状态,最终一致性三个短语的缩写,核心的思想是即使无法做到强一致性,但应用可以采用适合的方式达到最终一致性。 多数情况下,其实我们也并非一定要求强一致性,部分业务可以容忍一定程度的延迟一致,所以为了兼顾效率,发展出来了最终一致性理论BASE BA-基本可用(Basically Available):基本可用是指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。 S-软状态(Soft State):软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。 E-最终一致性(Eventual Consistency):最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。 一致性算法 分布式架构的核心就在一致性的实现和妥协,那么如何设计一套算法来保证不同节点之间的通信和数据达到无限趋向一致性,就非常重要了。 参考文章 分布式架构知识体系 通过一个订单查看微服务整个流程 分布式整体概览从CAP说起 学习资料 分布式系统合集

微服务介绍

微服务架构(MicroServices Architecture,MSA):微服务架构可以看做是面向服务架构和分布式服务架构的拓展,使用更细粒度的服务(所以叫微服务)和一组设计准则来考虑大规模的复杂系统架构设计。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。 常见的微服务组件及概念 服务注册:服务提供方将自己调用地址注册到服务注册中心,让服务调用方能够方便地找到自己。 服务发现:服务调用方从服务注册中心找到自己需要调用的服务的地址。 负载均衡:服务提供方一般以多实例的形式提供服务,负载均衡功能能够让服务调用方连接到合适的服务节点。并且,节点选择的工作对服务调用方来说是透明的。 服务网关:服务网关是服务调用的唯一入口,可以在这个组件是实现用户鉴权、动态路由、灰度发布、A/B 测试、负载限流等功能。 配置中心:将本地化的配置信息(properties, xml, yaml 等)注册到配置中心,实现程序包在开发、测试、生产环境的无差别性,方便程序包的迁移。 API 管理:以方便的形式编写及更新 API 文档,并以方便的形式供调用者查看和测试。 集成框架:微服务组件都以职责单一的程序包对外提供服务,集成框架以配置的形式将所有微服务组件(特别是管理端组件)集成到统一的界面框架下,让用户能够在统一的界面中使用系统。 分布式事务:对于重要的业务,需要通过分布式事务技术(TCC、高可用消息服务、最大努力通知)保证数据的一致性。 调用链:记录完成一个业务逻辑时调用到的微服务,并将这种串行或并行的调用关系展示出来。在系统出错时,可以方便地找到出错点。 支撑平台:系统微服务化后,系统变得更加碎片化,系统的部署、运维、监控等都比单体架构更加复杂,那么,就需要将大部分的工作自动化。现在,可以通过 Docker 等工具来中和这些微服务架构带来的弊端。 例如持续集成、蓝绿发布、健康检查、性能健康等等。严重点,以我们两年的实践经验,可以这么说,如果没有合适的支撑平台或工具,就不要使用微服务架构。 微服务架构的优点 降低系统复杂度:每个服务都比较简单,只关注于一个业务功能。 松耦合:微服务架构方式是松耦合的,每个微服务可由不同团队独立开发,互不影响。 跨语言:只要符合服务 API 契约,开发人员可以自由选择开发技术。这就意味着开发人员可以采用新技术编写或重构服务,由于服务相对较小,所以这并不会对整体应用造成太大影响。 独立部署:微服务架构可以使每个微服务独立部署。开发人员无需协调对服务升级或更改的部署。这些更改可以在测试通过后立即部署。所以微服务架构也使得 CI/CD 成为可能。 Docker 容器:和 Docker 容器结合的更好。 DDD 领域驱动设计:和 DDD 的概念契合,结合开发会更好。 微服务架构的缺点 微服务强调了服务大小,但实际上这并没有一个统一的标准:业务逻辑应该按照什么规则划分为微服务,这本身就是一个经验工程。有些开发者主张 10-100 行代码就应该建立一个微服务。微服务的目标是充分分解应用程序,以促进敏捷开发和持续集成部署。 微服务的分布式特点带来的复杂性:开发人员需要基于 RPC 或者消息实现微服务之间的调用和通信,而这就使得服务之间的发现、服务调用链的跟踪和质量问题变得的相当棘手。 分区的数据库体系和分布式事务:更新多个业务实体的业务交易相当普遍,不同服务可能拥有不同的数据库。CAP 原理的约束,使得我们不得不放弃传统的强一致性,而转而追求最终一致性,这个对开发人员来说是一个挑战。 测试挑战:传统的单体WEB应用只需测试单一的 REST API 即可,而对微服务进行测试,需要启动它依赖的所有其他服务。这种复杂性不可低估。 跨多个服务的更改:比如在传统单体应用中,若有 A、B、C 三个服务需要更改,A 依赖 B,B 依赖 C。我们只需更改相应的模块,然后一次性部署即可。但是在微服务架构中,我们需要仔细规划和* 调每个服务的变更部署。我们需要先更新 C,然后更新 B,最后更新 A。 部署复杂:微服务由不同的大量服务构成。每种服务可能拥有自己的配置、应用实例数量以及基础服务地址。这里就需要不同的配置、部署、扩展和监控组件。此外,我们还需要服务发现机制,以便服* 可以发现与其通信的其他服务的地址。因此,成功部署微服务应用需要开发人员有更好地部署策略和高度自动化的水平。 总结(问题和挑战):API Gateway、服务间调用、服务发现、服务容错、服务部署、数据调用。 不过,现在很多微服务的框架(比如 Spring Cloud、Dubbo)已经很好的解决了上面的问题。

高并发系统设计

软件开发通常会提到一个名词 “三高”,即高并发、高性能、高可用。 具体的指标定义,如:高并发方面要求QPS 大于10万;高性能方面要求请求延迟小于 100 ms;高可用方面要高于 99.99%。 三高 高并发 高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。高并发相关常用的一些指标有响应时间(Response Time),吞吐量(Throughput),每秒查询率QPS(Query Per Second),并发用户数等。 高并发 高并发架构设计 系统拆分 将一个系统拆分为多个子系统,用 dubbo 来搞。然后每个系统连一个数据库,这样本来就一个库,现在多个数据库,不也可以扛高并发么。 读写分离 读写分离,这个就是说大部分时候数据库可能也是读多写少,没必要所有请求都集中在一个库上吧,可以搞个主从架构,主库写入,从库读取,搞一个读写分离。读流量太多的时候,还可以加更多的从库。 分库分表 分库分表,可能到了最后数据库层面还是免不了抗高并发的要求,好吧,那么就将一个数据库拆分为多个库,多个库来扛更高的并发;然后将一个表拆分为多个表,每个表的数据量保持少一点,提高 sql 跑的性能。 缓存 缓存,必须得用缓存。大部分的高并发场景,都是读多写少,那你完全可以在数据库和缓存里都写一份,然后读的时候大量走缓存不就得了。毕竟人家 redis 轻轻松松单机几万的并发。所以你可以考虑考虑你的项目里,那些承载主要请求的读场景,怎么用缓存来抗高并发。 消息队列 MQ,必须得用 MQ。可能你还是会出现高并发写的场景,比如说一个业务操作里要频繁搞数据库几十次,增删改增删改,疯了。那高并发绝对搞挂你的系统,你要是用 redis 来承载写那肯定不行,人家是缓存,数据随时就被 LRU 了,数据格式还无比简单,没有事务支持。所以该用 mysql 还得用 mysql 啊。那你咋办?用 MQ 吧,大量的写请求灌入 MQ 里,排队慢慢玩儿,后边系统消费后慢慢写,控制在 mysql 承载范围之内。所以你得考虑考虑你的项目里,那些承载复杂写业务逻辑的场景里,如何用 MQ 来异步写,提升并发性。MQ 单机抗几万并发也是 ok 的,这个之前还特意说过。 ElasticSearch Elasticsearch,简称 es。es 是分布式的,可以随便扩容,分布式天然就可以支撑高并发,因为动不动就可以扩容加机器来扛更高的并发。那么一些比较简单的查询、统计类的操作,可以考虑用 es 来承载,还有一些全文搜索类的操作,也可以考虑用 es 来承载。 高性能 性能体现了系统的并行处理能力,在有限的硬件投入下,提高性能意味着节省成本。同时,性能也反映了用户体验,响应时间分别是100毫秒和1秒,给用户的感受是完全不同的。 高性能 高可用 表示系统可以正常服务的时间。一个全年不停机、无故障;另一个隔三差五出线上事故、宕机,用户肯定选择前者。另外,如果系统只能做到90%可用,也会大大拖累业务。 高可用 高扩展 表示系统的扩展能力,流量高峰时能否在短时间内完成扩容,更平稳地承接峰值流量,比如双11活动、明星离婚等热点事件。 常用指标 吞吐量 在了解qps、tps、rt、并发数之前,首先我们应该明确一个系统的吞吐量到底代表什么含义,一般来说,系统吞吐量指的是系统的抗压、负载能力,代表一个系统每秒钟能承受的最大用户访问量。

业务架构合集

业务场景 对账系统 有赞零售财务中台架构设计与实践 百万TPS支付账务系统的设计与实现 高效的风控规则引擎 最佳实践 最佳实践 服务化 架构设计 如何进行系统分析与设计 系统架构 领域驱动分层架构与对象模型 常见的软件架构套路 项目结构 互联网分层架构的本质 项目应该如何正确分层 分层架构 技术选型 技术选型 DevOps