三明网站建设公司,东莞市公共资源网,平东网站建设,网站建设目的文章目录 1、SpringCloud版本对应1.1 技术选型依据1.2 cloud组件演变#xff1a; 2、Eureka2.1 Eureka Server #xff1a; 提供服务注册服务2.2 EurekaClient #xff1a; 通过注册中心进行访问2.3 Eureka自我保护 3、Eureka、Zookeeper、Consul三个注册中心的异同点3.1 CP… 文章目录 1、SpringCloud版本对应1.1 技术选型依据1.2 cloud组件演变 2、Eureka2.1 Eureka Server 提供服务注册服务2.2 EurekaClient 通过注册中心进行访问2.3 Eureka自我保护 3、Eureka、Zookeeper、Consul三个注册中心的异同点3.1 CP架构 4、Ribbon 负载均衡服务调用4.1 SpringCloud Load Balance4.2 总结:4.3 Ribbon工作流程4.4 自定义Ribbon 负载均衡算法4.4.1 iRule接口4.4.2 Ribbon自带的负载均衡算法4.4.3 负载均衡算法替代4.4.3.1、在非启动类包及子包下创建配置类4.4.3.2、定义4.4.3.3、启动类增加RibbonClient注解 4.5 Ribbon负载均衡算法4.5.1 轮询算法原理4.5.2 轮询算法源码4.5.3 手写负载均衡算法 5、OpenFeign 服务接口调用5.1 概述5.2 Feign能干什么5.3 Feign集成了Ribbon5.4 应用5.4.1 引入依赖5.4.2 开启功能5.4.3 service中远程调用 5.5 超时控制在yml中开启OpenFeign客户端超时控制 5.6 日志打印5.6.1 日志级别5.6.2 配置类5.6.3 yml指定日志以什么级别监控哪个接口 6.Hystrix 断路器6.1 问题服务雪崩6.2 概念6.3 服务降级6.3.1 概念6.3.2 触发服务降级的情况6.3.3 应用6.3.3.1 依赖6.3.3.2 解决的问题6.3.3.3 生产者6.3.3.4 消费者6.3.3.5 配置全局fallback方法6.3.3.6 解耦合 6.4 服务熔断6.4.1 概念6.4.2 注解6.4.3 应用6.4.4 原来的主逻辑要如何恢复呢? 6.5 服务限流6.5.1 概念 6.6 服务监控 hystrixDashboard6.6.1 依赖6.6.2 主启动类添加注解EnableHystrixDashboard6.6.3 访问图形化界面6.6.4 调整需要监控的服务主启动类6.6.5 输入监控的url 7、Gateway 网关7.1 特性7.2 Gateway与Zuul的区别7.3 Gateway模型7.4 三大基本概念7.4.1 Route 路由7.4.2 Predicate 断言7.4.3 Filter 过滤 7.5 应用7.5.1 依赖7.5.2 yml配置文件7.5.3 配置类方式配置网关路由 7.6 动态路由7.7 Predicate 断言7.8 Filter 过滤器7.8.1 自定义过滤器 8、Config 服务配置8.1 作用 9、SpringCloud Alibaba10、Nacos 服务注册和配置中心10.1 应用10.1.1 依赖10.1.2 配置文件 10.2 Nacos发现实例模型10.3 注册中心对比10.4 Nacos 支持AP和CP模式的切换10.4.1 何时选择何种模式 10.5 Nacos 服务配置10.5.1 SpringCloud原生注解RefreshScope10.5.2 配置10.5.3 分类设计思想 10.6 Nacos 集群是持久化配置10.6.1 Nacos支持三种部署模式 11、Sentinel 熔断与限流11.1 是什么11.2 特征11.3 特性11.4 与Hystrix的区别11.5 两个部分11.6 应用11.6.1 依赖11.6.2 配置文件 11.7 流量配置规则11.7.1 直接默认11.7.2 关联11.7.3 Warm Up 预热11.7.4 排队等待 11.8 熔断降级11.8.1 概述RT(平均响应时间秒级)异常比列(秒级)异常数(分钟级) 11.8.2 Sentinel断路器没有半开状态 11.9 热点规则11.9.1 参数例外项11.9.2 运行时异常 11.10 系统规则11.11 SentinelResource11.12 熔断框架比较11.13 Sentinel的规则持久化11.13.1 依赖 12、 Seata 处理分布式事务12.1 Seata 简介212.2 Seata的安装12.2.1 修改配置文件12.2.2 在nacos上创建配置文件 seataServer.yaml12.2.3 安装路径seata\seata-server-1.6.0\seata\script\config-center下有一个config.txt文件修改后复制到seata路径下12.2.4 通过nacos-config.sh将config.txt文件的内容上传到nacos上12.2.5 通过seata-server.bat启动 12.3 业务说明12.4 应用12.4.1 依赖12.4.2 配置文件12.4.3 订单服务主业务TOrderServiceImpl12.4.4 库存服务 TStorageServiceImpl12.4.5 故障情况12.4.6 使用Seata对数据源进行代理 12.5 seata原理12.5.1 业务执行流程12.5.2 AT模式一阶段加载二阶段提交二阶段回滚 1、SpringCloud版本对应 1.1 技术选型依据
{git: {branch: 0e9bff9f3008546899af1a871def8e3a9cc852ff,commit: {id: 0e9bff9,time: 2023-06-12T10:28:25Z}},build: {version: 0.0.1-SNAPSHOT,artifact: start-site,versions: {spring-boot: 3.1.0,initializr: 0.20.0-SNAPSHOT},name: start.spring.io website,time: 2023-06-12T10:30:20.458Z,group: io.spring.start},bom-ranges: {codecentric-spring-boot-admin: {2.4.3: Spring Boot 2.3.0.M1 and 2.5.0-M1,2.5.6: Spring Boot 2.5.0.M1 and 2.6.0-M1,2.6.8: Spring Boot 2.6.0.M1 and 2.7.0-M1,2.7.4: Spring Boot 2.7.0.M1 and 3.0.0-M1,3.0.4: Spring Boot 3.0.0-M1 and 3.1.0-M1},solace-spring-boot: {1.1.0: Spring Boot 2.3.0.M1 and 2.6.0-M1,1.2.2: Spring Boot 2.6.0.M1 and 3.0.0-M1,2.0.0: Spring Boot 3.0.0-M1},solace-spring-cloud: {1.1.1: Spring Boot 2.3.0.M1 and 2.4.0-M1,2.1.0: Spring Boot 2.4.0.M1 and 2.6.0-M1,2.3.2: Spring Boot 2.6.0.M1 and 3.0.0-M1,3.0.0: Spring Boot 3.0.0-M1},spring-cloud: {Hoxton.SR12: Spring Boot 2.2.0.RELEASE and 2.4.0.RELEASE,2020.0.6: Spring Boot 2.4.0.RELEASE and 2.6.0,2021.0.7: Spring Boot 2.6.0 and 3.0.0,2022.0.3: Spring Boot 3.0.0 and 3.2.0-M1},spring-cloud-azure: {4.8.0: Spring Boot 2.5.0.M1 and 3.0.0-M1,5.2.0: Spring Boot 3.0.0-M1 and 3.1.0-M1},spring-cloud-gcp: {2.0.11: Spring Boot 2.4.0-M1 and 2.6.0-M1,3.5.1: Spring Boot 2.6.0-M1 and 3.0.0-M1,4.3.1: Spring Boot 3.0.0-M1 and 3.1.0-M1},spring-cloud-services: {2.3.0.RELEASE: Spring Boot 2.3.0.RELEASE and 2.4.0-M1,2.4.1: Spring Boot 2.4.0-M1 and 2.5.0-M1,3.3.0: Spring Boot 2.5.0-M1 and 2.6.0-M1,3.4.0: Spring Boot 2.6.0-M1 and 2.7.0-M1,3.5.0: Spring Boot 2.7.0-M1 and 3.0.0-M1,4.0.0: Spring Boot 3.0.0 and 3.1.0-M1},spring-shell: {2.1.10: Spring Boot 2.7.0 and 3.0.0-M1,3.0.4: Spring Boot 3.0.0 and 3.1.0-M1,3.1.0: Spring Boot 3.1.0 and 3.2.0-M1},vaadin: {14.10.1: Spring Boot 2.1.0.RELEASE and 2.6.0-M1,23.2.15: Spring Boot 2.6.0-M1 and 2.7.0-M1,23.3.13: Spring Boot 2.7.0-M1 and 3.0.0-M1,24.0.6: Spring Boot 3.0.0-M1 and 3.1.0-M1,24.1.0: Spring Boot 3.1.0-M1 and 3.2.0-M1},wavefront: {2.0.2: Spring Boot 2.1.0.RELEASE and 2.4.0-M1,2.1.1: Spring Boot 2.4.0-M1 and 2.5.0-M1,2.2.2: Spring Boot 2.5.0-M1 and 2.7.0-M1,2.3.4: Spring Boot 2.7.0-M1 and 3.0.0-M1,3.0.1: Spring Boot 3.0.0-M1 and 3.1.0-M1}},dependency-ranges: {okta: {1.4.0: Spring Boot 2.2.0.RELEASE and 2.4.0-M1,1.5.1: Spring Boot 2.4.0-M1 and 2.4.1,2.0.1: Spring Boot 2.4.1 and 2.5.0-M1,2.1.6: Spring Boot 2.5.0-M1 and 3.0.0-M1,3.0.4: Spring Boot 3.0.0-M1 and 3.2.0-M1},mybatis: {2.1.4: Spring Boot 2.1.0.RELEASE and 2.5.0-M1,2.2.2: Spring Boot 2.5.0-M1 and 2.7.0-M1,2.3.1: Spring Boot 2.7.0-M1 and 3.0.0-M1,3.0.2: Spring Boot 3.0.0-M1},pulsar: {0.2.0: Spring Boot 3.0.0 and 3.1.0-M1},pulsar-reactive: {0.2.0: Spring Boot 3.0.0 and 3.1.0-M1},camel: {3.5.0: Spring Boot 2.3.0.M1 and 2.4.0-M1,3.10.0: Spring Boot 2.4.0.M1 and 2.5.0-M1,3.13.0: Spring Boot 2.5.0.M1 and 2.6.0-M1,3.17.0: Spring Boot 2.6.0.M1 and 2.7.0-M1,3.20.5: Spring Boot 2.7.0.M1 and 3.0.0-M1,4.0.0-M3: Spring Boot 3.0.0-M1 and 3.1.0-M1},picocli: {4.7.0: Spring Boot 2.5.0.RELEASE and 3.1.0-M1},open-service-broker: {3.2.0: Spring Boot 2.3.0.M1 and 2.4.0-M1,3.3.1: Spring Boot 2.4.0-M1 and 2.5.0-M1,3.4.1: Spring Boot 2.5.0-M1 and 2.6.0-M1,3.5.0: Spring Boot 2.6.0-M1 and 2.7.0-M1}}
}学习推荐版本由SpringCloud来决定SpringBoot的版本。 1.2 cloud组件演变 2、Eureka
包含两个组件Eureka Server和Eureka Client
2.1 Eureka Server 提供服务注册服务
各个微服务节点通过配置启动后会在EurekaServer中进行注册这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息服务节点的信息可以在界面中直观看到。
2.2 EurekaClient 通过注册中心进行访问
是一个Java客户端用于简化Eureka Server的交互客户端同时也具备一个内置的、使用轮询(round-robin)负载具法的顶载均衡器。在应用启动后将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心 跳EurekaServer将会从服务注册表中把这个服务节点移除默认90秒)
2.3 Eureka自我保护 3、Eureka、Zookeeper、Consul三个注册中心的异同点
组件名语言CAP服务健康检查对外暴露接口SpringCloud集成EurekaJavaAP高可用可配支持HTTP已集成ZookeeperGoCP数据一致性支持HTTP/DNS已集成ConsulJavaCP数据一致性支持客户端已集成NacosAP高可用支持客户端已集成
C:Consistency (强一致性A:Availability 可用性)P:Partition tolerance (分布式分区容错性)
最多只能同时较好的满足两个。 CAP理论的核心是: 一个分布式系统不可能同时很好的满足一致性可用性和分区容错性这三个需求。
CAP理论关注粒度是数据而不是整体系统设计的策略。
因此根据CAP原理将NoSQL数据库分成了满足CA原则、满足CP原则和满足AP原则三大类:
CA-单点集群满足—致性可用性的系统通常在可扩展性上不太强大。CP-满足─致性分区容忍必的系统通常性能不是特别高。AP–满足可用性分区容忍性的系统通常可能对—致性要求低一些。 3.1 CP架构
当网络分区出现后为了保证一致性就必须拒绝接请求否则无法保证一致性。
结论违背了可用性A的要求只满足一致性和分区容错即CP。
4、Ribbon 负载均衡服务调用
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
Ribbon是Netflix发布的开源项目主要功能是提供客户端的软件负载均衡算法和服务调用。
Ribbon客户端组件提供一系列完善的配置项如连接超时重试等。
在配置文件中列出Load Balancer(简称LB)后面所有的机器Ribbon会自动的帮助你基于某种规则(如简单轮询随机连接等去连接这些机器。
我们很容易使用Ribbon实现自定义的负载均衡算法。
4.1 SpringCloud Load Balance
LB负载均衡(Load Balance)是什么
将用户的请求平摊的分配到多个服务上从而达到系统的HA高可用)。常见的负载均衡有软件NginxLVS硬件F5等。
集中式LB Load Balance
即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件如F5,也可以是软件,1如nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方;
Ribbon本地负载均衡客户端 VS Nginx服务端负载均衡区别
Nginx是服务器负载均衡客户端所有请求都会交给Nginx然后由Nginx实现转发请求。即负载均衡是由服务端实现的。
Ribbon本地负载均衡在调用微服务接口时候会在注册中心上获取注册信息服务列表之后缓存到JVM本地从而在本地实现RPC远程服务调用技术。
4.2 总结:
Ribbon其实就是一个软负载均衡的客户端组件
他可以和其他所需请求的客户端结合使用和eureka结合只是其中的一个实例。 4.3 Ribbon工作流程
Ribbon在工作时分成两步
第—步先选择EurekaServer ,它优先选择在同一个区域内负载较少的server.第二步再根据用户指定的策略在从server取到的服务注册列表中选择一个地址。其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。
4.4 自定义Ribbon 负载均衡算法
4.4.1 iRule接口 4.4.2 Ribbon自带的负载均衡算法
com.netflix.loadbalancer.RoundRobinRule
轮询
com.netflix.loadbalancer.RandomRule
随机 com.netflix.loadbalancer.RetryRule
先按照RoundRobinRule的策骼获取服务如果获取服务失败则在指定时间内会进行重试获取可用的服务
weightedResponseTimeRule
对RoundRobinRule的扩展响应速度越快的实例选择权重越大越容易被选择
BestAvailableRuleo
会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务然后选择一个并发是最小的服务
vailabilityFilteringRule
先过滤掉故障实例,再选择并发较小的实例
zoneAvoidanceRule
默认规则,复合判断server所在区域的性能和server的可用性选择服务器
官方文档明确给出了警告:
这个自定义配置类不能放在ComponentScan所扫描的当前包下以及子包下 否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享达不到特殊化定制的目的了。
4.4.3 负载均衡算法替代
4.4.3.1、在非启动类包及子包下创建配置类 4.4.3.2、定义
/*** Myrule : Ribbon 自定义负载均衡算法配置类** author zyw* create 2023/6/18*/
Configuration
public class Myrule {Beanpublic IRule getIRule(){//定义为随机return new RandomRule();}
}4.4.3.3、启动类增加RibbonClient注解
自定义需要指定的服务
/*** OderMain80 :** author zyw* create 2023/6/16*/
SpringBootApplication
EnableEurekaClient
RibbonClient(name CLOULD-PAYMENT-SERVICE,configuration Myrule.class)
MapperScan(com.zyw.springcloud.dao)
public class OderMain80 {public static void main(String[] args) {SpringApplication.run(OderMain80.class,args);}
}4.5 Ribbon负载均衡算法
4.5.1 轮询算法原理 4.5.2 轮询算法源码
public class RoundRobinRule extends AbstractLoadBalancerRule {private AtomicInteger nextServerCyclicCounter;public RoundRobinRule() {nextServerCyclicCounter new AtomicInteger(0);
}public Server choose(ILoadBalancer lb, Object key) {if (lb null) {log.warn(no load balancer);return null;}Server server null;int count 0;while (server null count 10) {//获取有正常运行的可达的服务集合ListServer reachableServers lb.getReachableServers();//获取可负载服务集合ListServer allServers lb.getAllServers();//获取有正常运行的可达的服务的数量int upCount reachableServers.size();//获取可负载服务的数量 服务器集群总数量int serverCount allServers.size();if ((upCount 0) || (serverCount 0)) {log.warn(No up servers available from load balancer: lb);return null;}int nextServerIndex incrementAndGetModulo(serverCount);server allServers.get(nextServerIndex);if (server null) {/* Transient. */Thread.yield();continue;}if (server.isAlive() (server.isReadyToServe())) {return (server);}// Next.server null;}if (count 10) {log.warn(No available alive servers after 10 tries from load balancer: lb);}return server;}//自旋锁//rest接口第几次请求数 % 服务器集群总数量 实际调用服务器位置下标每次服务器重启后rest接口计数从1开始。private int incrementAndGetModulo(int modulo) {for (;;) {//获取当前值int current nextServerCyclicCounter.get();//计算下次值int next (current 1) % modulo;//比较并交换if (nextServerCyclicCounter.compareAndSet(current, next))//得到当前下标值return next;}}}4.5.3 手写负载均衡算法
/*** LoadBalancer : 自定义负载均衡算法** author zyw* create 2023/6/20*/
public interface LoadBalancer {//收集Eurek上所有活着的服务总数ServiceInstance instances(ListServiceInstance serviceInstances);}/*** MyLB : 自定义负载均衡算法实现类** author zyw* create 2023/6/20*/
Component
public class MyLB implements LoadBalancer {private AtomicInteger atomicInteger new AtomicInteger(0);public final int getAndIncrement() {int currrnt;int next;do {currrnt this.atomicInteger.get();//Integer.MAX_VALUE 2147483647next currrnt 2147483647 ? 0 : currrnt 1;//自选锁直到得到期望值} while (!this.atomicInteger.compareAndSet(currrnt, next));System.out.println(第 next 次访问);return next;}/*** rest接口第几次请求数 % 服务器集群总数量 实际调用服务器位置下标每次服务器重启后rest接口计数从1开始。* param serviceInstances* return*/Overridepublic ServiceInstance instances(ListServiceInstance serviceInstances) {int index getAndIncrement() % serviceInstances.size();return serviceInstances.get(index);}
}
/*** OrderController : 订单系统控制层** author zyw* create 2023/6/16*/
Slf4j
RestController
RequestMapping(consumer/orderController)
Api(tags{订单系统控制层})
public class OrderController {Resourceprivate RestTemplate restTemplate;Resourceprivate DiscoveryClient discoveryClient;Resourceprivate LoadBalancer loadBalancer;GetMapping(/lb)ApiOperation(value 获取负载服务的端口号, response String.class)public String getPaymentBl(){ListServiceInstance serviceInstances discoveryClient.getInstances(CLOULD-PAYMENT-SERVICE);if (serviceInstances null || serviceInstances.size() 0){return null;}ServiceInstance serviceInstance loadBalancer.instances(serviceInstances);URI uri serviceInstance.getUri();return restTemplate.getForObject(uripaymentController/lb,String.class);}}5、OpenFeign 服务接口调用
5.1 概述
Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。
它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。
Spring Cloud对Feign进行了封装使其支持了Spring MVC标准注解和HttpMessageConverters。
Feign可以与Eureka和Ribbon组合使用以支持负载均衡
5.2 Feign能干什么
Feign旨在使编写Java Http客户端变得更容易。
前面在使用RibbonRestTemplate时利用RestTemplate对http请求的封装处理形成了一套模版化的调用方法。但是在实际开发中由于对服务依赖的调用可能不止一处往往一个接口会被多处调用所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以Feign在此基础上做了进一步封装由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可)即可完成对服务提供方的接口绑定简化了使用Spring cloud Ribbon时自动封装服务调用客户端的开发量。
5.3 Feign集成了Ribbon
利用Ribbon维护了Payment的服务列表信息并且通过轮询实现了客户端的负载均衡。
而与Ribbon不同的是通过feign只需要定义
服务绑定接口且以声明式的方法优雅而简单的实现了服务调用 5.4 应用
5.4.1 引入依赖 !-- openfeign --dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependency5.4.2 开启功能
//开启Feign
EnableFeignClients
SpringBootApplication
public class OderOpenFeignMain80 {public static void main(String[] args) {SpringApplication.run(OderOpenFeignMain80.class, args);}}5.4.3 service中远程调用
Component
FeignClient(value CLOULD-PAYMENT-SERVICE)
public interface PaymentFeginService {GetMapping(/paymentController/getById)public CommonResult getById(RequestParam(id) Long id);GetMapping(/paymentController/feign/timeout)public String paymentFeignTimeout();}5.5 超时控制
默认Feign客户端只等待一秒钟但是服务端处理需要超过1秒钟导致Feign客户端不想等待了直接返回报错。
为了避免这样的情况有时候我们需要设置Feign客户端的超时控制。
在yml中开启OpenFeign客户端超时控制
# 设置feign客户端超时时间OpenFeign默认支持ribbon
ribbon:# 指的是建立连接所用的时间适用于网络状况正常的情况下两端连接所用的时间ReadTimeout: 5000# 指的是建立俩进阶后从服务器读取到可用资源所用的时间ConnectTimeout: 50005.6 日志打印
5.6.1 日志级别
级别内容NONE默认的不显示任何日志BASIC仅记录请求方法、URL、响应状态码及执行时间HEADERS除了BASIC中定义的信息之外还有请求和响应的头信息FULL除了HEADERS 中定义的信息之外还有请求和响应的正文及元数据
5.6.2 配置类
Configuration
public class FeginConfig {BeanLogger.Level feignLoggerLevel(){return Logger.Level.FULL;}
}5.6.3 yml指定日志以什么级别监控哪个接口
logging:level:# feign 日志以什么级别监控哪个接口com.zyw.springcloud.service.PaymentFeginService: debug6.Hystrix 断路器
6.1 问题服务雪崩
多个微服务之间调用的时候假设微服务A调用微服务B和微服务C微服务B和微服务C又调用其它的微服务这就是所谓的**“扇出**。
如果扇出的链路上某个微服务的调用响应时间过长或者不可用对微服务A的调用就会占用越来越多的系统资源进而引起系统崩溃所谓的“雪崩效应”。
对于高流量的应用来说单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。
比失败更糟糕的是这些应用程序还可能导致服务之间的延迟增加备份队列线程和其他系统资源紧张导致整个系统发生更多的级联故障。
这些都表示需要对故障和延迟进行隔离和管理以便单个依赖关系的失败不能取消整个应用程序或系统。
所以通常当你发现一个模块下的某个实例失败后这时候这个模块依然还会接收流量然后这个有问题的模块还调用了其他的模块这样就会发生级联故障或
者叫雪崩。
6.2 概念
Hystrix是一个用于处理分布式系统的延迟和容错的开源库在分布式系统里许多依赖不可避免的会调用失败比如超时、异常等Hystrix能够保证在一个依赖出
问题的情况下不会导致整体服务失败避免级联故障以提高分布式系统的弹性。
断路器”本身是一种开关装置当某个服务单元发生故障之后通过断路器的故障监控类似熔断保险丝)向调用方返回一个符合预期的、可处理的备选响应
FallBack)而不是长时间的等待或者抛出调用方无法处理的异常这样就保证了服务调用方的线程不会被长时间、不必要地占用从而避免了故障在分布
式系统中的蔓延乃至雪崩。
6.3 服务降级
6.3.1 概念
服务不可用了不让客户端等待并立刻返回一个友好提示fallback。
6.3.2 触发服务降级的情况
程序运行异常超时服务熔断触发服务降级线程池/信号量打满
6.3.3 应用
6.3.3.1 依赖 !-- hystrix --dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-hystrix/artifactId/dependency6.3.3.2 解决的问题
超时导致服务器变慢转圈-》超时不再等待-》服务降级
出错宕机或程序运行出错-》出错要有兜底-》服务降级
生产者正常消费者自己出现故障或有自我要求自己的等待时间小于服务提供者响应时间
6.3.3.3 生产者
设置自身调用后超时时间的峰值超过峰值做服务降级fallback
一旦调用服务方法失败并抛出错误信息后会自动调用HystrixCommand标注好的fallbackMethod调用类中的指定方法
系统运行报错也会走fallbackMethod标注的方法。 /*** 超时** param id* return*///HystrixCommand 服务降级规则 响应超过3000毫秒则执行paymentInfo_TimeOutHandler方法HystrixCommand(fallbackMethod paymentInfo_TimeOutHandler, commandProperties {HystrixProperty(name execution.isolation.thread.timeoutInMilliseconds, value 3000)})Overridepublic String paymentInfo_TimeOut(Integer id) {int timeNumber 5;try {TimeUnit.SECONDS.sleep(timeNumber);} catch (InterruptedException e) {e.printStackTrace();}return 线程池 Thread.currentThread().getName() paymentInfo_OK,id: id \t O(∩_∩)O哈哈~ ,耗时 timeNumber 秒钟;}public String paymentInfo_TimeOutHandler(Integer id) {return o(╥﹏╥)o\r\n调用支付接口超时或异常\t \t当前线程池名称 Thread.currentThread().getName();}主启动类添加EnableCircuitBreaker注解
EnableCircuitBreaker
EnableEurekaClient
SpringBootApplication
public class PaymentHystrixMain8001 {public static void main(String[] args) {SpringApplication.run(PaymentHystrixMain8001.class,args);}
}6.3.3.4 消费者
配置文件中开启 feign 对 hystrix 的支持
feign:hystrix:# 开启 feign 对 hystrix 的支持enabled: true主启动类添加EnableHystrix注解
EnableHystrix
EnableFeignClients
SpringBootApplication
public class OrderHystrixMain80 {public static void main(String[] args) {SpringApplication.run(OrderHystrixMain80.class,args);}
}HystrixCommand GetMapping(/hystrix/TimeOut/{id})HystrixCommand(fallbackMethod paymentTimeOutFallbackMethod, commandProperties {HystrixProperty(name execution.isolation.thread.timeoutInMilliseconds, value 2000)})ApiOperation(value 超时接口, response String.class)public String paymentInfo_TimeOut(PathVariable(id)Integer id){String result paymentHystrixService.paymentInfo_TimeOut(id);return result;}public String paymentTimeOutFallbackMethod(Integer id) {return 我是消费者80对方支付系统繁忙请10秒后重试\r\no(╥﹏╥)o;}
需要设置熔断器超时峰值否则会报错
hystrix:command:default:execution:isolation:thread:# 设置熔断器超时峰值timeoutInMilliseconds: 50006.3.3.5 配置全局fallback方法
RestController
Slf4j
DefaultProperties(defaultFallback payment_Global_FallbackMethod)
RequestMapping(consumer)
Api(tags {整合Hystrix订单系统控制层})
public class OrderHystirxController {Resourceprivate PaymentHystrixService paymentHystrixService;GetMapping(/hystrix/OK/{id})HystrixCommandApiOperation(value 正常接口, response String.class)public String paymentInfo_OK(PathVariable(id) Integer id){String result paymentHystrixService.paymentInfo_OK(id);return result;}/*** 全局fallback方法* return*/public String payment_Global_FallbackMethod(){return payment系统繁忙请联系客服处理\n o(╥﹏╥)o;}}如果配置了HystrixCommand中的fallbackMethod属性则走专属配置的没有则走全局的。
6.3.3.6 解耦合
Component
FeignClient(value CLOUD-PROVIDER-HYSTRIX-PAYMENT,fallback PaymentFallbackService.class)
public interface PaymentHystrixService {GetMapping(/payment/hystrix/OK/{id})public String paymentInfo_OK(PathVariable(id) Integer id);GetMapping(/payment/hystrix/TimeOut/{id})public String paymentInfo_TimeOut(PathVariable(id)Integer id);}Component
public class PaymentFallbackService implements PaymentHystrixService {Overridepublic String paymentInfo_OK(Integer id) {return -----PaymentFallbackService fall back-paymentInfo_OK\r\no(╥﹏╥)o;}Overridepublic String paymentInfo_TimeOut(Integer id) {return -----PaymentFallbackService fall back-paymentInfo_TimeOut\r\no(╥﹏╥)o;}}6.4 服务熔断
6.4.1 概念
类似于保险丝达到最大服务访问量后直接拒绝访问拉闸限电然后调用服务降级的方法并返回友好提示。
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时会进行服务的降级进而熔断该节点微服务的调用快速返回错误的响应信息。
当检测到该节点微服务调用响应正常后恢复调用链路。
在Spring Cloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况
当失败的调用到一定阈值缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是HystrixCommand。
6.4.2 注解 //服务熔断HystrixCommand(fallbackMethod paymentCircuitBreaker_fallback, commandProperties {HystrixProperty(name circuitBreaker.enabled, value true),//是否开启断路器HystrixProperty(name circuitBreaker.requestVolumeThreshold, value 10),//请求次数HystrixProperty(name circuitBreaker.sleepWindowInMilliseconds, value 10000),//时间窗口期HystrixProperty(name circuitBreaker.errorThresholdPercentage, value 60)//失败率达到多少后跳闸})Overridepublic String paymentCircuitBreaker(PathVariable(id) Integer id) {涉及到断路器的三个重要参数:快照时间窗、请求总数阀值、错误百分比阀值。
1: 快照时间窗:断路器确定是否打开需要统计一些请求和错误数据而统计的时间范围就是快照时间窗默认为最近的10秒。
2∶请求总数阀值:在快照时间窗内必须满足请求总数阀值才有资格熔断。默认为20意味着在10秒内如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败断路器都不会打开。
3∶错误百分比阀值:当请求总数在快照时间窗内超过了阀值比如发生了30次调用如果在这30次调用中有15次发生了超时异常也就是超过 50%的错误百分比在默认设定50%阀值情况下这时候就会将断路器打开。
6.4.3 应用
Service
public class PaymentServiceImpl implements PaymentService {//服务熔断HystrixCommand(fallbackMethod paymentCircuitBreaker_fallback, commandProperties {HystrixProperty(name circuitBreaker.enabled, value true),//是否开启断路器HystrixProperty(name circuitBreaker.requestVolumeThreshold, value 10),//请求次数HystrixProperty(name circuitBreaker.sleepWindowInMilliseconds, value 10000),//时间窗口期HystrixProperty(name circuitBreaker.errorThresholdPercentage, value 60)//失败率达到多少后跳闸})Overridepublic String paymentCircuitBreaker(PathVariable(id) Integer id) {if (id 0) {throw new RuntimeException(******id不能负数);}String serialNumber IdUtil.simpleUUID();return Thread.currentThread().getName() \t 调用成功流水号: serialNumber;}public String paymentCircuitBreaker_fallback(PathVariable(id) Integer id) {return id不能负数请稍后再试/(ToT)/~~id: id;}}RestController
Slf4j
RequestMapping(/payment)
Api(tags {整合hystrix支付系统控制层})
public class PaymentController {Resourceprivate PaymentService paymentService;//服务熔断GetMapping(/hystrix/circuit/{id})ApiOperation(value 服务熔断接口, response String.class)public String paymentCircuitBreaker(PathVariable(id) Integer id) {String result paymentService.paymentCircuitBreaker(id);log.info(result: result);return result;}}6.4.4 原来的主逻辑要如何恢复呢?
对于这一问题hystrix也为我们实现了自动恢复功能。
当断路器打开对主逻辑进行熔断之后hystrix会启动一个休眠时间窗在这个时间窗内降级逻辑是临时的成为主逻辑当休眠时间窗到期断路器将进入半开状态释放一次请求到原来的主逻辑上如果此次请求正常返回那么断路器将继续闭合,主逻辑恢复如果这次请求依然有问题断路器继续进入打开状态休眠时间窗重新计时。
6.5 服务限流
6.5.1 概念
秒杀、高并发等操作严禁其一窝蜂的过来拥挤大家排队一秒钟N个有序进行。
6.6 服务监控 hystrixDashboard
除了隔离依赖服务的调用以外Hystrix还提供了准实时的调用监控(Hystrix Dashboard)Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息并以统计报表和图形的形式展示给用户包括每秒执行多少请求多少成功多少失败等。
Netfiix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。Spring Cloud也提供了Hystrix Dashboard的整合对监控内容转化成可视化界面。
6.6.1 依赖 !-- hystrix-dashboard --dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-hystrix-dashboard/artifactId/dependency6.6.2 主启动类添加注解EnableHystrixDashboard
SpringBootApplication
EnableHystrixDashboard//开启图形化监控界面
public class HystrixDashboardMain9001 {public static void main(String[] args) {SpringApplication.run(HystrixDashboardMain9001.class,args);}
}6.6.3 访问图形化界面
http://localhost:9001/hystrix6.6.4 调整需要监控的服务主启动类
EnableCircuitBreaker//开启熔断器
EnableEurekaClient
SpringBootApplication
public class PaymentHystrixMain8001 {public static void main(String[] args) {SpringApplication.run(PaymentHystrixMain8001.class, args);}/*** 此配置是为了服务监控而配置与服务容错本身无关springcloud升级后的玩* ServletRegistrationBean因为springboot的默认路径不是/hystrix.stream* 只要在自己的项目里配置上下面的servlet就可以了*/Beanpublic ServletRegistrationBean getServlet() {HystrixMetricsStreamServlet streamServlet new HystrixMetricsStreamServlet();ServletRegistrationBean registrationBean new ServletRegistrationBean(streamServlet);registrationBean.setLoadOnStartup(1);registrationBean.addUrlMappings(/hystrix.stream);registrationBean.setName(HystrixMetricsStreamServlet);return registrationBean;}
}6.6.5 输入监控的url 7、Gateway 网关
Gateway是在Spring生态系统之上构建的API网关服务基于Spring 5Spring Boot 2和Project Reactor等技术。
Gateway旨在提供一种简单而有效的方式来对API进行路由以及提供一些强大的过滤器功能例如:熔断、限流、重试等。
SpringCloud Gateway是Spring Cloud的一个全新项目基于Spring 5.0Spring Boot 2.0和Project Reactor等技术开发的网关它旨在为微服务架构提供—种简单有效的统—的API路由管理方式。
SpringCloud Gateway作为Spring Cloud生态系统中的网关目标是替代Zuul在Spring Cloud 2.0以上版本中没有对新版本的Zuul 2.0以上最新高性能版本进行集成仍然还是使用的Zuul 1.x非Reactor模式的老版本。而为了提升网关的性能SpringCloud Gateway是基于WebFlux框架实现的而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。
Spring Cloud Gateway的目标提供统一的路由方式且基于Filter链的方式提供了网关基本的功能例如:安全监控/指标和限流。
7.1 特性
基于Spring Framework 5, Project Reactpr和Spring Boot 2.0进行构建;动态路由:能够匹配任何请求属性;可以对路由指定Predicate(断言和Filter (过滤器);集成Hystrix的断路器功能;集成Spring Cloud服务发现功能;易于编写的 Predicate (断言和Filter (过滤器);请求限流功能;支持路径重写。
7.2 Gateway与Zuul的区别
在SpringCloud Finchley正式版之前Spring Cloud 推荐的网关是 Netflix提供的Zuul:
1、Zuul 1.x是一个基于阻塞IO的API Gateway2、Zuul 1.x基于Servlet 2.5使用阻塞架构它不支持任何长连接(如WebSocket) Zuul的设计模式和Nginx较像每次I/О操作都是从工作线程中选择一个执行请求线程被阻塞到工作线程完成但是差别是Ngink用C实现Zuul用Java 实现而JVM本身会有第—次加载较慢的情况使得Zuul的性能相对较差。3、Zuul 2.x理念更先进想基于Netty非阻塞和支持长连接但SpringCloud目前还没有整合。Zuul.2 .x的性能较Zuul.1.x有较大提升。在性能方面根据官方提供的基准测试Spring Cloud Gateway的RPS(每秒请求数)是Zuul的1.6倍。4、Spring Cloud Gateway建立在Spring Framework 5、Project Reactor和Spring Boot2之上使用非阻塞API。5、Spring Cloud Gateway还支持WebSocket并且与Spring紧密集成拥有更好的开发体验
7.3 Gateway模型
传统的Web框架比如说: struts2springmvc等都是基于Servlet APl与Servlet容器基础之上运行的。
但是在Servlet3.1之后有了异步非阻塞的支持。而WebFlux是一个典型非阻塞异步的框架它的核心是基于Reactor的相关API实现的。相对于传统的web框架来说它可以运行在诸如Netty, Undertow及支持Servlet3.1的容器上。非阻塞式函数式编程(Spring5必须让你使用java8)
Spring WebFlux是 Spring 5.0引入的新的响应式框架区别于Spring MVC它不需要依赖Servlet APl它是完全异步非阻塞的,并且基于Reactor来实现响应式流规范。
7.4 三大基本概念
7.4.1 Route 路由
路由是构建网关的基本模块它由ID目标URl一系列的断言和过滤器组成如果断言为true则匹配该路由
7.4.2 Predicate 断言
参考的是Java8的java.util.functjon.Predicate
开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数)如果请求与断言相匹配则进行路由
7.4.3 Filter 过滤
指的是Spring框架中GatewayFilter的实例使用过滤器可以在请求被路由前或者之后对请求进行修改。
7.5 应用
7.5.1 依赖 !-- gateway --dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-gateway/artifactId/dependency7.5.2 yml配置文件
server:port: 9527
eureka:instance:# eureka 服务端的实例名称hostname: cloud-gateway-serviceclient:# false 表示自己端就是注册中心我的职责就是维护服务实例而不需要去检索服务fetch-registry: trueservice-url:# 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址defaultZone: http://eureka7001.com:7001/eureka
spring:application:name: cloud-gatewaycloud:gateway:routes:- id : payment_routh # 路由的ID没有固定规则但要求唯一建议配合服务名uri: http://localhost:8001 # 匹配后提供服务的路由地址predicates:- Path/payment/getById/** # 断言路径相匹配的进行路由- id: payment_routh2uri: http://localhost:8001 # 匹配后提供服务的路由地址predicates:- Path/payment/lb/** # 断言路径相匹配的进行路由7.5.3 配置类方式配置网关路由
Configuration
public class GateWayConfig {/*** 配置了一个id为route-name的路由规则* 当访问地址 https://localhost:9527/guoji 时会自动转发到地址https://news.baidu.com/guonei* param routeLocatorBuilder* return*/Bean//路由定位器public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {RouteLocatorBuilder.Builder routes routeLocatorBuilder.routes();routes.route(path_route_zyw,r - r.path(/guonei).uri(https://news.baidu.com/guonei)).build();return routes.build();}
}
7.6 动态路由
默认情况下GateWay会根据注册中心注册的服务列表以注册中心上微服务名称为路径创建动态路由进行转发。
spring:application:name: cloud-gatewaycloud:gateway:discovery:locator:enabled: true # 开启从注册中心动态创建路由的功能利用微服务名进行路由routes:- id : payment_routh # 路由的ID没有固定规则但要求唯一建议配合服务名uri: lb://cloud-payment-service # 匹配后提供微服务的路由地址predicates:- Path/payment/getById/** # 断言路径相匹配的进行路由- id: payment_routh2uri: lb://cloud-payment-service # 匹配后提供微服务的路由地址predicates:- Path/payment/lb/** # 断言路径相匹配的进行路由7.7 Predicate 断言
predicates.After : 配置启用时间predicates.Before: 配置停用时间predicates.Between: 配置可用时间范围Cookie Route Predicate
需要两个参数Cookie name和正则表达式
路由规则会通过获取对应的Cookie name值和正则表达式去匹配如果匹配上就会执行路由。
Header Route Predicate
属性名正则表达式
Method Route Predicate
规定请求方式
Query Route Predicate
规定必传参数
spring:application:name: cloud-gatewaycloud:gateway:discovery:locator:enabled: true # 开启从注册中心动态创建路由的功能利用微服务名进行路由routes:- id : payment_routh # 路由的ID没有固定规则但要求唯一建议配合服务名uri: lb://cloud-payment-service # 匹配后提供微服务的路由地址predicates:- Path/payment/** # 断言路径相匹配的进行路由- After2023-06-26T15:32:05.25808:00[Asia/Shanghai] # 配置启用时间- Before2123-06-26T15:32:05.25808:00[Asia/Shanghai] # 配置停用时间- Between2023-06-26T15:32:05.25808:00[Asia/Shanghai],2123-06-28T15:32:05.25808:00[Asia/Shanghai] # 配置可用时间范围- Cookieusername,zyw- HeaderX-Request-Id,\d # 请求头要有 X-Request-Id属性并且值为整数的正则表达式- MethodGet # 规定请求的方式- QueryidCard,\d # 参数名要有idCard且值必须为整数时间格式生成工具类
import java.time.ZonedDateTime;
public class DateTest {public static void main(String[] args) {ZonedDateTime zbj ZonedDateTime.now();System.out.println(zbj zbj);}
}7.8 Filter 过滤器
生命周期pre 》 post
种类GatewayFilter单一的、GlobalFilter全局的
7.8.1 自定义过滤器
8、Config 服务配置
SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。 分为服务端和客户端
服务端也称为分布式配置中心它是一个独立的微服务应用用来连接配置服务器并为客户端提供获取配置信息加密/解密信息等访问接口
客户端则是通过指定的配置中心来管理应用资源以及与业务相关的配置内容并在启动的时候从配置中心获取和加载配置信息配置服务器默认采用git来存储配置信息这样就有助于对环境配置进行版本管理并且可以通过git客户端工具来方便的管理和访问配置内容
8.1 作用
集中管理配置文件不同环境不同配置动态化的配置更新分环境部署比如dev/test/prod/beta/release运行期间动态调整配置不再需要在萦个服务部署的机器上编写配置文件服务会向配置中心统一拉取配置自己的信息当配置发生变动时服务不需要重启即可感知到配置的变化并应用新的配置将配置信息以REST接口的形式暴露post、curl访问刷新均可
9、SpringCloud Alibaba
服务限流降级:默认支持Servlet、Feign、RestTemplate、Dubbo和RocketMQ限流降级功能的接入可以在运行时通过控制台实时修改限流降级规则还支持查看限流降级Metrics 监控。服务注册与发现:适配 Spring Cloud服务注册与发现标准默认集成了Ribbon的支持。分布式配置管理:支持分布式系统中的外部化配置配置更改时自动刷新。消息驱动能力:基于Spring Cloud Stream为微服务应用构建消息驱动力能力。阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于Cron表达式)任务调度服务。同时提供分布式的任务执行模型如网格任务。网格任务支持海量子任务均匀分配到所有Worker (schedulerx-client)上执行。
组件
Sentinel:把流量作为切入点从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。RocketMQ:一款开源的分布式消息系统基于高可用分布式集群技术提供低延时的、高可靠的消息发布与订阅服务。Dubbo: Apache DubboTM是一款高性能Java RPC框架。Seata:阿里巴巴开源产品一个易于使用的高性能微服务分布式事务解决方案。Alibaba Cloud ACM:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。Alibaba Cloud oSS:阿里云对象存储服务Object Storage Service简称OSS是阿里云提供的海星、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。Alibaba Cloud SchedulerX:阿里中间件团队开发的一款分布式任务调度产品提供秒级、精准、高可靠、高可用的定时(基于Cron表达式)任务调度服务。Alibaba Cloud SMS: 覆盖全球的短信服务友好、高效、智能的互联化通讯能力帮助企业迅速搭建客户触达通道。
10、Nacos 服务注册和配置中心
一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Nacos Eureka Config Bus
10.1 应用
10.1.1 依赖
父工程 !-- spring cloud alibaba 2.1.0.RELEASE --dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-alibaba-dependencies/artifactIdversion2.1.0.RELEASE/versiontypepom/typescopeimport/scope/dependency子工程 !-- SpringCloud alibaba nacos --dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-alibaba-nacos-discovery/artifactId/dependency10.1.2 配置文件
server:port: 9001servlet:context-path: /provider
spring:# 服务名称application:# 服务名称name: nacos-payment-providercloud:nacos:discovery:server-addr: localhost:8848 # 配置Nacos地址
management:endpoints:web:exposure:include: *10.2 Nacos发现实例模型 10.3 注册中心对比 10.4 Nacos 支持AP和CP模式的切换
C 所有节点在同一时间看到的数据是一致的
A所有请求都会收到响应
10.4.1 何时选择何种模式
一般来说 如果不需要存储服务级别的信息且服务实例是通过nacos-cient注册并能够保持心跳上报那么就可以选择AP模式。当前主流的服务如Spring cloud 和Dubo服务都适用于AP模式AP模式为了服务的可能性而减弱了一致性因此AP模式下只支持注册临时实例。
如果需要在服务级别编辑或者存储配置信息那么CP是必须K8S服务和DNS服务则适用于CP模式。 CP模式下则支持注册持久化实例此时则是以Raft协议为集群运行模式该模式下注册实例之前必须先注册服务如果服务不存在则会返回错误。
10.5 Nacos 服务配置
Nacos同springcloud-config一样在项目初始化时要保证先从配置中心进行配置拉取拉取配置之后才能保证项目的正常启动。
springboot中配置文件的加载是存在优先级顺序的bootstrap优先级高于application
10.5.1 SpringCloud原生注解RefreshScope
修饰Controller层可以支持Nacos的动态刷新功能
10.5.2 配置
bootstarp.yml
server:port: 3377
spring:# 服务名称application:# 订单服务name: nacos-config-clientcloud:nacos:discovery:server-addr: localhost:8848 # Nacos 服务注册中心地址config:server-addr: localhost:8848 # Nacos 配置中心地址file-extension: yaml # 指定yaml格式的配置group: TEST_GROUPnamespace: 6536b558-4546-48a3-ba53-eaf9e264006dapplicaton.yml
spring:profiles:active: test # 表示测试环境
# active: dev # 表示开发环境最后公式 s p r i n g . a p p l i c a t i o n . n a m e − {spring.application.name}- spring.application.name−{spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
10.5.3 分类设计思想
namespace用于区分部署环境
Group和DataID逻辑上区分两个目标对象
默认情况NamespacepublicGroupDEFAULT_GROUP,默认Cluster是DEFAULT Nacos默认的命名空间是publicNamespace主要用来实现隔离。 比方说我们现在有三个环境:开发、测试、生产环境我们就可以创建三个Namespace不同的Namespace之间是隔离的。 Group默认是DEFAULT_GROUPGroup可以把不同的微服务划分到同一个分组里面去 Service就是微服务;一个Service可以包含多个Cluster (集群)Nacos默认Cluster是DEFAULTCluster是对指定微服务的一个虚拟划分。
比方说为了容灾将Service微服务分别部署在了杭州机房和广州机房
这时就可以给杭州机房的Service微服务起一个集群名称(HZ)
给广州机房的Service微服务起一个集群名称(GZ)还可以尽量让同一个机房的微服务互相调用以提升性能。
最后是lnstance就是微服务的实例。
10.6 Nacos 集群是持久化配置
默认Nacos使用嵌入式数据库实现数据的存储。所以如果启动多个默认配置下的Nacos节点数据存储是存在一致性问题的。
为了解决这个问题Nacos采用了集中式存储的方式来支持集群化部署目前只支持MySQL的存储。
10.6.1 Nacos支持三种部署模式
单机模式-用于测试和单机试用。集群模式–用于生产环境确保高可用。多集群模式–用于多数据中心场景。
11、Sentinel 熔断与限流
11.1 是什么
分布式系统的流量防卫兵
随着微服务的流行服务和服务之间的稳定性变得越来越重要。
Sentinel以流星为切入点从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
11.2 特征
丰富的应用场景: Sentinel承接了阿里巴巴近10年的双十一大促流量的核心场景例如秒杀即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
完备的实时监控: Sentinel同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据甚至500台以下规模的集群的汇总运行情况。
广泛的开源生态: Sentinel提供开箱即用的与其它开源框架/库的整合模块例如与Spring Cloud、Dubbo、gRPC的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。
完善的SPI扩展点: Sentinel提供简单易用、完善的SPI扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
11.3 特性 11.4 与Hystrix的区别
HystrixSentinel需要手工搭建监控平台DashBoard单独一个组件独立出来没有一套Web界面可以进行更加颗粒化的配置流量监控、速率控制、服务熔断、服务降级支持界面化的细粒度统一配置
11.5 两个部分
核心库(Java客户端不依赖任何框架/库能够运行于所有Java运行时环境同时对 Dubbo /Spring Cloud 等框架也有较好的支持。
控制台(Dashboard)基于Spring Boot 开发打包后可以直接运行不需要额外的Tomcat等应用容器。
启动命令
java -jar sentinel-dashboard-1.7.1.jar11.6 应用
11.6.1 依赖 !-- SpringCloud alibaba sentinel-datasource-nacos 持久化 --dependencygroupIdcom.alibaba.csp/groupIdartifactIdsentinel-datasource-nacos/artifactId/dependency!-- SpringCloud alibaba sentinel --dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-sentinel/artifactId/dependency11.6.2 配置文件
server:port: 8401servlet:context-path: /sentinel
spring:# 服务名称application:# 订单服务name: nacos-sentinel-servicecloud:nacos:discovery:server-addr: localhost:8848 # Nacos 服务注册中心地址namespace: 13e20987-6fcc-41d6-a358-e16b86bce522config:server-addr: localhost:8848 # Nacos 配置中心地址file-extension: yaml # 指定yaml格式的配置group: DEV_GROUPnamespace: 13e20987-6fcc-41d6-a358-e16b86bce522sentinel:transport:# 配置Sentinel dashboard地址dashboard: localhost:8080# 默认8719端口加入被占用会自动从8719开始依次1扫描知道找到未被占用的端口port: 8719
management:endpoints:web:exposure:include: *11.7 流量配置规则 资源名: 唯一名称默认请求路径 针对来源: Sentinel可以针对调用者进行限流填写微服务名默认default(不区分来源) 阈值类型/单机阈值: QPS(每钟的请求数量): 当调用该api的QPS达到阈值的时候进行限流。线程数: 当调用该api的线程数达到阈值的时候进行限流 是否集群: 不需要集群 流控模式: 直接: api达到限流条件时直接限流关联: 当关联的资源达到阈值时就限流自己链路: 只记录指定链路上的流星指定资源从入口资源进来的流量如果达到阈值就进行限流)【api级别的针对来源】 流控效果: 快速失败: 直接失败抛异常Warm up: 根据codeFactor (冷加载因子默认3)的值从阈值/codeFactor经过预热时长才达到设置的QPS阈值排队等待: 匀速排队让请求以匀速的速度通过阈值类型必须设置为QPS否则无效
11.7.1 直接默认
》快速失败
11.7.2 关联 11.7.3 Warm Up 预热
Warm Up ( RuleConstant.CONTROL_BEHAVIOR_MARM_UuP方式即预热/冷启动方式。
当系统长期处于低水位的情况下当流量突然增加时直接把系统拉升到高水位可能瞬间把系统压垮。通过冷启动让通过的流量缓慢增加在一定时间内逐渐增加到阈值上限给冷系统一个预热的时间避免冷系统被压垮。
默认 coldFactor为3即请求QPS从threshold / 3 开始经预热时长逐渐升致设定的QPS阈值。 11.7.4 排队等待
匀速排队 RuleConstant.CONTROL_BEHAVIOR_RATE_LINITER 方式会严格控制请求通过的间隔时间也即是让请求以均匀的速度通过对应的是漏桶算法。 用于处理间隔性突发的流量例如消息队列。
在某一秒有大量的请求到来而接下来的几秒则处于空闲状态我们希望系统能够在接下来的空闲期间逐渐处理这些请求而不是在第一秒直接拒绝多余的请求。 11.8 熔断降级
11.8.1 概述
除了流量控制以外对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。由于调用关系的复杂性如果调用链路中的某个资源不稳定最终会导致请求发生堆积。Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时例如调用超时或异常比例升高)对这个资源的调用进行限制让请求快速失败避免影响到其它的资源而导致级联错误。当资源被降级后在接下来的降级时间窗口之内对该资源的调用都自动熔断默认行为是抛出DegradeException )。
Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时例如调用超时或异常比例升高)对这个资源的调用进行限制让请求快速失败避免影响到其它的资源而导致级联错谒。
当资源被降级后在接下来的降级时间窗口之内对该资源的调用都自动熔断默认行为是抛出DegradeException) 。
RT(平均响应时间秒级)
平均响应时间超出阈值且在时间窗口内通过的请求5两个条件同时满足后触发降级窗口期过后关闭断路器RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rtXXXX才能生效)
平均响应时间DEGRADE GRADE_RT ):当1s内持续进入5个请求对应时刻的平均响应时间(秒级均超过阈值 count以ms为单位)那么在接下的时间窗口( DegradeRule中的timewindow以s 为单位之内对这个方法的调用都会自动地熔断抛出DegradeException )。注意Sentinel默认统计的RT上限是4900ms超出此阈值的都会算作4900ms若需要变更此上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rtxxx来配置。 每秒钟进来10个线程我们Sentinel上配置的是希望200毫秒处理完本次任务如果没有处理完在未来1秒钟的时间窗口内断路器打开微服务不可用。
异常比列(秒级)
QPS 5且异常比例秒级统计超过阈值时触发降级时间窗口结束后关闭降级
异常比例( DEGRADE_GRADE_EXCEPTION_RATIO ):当资源的每秒请求量 5并且每秒异常总数占通过量的比值超过阈值 DegradeRule中的 count 之后资源进入降级状态即在接下的时间窗口( DegradeRule中的 timewindow以s为单位)之内对这个方法的调用都会自动地返回。异常比率的阈值范围是[0.01.0]代表0%- 100%。 异常数(分钟级)
异常数分钟统计超过阈值时触发降级;时间窗口结束后关闭降级
异常数 DE6RADE_GRADE_EXCEPTION_COUNT ):当资源近1分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的若 timewindow小于60s则结束熔断状态后仍可能再进入熔断状态。
时间窗口一定要大于等于60S。
11.8.2 Sentinel断路器没有半开状态
半开状态系统自动会去检测是否请求有异常没有异常就关闭断路器恢复使用有异常则继续打开断路器不可用。Hystrix
注意:异常降级仅针对业务异常对Sentinel限流降级本身的异常BlockException不生效。为了统计异常比例或异常数需要通过Tracer.trace(ex记录业务异常。
11.9 热点规则
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的Top K数据并对其访问进行限制。
比如:
商品ID为参数统计—段时间内最常购买的商品ID并进行限制用户ID为参数针对一段时间内频繁访问的用户ID进行限制
热点参数限流会统计传入参数中的热点参数并根据配置的限流阈值与模式对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制仅对包含热点参数的资源调用生效。
Sentinel利用LRU策略统计最近最常访问的热点参数结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
源码
Entry entry null;
try {entry SphU.entry(resourceNameEntryType.IN1paramAparamB)// Your logic here.
}catch (BlockException ex) {//Handle request rejection.
}finally{if (entry ! null) {entry.exit(1,paramAparamB);}
}测试案例 GetMapping(/testHotKey)SentinelResource(value testHotKey,blockHandler deal_testHotKey)public String testHotKey(RequestParam(value p1,required false)String p1,RequestParam(value p2,required false)String p2){return ----testHotKey;}//兜底方法public String deal_testHotKey(String p1, String p2, BlockException exception){//Sentinel系统默认提示Blocked by Sentinel (flow limiting)return deal_testHotKey,o(╥﹏╥)o;} SentinelResource(value “testHotKey”,blockHandler “deal_testHotKey”)
第一个参数只要QPS超过每秒1次马上降级处理用自定义的deal_testHotKey兜底方法
11.9.1 参数例外项 11.9.2 运行时异常
SentinelResource
处理的是Sentinel控制台配置的违规情况有blockHandler 方法配置的兜底处理
主管配置出错运行出错该走异常走异常
11.10 系统规则
Sentinel系统自适应限流从整体维度对应更入口流量进行控制结合应用的Load、CPU使用率、总体平均RT、入口QPS和并发线程数等几个维度的监控指标通过自适应的流控策略让系统的入口流量和系统的负载达到一个平衡让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统规则支持以下的模式:
Load自适应(仅对Linux/Unix-like机器生效)系统的load1作为启发指标进行自适应系统保护。当系统load1超过设定的启发值且系统当前的并发线程数超过估算的系统容量时才会触发系统保护BBR阶段)。系统容量由系统的maxQps * minRt估算得出。设定参考值一般是cPUcores * 2.5。CPU usage (1.5.0版本)当系统CPU使用率超过阈值即触发系统保护取值范围0.0-1.0)比较灵敏。平均RT当单台机器上所有入口流量的平均RT达到阈值即触发系统保护单位是毫秒。·并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。入口QPS当单台机器上所有入口流量的QPS达到阈值即触发系统保护。
11.11 SentinelResource
自定义异常兜底处理类
public class CustomerBlockFallback {public static CommonResult handlerFallback(PathVariable(id) Long id,Throwable e){return CommonResult.BlockHandler(兜底异常--Fallback,exception内容:e.getMessage());}
}自定义Sentinel控制台配置违规处理类
public class CustomerBlockHandler {public static CommonResult handlerblock(PathVariable(id) Long id, BlockException exception) {return CommonResult.BlockHandler(exception.getMessage());}
}业务层
Service
public class PaymentServiceImpl {Resourceprivate PaymentService paymentService;SentinelResource(value getPaymentById,fallbackClass CustomerBlockFallback.class,fallback handlerFallback, //fallback只负责业务异常blockHandlerClass CustomerBlockHandler.class,blockHandler handlerblock, //只负责Sentinel控制台配置违规exceptionsToIgnore {IllegalArgumentException.class} //排除该异常的兜底方法)public CommonResultPayment getPaymentById(Long id) {if (id0){throw new IllegalArgumentException(IllegalArgumentException,非法参数异常);}CommonResultPayment paymentById paymentService.getPaymentById(id);if (paymentById.getData()null){throw new NullPointerException(NullPointerException,该ID没有对应记录空指针异常);}return paymentById;}
}11.12 熔断框架比较
SentinelHystrixresilience4j隔离策略信号量隔离并发线程数限流线程池隔离/信号量隔离信号量隔离熔断降级策略基于响应时间、异常比例、异常数基于异常比例基于异常比例、响应时间实时统计实现滑动窗口LeapArray滑动窗口基于RxJavaRing Bit Buffer动态规则配置支持多种数据源支持多种数据源有限支持扩展性多个扩展点插件的形式接口的形式基于注解的支持支持支持支持限流基于QPS支持基于调用关系的限流有限的支持Rate Limiter
11.13 Sentinel的规则持久化
解决方案保存进Nacos官方要求、Mysql、Redis
11.13.1 依赖 !-- SpringCloud alibaba sentinel-datasource-nacos 持久化 --dependencygroupIdcom.alibaba.csp/groupIdartifactIdsentinel-datasource-nacos/artifactId/dependency11.13.2 配置文件
spring:# 服务名称application:# 订单服务name: nacos-payment-consumercloud:nacos:discovery:server-addr: localhost:8848 # Nacos 服务注册中心地址namespace: 13e20987-6fcc-41d6-a358-e16b86bce522config:server-addr: localhost:8848 # Nacos 配置中心地址file-extension: yaml # 指定yaml格式的配置group: DEV_GROUPnamespace: 13e20987-6fcc-41d6-a358-e16b86bce522sentinel:transport:# 配置Sentinel dashboard地址dashboard: localhost:8080# 默认8719端口加入被占用会自动从8719开始依次1扫描知道找到未被占用的端口port: 8719datasource:dsl:nacos:server-addr: localhost:8848namespace: 13e20987-6fcc-41d6-a358-e16b86bce522dataId: nacos-payment-consumergroupId: DEV_GROUPdata-type: jsonrule-type: flowresource: 资源名称;
limitApp: 来源应用;
grade: 阈值类型0表示线程数1表示QPS;
count: 单机阈值;
strategy: 流控模式0表示直接1表示关联2表示链路;
controlBehavior: 流控效果0表示快速失败1表示Warm Up2表示排队等待;
clusterMode: 是否集群。
12、 Seata 处理分布式事务
单体应用被拆分成微服务应用原来的三个模块被拆分成三个独立的应用分别使用三个独立的数据源业务操作需要调用三个服务来完成。
此时每个服务内部的数据一致性由本地事务来保证但是全局的数据一致性问题没法保证。
一次业务操作需要跨多个数据源或者需要跨多个系统进行远程调用就会产生分布式事务问题。
案例
用户购买商品的业务逻辑
仓储服务对给定的商品扣除仓储数量。订单服务根据采购需求创建订单。账户服务从用户账户中扣除余额。 12.1 Seata 简介2
概念Seata是一款开源的分布式事务解决方案致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
分布式事务的处理过程1 ID 3 组件模型
1 ID全剧唯一的事务ID
术语3组件
Tc-事务协调者维护全局和分支事务的状态驱动全局事务提交或回滚。TM-事务管理器定义全局事务的范围︰开始全局事务、提交或回滚全局事务。RM-资源管理器管理分支事务处理的资源与TC交谈以注册分支事务和报告分支事务的状态并驱动分支事务提交或回滚。 12.2 Seata的安装
12.2.1 修改配置文件
# Copyright 1999-2019 Seata.io Group.
#
# Licensed under the Apache License, Version 2.0 (the License);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an AS IS BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.server:port: 7091spring:application:name: seata-serverlogging:config: classpath:logback-spring.xmlfile:path: ${user.home}/logs/seataextend:logstash-appender:destination: 127.0.0.1:4560kafka-appender:bootstrap-servers: 127.0.0.1:9092topic: logback_to_logstashconsole:user:username: seatapassword: seataseata:config:# support: nacos, consul, apollo, zk, etcd3type: nacosnacos:server-addr: 127.0.0.1:8848namespace: c7e4e5e4-8693-40e1-b75e-1ce1b46bf976group: SEATA_GROUP##if use MSE Nacos with auth, mutex with username/password attribute#access-key: #secret-key: data-id: seataServer.yamlusername: nacospassword: nacosregistry:# support: nacos, eureka, redis, zk, consul, etcd3, sofatype: nacos# preferred-networks: 30.240.*nacos:application: seata-serverserver-addr: 127.0.0.1:8848group: SEATA_GROUPnamespace: c7e4e5e4-8693-40e1-b75e-1ce1b46bf976cluster: defaultusername: nacospassword: nacos##if use MSE Nacos with auth, mutex with username/password attribute#access-key: #secret-key: store:# support: file 、 db 、 redismode: dbdb:datasource: druiddb-type: mysqldriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/seata?useUnicodetruerewriteBatchedStatementstrueserverTimezoneGMTuser: rootpassword: 123456min-conn: 5max-conn: 100global-table: global_tablebranch-table: branch_tablelock-table: lock_tabledistributed-lock-table: distributed_lockquery-limit: 100max-wait: 5000
# server:
# service-port: 8091 #If not configured, the default is ${server.port} 1000security:secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017tokenValidityInMilliseconds: 1800000ignore:urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login12.2.2 在nacos上创建配置文件 seataServer.yaml
metrics:enabled: falseexporterList: prometheusexporterPrometheusPort: 9898registryType: compact
server:maxCommitRetryTimeout: -1maxRollbackRetryTimeout: -1recovery:asynCommittingRetryPeriod: 3000committingRetryPeriod: 3000rollbackingRetryPeriod: 3000timeoutRetryPeriod: 3000rollbackRetryTimeoutUnlockEnable: falseundo:logDeletePeriod: 86400000logSaveDays: 7
store:db:branchTable: branch_tabledatasource: druiddbType: mysqldriverClassName: com.mysql.cj.jdbc.DriverglobalTable: global_tablelockTable: lock_tablemaxConn: 30maxWait: 5000minConn: 5password: rootqueryLimit: 100url: jdbc:mysql://127.0.0.1:3306/seata?useUnicodetruerewriteBatchedStatementstrueserverTimezoneGMTuser: rootmode: db
transport:compressor: noneserialization: seata
12.2.3 安装路径seata\seata-server-1.6.0\seata\script\config-center下有一个config.txt文件修改后复制到seata路径下
#For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html
#Transport configuration, for client and server
transport.typeTCP
transport.serverNIO
transport.heartbeattrue
transport.enableTmClientBatchSendRequestfalse
transport.enableRmClientBatchSendRequesttrue
transport.enableTcServerBatchSendResponsefalse
transport.rpcRmRequestTimeout30000
transport.rpcTmRequestTimeout30000
transport.rpcTcRequestTimeout30000
transport.threadFactory.bossThreadPrefixNettyBoss
transport.threadFactory.workerThreadPrefixNettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefixNettyServerBizHandler
transport.threadFactory.shareBossWorkerfalse
transport.threadFactory.clientSelectorThreadPrefixNettyClientSelector
transport.threadFactory.clientSelectorThreadSize1
transport.threadFactory.clientWorkerThreadPrefixNettyClientWorkerThread
transport.threadFactory.bossThreadSize1
transport.threadFactory.workerThreadSizedefault
transport.shutdown.wait3
transport.serializationseata
transport.compressornone#Transaction routing rules configuration, only for the client
service.vgroupMapping.my_test_tx_groupdefault
#If you use a registry, you can ignore it
service.default.grouplist127.0.0.1:8091
service.enableDegradefalse
service.disableGlobalTransactionfalse#Transaction rule configuration, only for the client
client.rm.asyncCommitBufferLimit10000
client.rm.lock.retryInterval10
client.rm.lock.retryTimes30
client.rm.lock.retryPolicyBranchRollbackOnConflicttrue
client.rm.reportRetryCount5
client.rm.tableMetaCheckEnabletrue
client.rm.tableMetaCheckerInterval60000
client.rm.sqlParserTypedruid
client.rm.reportSuccessEnablefalse
client.rm.sagaBranchRegisterEnablefalse
client.rm.sagaJsonParserfastjson
client.rm.tccActionInterceptorOrder-2147482648
client.tm.commitRetryCount5
client.tm.rollbackRetryCount5
client.tm.defaultGlobalTransactionTimeout60000
client.tm.degradeCheckfalse
client.tm.degradeCheckAllowTimes10
client.tm.degradeCheckPeriod2000
client.tm.interceptorOrder-2147482648
client.undo.dataValidationtrue
client.undo.logSerializationjackson
client.undo.onlyCareUpdateColumnstrue
server.undo.logSaveDays7
server.undo.logDeletePeriod86400000
client.undo.logTableundo_log
client.undo.compress.enabletrue
client.undo.compress.typezip
client.undo.compress.threshold64k
#For TCC transaction mode
tcc.fence.logTableNametcc_fence_log
tcc.fence.cleanPeriod1h#Log rule configuration, for client and server
log.exceptionRate100#Transaction storage configuration, only for the server. The file, db, and redis configuration values are optional.
store.modedb
store.lock.modedb
store.session.modedb
#Used for password encryption
store.publicKey#If store.mode,store.lock.mode,store.session.mode are not equal to file, you can remove the configuration block.
store.file.dirfile_store/data
store.file.maxBranchSessionSize16384
store.file.maxGlobalSessionSize512
store.file.fileWriteBufferCacheSize16384
store.file.flushDiskModeasync
store.file.sessionReloadReadSize100#These configurations are required if the store mode is db. If store.mode,store.lock.mode,store.session.mode are not equal to db, you can remove the configuration block.
store.db.datasourcedruid
store.db.dbTypemysql
store.db.driverClassNamecom.mysql.cj.jdbc.Driver
store.db.urljdbc:mysql://127.0.0.1:3306/seata?useUnicodetruerewriteBatchedStatementstrue
store.db.userroot
store.db.passwordroot
store.db.minConn5
store.db.maxConn30
store.db.globalTableglobal_table
store.db.branchTablebranch_table
store.db.distributedLockTabledistributed_lock
store.db.queryLimit100
store.db.lockTablelock_table
store.db.maxWait5000#These configurations are required if the store mode is redis. If store.mode,store.lock.mode,store.session.mode are not equal to redis, you can remove the configuration block.
store.redis.modesingle
store.redis.single.host127.0.0.1
store.redis.single.port6379
store.redis.sentinel.masterName
store.redis.sentinel.sentinelHosts
store.redis.maxConn10
store.redis.minConn1
store.redis.maxTotal100
store.redis.database0
store.redis.password123456
store.redis.queryLimit100#Transaction rule configuration, only for the server
server.recovery.committingRetryPeriod1000
server.recovery.asynCommittingRetryPeriod1000
server.recovery.rollbackingRetryPeriod1000
server.recovery.timeoutRetryPeriod1000
server.maxCommitRetryTimeout-1
server.maxRollbackRetryTimeout-1
server.rollbackRetryTimeoutUnlockEnablefalse
server.distributedLockExpireTime10000
server.xaerNotaRetryTimeout60000
server.session.branchAsyncQueueSize5000
server.session.enableBranchAsyncRemovefalse
server.enableParallelRequestHandlefalse#Metrics configuration, only for the server
metrics.enabledfalse
metrics.registryTypecompact
metrics.exporterListprometheus
metrics.exporterPrometheusPort9898
12.2.4 通过nacos-config.sh将config.txt文件的内容上传到nacos上 12.2.5 通过seata-server.bat启动
12.3 业务说明
这里我们会创建三个服务一个订单服务一个库存服务一个账户服务。当用户下单时会在订单服务中创建一个订单然后通过远程调用库存服务来扣减下单商品的库存再通过远程调用账户服务来扣减用户账户里面的余额最后在订单服务中修改订单状态为已完成。该操作跨越三个数据库有两次远程调用很明显会有分布式事务问题。
12.4 应用
12.4.1 依赖 !-- seata --dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-seata/artifactIdexclusionsexclusionartifactIdseata-all/artifactIdgroupIdio.seata/groupId/exclusion/exclusions/dependency!-- seata 版本需要与实际使用的吻合 --dependencygroupIdio.seata/groupIdartifactIdseata-all/artifactIdversion1.6.0/version/dependency12.4.2 配置文件
spring:# 服务名称application:# 订单服务name: seata-order-servicecloud:nacos:discovery:server-addr: localhost:8848 # Nacos 服务注册中心地址namespace: c7e4e5e4-8693-40e1-b75e-1ce1b46bf976config:server-addr: localhost:8848 # Nacos 配置中心地址file-extension: yaml # 指定yaml格式的配置group: SEATA_GROUPnamespace: c7e4e5e4-8693-40e1-b75e-1ce1b46bf976sentinel:transport:# 配置Sentinel dashboard地址dashboard: localhost:8080# 默认8719端口加入被占用会自动从8719开始依次1扫描知道找到未被占用的端口port: 8719alibaba:seata:# 自定义事务组名称需要与seata-server中的对应tx-service-group: default_tx_group# seata 配置 代替file.conf和registry.conf配置
sfs:nacos:server-addr: 127.0.0.1:8848namespace: c7e4e5e4-8693-40e1-b75e-1ce1b46bf976group: SEATA_GROUPusername: nacospassword: nacos
seata:enabled: trueapplication-id : ${spring.application.name}tx-service-group: default_tx_groupuse-jdk-proxy: trueenable-auto-data-source-proxy: trueregistry:type: nacosnacos:application: seata-serverserver-addr: ${sfs.nacos.server-addr}namespace: ${sfs.nacos.namespace}group: ${sfs.nacos.group}username: ${sfs.nacos.username}password: ${sfs.nacos.username}config:type: nacosnacos:server-addr: ${sfs.nacos.server-addr}namespace: ${sfs.nacos.namespace}group: ${sfs.nacos.group}username: ${sfs.nacos.username}password: ${sfs.nacos.username}service:vgroupMapping:default_tx_group: default12.4.3 订单服务主业务TOrderServiceImpl
Service
Slf4j
public class TOrderServiceImpl implements TOrderService {Resourceprivate TOrderDao tOrderDao;Resourceprivate StorageService storageService;Resourceprivate AccountService accountService;/*** 创建订单-调用库存服务扣减库存-调用账户服务扣减账户余额-修改订单状态* param tOrder* return*/Overridepublic boolean create(TOrder tOrder) {//1.创建订单log.info(-----开始创建订单);tOrder.setStatus(0);tOrderDao.insert(tOrder);//2.扣减库存log.info(-----订单微服务开始调用库存做扣减Count);storageService.decrease(tOrder.getProductId(),tOrder.getCount());log.info(-----订单微服务开始调用库存做扣减end);//3.扣减账户log.info(-----订单微服务开始调用账户做扣减Money);accountService.decrease(tOrder.getUserId(),tOrder.getMoney());log.info(-----订单微服务开始调用账户做扣减end);//4.修改订单状态01log.info(-----修改订单状态开始);boolean flag this.updateStatus(tOrder.getUserId(), 1);log.info(-----修改订单状态结束);log.info(-----下订单结束了O(∩_∩)O哈哈~);return flag;}Overridepublic boolean updateStatus(Long userId,Integer status) {QueryWrapperTOrder wrapper new QueryWrapper();wrapper.eq(user_id, userId);TOrder tOrder new TOrder();tOrder.setStatus(status);return tOrderDao.update(tOrder, wrapper)0;}
}12.4.4 库存服务 TStorageServiceImpl
Service
Slf4j
public class TStorageServiceImpl implements TStorageService {Resourceprivate TStorageDao dao;/*** 扣减库存** param productId* param count* return*/Overridepublic boolean decrease(Long productId, Integer count) {log.info(------seata-storage-service中扣减库存开始);QueryWrapperTStorage wrapper new QueryWrapper();wrapper.eq(product_id, productId);TStorage tStorage dao.selectOne(wrapper);TStorage newTStorage new TStorage();newTStorage.setUsed(tStorage.getUsed() count);newTStorage.setResidue(tStorage.getResidue() - count);return dao.update(newTStorage, wrapper) 0;}
}12.4.5 账户服务 TStorageServiceImpl
Service
Slf4j
public class TAccountServiceImpl implements TAccountService {Resourceprivate TAccountDao dao;Overridepublic Boolean decrease(Long userId, BigDecimal money) {log.info(------开始扣减账户);//模拟超时异常全局事务回滚try {TimeUnit.SECONDS.sleep(20);}catch (InterruptedException e){e.printStackTrace();}QueryWrapperTAccount param new QueryWrapper();param.eq(user_id,userId);TAccount tAccount dao.selectOne(param);QueryWrapperTAccount wapper new QueryWrapper();wapper.eq(user_id,userId);TAccount newTAccount new TAccount();newTAccount.setUsed(tAccount.getUsed().add(money));newTAccount.setResidue(new BigDecimal(Double.toString(tAccount.getResidue().subtract(money).doubleValue())));log.info(------结束扣减账户);return dao.update(newTAccount,wapper)0;}
}12.4.5 故障情况
当库存和账户扣减后订单状态并没有改变二期由于Feign的重试机制账户余额还有可能重复扣减
12.4.6 使用Seata对数据源进行代理
MyBatis版
Configuration
public class DataSourceMyBatisConfig {Value(${mybatis-Plus.mapper-locations})private String mapperLocations;BeanPrimary//让MyBatis-Plus优先使用我们配置的数据源ConfigurationProperties(prefix spring.datasource)public DataSource druidDatasource() {return new DruidDataSource();}Beanpublic DataSourceProxy dataSourceProxy(DataSource dataSource) {return new DataSourceProxy(dataSource);}Beanpublic SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSourceProxy);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());return sqlSessionFactoryBean.getObject();}}MyBatis-Plus版
/*** DataSourceProxyConfig :* 使用Seata对数据源进行代理** author zyw* create 2023/7/10*/
Configuration
MapperScan(com.zyw.springcloud.dao)
public class DataSourceMyBatisPlusConfig {Value(${mybatis-Plus.mapper-locations})private String mapperLocations;BeanConfigurationProperties(prefix spring.datasource)public DataSource druidDataSource(){return new DruidDataSource();}PrimaryBean(dataSource)public DataSourceProxy dataSource(DataSource druidDataSource){return new DataSourceProxy(druidDataSource);}/*** 配置mybatis-plus的分页* return*/Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor interceptor new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//指定数据库return interceptor;}Beanpublic SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSourceProxy)throws Exception{MybatisSqlSessionFactoryBean sqlSessionFactoryBean new MybatisSqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSourceProxy);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));// 配置spring的本地事务sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());// 配置mybatis-plus的log打印MybatisConfiguration cfg new MybatisConfiguration();cfg.setJdbcTypeForNull(JdbcType.NULL);cfg.setMapUnderscoreToCamelCase(true);cfg.setCacheEnabled(false);cfg.setLogImpl(StdOutImpl.class);sqlSessionFactoryBean.setConfiguration(cfg);return sqlSessionFactoryBean.getObject();}
}
12.5 seata原理
12.5.1 业务执行流程
TM开启分布式事务TM向TC注册全局事务记录);按业务场景编排数据库、服务等事务内资源RM向TC汇报资源准备状态;TM结束分布式事务事务一阶段结束(TM通知TC提交/回滚分布式事务);TC汇总事务信息决定分布式事务是提交还是回滚;TC通知所有RM提交/回滚资源事务二阶段结束。
12.5.2 AT模式
前提 给予支持本地ACID事务的关系型数据库。 Java应用通过JDBC访问数据库。
整体机制 一阶段业务数据和回归日志记录在同一个本地事务中提交释放本地锁和连接资源 二阶段提交异步化非常快速的完成回滚通过一阶段的回滚日志进行反向补偿
一阶段加载
在一阶段Seata会拦截“业务SQL” ,
1解析SQL语义找到“业务SQL”要更新的业务数据在业务数据被更新前将其保存成“before image”,前置镜像
2执行“业务SQL”更新业务数据在业务数据更新之后
3其保存成“after image”最后生成行锁。
以上操作全部在一个数据库事务内完成这样保证了一阶段操作的原子性 二阶段提交
因为业务SQL在一阶段已经提交至数据库所有Seata框架只需将一阶段保存的快照数据和行锁删除完成数据清理即可。 二阶段回滚
二阶段如果是回滚的话Seata就需要回滚一阶段已经执行的“业务SQL”还原业务数据。 回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写对比“数据库当前业务数据”和“after image”,如果两份数据完全一致就说明没有脏写可以还原业务数据如果不一致就说明有脏写出现脏写就需要转人工处理。