微服务Spring Cloude
微服务01
2024 10 1 更新 微服务事务的场景 cap- ca cp原则 Seata的模式

总结-
微服务01
远程调用 RestTemplate->nacos->OpenFeign->
微服务02 GetWay网关->nacos进阶
微服务3 微服务保护->Sentinel->微服务事务
微服务了解
1.微服务和单体服务区别
单体架构-项目的文件都在同一个工程中开发-项目部署简单
但是
团队协作不好分工
系统发布效率低
系统可用性差-无分布式效果-导致功能之间相互影响大
微服务原则
单一职责-一个微服务辅助一部分业务功能-核心数据不依赖其他模块
团队自治-每个微服务都有独立的开发测试人员
服务自治-每个微服务独立部署
- 团队协作成本高?
- 系统发布效率低?
- 系统可用性差?
2.SpringCloude
SpringCloud框架是Java领域的微服务组件的集合。

SpringCloud依托于SpringBoot的自动装配能力,大大降低了其项目搭建、组件使用的成本
Spring Cloud 是在 Spring Boot 之上构建的一组工具和框架,主要用于解决分布式系统中的常见问题,如服务注册与发现、负载均衡、配置管理、断路器等。它提供了一整套微服务架构下的解决方案。
3.微服务拆分原则
什么时候拆?
如何拆?
大多数小型项目来说,一般是先采用单体架构,随着用户规模扩大、业务复杂后再逐渐拆分为微服务架构。高内聚:每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高。
低耦合:每个微服务的功能要相对独立,尽量减少对其它微服务的依赖,或者依赖接口的稳定性要强。

- 纵向拆分
就是按照项目的功能模块来拆分。
例如黑马商城中,就有用户管理功能、订单管理功能、。按照功能模块将他们拆分为一个个服务,就属于纵向拆分。这种拆分模式可以尽可能提高服务的内聚性。 - 横向拆分
各个功能模块之间有没有公共的业务部分,如果有将其抽取出来作为通用服务。
例如用户登录是需要发送消息通知,记录风控数据,下单时也要发送短信,记录风控数据。因此消息发送、风控数据记录就是通用的业务功能,因此可以将他们分别抽取为公共服务:消息中心服务、风控管理服务。这样可以提高业务的复用性,避免重复开发。同时通用业务一般接口稳定性较强,也不会使服务之间过分耦合。
4.微服务拆分


springcloude技术栈
1.远程调用
0.场景

itmservice.chaxun(id);->改为远程调用
1.RestTemplate
同步客户端执行HTTP请求

使用


可以看到,利用RestTemplate发送http请求与前端ajax发送请求非常相似,都包含四部分信息:
① 请求方式
② 请求路径
③ 请求参数
返回值类型
缺点
负载均衡麻烦-无法指定请求路径
-我们就需要一个注册中心-帮我们收集实例-当访问对应就给与一个
Nacos-注册中心
0.了解nacos
Nacos 是阿里巴巴开源的一款用于动态服务发现、配置管理和服务管理的工具,主要用于微服务架构中的服务治理。它的核心目标是简化分布式系统中的服务注册、服务发现和配置管理,并提供一站式解决方案。
Nacos 是一个面向微服务架构的服务治理平台,它的主要作用包括:
- 动态服务注册与发现,帮助微服务自动化发现其他服务。
- 配置管理,提供了强大的配置存储、版本控制、动态刷新功能,提升了微服务架构下的配置灵活性。
- 健康检查,保障服务的高可用性和故障隔离能力。
1.注册中心入门
服务提供者:提供接口供其它微服务访问,比如item-service
服务消费者:调用其它微服务提供的接口,比如cart-service
为了管理这些服务·务消费者三者间关系如下:

2.注册中心配置-Docker
1.导入nacos表

2.修改配置文件

3.
2.注册中心配置-spring cloude
服务注册
1.引入依赖
2.配置nacos地址
3.重启

在nacos的服务列表中即可看到

服务发现
1.引入依赖

2.配置nacos

3.发现调用服务
负载均衡调用服务选择
随机-轮训-ip的hash-最少访问


但是代码还是非常嘈杂以及难看-
OpenFeign-远程调用

1.快速入门案例
1.导入案例

2.启动注解
快速入门案例 1.导入案例

