文章目录
微服务架构
- 前言
本次学习的各种服务都是基于虚拟机中的docker进行下载配置的,个人认为在使用docker过程中一定要注意的有以下几点:
- 进行配置文件的修改时,尽量将容器中配置文件直接复制到宿主机中,进行修改,然后将原来的容器删除,重新生成一个挂载在宿主机内的本地配置文件的容器
- 一定要注意每一天开始初始化linux时要检查防火墙的状态并及时调整开放端口号或关闭防火墙
- 提示
对于关键配置文件的修改都是先备份在修改
- 概述
每个服务运行在独立的进程,服务与服务之间采用轻量级的通信机制进行交互(通常是基于HTTP协议的RESTful API),都能被独立的部署到生产环境中,对具体的生产环境及要求采用不同的语言或开发工具进行构建
- 相关服务使用的组件变更
SpringCloud
二、上篇SpringCloud本Cloud
1.SpringCloud的命名规则及版本关系
- 命名规则:
Spring Cloud采用了英国伦敦地铁站的名称来命名,并由地铁站名称字母A-Z依次类推的形式来发布迭代版本
- 版本关系:
Springcloud是一个由许多子项目组成的综合项目,各子项目有不同的发布节奏。为了管理SpringCloud与各子项目的版本依赖关系,发布了一个清单,其中包括了某个SpringCloud版本对应的子项目版本。为了避免SpringCloud版本号与子项目版本号混淆,SpringCloud版本采用了名称而非版本号的命名,这些版本的名字采用了伦敦地铁站的名字,根据字母表的顺序来对应版本时间顺序。
- 举例:
例如Angel是第一个版本, Brixton是第立个版本。当SpringCloud的发布内容积累到临界点或者一个重大BUG被解决后,会发布一个"service releases"版本简称SRX版本,比如Greenwich.SR2就是SpringCloud发布的Greenwich版本的第2个SRX版本。
1.1 springboot与springcloud的版本依赖
1.2 本次博文使用的环境及版本
2.相关配置环境
2.1 dependencyManagement
- 概述
Maven 使用dependencyManagement 元素来提供了一种管理依赖版本号的方式。
通常会在一个组织或者项目的最顶层的父POM 中看到dependencyManagement 元素。
使用pom.xml 中的dependencyManagement 元素能让所有在子项目中引用一个依赖而不用显式的列出版本号。Maven 会沿着父子层次向上走,直到找到一个拥有dependencyManagement 元素的项目,然后它就会使用这个dependencyManagement 元素中指定的版本号。
2.1.1 好处
如果有多个子项目都引用同一样依赖,则可以避免在每个使用的子项目里都声明一个版本号,这样当想升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要一个一个子项目的修改 ;另外如果某个子项目需要另外的一个版本,只需要声明version就可。
2.1.2 注意
- dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。
-
如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;
-
如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
2.2 @Autowired和@Resource
两者都可以进行依赖注入的注解,但是Autowired是spring的,Resource是Java自带的
三、服务注册中心
- 概述
就是负责统筹规划,让复杂服务之间的相互调用简化且统一管理
1.Eureka(已停更)
1.1 相关概念及概述
- 服务治理
在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
- 服务注册
Eureka采用了CS的设计架构,Eureka Server作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户嫦连接到 Eureka Sever并维持心跳连接。
- 结构图
- 以前我们这么调用
1.2 两大组件
- Eureka Server:提供服务注册功能
各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
- EurekaClient:通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。
1.3 自我保护机制
如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳, EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
- 注意
但如果但时间内丢失了大量的实例心跳(该现象可能出现在如果网络不通,但是EurekaClient出现宕机,此时如果换做别的注册中心如果一定时间内没有收到心跳会将剔除该服务,这样就出现了严重失误,因为客户端还能正常发送心跳,只是网络延迟问题,而保护机制是为了解决此问题而产生的),Eureka 将暂时开启自我保护机制不会删除这个服务结点,并且会保存这个结点的实例信息,做到不盲目注销服务结点
- 如何关闭自我保护机制
将该参数 设置为false
并且设置缩短等待时间
2.zookeeper
这个用idea连接虚拟机docker里的zookeeper容器一直超时失败,暂且略过
3.Consul
4.Eureka、zookeeper、Consul三个注册中心异同
- C:Consistency(强一致性)
- A:Availability(可用性)
- P:Partition tolerance(分区容错性)
CAP最多只能同时较好的满足两个。
CAP理论的核心是:
一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,
因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:
- CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
- CP - 满足一致性,分区容忍必的系统,通常性能不是特别高。
- AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。
四、服务调用
1.Ribbon
1.1 作用
最大的作用就是负载均衡(LB):Ribbon其实就是一个软负载均衡的客户端组件,它可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。总结就是负载均衡+RestTemplate调用
1.2 负载均衡(LB)
- 集中式LB
就是Ribbon在客户端进行的分类均衡,通过注册中心获取服务列表并缓存到本地
- 进程内LB
是nginx在服务器内进行的分类均衡
1.3 架构图
1.4 Ribbon中负载均衡访问策略
2.OpenFeign
2.1 概述
Feign是一个声明式的Web客户端,让编写Web服务端变得非常容易,只需要创建一个接口并在接口上添加注解即可
2.2 作用
在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。
五、服务降级
1.Hystrix断路器
1.1 概述
- Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
- 断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
1.2 作用
- 服务降级
- 服务熔断
- 接近实时的监控
1.3 服务降级
- 概述
服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示(好看的错误页面),fallback(一般放在客户端),大白话说就是再怎么异常错误你都得有一个兜底的方案(系统默认和自定义)
- 引发原因
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池/信号量打满也会导致服务降级
1.4 服务熔断
- 概述
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
- 雪崩效应
当很多服务相互引用时,引用链是很长的一条,当其中某一个服务出现错误异常一直卡在那里,CPU占用率飙升,知道整个应用卡死崩溃
- 应用场景
服务的降级
——>进而熔断
——>恢复调用链路
- 恢复调用链路
这个过程也是慢慢的一步步来增加恢复的
- 重要参数设置
//服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器(保险丝)
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //请求次数:在快照时间窗内,必须满足请求总数阀值才有资格熔断,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败,断路器都不会打开。
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //时间窗口期:以10s为一个快照,统计一些信息或错误信息
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少开启熔断机制
//这几个参数全部设置完以后大致的意思就是,在10秒钟以内如果失败调用的次数到达10次就会启用断路器
})
1.5 服务限流
- 概述
秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
1.6 总结
- 前言
服务降级和服务熔断就是周杰和周杰伦的区别
- 调用失败会触发降级,而降级会调用fallback方法
- 但无论如何降级的流程一定会先调用正常方法再调用fallback方法
- 假如单位时间内调用失败次数过多小也就是降级次数过多,则触发熔断
- 熔断以后就会跳过正常方法直接调用fallback方法
- 所谓
熔断后服务不可用
就是因为跳过了正常方法直接执行fallback
六、服务网关
1.zuul
NETFIX研发,已逐渐被淘汰
2.Gateway
2.1 概述
- 由spring开发,SpringCloud Gateway 使用的Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架。
2.2 作用
- 反向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控
2.3 配置方式
- yaml文件
- 书写一个配置类
2.4 配置参数
- 概述
Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。
Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些Predicate都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以进行组合
- 种类
- After Route Predicate
- Before Route Predicate
- Between Route Predic
- Cookie Route Predicate
- Header Route Predicate
- Host Route Predicate
- Method Route Predicate
- Path Route Predicate
- Query Route Predicate
2.5 Filters
- 概述
路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。
Spring Cloud Gateway 内置了多种路由过滤器(31种之多),他们都由GatewayFilter的工厂类来产生
- 作用
全局日志记录、统一网关鉴权
七、服务配置
1.SpringCloud Config
1.1 概述
SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。
1.2 作用
- 集中管理配置文件
你可想象如果有很多很多服务,而每个服务的yaml文件都必须写,每次更换配置难道要一个一个换?
- 不同环境不同配置,动态化的配置更新,分环境部署比如dev/test/prod/beta/release
- 运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统—拉取配置自己的信息
- 当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置
- 将配置信息以REST接口的形式暴露
1.3 客户端访问服务器端配置访问规则(在服务端进行配置)
总共有5中访问规则
推荐使用第一个:label指定分支、application指定文件名、profile指定是哪个环境的配置文件
例如:http://config-3344.com:3344/onebranch/config-test.yaml
1.4 bootstrap.yaml配置文件
- 概述
applicaiton.yml是用户级的资源配置项;bootstrap.yml是系统级的,优先级更加高,在系统内也优先加载,所以要将Client模块下的application.yml文件改为bootstrap.yml,这是很关键的
1.5 客户端动态刷新
- 前言
由于客户端的访问路径使用过bootstrap配置文件进行修改的,这样每一次都会重启,所以我们需要客户端的动态刷新
2.Nacos
八、服务总线
1.SpringCloud Bus
1.1 全局通知
- 概述
由于只使用SpringCloud Config的局限性(客户端动态刷新还需要手动发一个post请求才能生效,类似于中了幻术需要别人解开)。所以Config配合Bus能实现全局配置动态刷新。Bus支持两种消息代理:RabbitMQ 和 Kafka
- 推送方式(我们使用第二种)
- 利用消息总线触发一个客户端/bus/refresh,而刷新所有客户端的配置
- 利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,而刷新所有客户端的配置,(注意第二种方式也还是需要通过配置中心服务发送post请求,但只用发一次)
post请求的格式为:curl -X POST “http://localhost:3344/actuator/bus-refresh”
不推荐使用第一种的原因:
- 打破了微服务的职责单一性,因为微服务本身是业务模块,它本不应该承担配置刷新的职责。
- 破坏了微服务各节点的对等性。
- 有一定的局限性。例如,微服务在迁移时,它的网络地址常常会发生变化,此时如果想要做到自动刷新,那就会增加更多的修改
- 作用
Spring Cloud Bus能管理和传播分布式系统间的消息,就像一个分布式执行器,可用于广播状态更改、事件推送等,也可以当作微服务间的通信通道。将配置消息推送给配置中心服务,该服务将消息推送至每个分布式客户端服务
1.2 指定通知
- 概述
即我们只同时某个特定的客户服务
- 发送方式
我们在发送post的请求时进行差异化发送
公式:http://localhost:配置中心的端口号/actuator/bus-refresh/{destination}
例子:curl -X POST “http://localhost:3344/actuator/bus-refresh/config-client:3355”
2.Nacos
1.SpringCloud Stream(消息驱动)
1.1 概述
屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型;就是说每一层服务传送消息时可能用了不同的消息中间件,而不同的中间件带来的切换、开发、维护问题非常严重且繁琐,所以有一个能够统一管理这些的消息驱动就显得十分重要了
- 注意
现在只支持
RabbitMQ
和kafka
;其他中间件可在SpringCloudAlibaba中得到配置和支持
1.2 官方说明
Spring Cloud Stream是用于构建与共享消息传递系统连接的高度可伸缩的事件驱动微服务框架,该框架提供了一个灵活的编程模型,它建立在已经建立和熟悉的Spring熟语和最佳实践上,包括支持持久化的发布/订阅、消费组以及消息分区这三个核心概念
1.3 Binder
- SpringCloud Stream实现的核心
通过定义绑定器Binder作为中间层,实现了应用程序与消息中间件细节之间的隔离。
生产者是output
、消费者是input
- 相关配合流程即组件
- Binder:很方便的连接中间件,屏蔽差异
- Channel:通道,是队列Queue的一种抽象,在消息通讯系统中就是实现存储和转发的媒介,通过Channel对队列进行配置
- Source和Sink:简单的可理解为参照对象是Spring Cloud Stream自身,从Stream发布消息就是输出,接受消息就是输入。
1.4 编码API和常用注解
1.5 分组消费
- 分组消费
当一个订单消息被不同组的两个服务接收到,就会造成消息错乱,所以为了避免这样的消息我们可以采用分组消费的方式,即提供消费组
- 注意
在Stream中处于同一个group中的多个消费者是竞争关系,就能够保证消息只会被其中一个应用消费一次。
- 不同组是可以全面消费的(重复消费)
- 同一组内会发生竞争关系,只有其中一个可以消费。
1.6 持久化问题
2.SpringCloud Sleuth(分布式请求链路追踪)
2.1 能够解决的问题
在微服务框架中,一个由客户端发起的请求在后端系统中会经过多个不同的的服务节点调用来协同产生最后的请求结果,每一个前段请求都会形成一条复杂的分布式服务调用链路,链路中的任何一环出现高延时或错误都会引起整个请求最后的失败。
2.2 ZIPKIN
Sleuth内部集成了ZIPKIN(只需要调用jar包即可),Sleuth负责追踪链路,收集并整理;而ZIPKIN负责展示
2.3 链路结构图
一条链路通过Trace Id唯一标识,Span标识发起的请求信息,各span通过parent id 关联起来
- 名词解释
- Trace:类似于树结构的Span集合,表示一条调用链路,存在唯一标识
- span:表示调用链路来源,通俗的理解span就是一次请求信息
下篇SpringCloud之Alibaba
一、SpringCloud Alibaba入门简介
1.应用
- 服务限流降级:默认支持 Servlet、Feign、RestTemplate、Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
- 服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
- 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
- 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
- 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
- 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
二、SpringCloud Alibaba(Nacos服务注册和配置中心)
-
前言
前四个字母分别为Naming和Configuration的前两个字母,最后的s为Service。
-
与其他服务注册中心比较(Nacos 支持AP和CP模式的切换)
1.Nacos概述
- 功能
Nacos就是注册中心 + 配置中心的组合 等价于 Nacos = Eureka+Config +Bus
2.Nacos与其他服务中心对比
3.Nacos作为配置中心-分类配置
- 前言
对于单个生产环境的配置就不做赘述
3.1 区分不同环境的标识
- 图示
3.2 标识的不同之处
- 图示
- 最外层的namespace是可以用于区分部署环境的,Group和DataID逻辑上区分两个目标对象。
- 默认情况:Namespace=public,Group=DEFAULT_GROUP, 默认Cluster是DEFAULT
- Namespace
Nacos默认的命名空间是public,Namespace主要用来实现隔离。
比方说我们现在有三个环境:开发、测试、生产环境,我们就可以创建三个Namespace,不同的Namespace之间是隔离的。 - Group
Group默认是DEFAULT_GROUP,Group可以把不同的微服务划分到同一个分组里面去
- Service
Service就是微服务;一个Service可以包含多个Cluster(集群),Nacos默认Cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。
- Instance
就是微服务的实例。
4.Nacos集群和持久化配置(重要)
4.1 环境
- Linux
- Mysql 5.7
- Nacos
4.2 集群配置
-
结构图
-
注意
在单节点的Nacos中持久化问题是使用内嵌数据库
Derby
来实现的(但由于会存在单点故障),而在多结点集群中如果每个结点还是用自己的内嵌数据库就会存在同步问题,所以在集群中默认统一使用mysql
数据库,并且目前只支持mysql
4.3 后续
无论是配置nginx的集群还是nacos的集群都以失败告终,博主都是以docker来配置的集群,每次都是错误的;nginx的集群是根本响应不了负载均衡,而nacos更扯淡,更改完nginx的配置文件跟没改一样,各访问各的,真的服了;算了这章暂且略过,进行下面的sentinel
三、SpringCloud Alibaba(Sentinel实现熔断与限流)
0.前言
任何服务如果没有做持久化都会在关闭的一瞬间对应的服务信息监控信息都是消失了的,所以适当使用持久化是一件很重要的事情
1.Sentinel概述
Alibaba研发的升级版Hystrix断路器,功能更加强大
- 功能特性
2.流控规则
-
配置界面
-
相关名词解释
3.降级规则
3.1 慢调用比例(平均响应时间,秒级)
平均响应时间(RT)超出阈值且在时间窗口内通过的请求>=5,两个条件同时满足后触发降级
窗口期过后关闭断路器
RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)
3.2 异常比列(秒级)
QPS >= 5 且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
3.3 异常数(分钟级)
异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
3.4 半开状态
半开的状态系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用。具体可以参考Hystrix
- 注意
Sentinel的断路器是没有半开状态的,你要么就一直正常,要么就停止
4.热点规则
- 热点
热点即经常访问的数据,很多时候我们希望统计或者限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作
4.1 自定义兜底返回信息
- 说明
与Hystrix断路器的HystrixCommand注解很相似,这里我们用的注解是SentinelResource
- HystrixCommand实例
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")
})
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
//@HystrixCommand //加了@DefaultProperties属性注解,并且没有写具体方法名字,就用统一全局的
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
}
//下面是全局fallback,为了节省资源不然一个方法一个fallback
public String payment_Global_FallbackMethod() {
return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
}
- Sentinel实例
//测试热点规则的自定义兜底信息页面
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "handle_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false)String p1,
@RequestParam(value = "p2",required = false)String p2){
return "------------testHotKey";
}
public String handle_testHotKey(String p1, String p2, BlockException e){
return "------this is my way to deal exception,o(╥﹏╥)o";
}
4.2 参数列外项(高级选项)
- 说明
就是当你被监控的那个参数是一个你指定的值(特殊的值),我们就不做限制,想怎么访问就怎么访问
5.@SentinelResource
5.1 说明
如果只使用普通的基础功能还是会出现Hystrix时就存在的问题:
- 系统默认的,没有体现我们自己的业务要求。
- 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。
- 每个业务方法都添加一个兜底的,那代码膨胀加剧。
- 全局统一的处理方法没有体现。
6.服务熔断
6.1 Sentinel整合ribbon+openFeign+fallback
6.1.1 Sentinel+ribbon+fallback
- 配置类
/**
* @author lhj
* @create 2021/10/21 20:17
* ribbon+sentinel
*/
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //开始ribbon负载均衡的必要条件哦
public RestTemplate getRestTemplate()
{
return new RestTemplate();
}
}
- 主控制类
/**
* @author lhj
* @create 2021/10/21 20:18
*/
@RestController
public class CircleBreakerController {
public static final String SERVER_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/fallback/{id}")
//@SentinelResource(value = "fallback") //什么都没有配置
//@SentinelResource(value = "fallback", fallback = "handlerFallback") //fallback负责java业务异常处理
//@SentinelResource(value = "fallback", blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
@SentinelResource(value = "fallback", fallback = "handlerFallback",blockHandler = "blockHandler") //当两个都配置是,并且控制台异常和java业务异常都被捕获时,显示的错误信息室sentinel控制台的异常信息,也就是两者的异常sentinel优先级更加高一点
//@SentinelResource(value = "fallback", fallback = "handlerFallback",blockHandler = "blockHandler" ,exceptionsToIgnore = {IllegalArgumentException.class}) //exceptionsToIgnore这个参数意思就是使sentinel忽略这个异常。不拦截此异常,所以此时java业务的异常不会受sentinel管理,会直接报java异常
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVER_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4)
throw new IllegalArgumentException("非法参数异常");
else if (result.getData() == null)
throw new NullPointerException("该ID没有对应记录,空指针异常");
return result;
}
//这个是fallback
public CommonResult handlerFallback(@PathVariable Long id, Throwable e) {
Payment payment = new Payment(id, "null");
return new CommonResult<>(444, "兜底异常handlerFallback ,exception内容" + e.getMessage(), payment);
}
//本例是bLockHandler
public CommonResult blockHandler(@PathVariable Long id, BlockException blockException){
Payment payment = new Payment(id,"null");
return new CommonResult<>( 445, "blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage());
}
}
- yaml
server:
port: 84
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: 192.168.199.150:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: 192.168.199.150:8858
clientIp: 192.168.199.148 #如果不指定Ip自动寻找到另一个虚拟IP了
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-provider
6.1.2 Sentinel+fallback+openFeign
- 服务接口及实现类
/**
* @author lhj
* @create 2021/10/21 21:28
* 使用openfeign就不用创建RestTemplate,服务就能相互调用了
*/
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService {
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
/**
* @author lhj
* @create 2021/10/21 21:34
*/
@Component
public class PaymentFallbackService implements PaymentService {
@Override
public CommonResult<Payment> paymentSQL(Long id) {
return new CommonResult<>(44444,"服务降级返回,-------PaymentFallbackService",new Payment(id,"errorSerial"));
}
}
- 主控制类
/**
* @author lhj
* @create 2021/10/21 20:18
*/
@RestController
public class CircleBreakerController {
//-----------------------openfeign---------------------
@Resource
private PaymentService paymentService;
@GetMapping("/consumer/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
return paymentService.paymentSQL(id);
}
}
6.1.3 结论
- Sentinel+ribbon+fallback
SentinelResource注解和LoadBalanced注解的配合使用,完成了客户端的负载均衡和对于java业务层面的异常和sentinel控制台制定的规则违背异常的处理
- Sentinel+fallback+openFeign
由于openFeign的加入,我们可以不用再使用RestTemplate来实现服务之间的调用,然后实现服务降级
6.2 熔断框架比较
7.Sentinel的持久化
7.1 概述
大致思路就是配合nacos实现持久化,因为nacos基础中是使用mysql实现持久化功能的,而sentinel也可融合进去,将限流配置规则持久化进Nacos保存
7.2 步骤
-
在nacos中添加如下规则
-
修改主业务的yaml
添加如下
datasource:
ds1:
nacos:
server-addr: 192.168.199.150:8848 #自己的nacos服务器地址
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
四、SpringCloud Alibaba(Seata处理分布式事务)
- 概述
Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
- 分类
- AT模式(我们所学习的)
- TCC模式
- SAGA模式
- XA模式
1.分布式事务的问题
1.1 以前和现在的对比
- 以前
在初级数据库中我们是使用单台主机的项目和数据库,即1:1 ,非常易管理
- 现在
在以后企业中可能单个服务需要很多个不同的数据库,又或者多个服务使用多个数据库,而需要的数据有在不同的数据库中。这时虽然每个服务内部的一致性是由本地负责的但全局事务管理就异常的复杂且需要谨慎小心。
2.AT模式分布式服务的结构
-
分布式事务处理过程的全局唯一的事务ID(Transaction ID XID)
XID的结构:
seata宿主机IP + Port + XXXXXXXXXX
-
三组件模型(TC、TM、RM)
2.1 三组件模型
Transaction:业务
- Transaction Coordinator (TC):全局管理者
事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚;
- Transaction Manager (
TM
):管理的具体实施者
控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议;
- Resource Manager(RM):对不同管理的应对处理
控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚
2.2 XID及组件工作过程
- TM 向 TC 申请开启一个全局事务并注册全局事务记录,全局事务创建成功并生成一个全局唯一的
XID
; XID
在微服务调用链路的上下文中传播;并准备资源- RM 向 TC 注册分支事务,将其纳入
XID
对应全局事务的管辖; - TM 向 TC 发起针对 XID 的全局提交或回滚决议;此时事务一阶段结束
- TC 调度
XID
下管辖的全部分支事务进行事务汇总并且决定完成提交或回滚请求。 - TC通知所有RM提交或回滚资源,事务第二阶段结束
3.@GlobalTransactional
这个注解就是实现事务管理回滚的关键,在对于业务方法上面加上
@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)
public void create(Order order)
{
。。。。。。
}
4. AT模式详解
4.1 AT模式的构成
AT模式在实行起来是对业务无任何侵入的,能做到的原因就是它的两段提交协议的配合
4.2 一阶段(加载)
- 概述
业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
- 大白话说此阶段都干了什么
在业务开始进行SQL语句对于数据库的更改时,seata会拦截此时的这个SQL语句,然后对数据库原数据会保存一个before快照(log信息),然后再允许SQL语句进行操作,完成之后又会在保存一个after快照(log信息),并且生成一个锁;这些操作都是在一个数据库事务中完成的
4.3 二阶段(提交)
- 概述
提交异步化,非常快速地完成。
- 大白话
如果提交一切顺利,seata会将保存的两个快照和行锁全部删掉,此数据库事务提交完成
4.4 二阶段(回滚)
- 概述
回滚通过一阶段的回滚日志进行反向补偿。
- 大白话
如果此时发生了一些意外,seata在二阶段进行回滚的话,对于业务SQL和数据库的业务数据进行还原,此时用到的就是before的快照进行回滚;但在此操作之前,会对比此时数据库业务数据与after快照是否完全一致,来校验是否发生脏写,两份如果一致则继续回滚操作,但如果不一致就说明发生脏写,此时需要程序员本员进行修改
4.5 总结图
- 配置过程图
5.实例模拟订单支付服务
5.1 业务需求
下订单->减库存->扣余额->改(订单)状态
5.2 未跨越的坎
这里博主又遇到好多问题,由于采用的是虚拟机中的docker提供seata服务,导致了很多问题:
- seata版本问题
- 如果你拉取的是最新版本的seata,在它的resource文件中是没有提供file.conf和registry.conf这两个文件的,因为最新版是是支持在idea中的yml文件进行配置,在服务器端进不进行配置我就不知道了
- 如果你是低版本,那么resource中是提供file.conf和registry.conf两个文件的,可以对此进行配置,但博主采用的是将配置文件挂载在本地来修改,然而修改完后seata服务一直restarting,哎…
- 配置项问题
- 在配置中尤其要注意默认组的问题,图中画的两项一定名字要一致不然必定报错,但博主改成一样还是继续报错…
- 错误信息
2021-10-22 18:13:06.497 ERROR 16360 --- [ main] i.s.c.r.netty.NettyClientChannelManager : can not get cluster name in registry config 'service.vgroupMapping.fsp_tx_group', please make sure registry config correct