Springcloud是一个基于SpringBoot实现的服务治理工具包,在微服务架构中用于管理和协调服务的微服务:就是把一个单体项目,拆分为多个微服务,每个微服务可以独立技术选型,独立开发,独立部署,独立运维。并且多个服务相互协调,相互配合,最终完成用户的价值。SpringCloud是一系列框架的有序集合。其主要的设施有,服务发现与注册,配置中心,消息总线,负载均衡,断路器,数据监控等,通过SpringBoot的方式,可以实现一键启动,和部署。 核心组件 一、分布式任务调度 提供秒级、精准、高可靠、高可用的定时(基于Cron表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有Worker上执行。 XXLJOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。 官方网站教程: https:www。xuxueli。comxxljobE3808AE58886E5B883E5BC8FE4BBBBE58AA1E8B083E5BAA6E5B9B3E58FB0XXLJOBE3808B 架构图: 二、Feign实现服务调用 Feign是Netflix开发的个轻量级RESTful的HTTP服务客户端(它来发起请求,远程调的),是以Java接注解的式调Http请求,不像Java中通过封装HTTP请求报的式直接调,Feign被泛应在SpringCloud的解决案中。类似于Dubbo,服务消费者拿到服务提供者的接,然后像调本地接法样去调,实际发出的是远程的请求。 服务消费者程启动类使注解EnableFeignClients添加Feign持 注意:此时去掉Hystrix熔断的持注解EnableCircuitBreaker即可包括引的依赖,因为Feign会动引。FeignRestTemplateRibbonHystrix 三、Sentinel服务容错 雪崩问题 微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。 解决雪崩问题的常见方式有四种: 超时处理: 超时处理:设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待。 舱壁模式: 限定每个业务能使用的线程数,避免耗尽整个tomcat的资源,也叫线程隔离。 熔断降级: 由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截该业务的一切请求。 流量控制: 限制业务访问的QPS,避免服务因流量的突增而故障。 总结: 避免因瞬间高并发流量而导致服务故障流量控制。 避免因服务故障引起的的雪崩问题超时处理、线程隔离、降级熔断 Sentinel具有以下特征: 丰富的应用场景: Sentinel承接了阿里巴巴近10年的双十一大促流量的核心场景,例如秒杀、消息消峰填谷、集群流程控制、实时熔断下游不可用应用等。 完备的实时监控:Sentinel同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至500台以下规模的集群的汇总运行情况。 广泛的开源生态:Sentinel提供开箱即用的与其它开源框架库的整合模块,例如SpringCloud、Dubbo、gRPC的整合。只需要引入相应依赖进行简单的配置即可快速地接入Sentinel。 完善的SPI扩展点:Sentinel提供简单易用、完善的SPI扩展接口。可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。 四、Gateway服务网关 API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控(黑白名单)、路由转发等等。添加上API网关之后,系统的架构图变成了如下所示: SpringCloudGateway的目标,不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控指标,和限流。 1、实现熔断降级: 在分布式系统中,网关作为流量的入口,因此会有大量的请求进入网关,向其他服务发起调用,其他服务不可避免的会出现调用失败(超时、异常),失败时不能让请求堆积在网关上,需要快速失败并返回给客户端,想要实现这个要求,就必须在网关上做熔断、降级操作。 2、分布式限流 令牌桶算法是对漏桶算法的一种改进,桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。 五、Sleuth链路追踪 在大型系统的微服务化构建中,一个系统被拆分成了许多微服务。这些模块负责不同的功能,组合成系统,最终可以提供丰富的功能。在这种架构中,一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台服务器,横跨多个不同的数据中心,也就意味着这种架构形式也会存在一些问题: 如何快速发现问题? 如何判断故障影响范围? 如何梳理服务依赖以及依赖的合理性? 如何分析链路性能问题以及实时容量规划? 分布式链路追踪(DistributedTracing),就是将一次分布式请求还原成调用链路,进行日志记录,性能监控并将一次分布式请求的调用情况集中展示。比如各个服务节点上的耗时、请求具体到达哪台机器上IP、每个服务节点的请求状态200、500等等。 查看日志文件并不是一个很好的方法,当微服务越来越多日志文件也会越来越多,通过Zipkin可以将日志聚合,并进行可视化展示和全文检索。 Zipkin是Twitter的一个开源项目,它基于GoogleDapper实现,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储展现、查找和我们可以使用它来收集各个服务器上请求链路的跟踪数据,并通过它提供的RESTAPI接口来辅助我们查询跟踪数据以实现对分布式系统的监控程序,从而及时地发现系统中出现的延迟升高问题并找出系统性能瓶颈的根源 Zipkin提供了可插拔数据存储方式:InMemory、MySql、Cassandra以及Elasticsearch。 上图展示了Zipkin的基础架构,它主要由4个核心组件构成: Collector:收集器组件,它主要用于处理从外部系统发送过来的跟踪信息,将这些信息转换为Zipkin内部处理的Span格式,以支持后续的存储、分析、展示等功能。 Storage:存储组件,它主要对处理收集器接收到的跟踪信息,默认会将这些信息存储在内存中,我们也可以修改此存储策略,通过使用其他存储组件将跟踪信息存储到数据库中。 RESTfulAPI:API组件,它主要用来提供外部访问接口。比如给客户端展示跟踪信息,或是外接系统访问以实现监控等。 WebUI:UI组件,基于API组件实现的上层应用。通过UI组件用户可以方便而有直观地查询和分析跟踪信息。 Zipkin分为两端,一个是Zipkin服务端,一个是Zipkin客户端,客户端也就是微服务的应用。客户端会配置服务端的URL地址,一旦发生服务间的调用的时候,会被配置在微服务里面的Sleuth的监听器监听,并生成相应的Trace和Span信息发送给服务端。 注意:SpringCloudalibaba技术栈中并没有提供自己的链路追踪技术的,我们可以采用SleuthZipkin来做链路追踪解决方案,Springcloud并不是自己技术而是把所有框架整合在一起来解决微服务上的问题。 五、Rocketmq消息驱动 RocketMQ作为一款纯java、分布式、队列模型的开源消息中间件,支持事务消息、顺序消息、批量消息、定时消息、消息回溯等。主要功能是异步解耦和流量削峰。 消息中间件的使用场景: 异步与解耦: 当我们下了一个订单之后,订单系统会进行RPC同步调用支付系统、库存系统、物流系统等,那么系统之间就会有耦合性,耦合性越高的话,容错性就越低,比如我们的支付系统如果宕机了,就会导致我们整个交易的异常,从而影响用户的体验。 如果我们中间加入了消息中间件,不管是支付还是库存等系统,都是通过异步的方式进行调用的,如果其中一个系统宕机了,不会影响我们用户下单的使用。本质上MQ第一步完成了异步,第二步完成了解耦。那么系统的容错性就越高。 流量削峰 流量削峰也可以叫削峰填谷,比如一些互联网公司大促场景,双十一、店庆或者秒杀活动,都会使用到消息中间件。 如果在不使用消息中间件或者没有流量削峰,每秒是很高的并发,这个时候如果我们的A系统,如果要将数据写入到我们的MYSQL中,受限于MYSQL本身服务的上限,最大我们只能每秒处理200请求,这个时候会有大量的消息进行堆积,从而导致A系统的奔溃。 这个时候我们可以将用户的请求消息通过MQ进行写入,因为消息中间件本身是对数据量处理比较高的一个系统,所以对于每秒2000请求,消息中间件可以处理,然后A系统作为消息中间件的一个消费者,以固定的速度从MQ中拉取200个消息,完成我们的业务操作,用时间换空间从而确保我们A系统的稳定性。 数据分发 如果S系统,在对系统进行开发的时候,需要对接多个(A、B、C、D)系统,使用传统的接口调用,中间有改动就需要修改我们的代码,当新增了A系统,我们需要去修改代码去调用A系统来完成对应的业务逻辑,如果我们当中的D系统需要移除,同样也需要修改代码删除对应的接口调用。 如果S系统使用了消息中间件,我们S系统只要将消息交给MQ,剩下的不论是新增还是移除,还是原有的,他们都只是消息中间件的一个消费者,这个时候我们就便于数据的分发。 比如我们新增一个系统,我们只需要新增一个MQ的消费者,直接从MQ里面拿消息就可以,当我们需要移除一个系统的时候,只需要取消对MQ消息的监听即可。对于我们原有的S系统不需要进行额外的修改。如果使用MQ作为数据分发,减少数据的修改,提高开发的效率。 RocketMQ主要有四大核心组成部分:NameServer、Broker、Producer以及Consumer四部分。这些角色通常以集群的方式存在,RocketMQ基于纯Java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点。 NameServer:NameServer是一个服务与注册的发现中心。 Broker:消息服务器(Broker)是消息存储中心,主要作用是接收来自Producer的消息并存储,Consumer从这里取得消息。 Producer:Producer也称为消息发布者(生产者),负责生产并发送消息至Topic。 Consumer:也称为消息订阅者,负责从Topic接收并消费消息。 rocketmq发生消息堆积时,我们可以通过jstack打印出线程的堆栈信息(可连续打印多次观察变化)。重点搜索ConsumeMessageThread开头的线程状态,例如下图所示: 如果发现大量的线程总是处于runnable状态,且堆栈信息中包含类似HttpClientUtil。doGet的信息,且有可能是因为http请求处理慢,导致大量线程被占用,消费能力不足导致消息堆积。解决思路,优优http请求,如设置较短的过期时间等。 如果发现大量的消费线程处于WAITING(parking)状态,说明消费线程在等待待消费的消息。如果仍然存在消息堆积,则极有可能是拉取能力不足,重点应该加强rocketmq拉取消息的能力。 还有一种假堆积,就是某条消息消费时,因为某种原因,一直卡住了(既不是消费成功也不是消费失败,而是类似于死循环无法返回消费状态)。这时候会导致无法更新broker端的消费offset(但后面的消息还是可以正常拉取消费的),这样子就导致了消费堆压报警(判断消息堆积是brokeroffsetconsumeroffset(consumer上报broker的))。所以说消费消息的逻辑一定要简单,尽量不要抛出异常或有堵塞发生 六、Nacos注册中心以及服务配置 Nacos架构: ProviderAPP:服务提供者 ConsumerAPP:服务消费者 NameServer:通过VIP(VirtualIP)或DNS的方式实现Nacos高可用集群的服务路由 NacosServer:Nacos服务提供者,里面包含的OpenAPI是功能访问入口,ConigService、NamingService是Nacos提供的配置服务、命名服务模块。 ConsitencyProtocol是一致性协议,用来实现Nacos集群节点的数据同步,这里使用的是Raft算法(Etcd、Redis哨兵选举) NacosConsole:控制台注册中心的原理 服务实例在启动时注册到服务注册表,并在关闭时注销 服务消费者查询服务注册表,获得可用实例 服务注册中心需要调用服务实例的健康检查API来验证它是否能够处理请求 注册流程: 整个注册中心的注册和发现流程主要有三个方面来完成:服务的提供方(以下简称server)、服务的消费者(以下简称client)、注册中心(nacos)。首先我们来探讨server与nacos的交互过程。 server需要通过nacos官方的OpenApi提供的接口来发起服务注册请求,随后server会定时向nacos发送心跳信息来进行心跳检测,对于使用者来说这一步可以采用ScheduledExecutorService创建定时任务来完成。nacos会异步的处理注册请求任务和心跳任务。 Nacos心跳机制: nacos和client之间采取推拉结合的交互方式,一方面client可以通过定时任务每隔10s向nacos发起查询请求,如果服务列表改变nacos就会返回新列表,另一方面当本地服务实例发生变化时(即server实例注册成功或者心跳停止断开链接),nacos会主动通过UDP协议推送到client,udp协议非常快,不需要保持长连接。在注册中心的场景中client数量往往多于server,如果每一次服务更新,nacos要和成千上万的服务消费者去建立Tcp的话性能肯定是不行的。而如果UDP通知失败,客户端每10秒还会主动去拉一次,客户端拉取和服务器推送是互补的,这样既能保证server实例更新的时效性,又能提高效率。 server向nacos发起注册任务请求,并维持一个心跳检测的定时任务,naocs会通过阻塞队列异步地处理这些请求,并实时的通过UDP推送到client,为防止UDP数据丢失,client也会通过定时任务每隔10s向nacos发送拉取请求,当服务列表改变,nacos再返回。 七、Seata分布式事务 Seata介绍 官网:http:seata。iozhcnindex。html Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案。 AT模式 前提 基于支持本地ACID事务的关系型数据库。 Java应用,通过JDBC访问数据库。 整体机制 两阶段提交协议的演变: 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。 二阶段: 提交异步化,非常快速地完成。 回滚通过一阶段的回滚日志进行反向补偿。 写隔离 一阶段本地事务提交前,需要确保先拿到全局锁。 拿不到全局锁,不能提交本地事务。 拿全局锁的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。 以一个示例来说明: 两个全局事务tx1和tx2,分别对a表的m字段进行更新操作,m的初始值1000。 tx1先开始,开启本地事务,拿到本地锁,更新操作m1000100900。本地事务提交前,先拿到该记录的全局锁,本地提交释放本地锁。tx2后开始,开启本地事务,拿到本地锁,更新操作m900100800。本地事务提交前,尝试拿该记录的全局锁,tx1全局提交前,该记录的全局锁被tx1持有,tx2需要重试等待全局锁。 tx1二阶段全局提交,释放全局锁。tx2拿到全局锁提交本地事务。 如果tx1的二阶段全局回滚,则tx1需要重新获取该数据的本地锁,进行反向补偿的更新操作,实现分支的回滚。 此时,如果tx2仍在等待该数据的全局锁,同时持有本地锁,则tx1的分支回滚会失败。分支的回滚会一直重试,直到tx2的全局锁等锁超时,放弃全局锁并回滚本地事务释放本地锁,tx1的分支回滚最终成功。 因为整个过程全局锁在tx1结束前一直是被tx1持有的,所以不会发生脏写的问题。 读隔离 在数据库本地事务隔离级别读已提交(ReadCommitted)或以上的基础上,Seata(AT模式)的默认全局隔离级别是读未提交(ReadUncommitted)。 如果应用在特定场景下,必需要求全局的读已提交,目前Seata的方式是通过SELECTFORUPDATE语句的代理。 SELECTFORUPDATE语句的执行会申请全局锁,如果全局锁被其他事务持有,则释放本地锁(回滚SELECTFORUPDATE语句的本地执行)并重试。这个过程中,查询是被block住的,直到全局锁拿到,即读取的相关数据是已提交的,才返回。 出于总体性能上的考虑,Seata目前的方案并没有对所有SELECT语句都进行代理,仅针对FORUPDATE的SELECT语句。 工作机制 以一个示例来说明整个AT分支的工作过程。 业务表:product Field Type Key id bigint(20) PRI name varchar(100) since varchar(100) AT分支事务的业务逻辑: updateproductsetnameGTSwherenameTXC; 一阶段 过程: 解析SQL:得到SQL的类型(UPDATE),表(product),条件(wherenameTXC)等相关的信息。 查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据。 selectid,name,sincefromproductwherenameTXC; 得到前镜像: id name since 1hrTXC 2014hr执行业务SQL:更新这条记录的name为GTS。 查询后镜像:根据前镜像的结果,通过主键定位数据。 selectid,name,sincefromproductwhereid1; 得到后镜像: id name since 1hrGTS 2014hr1、插入回滚日志:把前后镜像数据以及业务SQL相关的信息组成一条回滚日志记录,插入到UNDOLOG表中。 { branchId:641789253, undoItems:〔{ afterImage:{ rows:〔{ fields:〔{ name:id, type:4, value:1 },{ name:name, type:12, value:GTS },{ name:since, type:12, value:2014 }〕 }〕, tableName:product }, beforeImage:{ rows:〔{ fields:〔{ name:id, type:4, value:1 },{ name:name, type:12, value:TXC },{ name:since, type:12, value:2014 }〕 }〕, tableName:product }, sqlType:UPDATE }〕, xid:xid:xxx } 二阶段回滚 收到TC的分支回滚请求,开启一个本地事务,执行如下操作。 通过XID和BranchID查找到相应的UNDOLOG记录。 数据校验:拿UNDOLOG中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理,详细的说明在另外的文档中介绍。 根据UNDOLOG中的前镜像和业务SQL的相关信息生成并执行回滚的语句: updateproductsetnameTXCwhereid1; 提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给TC。 二阶段提交 收到TC的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给TC。 异步任务阶段的分支提交请求将异步和批量地删除相应UNDOLOG记录。 附录 回滚日志表 UNDOLOGTable:不同数据库在类型上会略有差别。 以MySQL为例: Field Type branchid bigintPK xid varchar(100) context varchar(128) rollbackinfo longblob logstatus tinyint logcreated datetime logmodified datetime 注意此处0。7。0增加字段contextCREATETABLEundolog( idbigint(20)NOTNULLAUTOINCREMENT, branchidbigint(20)NOTNULL, xidvarchar(100)NOTNULL, contextvarchar(128)NOTNULL, rollbackinfolongblobNOTNULL, logstatusint(11)NOTNULL, logcreateddatetimeNOTNULL, logmodifieddatetimeNOTNULL, PRIMARYKEY(id), UNIQUEKEYuxundolog(xid,branchid) )ENGINEInnoDBAUTOINCREMENT1DEFAULTCHARSETutf8; TCC模式 回顾总览中的描述:一个分布式的全局事务,整体是两阶段提交的模型。全局事务是由若干分支事务组成的,分支事务要满足两阶段提交的模型要求,即需要每个分支事务都具备自己的: 一阶段prepare行为 二阶段commit或rollback行为 根据两阶段行为模式的不同,我们将分支事务划分为Automatic(Branch)TransactionMode和Manual(Branch)TransactionMode。 AT模式(参考链接TBD)基于支持本地ACID事务的关系型数据库: 一阶段prepare行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。 二阶段commit行为:马上成功结束,自动异步批量清理回滚日志。 二阶段rollback行为:通过回滚日志,自动生成补偿操作,完成数据回滚。 相应的,TCC模式,不依赖于底层数据资源的事务支持: 一阶段prepare行为:调用自定义的prepare逻辑。 二阶段commit行为:调用自定义的commit逻辑。 二阶段rollback行为:调用自定义的rollback逻辑。 所谓TCC模式,是指支持把自定义的分支事务纳入到全局事务的管理中。 Saga模式 Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。 理论基础:HectorKenneth发表论Sagas(1987) 适用场景: 业务流程长、业务流程多 参与者包含其它公司或遗留系统服务,无法提供TCC模式要求的三个接口 优势: 一阶段提交本地事务,无锁,高性能 事件驱动架构,参与者可异步执行,高吞吐 补偿服务易于实现 缺点: 不保证隔离性(应对方案见用户文档) 分布式事务基础 分布式事务解决方案 Seata介绍 Seata实现分布式事务控制等等