2.启动注解

3.代码编写

private final ItemClient itemclient;
List<>items=itemclient.query();
如上feign帮我们进行了 服务拉取 负载均衡 -请发送的所有工作
2.OpenFeign连接池
我们发起请求的话-需要建立连接-再销毁-但连接池可以减少这个过程产生的消耗
使用ok-http连接池
1.导入依赖
2.配置启用

OkHttp连接池的操作如下:
连接的创建和管理: OkHttp连接池会在应用程序启动时创建一组HTTP连接,并在需要时将其保持在连接池中。这些连接可以被多个线程共享和重用,而不是每次请求都重新创建连接。
连接的重用: 当一个线程需要执行一个HTTP请求时,它可以从连接池中获取一个可用的连接,而不必等待新连接的创建。如果连接池中没有可用的连接,线程可能会被阻塞,直到有连接可用为止。
连接的释放: 当一个线程完成了对某个HTTP服务的请求,它会将连接放回连接池,而不是关闭连接。这样可以避免频繁地打开和关闭连接,提高了连接的重用率。
OkHttp连接池可以加快效率的原因包括:
减少连接的创建和销毁开销:通过重用连接,可以避免频繁地创建和销毁连接,从而减少了系统资源的消耗和网络延迟。
提高并发性能:连接池可以确保连接的有效重用,从而使多个线程能够并发地共享同一个连接,提高了系统的并发处理能力。
降低网络延迟:通过重用连接和减少连接的创建次数,可以减少网络请求的等待时间,从而降低了网络延迟,提高了系统的响应速度。
总的来说,OkHttp连接池的有效管理和重用HTTP连接可以显著提高应用程序的性能和效率,特别是在需要频繁进行HTTP通信的场景下。
这里的请求-是固定的 故此可以保存
3.api抽取
方案1 结构层次低-耦合度高
方案2 结构层次复杂-哦合度低
包扫描问题

也是注册为bean-但我忘记常量注入了
fegin日志配置

修改日志

微服务02
网关过滤器-全局-和指定->网关登录鉴权->
微服务到微服务->Feing拦截器
naocs->配置共享 -配置热更新-动态路由
Getway网关
1.网关了解

2.SpringGetWay网关
1.创建网关微服务
2.引入依赖
3.编写启动类
4.配置网关路由

配置

规则合集



3.网关登录鉴权
登录思路-很多敏感思路都需要鉴权-我们又不可能重复写鉴权代码
故此网关思路即可
既然网关是所有微服务的入口,一切请求都需要先经过网关。我们完全可以把登录校验的工作放到网关去做,这样之前说的问题就解决了:
- 只需要在网关和用户服务保存秘钥
- 只需要在网关开发登录校验功能

1.网关传递用户信息-加信息头
2.服务直接传递信息->加信息头
网关过滤器
1.过滤器了解
网关的请求转发-由于getway实现
具体实现


定义一个网关过滤器
进行登录逻辑判断->再Nett….之前
网关过滤器
1.路由过滤器 GatewayFilter指定规则
2.全局过滤器 GlobalFilter 全局规则
过滤器链之外还有一种过滤器,HttpHeadersFilter,用来处理传递到下游微服务的请求头。
FilteringWebHandeler处理会将全局-装饰为路由-放到同一过滤器链中
依次执行
spring cloude
指定的 路由过滤器
https://docs.spring.io/spring-cloud-gateway/docs/3.1.7/reference/html/#gatewayfilter-factories
2.内置过滤器
gateway内置的GatewayFilter过滤器使用起来非常简单,无需编码,只要在yaml文件中简单配置即可。而且其作用范围也很灵活,配置在哪个Route下,就作用于哪个Route.
AddRequestHeaderGatewayFilterFacotry
使用配置

3.网关路由器 GatewayFilter
自定义GatewayFilter不是直接实现GatewayFilter,而是实现AbstractGatewayFilterFactory

该类的名称一定要以GatewayFilterFactory为后缀!
spring:
cloud:
gateway:
default-filters:
- PrintAny # 此处直接以自定义的GatewayFilterFactory类名称前缀类声明过滤器
4.动态配置参数 GatewayFilter
过滤器动态配置参数


5.全局过滤器 GlobalFilter

