# cloud-study-2024 **Repository Path**: ninghongkang/cloud-study-2024 ## Basic Information - **Project Name**: cloud-study-2024 - **Description**: 微服务是一种软件架构风格,它是以专注于单一职责的很多小型项目为基础,组合出复杂的大型应用——维基百科 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2024-05-18 - **Last Updated**: 2025-03-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # cloud-study-2024 ## 介绍 微服务是一种软件架构风格,它是以专注于单一职责的很多小型项目为基础,组合出复杂的大型应用——维基百科 ## 软件架构 软件架构说明 SSM+MySQL+SpringBoot+SpringCloud Alibaba(Nacos、OpenFeign、Gateway) ## 架构转变说明 ### **单体架构** 将业务的所有功能集中在一个项目中开发,打成一个包部署。 **优点** - 架构简单 - 部署成本低 **缺点** - 团队协作成本高 - 系统发布效率低 - 系统可用性差 总结: - 单体架构适合开发功能相对简单,规模较小的项目。 ### 微服务架构 **微服务**架构,是服务化思想指导下的一套最佳实践架构方案。服务化,就是把单体架构中的功能模块拆分为多个独立项目 **优点** - 粒度小 - 团队自治 - 服务自治 SpringCloud是目前国内使用最广泛的微服务框架。官网地址: SpringCloud基于SpringBoot实现了微服务组件的自动装配,从而提供了良好的开箱即用体验。但对于SpringBoot的版本也有要求: | **SpringCloud**版本 | SpringBoot版本 | |---------------------------------------------------------------------------------------------------------------------|-----------------------------------------| | [2022.0.x](https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-2022.0-Release-Notes) aka Kilburn | 3.0.x | | [2021.0.x](https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-2021.0-Release-Notes) aka Jubilee | 2.6.x, 2.7.x (Starting with 2021.0.3) | | [2020.0.x](https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-2020.0-Release-Notes) aka Ilford | 2.4.x, 2.5.x (Starting with 2020.0.3) | | [Hoxton](https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-Hoxton-Release-Notes) | 2.2.x, 2.3.x (Starting with SR5) | | [Greenwich](https://github.com/spring-projects/spring-cloud/wiki/Spring-Cloud-Greenwich-Release-Notes) | 2.1.x | | [Finchley](https://github.com/spring-projects/spring-cloud/wiki/Spring-Cloud-Finchley-Release-Notes) | 2.0.x | | [Edgware](https://github.com/spring-projects/spring-cloud/wiki/Spring-Cloud-Edgware-Release-Notes) | 1.5.x | | [Dalston](https://github.com/spring-projects/spring-cloud/wiki/Spring-Cloud-Dalston-Release-Notes) | 1.5.x | ## 服务拆分原则 **什么时候需要拆分呢?** - **创业型项目**:先采用单体架构,快速开发,快速试错。随着规模扩大,逐渐拆分。 - 敏捷开发,逐步迭代 - **确定的大型项目**:资金充足,目标明确,可以直接选择微服务架构,避免后续拆分的麻烦 - 比如说大型公司的项目,如淘宝 **怎么拆分呢?** 从拆分目标来说,要做到: - **高内聚**:每个微服务的**职责要尽量单一**,包含的业务相互关联度高、完整度高。 - 比如说,订单服务只负责处理订单,代理服务只处理登录,鉴权服务只负责鉴权。 - **低耦合**:每个微服务的功能要相对独立,尽量减少对其它微服务的依赖。 从拆分方式来说,一般包含两种方式: - **纵向拆分**:按照业务模块来拆分。 - **横向拆分**:抽取公共服务,提高复用性 ### 远程调用 **微服务拆分后遇到的第一个问题** 在单体项目被拆分为微服务之后,不可避免的会出现跨微服务的业务,此时微服务之间就需要进行远程调用。微服务之间的远程调用被称为RPC,即远程过程调用。RPC的实现方式有很多,比如: - 基于Http协议 - 基于Dubbo协议 Java发送http请求可以使用Spring提供的RestTemplate,使用的基本步骤如下: - 注册RestTemplate到Spring容器 - 调用RestTemplate的API发送请求,常见方法有: - getForObject:发送Get请求并返回指定类型对象 - PostForObject:发送Post请求并返回指定类型对象 - put:发送PUT请求 - delete:发送Delete请求 - exchange:发送任意类型请求,返回ResponseEntity ## 服务治理(服务注册、发现与负载均衡) [服务注册、发现与负载均衡](./doc/5-服务治理) **服务注册** ![1716188697051](./assets/服务注册.png) **服务发现** ![1716188750679](./assets/服务发现.png) **小结** 服务治理中的三个角色分别是什么? - 服务提供者:暴露服务接口,供其它服务调用 - 服务消费者:调用其它服务提供的接口 - 注册中心:记录并监控微服务各实例状态,推送服务变更信息 消费者如何知道提供者的地址? - 服务提供者会在启动时注册自己信息到注册中心,消费者可以从注册中心订阅和拉取服务信息 消费者如何得知服务状态变更? - 服务提供者通过心跳机制向注册中心报告自己的健康状态,当心跳异常时注册中心会将异常服务剔除,并通知订阅了该服务的消费者 当提供者有多个实例时,消费者该选择哪一个? - 消费者可以通过负载均衡算法,从多个实例中选择一个 ## OpenFeign [OpenFeign的快速入门、连接池、最佳实战、日志输出](./doc/6-OpenFeign) ### 快速入门 **如何利用OpenFeign实现远程调用?** - 引入OpenFeign和SpringCloudLoadBalancer依赖 - 利用@EnableFeignClients注解开启OpenFeign功能 - 编写FeignClient ![1716202629378](./assets/openfeign快速入门1.png) ![1716202673450](./assets/openfeign快速入门2.png) 如何配置**OpenFeign的连接池**? - 引入http客户端依赖,例如OKHttp、HttpClient - 配置yaml文件,打开OpenFeign连接池开关 ![1716202743460](./assets/openfeign连接池_okhttp.png) ### 最佳实践 OpenFeign使用的**最佳实践方式**是什么? - 由服务提供者编写独立module,将FeignClient及DTO抽取 其实这里有两种方案。 - 一:在每个微服务都是独立的project前提下,服务提供者编写独立module(子module),将FeignClient及DTO抽取在这个module中,其他微服务需要发起远程调用时,引入这个module - 优点:耦合度较低。服务提供者对服务比较熟悉,编写出来的代码不容易出bug - 缺点:相对麻烦,工程结构相对更复杂 - 二:在使用maven聚合模块的前提下,抽取出一个公共module,将所有FeignClient及DTO抽取在这个module中 - 优点:抽取更加简单,工程结构也比较清晰 - 缺点:整个项目耦合度偏高 将FeignClient抽取为独立module,SpringBootApplication启动类扫描不到时,无法注入Bean ![](./assets/openfeign最佳实践扫不到FeignClient_Bean.png) 解决方案如下: ![](./assets/openfeign最佳实践扫描不到FeignClient_Bean解决方案.png) ### 日志 如何配置**OpenFeign输出日志的级别**? - 声明类型为Logger.Level的Bean - 在@FeignClient或@EnableFeignClients注解上使用 ![1716202142309](./assets/openfeign日志输出.png) **思考**:如何才能在每个微服务中都拿到用户信息?如何在微服务之间传递用户信息? ## 网关Gateway [Gateway 网关路由、网关登录校验](./doc/7-Gateway) ### 路由过滤总结 路由过滤器、defaultFilter、全局过滤器的执行顺序? - ①order值越小,优先级越高 - ②当order值一样时,顺序是defaultFilter最先,然后是局部的路由过滤器,最后是全局过滤器 ### 网关到微服务、微服务之间用户信息传递打通 要实现网关到微服务、微服务之间用户信息传递打通的打通,有如下两步骤: **打通网关到微服务的用户信息传递** - 1)在网关的登录校验过滤器中,把获取到的用户写入请求头 - 即在网关全局过滤器`GobalFilter`中将用户写入请求头。 - 如下就是将用户信息写入header的示例: ```java exchange.mutate() // mutate就是对下游请求做更改 .request(builder -> builder.header("user-info", userInfo)) .build(); ``` - 2)在hm-common(公用module)中编写SpringMVC拦截器,获取登录用户信息(例如通过ThreadLocal) - 后续所有需要用户信息的微服务直接依赖这个module,就可以获取用户信息。无需重复编写 **打通微服务之间用户信息传递打通** 说明: ​ 因为微服务之间的调用是基于OpenFeign,不走网关,所以通过网关全局过滤器写入用户信息到Header中的方式不生效。 此时,可以基于OpenFeign传递用户信息 - 1)OpenFeign中提供了一个拦截器接口(`feign.RequestInterceptor` ),所有由OpenFeign发起的请求(远程调用)都会先调用拦截器处理请求。我们在实现这个接口将用户信息写入header即可。 ## 微服务的保护 Sentinel 在微服务远程调用的过程中,还存在几个问题需要解决。 首先是**业务健壮性**问题: 例如在之前的查询购物车列表业务中,购物车服务需要查询最新的商品信息,与购物车数据做对比,提醒用户。大家设想一下,如果商品服务查询时发生故障,查询购物车列表在调用商品服 务时,是不是也会异常?从而导致购物车查询失败。但从业务角度来说,为了提升用户体验,即便是商品查询失败,购物车列表也应该正确展示出来,哪怕是不包含最新的商品信息。 还有**级联失败**问题: 还是查询购物车的业务,假如商品服务业务并发较高,占用过多Tomcat连接。可能会导致商品服务的所有接口响应时间增加,延迟变高,甚至是长时间阻塞直至查询失败。 此时查询购物车业务需要查询并等待商品查询结果,从而导致查询购物车列表业务的响应时间也变长,甚至也阻塞直至无法访问。而此时如果查询购物车的请求较多,可能导致购物车服务的Tomcat连接占用较多,所有接口的响应时间都会增加,整个服务性能很差, 甚至不可用。 ![](./assets/级联问题1.png) 依次类推,整个微服务群中与购物车服务、商品服务等有调用关系的服务可能都会出现问题,最终导致整个集群不可用。 ![](./assets/级联问题2.png) 这就是**级联失败**问题,或者叫**雪崩**问题 解决方案: [微服务的保护 Sentinel](./doc/8-服务保护) 服务保护方案总结: - 请求限流 - 线程隔离 - 服务熔断 这些方案或多或少都会导致服务的体验上略有下降,比如请求限流,降低了并发上限;线程隔离,降低了可用资源数量;服务熔断,降低了服务的完整度,部分服务变的不可用或弱可用。因此这些方案都属于服务 **降级**的方案。但通过这些方案,服务的健壮性得到了提升 ## 分布式事务 下单业务,前端请求首先进入订单服务,创建订单并写入数据库。然后订单服务调用购物车服务和库存服务: - 购物车服务负责清理购物车信息 - 库存服务负责扣减商品库存 ![1716459089004](assets/分布式事务问题1.png) 在分布式系统中,如果一个业务需要多个服务合作完成,而且每一个服务都有事务,多个事务必须同时成功或失败,这样的事务就是**分布式事务 ** 其中的每个服务的事务就是一个**分支事务**。整个业务称为**全局事务**。 解决方案: [分布式事务 Seata](./doc/9-分布式事务) Seata分布式事务总结如下: ### **XA模式** ![1716471251652](assets/分布式事务-XA模式.png) - 优点: - 事务的强一致性,满足ACID原则。 - 常用数据库都支持,实现简单,并且没有代码侵入 - 缺点: - 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差 - 依赖关系型数据库实现事务 ### AT模式 ![1716471345154](assets/分布式事务-AT模式.png) - AT模式与XA模式最大的区别是什么 - XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源 - XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚 - XA模式强一致;AT模式最终一致 ### TCC模式 参考:https://seata.apache.org/zh-cn/docs/user/mode/tcc 关于TCC(Try-Confirm-Cancel)的概念,最早是由Pat Helland于2007年发表的一篇名为《Life beyond Distributed Transactions:an Apostate’s Opinion》的论文提出。 TCC事务机制相比于上面介绍的,解决了其几个缺点: (1)解决了协调者单点,由主业务方发起并完成这个业务活动。业务活动管理器也变成多点,引入集群。 (2)同步阻塞:引入超时,超时后进行补偿,并且不会锁定整个资源,将资源转换为业务逻辑形式,粒度变小。 (3)数据一致性:有了补偿机制之后,由业务活动管理器控制一致性。 对于TCC的解释: (1)Try阶段:尝试执行,完成所有业务检查(一致性),预留必须业务资源(准隔离性) (2)Confirm阶段:确认执行真正执行业务,不作任何业务检查,只使用Try阶段预留的业务资源,Confirm操作满足幂等性。要求具备幂等设计,Confirm失败后需要进行重试。 (3)Cancel阶段:取消执行,释放Try阶段预留的业务资源 Cancel操作满足幂等性Cancel阶段的异常和Confirm阶段异常处理方案基本上一致。 举个简单的例子: ​ 如果你用100元买了一瓶水。 Try阶段:你需要向你的钱包检查是否够100元并锁住这100元,水也是一样的。 ​ 如果有一个失败,则进行cancel(释放这100元和这一瓶水),如果cancel失败不论什么失败都进行重试cancel,所以需要保持幂等。 ​ 如果都成功,则进行confirm,确认这100元扣,和这一瓶水被卖,如果confirm失败无论什么失败则重试(会依靠活动日志进行重试) 对于TCC来说适合一些: (1)强隔离性,严格一致性要求的活动业务。 (2)执行时间较短的业务。 ​ seata官方是这么说的:TCC 模式是高性能分布式事务解决方案,适用于核心系统等对性能有很高要求的场景。 缺点: - 缺点还是比较明显的,在2,3步中都有可能失败。 - TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用TCC不太好定义及处理。 - 即 侵入式的分布式事务解决方案 - 即 需要业务系统自行实现 Try,Confirm,Cancel 三个操作,对业务系统有着非常大的入侵性,设计相对复杂 ### Saga模式(了解) 参考:https://seata.apache.org/zh-cn/docs/user/mode/saga Saga是30年前一篇数据库论文提到的一个概念。其核心思想是将长事务拆分为多个本地短事务,由Saga事务协调器协调,如果正常结束那就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。 Saga的组成: 每个Saga由一系列sub-transaction Ti 组成 每个Ti 都有对应的补偿动作Ci,补偿动作用于撤销Ti造成的结果,这里的每个T,都是一个本地事务。 可以看到,和TCC相比,Saga没有“预留 try”动作,它的Ti就是直接提交到库。 Saga的执行顺序有两种: T1, T2, T3, ..., Tn T1, T2, ..., Tj, Cj,..., C2, C1,其中0 < j < n Saga定义了两种恢复策略: 向后恢复,即上面提到的第二种执行顺序,其中j是发生错误的sub-transaction,这种做法的效果是撤销掉之前所有成功的sub-transation,使得整个Saga的执行结果撤销。 向前恢复,适用于必须要成功的场景,执行顺序是类似于这样的:T1, T2, ..., Tj(失败), Tj(重试),..., Tn,其中j是发生错误的sub-transaction。该情况下不需要Ci。 这里要注意的是,在saga模式中不能保证隔离性,因为没有锁住资源,其他事务依然可以覆盖或者影响当前事务。 还是拿100元买一瓶水的例子来说,这里定义 T1=扣100元 T2=给用户加一瓶水 T3=减库存一瓶水 C1=加100元 C2=给用户减一瓶水 C3=给库存加一瓶水 我们一次进行T1,T2,T3如果发生问题,就执行发生问题的C操作的反向。 上面说到的隔离性的问题会出现在,如果执行到T3这个时候需要执行回滚,但是这个用户已经把水喝了(另外一个事务),回滚的时候就会发现,无法给用户减一瓶水了。这就是事务之间没有隔离性的问题,可以看到saga模式没有隔离性的影响还是较大,可以参照华为的解决方案:从业务层面入手加入一 Session 以及锁的机制来保证能够串行化操作资源。也可以在业务层面通过预先冻结资金的方式隔离这部分资源, 最后在业务操作的过程中可以通过及时读取当前状态的方式获取到最新的更新。 ### 本地消息表:star: 此方案的核心是将需要分布式处理的任务通过消息日志的方式来异步执行。消息日志可以存储到本地文本、数据库或消息队列,再通过业务规则自动或人工发起重试。人工重试更多的是应用于支付场景,通过对账系统对事后问题的处理。 ![](./assets/分布式事务-本地消息表.png) 对于本地消息队列来说核心是把大事务转变为小事务。还是举上面用100元去买一瓶水的例子。 (1)当你扣钱的时候,你需要在你扣钱的服务器上新增加一个本地消息表,你需要把你扣钱和写入减去水的库存到本地消息表放入同一个事务(依靠数据库本地事务保证一致性) (2)这个时候有个定时任务去轮询这个本地事务表,把没有发送的消息,扔给商品库存服务器,叫他减去水的库存,到达商品服务器之后这个时候得先写入这个服务器的事务表,然后进行扣减,扣减成功后,更新事务表中的状态。 (3)商品服务器通过定时任务扫描消息表或者直接通知扣钱服务器,扣钱服务器本地消息表进行状态更新 (4)针对一些异常情况,定时扫描未成功处理的消息,进行重新发送,在商品服务器接到消息之后,首先判断是否是重复的,如果已经接收,在判断是否执行,如果执行在马上又进行通知事务,如果未执行,需要重新执行需要由业务保证幂等,也就是不会多扣一瓶水。 本地消息队列是BASE理论,是最终一致模型,适用于对一致性要求不高的。实现这个模型时需要注意重试的幂等。 ### MQ事务:star: 在RocketMQ中实现了分布式事务,实际上其实是对本地消息表的一个封装,将本地消息表移动到了MQ内部 下面简单介绍一下MQ事务。 ![](./assets/分布式事务-MQ事务.png) 基本流程如下: ​ 第一阶段Prepared消息,会拿到消息的地址。 ​ 第二阶段执行本地事务。 ​ 第三阶段通过第一阶段拿到的地址去访问消息,并修改状态,消息接受者就能使用这个消息。 如果确认消息失败,在RocketMq Broker中提供了定时扫描没有更新状态的消息,如果有消息没有得到确认,会向消息发送者发送消息,来判断是否提交,在rocketmq中是以listener的形式给发送者,用来处理。 ![](./assets/分布式事务-MQ事务2.png) 如果消费超时,则需要一直重试,消息接收端需要保证幂等。如果消息消费失败,这个就需要人工进行处理,因为这个概率较低,如果为了这种小概率时间而设计这个复杂的流程反而得不偿失 ## RabbitMQ [SpringAMQP WorkQueues消息模型 Fanout、Direct、Topic交换机 MQ的可靠性保障 延迟队列](./doc/10-消息队列RabbitMQ) ## ElasticSearch [](./doc/12-ElasticSearch) 更详细的ElasticSearch操作,请参考以下: [CRUD](https://gitee.com/ninghongkang/ElasticSearch/blob/master/es-hotel-demo/doc/day05-Elasticsearch01/%E8%AE%B2%E4%B9%89/%E5%88%86%E5%B8%83%E5%BC%8F%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E01.md) [全文检索 地理查询 复合查询](https://gitee.com/ninghongkang/ElasticSearch/blob/master/es-hotel-demo/doc/day06-Elasticsearch02/%E8%AE%B2%E4%B9%89/%E5%88%86%E5%B8%83%E5%BC%8F%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E02.md) [数据聚合 自动补全 数据同步](https://gitee.com/ninghongkang/ElasticSearch/blob/master/es-hotel-demo/doc/day07-Elasticsearch03/%E8%AE%B2%E4%B9%89/%E5%88%86%E5%B8%83%E5%BC%8F%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E03.md) ## 参考 在线文档:https://b11et3un53m.feishu.cn/wiki/space/7229522334074372099?ccm_open_type=lark_wiki_spaceLink&open_tab_from=wiki_home