案例 -网关登录鉴权
登录效验编写-
思路
全局过滤器中-对jwt进行解析-同时解析完毕将信息构造请求头中-以待微服务拿取-
如果解析失败-就直接返回
1.网关拦截-转user->传递到请求头中

2.网关到微服务



3.微服务到微服务的传递
OpenFeign传递
OpenFeign-提供了一个拦截器

nacos配置管理

解决配置写死-以及重复配置等问题

1.配置共享
1.naocs添加配置
password : ${hm.db.password:}
- 数据库database:可以通过${hm.db.database:test}来设定,默认值test
2.微服务拉取配置
将共享配置与本地的application.yaml配置合并

依赖

bootstart.yaml

hm.db.database:test
hm:
db:
database:hm
->指定参数
2.配置热更新
例–如我们的购物车上线是10–我们想要通过配置管理-
这里也可以用数据库的方法进行控制-但是配置方法更节约资源
1.nacos添加配置
2.微服务读取配置

指定配置

get读取即可
类似心跳检测机制一样的更新
3.动态路由
动态路由
网关的路由配置全部是在项目启动时由org.springframework.cloud.gateway.route.CompositeRouteDefinitionLocator在项目启动的时候加载,并且一经加载就会缓存到内存中的路由表内(一个Map),不会改变。也不会监听路由变更,所以,我们无法利用上节课学习的配置热更新来实现路由更新。
因此,我们必须监听Nacos的配置变更,然后手动把最新的路由更新到路由表中。
1.nacos配置实时更新
https://nacos.io/zh-cn/docs/sdk.html

1 | |
1 | |
1 | |

路由更新接口

如上-网关路由的检测-和网关路由的更改已经完毕-接下来就是实例


# 微服务02
网关过滤器-全局-和指定->网关登录鉴权->
微服务到微服务->Feing拦截器
naocs->配置共享 -配置热更新-动态路由
微服务03
微服务保护
服务保护方案
请求限流
隔离和熔断
分布式事务
初识分布式事务
Seata
知道雪崩问题产生原因及常见解决方案
能使用Sentinel实现服务保护
理解分布式事务产生的原因
能使用Seata解决分布式事务问题
理解AT模式基本原理
1.微服务问题
级联问题-雪崩问题

请求限流,降低了并发上限;线程隔离,降低了可用资源数量;服务熔断,降低了服务的完整度
都是属于降级处理
2.请求限流
限制或控制接口访问的并发流量,避免服务因流量激增而出现故障。
请求限有一个限流器,并发请求,经过限流器就变的平均。

3.线程隔离
当一个业务接口响应时间长,而且并发高时,我们可以控制该线程的数量。
即便该微服务出现问题。浪费的也只是20线程的资源~这里的20是我们设置的线程数量
4.服务熔断
线程熔断虽然避免了雪崩问题。但是依然无法解决。浪费资源问题
- 编写服务降级逻辑:就是服务调用失败后的处理逻辑,根据业务场景,可以抛出异常,也可以返回友好提示或默认数据。
- 异常统计和熔断:统计服务提供方的异常比例,当比例过高表明该接口会影响到其它服务,应该拒绝调用该接口,而是直接走降级逻辑。
熔断后写一个心跳机制-如果不超时或者异常
让他恢复
Sentinel
1.安装
微服务保护技术
Sentinel是阿里巴巴开源的一款服务保护框架
Sentinel 的使用可以分为两个部分:
核心库(Jar包):不依赖任何框架/库,能够运行于 Java 8 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。在项目中引入依赖即可实现服务限流、隔离、熔断等功能。
控制台(Dashboard):Dashboard 主要负责管理推送规则、监控、管理机器信息等。
控制台整合
https://sentinelguard.io/zh-cn/docs/dashboard.html
微服务整合Sentinel
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090

2.簇点链路
簇点链路,指的是在一次请求中经过的各个资源点,例如一个 SpringMVC 的接口 /carts,这是一个被 Sentinel 监控的资源。在默认情况下,Sentinel 会将请求路径(如 /carts)作为唯一标识来识别和监控资源。但在 RESTful 风格的 API 中,同一路径可能会根据 HTTP 方法执行不同的操作,如查询、删除或修改。例如:
GET /carts用于查询购物车POST /carts用于添加购物车DELETE /carts用于删除购物车
由于默认情况下 Sentinel 不区分 HTTP 方法,这会导致多个不同功能的接口被 Sentinel 认为是同一个资源。如果某个操作发生限流、熔断,其他操作也会受到影响,显然是不合理的。
所以我们可以选择打开Sentinel的请求方式前缀,把请求方式 + 请求路径作为簇点资源名:
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090
http-method-specify: true # 开启请求方式前缀

3.请求限流

QPS = 总请求数 / 时间(秒)
6=6/1

4.线程隔离
限流-一旦雪崩还是没能解决
对查询商品的FeignClient接口做线程隔离



即便服务有什么问题。该服务能使用的线程数一直是10
5.服务熔断
服务熔断
问题-我们设置了线程熔断和QPS限制-那么被拒绝的服务就会被抛出异常
我们应该给一个降级处理
2-
查询的延迟极高-还是会一直消耗资源-我们可以设置阈值-到达就熔断服务
1.编写降级处理
FeignClient编写失败后的降级逻辑
1.FallbackClass,无法对远程调用的异常做处理
2.:FallbackFactory,可以对远程调用的异常做处理,我们一般选择这种方式

ItemClientFallback 类实现了 FallbackFactory<ItemClient>,当 ItemClient 调用失败时,create() 方法会被调用,并接收一个 Throwable cause 参数,表示引发失败的异常。
该方法为默认实现

注册为Bean

指定回调
降级之后-我们就设置服务熔断
断路器的工作模式
Sentinel中的断路器不仅可以统计某个接口的慢请求比例,还可以统计异常请求比例。当这些比例超出阈值时,就会熔断该接口,即拦截访问该接口的一切请求,降级处理;当该接口恢复正常时,再放行对于该接口的请求。


微服务事务
1.微服务事务了解
- 在单体应用中,事务是通过数据库的 ACID 特性(原子性、一致性、隔离性、持久性)来保证的。然而,在微服务架构中,每个服务都有自己独立的数据源(数据库、缓存等),通常无法再通过传统的本地事务管理工具来保证整体事务的一致性。这时会出现如下问题:
- 跨服务的事务一致性:比如用户下单时,订单服务、库存服务、支付服务需要在不同的数据库中进行操作,如何保证所有服务的数据操作要么全部成功,要么全部回滚?
- 网络延迟与失败:微服务间的调用是通过网络进行的,网络的延迟或失败会使事务变得复杂。
- 独立性与隔离性:不同的微服务通常需要尽量保持高自治性和松耦合性,但在分布式事务中,需要一定的协调性。
- 在单体应用中,事务是通过数据库的 ACID 特性(原子性、一致性、隔离性、持久性)来保证的。然而,在微服务架构中,每个服务都有自己独立的数据源(数据库、缓存等),通常无法再通过传统的本地事务管理工具来保证整体事务的一致性。这时会出现如下问题:
2.分布式事务场景
1,.单服务请求多数据库完成一次事务

2.多服务请求单数据库完成一次事务

1、跨服务完成一次事务
2、跨数据源完成一次事务
3.Cap原则
CAP是 Consistency、Availability、Partition tolerance三个词语的缩写,分别表示一致性、可用性、分区容忍性。

C一致性: 向系统写一个新数据再次读取到的也一定是这个新数据。拿上图举例,请求订单服务下单,订单服务请求库存服务扣减库存,只要下单成功则库存扣减成功。
A可用性:任何时间都可以访问订单服务和库存服务,系统保证可用。
p分区容忍性:也叫分区容错性,分布式系统在部署时服务器可能部署在不同的网络分区,比如上图中订单服务在北京,库存服务在上海,由于处于不同的网络分区如果网络通信异常就会导致节点 之间无法通信,当出现由于网络问题导致节点 之间无法通信,此时仍然是对外提供服务的这叫做满足分区容忍性。
CAP理论要强调在分布式系统中C、A、P这三点不能全部满足。
4.CP

5.Ap

6.实际场景
满足CP的要求:
创建订单和优惠券核销要么都成功要么都失败,不能存在一个成功一个失败,如果要实现CP需要在下单和核销优惠券操作前进行一次预操作,如果预操作成功将优惠券锁定避免在执行事务期间优惠券被其它订单使用。
满足AP的要求:
创建订单和优惠券核销要么都成功要么都失败,可以暂时存在一个成功一个失败,最终要保证数据的一致性,如果要实现AP,不需要提前锁定资源,在执行事务期间有一个失败则么另一个操作回滚即可,最终实现数据一致性。
基于上边的分析,实现CP更麻烦,实现AP同样满足的需求,本项目优惠券核销操作实现AP。
实现方案

2.Seata
0.seata模式

1.Seata了解
分布式事务-各个单个事务直接无法感应对方的失败或者成功
事务协调者,与多个分支事务通信,检测每个分支事务的执行状态,保证全局事务下的每一个分支事务同时成功或失败即可。大多数的分布式事务框架都是基于这个理论来实现的。
回调检查-或者建立通信协议

2.Seata部署
部署TC-DOCKER
1.
2.
3.
微服务集成Seata



seata的客户端在解决分布式事务的时候需要记录一些中间数据,保存在数据库中。因此我们要先准备一个这样的表。
将课前资料的seata-at.sql-导入进行事务控制的数据库中

3.Seata使用
@Transactional注解改为Seata提供的@GlobalTransactional
微服务事务
@GlobalTransactional注解就是在标记事务的起点,将来TM就会基于这个方法判断全局事务范围,初始化全局事务。
4.分布式解决方案
两阶段提交(2PC,Two-Phase Commit):
- 在第一阶段(准备阶段),所有涉及事务的服务都会“准备”它们的操作,并告知协调者准备状态。
- 在第二阶段(提交阶段),如果所有服务都准备成功,则协调者通知所有服务提交事务;否则,所有服务都回滚事务。
- 缺点:两阶段提交的锁定资源时间较长,可能导致性能问题和死锁。
TCC 模型(Try-Confirm-Cancel):
- TCC 模型是一种细化的两阶段提交方案,服务在业务逻辑上实现
Try(准备)、Confirm(确认)、Cancel(取消)三个步骤。 - 在
Try阶段,服务会预留资源(例如预扣库存);在Confirm阶段,服务确认资源的操作;在Cancel阶段,取消预留操作。
基于消息的最终一致性:
- 使用消息队列来保证跨服务的数据一致性。当事务的一部分成功后,其他微服务通过异步消息更新自己的数据,保证最终一致性。
- 优点:弱化了实时性,较适合对一致性要求不高的场景。
SAGA 模型:
- SAGA 是一种分布式事务模式,将每个服务的事务划分为多个小事务,每个小事务有对应的补偿操作。
- 如果某个小事务失败,已经完成的小事务会通过调用补偿逻辑(如回滚、撤销操作)来恢复到一致状态。
5.Xa模式
A是规范-原理是两阶段提交

一阶段:
tm告诉TC开启全局事务
tm-调用rm
- 事务协调者TC通知每个事务参与者执行本地事务
- rm执行sql
- 本地事务执行完成后报告事务执行状态给事务协调者,此时事务不提交,继续持有数据库锁
二阶段:
- 事务协调者基于一阶段的报告来判断下一步操作
- 如果一阶段都成功,则通知所有事务参与者,提交事务
- 如果一阶段任意一个参与者失败,则通知所有事务参与者回滚事务
也就是-事务锁只有等一阶段所有RM执行完毕-等待通知-开启2阶段

优缺点
行锁的缺点-优点


6.At模式
AT模式同样是分阶段提交的事务模型,不过缺弥补了XA模型中资源锁定周期过长的缺陷。

取消行锁-改为本地log缓存

阶段一 每个数据库都直接提交-同时备份一个undo-log
一旦某个地方出现异常-RM回滚-同时TC-检测-分发没个事务进行回滚
流程



AT模式下分支事务会立即提交,虽然回滚可以通过快照恢复原始数据,但在异常发生之前,其他事务读取的数据可能是已经修改但未最终确定的状态。因此,若需要保证严格的数据一致性,可能需要结合隔离级别控制和快照机制,或者对已读数据的后续处理进行特殊设计。
Xa At区别

tm->告知TC开启事务
RM执行sql-并且保存sql快照->提交
事务介绍->tm告知->tc结束
tc检查是否有异常-无异常提交-有异常回档