Spring Cloud
微服务概述
概念
- 微服务架构是一种架构模式或者说是一种架构风格,它提倡将单一应用程序划分成一组小的服务,每个服务运行在其独立的自己的进程中,服务之间互相协调、互相配合,为用户提供最终价值。
- 服务之间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API)。每个服务都围绕着具体业务进行构建,并且能够被独立地部署到生产环境、类生产环境等。
- 另外,应尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建,可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储。
技术维度理解
- 微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底地去耦合,每一个微服务提供单个业务功能的服务,一个服务做一件事,从技术角度看就是一种小而独立的处理过程,类似进程概念,能够自行单独启动或销毁,拥有自己独立的数据库。
微服务拆分
方法论
- 领域驱动设计( Domain Driven Design )
- 面向对象( by name./ by verb. )
个人心得
- 职责划分
- 通用性划分
合理的粒度
- 良好地满足业务
- 幸福感
- 增量迭代
- 持续进化
SpringCloud概述
基本概念
-
基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件
-
SpringCloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等,它们都可以用SpringBoot的开发风格做到一键启动和部署。
-
SpringCloud=分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶
相关项目
Springboot和SpringCloud的关系
- SpringBoot专注于快速方便的开发单个个体微服务
- SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务
SpringCloud对比Dubbo
- SpringCloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式。牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题。REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约。
SpringCloud第一季时相关框架
现在项目相关服务架构
SpringCloud和SpringBoot版本选择
SpringCloud要与SpringBoot版本相对应
-
查看网址:https://spring.io/projects/spring-cloud#overview
-
更详细的:https://start.spring.io/alctuator/info
本次课程版本选择
pom
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<properties>
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
版本选择推荐
- 只用boot,直接上最新
- 同时用cloud,则需要cloud决定boot版本
SpringCloud相关组件选择
服务注册中心
- Eureka 已停更 抛弃
- Zookeeper 可使用
- consul 可使用
- Nacos 强烈推荐
服务调用
- Ribbon 可使用
- LoadBalancer 可使用
服务调用2
- feign 已停用 抛弃
- openfeign 可使用
服务降级
-
Hystrix 维护 抛弃
-
resilience4j 可使用
-
sentienl 可使用
服务网关
- Zuul 抛弃
- Zuul2 已夭折
- gateway 可使用
服务配置
- Config 抛弃
- Nacos 强烈推荐
服务总线
- Bus 抛弃
- Nacos 强烈推荐
PS
Maven中DependencyManagement和Dependencies区别
- DependencyManagement
- 用于锁定子工程依赖版本 并不导入
- Dependencies
- 直接导入
maven跳过单元测试
建工程五步
- 建Module
- 改pom
- 写yml
- 主启动类
- 业务类
相关细节
- 父工程创建完后执行mvn:install将父工程发布到仓库方便子工程继承
配置允许容器中相同名字bean覆盖的配置
main :
allow-bean-definition-overriding: true
RestTemplate
官网地址:
https://docs.spring.io/spring-framewprk/docs/5.2.2.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html
使用
- 使用restTemplate访问restful接口非常的简单粗暴无脑。
- (url, requestMap, ResponseBean.class)这三个参数分别代表(REST请求地址、请求参数、HTTP响应转换被转换成的对象类型)。
相关方法
- get请求
- getForObject
- getForEntity
- post请求
- postForObject
- postForEntity
- forEntity和forObject
- object直接拿到相应数据
- entity还会拿到状态码等等
服务注册中心
作用:实现服务的自动注册、发现、状态监控。
Eureka
基本使用
单机步骤
-
导包
- 服务端:spring-cloud-starter-netflix-eureka-server
- 客户端:spring-cloud-starter-netflix-eureka-client
-
配置yml
#服务端 eureka: client: service-url: defaultZone: http://127.0.0.1:${server.port}/eureka fetch-registry: false #不注册到自己这里 register-with-eureka: false #不注册到自己这里 server: enable-self-preservation: false #关闭自我保护 eviction-interval-timer-in-ms: 5000 #失效时间 --- #客户端 eureka: client: service-url: defaultZone: http://127.0.0.1:${server.port}/eureka
-
主启动类
- 服务端:@EnableEurekaServer
- 客户端:@EnableEurekaClient
高可用
- 基本思路:相互注册
步骤
-
先搭建好单机步骤
-
修改yml
#客户端 server: port: ${PORT:50101} spring: application: name: xc‐govern‐center eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: ${EUREKA_SERVER:http://eureka02:50102/eureka/} server: enable-self-preservation: false eviction-interval-timer-in-ms: 60000 instance: hostname: ${EUREKA_DOMAIN:eureka01} #需要设置好主机名
-
对应yml设置启动参数
-
修改客户端yml
- 将客户端注册到所有注册中心机器上
获得服务信息
-
修改客户端yml
instance: instance-id: payment8002 prefer-ip-address: true #访问路径可以显示P地址
-
主启动类加@EnableDiscoverClient
-
通过discoveryClient对象得到服务相关信息
Zookeeper
概述
- zookeeper是一个分布式协调工具,可以实现注册中心功能
- 关闭Linux服务器防火墙后启动zookeeper服务器
- zookeeper服务器取代Eureka服务器,zk作为服务注册中心
基本使用
-
centOS7上启动zookeeper
-
导包:spring-cloud-starter-zookeeper-discovery
-
配置yml
cloud : zookeeper: connect-string: 192.168. 111.144:2181
-
主启动类加上@EnableDiscoverClient
-
获得微服务信息命令
- get /services/cloud-provider-payment/ bb75f135-6743-4346-a4ca-656cdac3bdd3
-
其微服务结点为临时结点
坑
jar版本和zookeeper版本jar包不符
解决步骤
-
排除自带的zookeeper包
-
导入我们正确版本的zookeeper包
<dependency> <groupId>org.springframework.cloud</ groupId> <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId> <!--先排除自带的zookeeper3.5.3--> <exclusions> <exclusion> <groupId>org.apache.zookeeper</ groupId> <artifactId>zookeeper</artifactId> </ exclusion> </exclusions> </dependency> <!--添加zookeeper3.4.9版本--> <dependency> <groupId>org.apache.zookeeper</ groupId> <artifactId>zookeeper</ artifactId> <version>3.4.9</version> </dependency>
consul
概述
- Consul是一个开源的分布式服务发现和配置管理系统,由HashiCorp公司用Go语言开发。
- 提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。
使用步骤
-
cmd 执行
consul agent -dev
启动consul -
8500端口访问首页
-
导包spring-cloud-starter-consul-discovery
-
配置yml
cloud: consul: host: localhost port: 8500 discovery: #hostname: 127.0.0.1 service-name: ${spring. application.name}
三个组件相互对比
- Eureka侧重AP、zookeeper、consul侧重CP
- Consul和Eureka有HTTP,zookeeper为客户端
CAP理论
- 最多只能同时较好的满足两个。
- CAP理论的核心是: 一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,
- 因此,根据CAP原理将NoSQL数据库分成了满足CA原则、满足CP原则和满足AP原则三大类:
- CA-单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。.
- CP-满足一致性,分区容忍必的系统,通常性能不是特别高。
- AP -满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。
服务调用
ribbon
- Eureka已自带ribbon
修改负载均衡的方式
-
已有可选择方式
-
切换方法
-
代码配置方法
- 在componentScan扫描不到的地方创建配置类
@Configuration public class RibbonConfiguration { @Bean public IRule ribbonRule() { return new RandomRule() ; } @Bean public IPing ping(){ return new PingUrl() ; } }
- 新建配置类
@Configuration . @RibbonClient(name = "user-center", configuration = MySelfRule.class) pubLic class UserCenterRibbonConfiguration { }
-
配置类配置
user-center: #微服务名 ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.Random
-
方法对比
- 使用建议
- 尽量使用属性配置,属性方式实现不了的情况下再考虑用代码配置
- 在同一个微服务内尽量保持单- -性,比如统一-使用属性配置,不要两种方式混用,增加定位代码的复杂性
-
其他相关配置
全局配置
@Configuration
@RibbonClients(defaultConfiguration = RibbonConfiguration.class)
public class UserCenterRibbonConfiguration {
}
饥饿加载
ribbon:
eager-load:
enabLed: true
clients: user-center
其他配置
原理相关
为什么要在componentScan扫描不到的地方
- 防止父子上下文重叠的问题,若重叠则会使此配置变为全局配置
核心api
自定义负载均衡
权重负载均衡
- nacos配置权重
-
写负载均衡算法类
-
继承AbstractLoadBalancerRule 类
@Slf4j public class NacosWeigbtedRule extends AbstractLoadBalancerRule { @Autowired private NacosDiscoveryProperties nacosDiscoveryProperties; @Override public void initWithNiwsConfig ( IClientConfig clientConfig) { //读取配置文件,并初始化NacosWeightedRule } @Override public Server choose(Object key) { try { BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer(); log. info("Zb = {}", loadBalancer); //想要请求的微服务的名称 String name = loadBalancer.getName( ) ; // 拿到服务 发现的相关API NamingService namingService = nacosDiscoveryProperties.namingMaintainServiceInstance(); // nacos client自动通过基于权重的负载均衡算法,给我们选择一 个实例。 NamingService namingService = nacosDiscoveryProperties.namingServiceInstance(); Instance instance = namingService.selectOneHealthyInstance(name); log.info("选择的实例是:{}",instance); return new NacosServer( instance) ; } catch (NacosException e) { return null; } } }
-
集群负载均衡
@Slf4j
public class NacosClusterRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Override
public void initWithNiwsConfig (IClientConfig clientConfig) {
// 1。找到指定服务的所有实例A
List<Instance> instances = namingService.seLectInstance;
// 2.过滤出相同集群下的所有实例B
List<Instance> sameClusterInstances = instances.stream (
.filter( instance -> 0bjects.equals(instance.getCluster()
.collect(Collectors. toList());
// 3。如果B是空,就用A
List<Instance> instancesToBeChosen = new ArrayList<>( ) ;
if (CollectionUtils. isEmpty( sameClusterInstances)) {
instancesToBeChosen = instances ;
Zog.warn("发生跨集群的调用, name = {},ClusterName ={}", name , ClusterName ,instances);
}
else{
instancesToBeChosen = sameClusterInstances ;
}
// 4.基于权重的负载均衡算法,这回1个买例
Instance instance = ExtendBalancer . getHos tByRandomWeight2( i)
log. info("选择的实例是port = {}, instance = {}", instance. get
return new NacosServer(instance) ;
} catch ( NacosException e) {
Log.error("发生异常了",e) ;
return nulL;
}
}
class ExtendBalancer extends Balancer {
public staticInstance getHostByRandomWeight2(List<Instance> hosts )
return getHostByRandomWeight (hosts) ;
}
- ps
- 某些类的方法如果没有权限访问则可以尝试建一个类去继承这个类再调用它的方法
服务调用2
openFegin
概述
- Feign是一 个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。
- 它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡
使用方法
- 导包Spring-cloud-starter-openfeign
- 建立相关接口
- 方法与Controller一致(包括SpringMVC注解)
- 主启动类
- 加@EnableFeignClients
- 直接调用
请求超时机制
-
OpenFeign默认等待1秒,超过后报错
-
默认支持Ribbon
-
yml中可修改超时时间设置
#没置feign客户端超时时间(OpenFeign默认支持ribbon) ribbon: #指的是建立连接所用的时间,适用于网络状况正E常的情况下,两端连接所用的时间 ReadTimeout: 5000 #指的是建立连接后从服务器读取到可用资源所用的时间 ConnectTimeout: 5000
其他相关配置
#1 hystrix可配置的部分
hystrix.command.default.execution.timeout.enable=true #为false则超时控制有ribbon控制,为true则hystrix超时和ribbon超时都是用,但是谁小谁生效,默认为true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000#熔断器的超时时长默认1秒,最常修改的参数
circuitBreaker.requestVolumeThreshold=20 #触发熔断的最小请求次数,默认为20
circuitBreaker.sleepWindowInMilliseconds=5000 #休眠时长,默认为5秒
circuitBreaker.errorThresholdPercentage=50 #触发熔断的失败请求最小占比,默认50%
#2 ribbon的可配置部分
ribbon.ReadTimeout=1000 #处理请求的超时时间,默认为1秒
ribbon.ConnectTimeout=1000 #连接建立的超时时长,默认1秒
ribbon.MaxAutoRetries=1 #同一台实例的最大重试次数,但是不包括首次调用,默认为1次
ribbon.MaxAutoRetriesNextServer=0 #重试负载均衡其他实例的最大重试次数,不包括首次调用,默认为0次
ribbon.OkToRetryOnAllOperations=false #是否对所有操作都重试,默认false
#3 Feign的可配置部分
feign.hystrix.enabled=false #Feign是否启用断路器,默认为false
feign.client.config.default.connectTimeout=10000 #Feign的连接建立超时时间,默认为10秒
feign.client.config.default.readTimeout=60000 #Feign的请求处理超时时间,默认为60
feign.client.config.default.retryer=feign.Retryer.Default #Feign使用默认的超时配置,在该类源码中可见,默认单次请求最大时长1秒,重试5次
日志打印功能
-
概念
- 可以打印相关请求信息
-
分类
- NONE:默认的,不显示任何日志;
- BASIC:仅记录请求方法、URL、 响应状态码及执行时间;
- HEADIERS:除了BASIC中定义的信息之外,还有请求和响应的头信息;
- FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。
-
使用方法
-
代码配置方式
-
写一个配置类
@Configuration public class FeignConfig{ @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
-
将这个包的日志输出等级改为debug
-
-
配置文件配置方式(单个)
feign: client: config: #想要调用的微服务的名称. user-center: loggerLevel: full
-
配置文件(全局)
feign: client: config: #想要调用的微服务的名称. default: loggerLevel: full
-
配置方式对比
-
最佳实践
- 尽量使用属性配置,属性方式实现不了的情况下再考虑用代码配置
- 在同一个微服务内尽量保持单一性,比如统一 使用属性配置,不要两种方式混用,增加定位代码的复杂性
-
ribbon和feign对比
Feign的继承特性
概述
- 将controller的接口统一管理,并且将此接口作为feign的调用接口
争议
- 官方观点:不建议使用
- 业界观点:很多公司使用
- 个人观点:权衡利弊
常见问题
get请求包含多个参数
- 加上@SpringQueryMap注解
@FeignClient("microservice-provider-user")
public interface UserFeignClient {
@GetMapping("/get")
public User get0(@SpringQueryMap User user);
}
restTemplate VS Feign
如何选择
- 原则:尽量用Feign ,杜绝使用RestTemplate
- 事无绝对,合理选择
Feign性能优化
-
连接池(提升15%)
-
配置方法
- 切换底层为httpclient
feign: client: config : #全局配置 default: LoggerLevel: full httpclient: #让feign使用apache httpclient做请求; 而不是默认的urLc enabled: true # feign的最大连接数 max-connections: 2 00 # feign单 个路径的最大连接数 max-connections-per-route: 50 okhttp: #配置为okhttp enabled: true
-
-
日志级别
服务降级
Hystrix
概述
分布式系统面临问题
- 复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。
服务雪崩
- 通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩。
Hystrix概念
- Hystrix是一个用于处理分布式系统的延迟和容错的开源库, 在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下, 不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
- ”断路器”本身是一种开关装置, 当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝) , 向调用方返回-个符合预期的、可处理的备选响应(FallBack) ,而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
作用
- 服务降级
- 服务熔断
- 接近实时的监控
- 。。。。。。
服务降级
概念
- 服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,fallback
什么时候会引发服务降级
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池/信号量打满也会导致服务降级
服务熔断
概念
- 类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
服务限流
概念
- 秒杀高并发等操作,严禁- -窝蜂的过来拥挤,大家排队,-秒钟N个,有序进行
基本使用
模仿高并发场景
- 创建一个基本项目,提供两个接口
- 其中一个需要等待3秒模仿复杂业务
- 另一个马上返回
- 使用Jmeter压测,测试20000请求复杂业务
- 结果
- 简单接口仍然请求变慢
- 原因
- tomcat的默认的工作线程数被打满了,没有多余的线程来分解压力和处理。
hystrix使用
-
导包 :spring -cloud-starter-netflix-hystrix
-
使用@HystrixCommand注解,并写好兜底方法
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler" , commandProperties = { @HystrixProperty(name=" execution.isolation.thread.timeoutInMilliseconds" , value=" 3000" ) }) public String paymentInfo_TimeOutHandler(Integer id){ return "/( ToT)/调用支付接口超时或异常: \t"+ “\t当前线程池名字" + Thread. current Thread(). getName(); }
相关解释
- fallbackMethod
- 兜底方法名
- commandProperties
- 配置相关属性
- timeoutInMilliseconds
- 执行兜底方法超时时间(默认单位毫秒)
- fallbackMethod
-
主启动类加上:@EnableHystrix
什么时候会执行兜底方法
- 超时
- 方法抛异常
相关注解和方法
-
@DefaultProperties(defaultFallback = "“)
- 默认兜底方法
- 加了@HystrixComand注解的方法,没有设置自己的兜底方法则默认调用这个
- 通用的和独享的各自分开,避免了代码膨胀,合理减少了代码量,O(∩_ ∩)O哈哈~
-
feign接口系列
-
配置yml
feign: hystrix: enabled : true
-
写一个类实现client接口
-
配置@FeignClient注解
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT" , fallback = PaymentFallbackService.class)
-
在请求服务器超时、异常或者宕机时,会默认执行实现类的相对应方法作为兜底方法
-
熔断
熔断机制概述
- 熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。
- 当检测到该节点微服务调用响应正常后,恢复调用链路。
- 在Spring Cloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。
- 熔断机制的注解是@HystrixCommand.
原理图
相关状态
- 熔断打开
- 请求不再进行调用当前服务,内部设置时钟- -般为MTTR (平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态
- 熔断关闭
- 熔断关闭不会对服务进行熔断
- 熔断半开
- 部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断
相关配置
/=====服务熔断
@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"),// 失败率达到多少后跳闸
})
三个重要参数
-
快照时间窗 : 断路器确定是否打开需要统计一些请求和错误数据, 而统计的时间范围就是快照时间窗,默认为最近的10秒。
-
请求总数阀值:在快照时间窗内,必须满足请求总数阀值才有资格熔断。默认为20,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败,断路器都不会打开。
-
错误百分比阀值:当请求总数在快照时间窗内超过了阀值,比如发生了30次调用,如果在这30次调用中,有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阀值情况下,这时候就会将断路器打开。
熔断与链路恢复流程
- 当满足一定的阀值的时候(默认10秒内超过20个请求次数)
- 当失败率达到一定的时候(默认10秒内超过50%的请求失败)
- 到达以上阀值,断路器将会开启
- 当开启的时候,所有请求都不会进行转发
- 一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4和5
断路器打开后
- 再有请求调用的时候,将不会调用主逻辑,而是直接调用降级fallback。通过断路器, 实现了自动地发现错误并将降级逻辑切换为主逻辑,减少响应延迟的效果。
原来的主逻辑如何恢复呢?
- 对于这一问题,hystrix也为我们实现了 自动恢复功能。
- 当断路器打开,对主逻辑进行熔断之后,hystrix会启动一 个休眠时间窗, 在这个时间窗内,降级逻辑是临时的成为主逻辑
- 当休眠时间窗到期,断路器将进入半开状态,释放一次请求到原来的主逻辑上,如果此次请求正常返回,那么断路器将继续闭合,主逻辑恢复,如果这次请求依然有问题,断路器继续进入打开状态,休眠时间窗重新计时。
更多相关配置参数
- 在HystrixPropertiesManager类中
/====================All
@HystrixCommand(fallbackMethod = "str. _fallbackMethod",
groupKey = " strGroupCommand" ,
commandKey = " strCommand" ,
threadPoolKey = "strThreadPool" ,
I
commandProperties = {
//设置隔离策略,THREAD 表示线程池SEMAPHORE: 信号池膈离
@HystrixProperty(name = "execution. isolation.strategy", value = "THREAD"),
//当隔离策略选择信号他隔离的时候,用来没置信号他的大小(最大并发数)
@HystrixProperty(name = " execution. isolation. semaphore . maxConcurrentRequests", value = "10"),
//配置命令执行的超时时间
@HystrixProperty(name = "execution. isolation . thread. timeoutinMilliseconds", value = "10"),
//是否启用超时时间
@HystrixProperty(name = " execution. timeout . enabled", value = "true"),
//执行超时的时候是否中断
@HystrixProperty(name = "execution. isolation. thread. interruptOnTimeout", value = "true"),
//执行被取消的时候是否中断
@HystrixProperty(name = "execution. isolation. thread. interruptOnCance1", value = "true"),
//允许回调方法执行的最大并发数
@HystrixProperty(name = "fallback. isolation. semaphore . maxConcurrentRequests", value = "10"),
//服务降級是否启用,是否执行回调西数
@HystrixProperty(name = "fallback.enabled", value = "true") ,
// 该属性用来没置在演动时间窗中,断路器熔断的最小请求数。例如,默认该值为20的时候,
//如果熔动时间窗(默610秒)内仅收到了19个请求, 即使这19个请求都失败了,断路器也不会打开。
@HystrixProperty(name = "circuitBreaker . requestVolumeThreshold", value = "20"),
//该属性用来设置在演动时间窗中,表示在演动时间窗中,在请求数量超过
// circuitBreaker. requestVolume Threshold 的情况下,如果错误请求数的百分比超过50,
. //就把断路器没置为“打开”状态,否则就没置为"关闭”状态。
@HystrixProperty(name = "circuitBreaker . errorThresholdPercentage", value = "50"),
//该属性用来没置当断路器打开之后的休眠时间窗。休眠时间窗结束之后,
//会将断路器置为”半开”状态,尝t熔断的请求命令,如果依然失败就将断路器继续设置为“打开"状态,
//如果成功就没置为"关闭”状态。
@HystrixProperty(name = "circuitBreaker . sleepWindowinMilliseconds", value = "5000"),
//断路器强制打开
@HystrixProperty(name = "circuitBreaker . forceOpen", value = "false"),
//断路器强制关闭
@HystrixProperty(name = "circuitBreaker . forceClosed", value = "false"),
//颇动时间窗设置,该时间用于断路器判断健康度时需要收集信息的持续时间
@HystrixProperty(name = "metrics . rollingStats . timeinMilliseconds", value = "10000"),
//该属性用来没置烧动时间窗统计指标信息时划分”桶”的数量,断路器在收業指标信息的时候会根据
//设置的时间窗长度拆分成多个”桶”来累计各度量值,每个”桶"记录了- 段时间内的来集指标。
//比如10秒内拆分成10个”桶"收集这样,所以timeinMilliseconds 必须能被numBuckets 整除。否则会抛异常
@HystrixProperty(name =” metrics . rollingStats . numBuckets", value = "10"),
//该属性用来没置采集影响断路器状态的健康快照(请求的成功、错误 百分比)的间隔等待时间。
@HystrixProperty(name = "metrics .healthSnapshot . intervalinMilliseconds", value = "500"),
//是否开启请求缓存
@HystrixProperty(name = "requestCache. enabled", value = "true"),
// HystrixCommand的执行和事件是否打印日志到HystrixRequestlog中
@HystrixProperty(name = "requestLog . enabled", value = "true"),
」,
threadPoolProperties = {
//该参数用来没置执行命令线程池的核心线程数,该值也就是命令执行的最大并发量
@HystrixProperty(name = "coreSize", value = "10"),
// 该参数用来没置线程他的最大队列大小。当改置为, -1 时,线程池将使用SynchronousQueue 实现的队列,
//否则将使用LinkedBlockingQueue实现的队列。
@HystrixProperty(name = "maxQueueSize", value =“-1"),
// 该参数用来为队列设置拒绝阀值。通过该参数,即使队列没 有达到最大值也能拒绝请求。
//该参数主要是对LinkedBlockingQueue队列的补充,因为LinkedBlockingQueue
//队列不能动态修改它的对象大小,而通过该属性就可以调整拒绝请求的队列大小了。
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "5")
})
实时监控
概述
- 除了隔离依赖服务的调用以外,Hystrix还提供 了准实时的调用监控(Hystrix Dashboard)
- Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。
- Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。
基本使用
-
所有服务提供者都要导入spring-boot-starter-actualor包
-
新建监控工程
-
主启动类加注解@EnableHystrixDashboard
-
被监控的类需要加入一个配置
/** *此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑 *ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream", *只要在自己的项目里配置上下面的servlet就可以了*/ @Bean public ServletRegistrationBean getServlet() { HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet); registrationBean.setLoadOnStartup(1); registrationBean.addUrlMappings(" /hystrix.stream"); registrationBean.setName ( "HystrixMetricsStreamServlet"); return registrationBean; }
-
访问网址:localhost:9001/hystrix
-
输入网址: localhost:9002/hystrix/stream
如何看
-
7色
-
1圈
- 实心圆:共有两种含义。它通过颜色的变化代表了实例的健康程度,它的健康度从绿色<黄色<橙色<红色递减。
- 该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大。所以通过该实心圆的展示,就可以在大量的实例中快速的发现故障实例和高压力实例。
-
1线
- 曲线:用来记录2分钟内流量的相对变化,可以通过它来观察到流量的上升和下降趋势。
-
整图说明A
- 整图说明2
网关
网关定位
SpringCloud Gateway
概述
概念
- SpringCloud Gateway是Spring Cloud的一个全新项目,基于Spring 5.0+ Spring Boot 2.0和Project Reactor等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API路由管理方式。
作用
- 反向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控
- 。。。
强大之处
- SpringCloud Gateway使用的Webflux中的reactor netty响应式编程组件,底层使用了Netty通讯框架。
特性
- 基于Spring Framework 5, Project Reactor和Spring Boot 2.0进行构建;
- 动态路由:能够匹配任何请求属性;
- 可以对路由指定Predicate (断言)和Filter (过滤器) ;
- 集成Hystrix的断路器功能;
- 集成Spring Cloud服务发现功能;
- 易于编写的Predicate (断言)和Filter (过滤器) ;
- 请求限流功能;
- 支持路径重写。
与zuul的区别
- Zuul 1.x, 是一个基于阻塞I/ 0的API Gateway
- Zuul 1.x基于Servlet 2.5使用阻塞架构它不支持任何长连接(如WebSocket) Zuul的设计模式和Nginx较像,每次I/0操作都是从工作线程中选择一个执行, 请求线程被阻塞到工作线程完成,但是差别是Nginx用C++实现,Zuul 用Java实现,而JVM本身会有第一次加载较慢的情况, 使得Zuul 的性能相对较差。
- Zuul 2.x理念更先进,想基于Netty非阻塞和支持长连接,但SpringCloud目前还没有整合。Zuul 2.x的性能较Zuul 1.x有较大提升, 在性能方面,根据官方提供的基准测试,Spring Cloud Gateway的RPS (每秒请求数)是Zuul的1. 6倍。
- Spring Cloud Gateway建立在Spring Framework 5、Project Reactor和Spring Boot2之上,使用非阻塞 API。
- Spring Cloud Gateway还支持WebSocket,并且与Spring紧密集成拥有更好的开发体验
- 性能是zuul的1.6倍
阻塞和异步非阻塞
传统servlet I/O
- 流程:
- servlet由servlet container进行生命周期管理。
- container启动时构造servlet对象并调用servlet init()进行初始化;
- container运行时接受请求,并为每个请求分配一个线程(一般从线程池中获取空闲线程)然后调用service)。
- container关闭时调用servlet destory(销毁servlet);
- 缺点
- servlet是一个简单的网络I/O模型 ,当请求进入servlet container时,servlet container就会为其绑定一个线程, 在并发不高的场景下这种模型是适用的。
- 但是一旦高并发(此如抽风用jemeter国),线程数量就会上涨,而线程资源代价是昂贵的(上线文切换,内存消耗大)严重影响请求的处理时间。
- 在一些简单业务场景下,不希望为每个request分配一个线程, 只需要1个或几个线程就能应对极大并发的请求,这种业务场景下servlet模型没有优势
- Zuul 1.X是基于servlet之上的一个阻塞式处理模型,即spring实现了处理所有request请求的一个servlet (DispatcherServlet) 并由该servlet阻塞式处理处理。所以Springcloud Zuul无法摆脱servlet模型的弊端
- 传统的Web框架,比如说: struts2, springmvc等 都是基于Servlet API与Servlet容器基础之上运行的。
异步非阻塞
- 在Servlet3.1之后有了异步非阻塞的支持。而WebFlux是一个典型非阻塞异步的框架,它的核心是基于Reactor的相关API实现的。相对于传统的web框架来说,它可以运行在诸如Netty, Undertow及 支持Servlet3.1的容器上。非阻塞式+函数式编程(Spring5必须让你使用java8)
- Spring WebFlux是Spring 5.0 引入的新的响应式框架,区别于Spring MVC,它不需要依赖Servlet API,它是完全异步非阻塞的,并且基于Reactor来实现响应式流规范。
基本概念
- Route(路由)
- 路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由参考的是Java8的java.util.function.Predicate
- Predicate(断言)
- 开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
- Filter(过滤)
- 指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
- web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行些精细化控制。
- predicate就是我们的匹配条件;
- filter, 就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri, 就可以实现一个具体的路由了
核心逻辑
- 路由转发+执行过滤器链
流程
- 客户端向Spring Cloud Gateway发出请求。然后在Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway Web Handler。
- Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前( “pre” )或之后( “post” )执行业务逻辑。
基本使用
-
导包:spring-cloud-starter-gateway
-
配置yml
cloud: gateway: routes : - id: payment_ routh #ayment_ route #路由的ID,没有固定规则但要求唯-,建议配合服务名 uri: http://localhost:8001 #匹配后提供服务的路由地址 predicates : Path=/ payment/get/** #断言,路径相匹配的进行路由 - id: payment_ routh2 #payment_ route #路由的ID,没有固定规则但要求唯- -,建议配合服务名 uri: http://localhost:8001 #匹配后提供服务的路由地址 predicates: - Path=/payment/lb/** #断言,路径相匹配的进行路由
-
需要移除jar包:
- spring- boot-starter-web
- spring- boot- starter- actuato
-
转发规律
-
访问${GATEWAY_ URL}/{微服务X}/**会转发到微服务X的/**路径
更多功能
配置类配置方法
@Bean
public RouteLocator customRouteLocator( RouteLocatorBuilder builder)
RouteLocatorBuilder. Builder routes = builder.routes();
routes.route("path_route:patguigu", r -> r. path( "/guonei").uri("http://news.baidu.com/ guonei")).build();
return routes . build();
}
@Bean
public RouteLocator cus tomRouteLocator2(RouteLocatorBuilder builder){
RouteLocatorBuilder. Builder routes = builder.routes();
routes.route("path_ route_ atguigu2", r -> r.path(" /guoji") . uri("http://news . baidu. com/ guoji")).build();
return routes.build();
}
微服务名作为路由
-
改变yml
spring: cloud: gateway: discovery: locator: enabled: true #开启从注册中心动态创建路由的功能,利用徽服务名进行路由
-
uri改成
- 微服务不能配置context-path
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
断言的使用
作用
- 说白了,Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。
断言种类
使用方法
predicates:
- Path=/payment/1b/** # 断言,路径相匹配的进行路由
- After=2020-02-05T15:10:03.685+08:00[Asia/Shanghai]
#断言,路径相匹配的进行路由
- Before=2020-02-05T15:10:03.685+08:00[As ia/Shanghai]
#断言,路径相匹配的进行路由
- Between=2020-02-02717:45:06.206+08: 00[Asia/Shanghai], 2020-03- 25T18:59:06.206+08:00[Asia/Shanghai]
- Cookie=username , zzyy
- Header=X-Request-Id, \d+ # 请求头要有X- Request- Id属性并且值为整数的正则表达式
- Host=**.atguigu.com
- Method=GET
- Query=username, \d+ # 要有参数名username并且羞还要是整数才能路由
得到时间格式
ZonedDateTime zbj = ZonedDateTime.now(); //默认时区
System.out.println(zbj);
//2020-02- 21[15:51:37.485+98:00[Asia/Shanghai]L
cmd命令行发送请求
curl http://localhost:9588/paymentlnfo --cookie "username=zzyy"
curl http://localhost:9527/payment/lb --cookie "username= zzyy"
自定义断言
-
创建新的类
- 类名 断言名+FoutePredicateFactory
- 继承 AbstractRoutePredicateFactory
- 实现方法 apply(DayTimeBetweenConfig config)
- shortcutFieldOrder()
public class DayTimeBetweenRoutePredicateFactory extends AbstractRoutePredicateFactory<DayTimeBetweenConfig> { public DayTimeBetweenRoutePredicateFactory(Class<DayTimeBetweenConfig> configClass) { super(configClass); } @Override public Predicate<ServerWebExchange> apply(DayTimeBetweenConfig config) { LocalTime start = config.getStart(); LocalTime end = config.getEnd(); return exchange->{ LocalTime now = LocalTime.now(); return now.isAfter(start)&&now.isBefore(end); }; } @Override public List<String> shortcutFieldOrder() { return Arrays.asList("start","end"); } }
-
创建新的配置类
-
属性为 断言的参数
-
过滤器的使用
概述
- 路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。
- Spring Cloud Gateway内置了多种路由过滤器,他们都由GatewayFilter的工厂 类来产生
内置过滤器
- 参考手记:http://www.imooc.com/article/290816
作用
- 全局日志记录
- 统一网关鉴权
- 。。。
全局过滤器
-
写一个类实现GlobalFilter ,ordered
-
放入spring容器
-
使用案例
@Component @Slf4j public class MyLogGatewayFilter implements GlobalFilter ,ordered{ @override public Mono<Void> filter(ServerwebExchange exchange, GatewayF ilterChain chain) log. info("***********come in MyLogGatewayFilter: " +new Date()); string uname = exchange.getRequest().getQueryParams().getFirst( key: "uname"); if(uname == nu1l) { log.info(*******用户名为nul1,非法用户,0(π_ π)o"); exchange.getResponse().setstatusCode(HttpStatus .NOT_ACCEPTABLE); return exchange.getResponse().setComplete(); } return chain.filter( exchange); } @override public int getorder(){ return 0; } }
过滤器 核心API
- exchange.getRequest().mutate().xXx //修改request
- exchange.mutate().xxx //修改exchange
- chain.filter(exchange) //传递给下一个过滤器处理
- exchange.getResponse() //拿到响应
过滤器工厂配置方法
方式一:
-
写一个类继承AbstractGatewayFilterFactory
-
配置形式
filters: - name: RequestSize args: maxSize: 5000000
方式二:
-
写一个类继承继承: AbstractNameValueGatewayFilterFactory
-
配置形式
filters: - AddRequestHeader=S-Header,Bar
-
config. getName(),config. getValue ()来获得参数
过滤器和断言的相关源码
Gateway Handler Mapping
- org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping
Gateway Web Handler :
- org.springframework.cloud.gateway.handler.FilteringWebHandler
执行顺序
-
Order越小越靠前
-
按配置顺序从1开始
-
如果配置了默认过滤器,则先执行相同Order的默认过滤器
-
如需自行控制Order ,可返回OrderedGatewayFilter
相关手记
网关监控:http://www.imooc.com/article/290822
排错、调试方法:http://www.imooc.com/article/290824
其他配置:http://www.imooc.com/article/290825
限流方式:http://www.imooc.com/article/290828
服务配置
Spring Cloud Config
概述
- 微服务意味着要将单体应用中的业务拆分成一个个子服务, 每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。
- SpringCloud提供了ConfigServer来解决这个问题
配置流程
是什么
- SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部置。
作用
- 集中管理配置文件
- 不同环境不同配置,动态化的配置更新,分环境部署比如dev/test/prod/beta/release
- 运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息
- 当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置
- 将配置信息以REST接口的形式暴露post、curl访问刷新均可…
与gitHub整合配置
- 由于SpringCloud Config默认使用Git来存储配置文件(也有其它方式,比如支持SVN和本地文件),但最推荐的还是Git,而且使用的是http/https访问的形式
基本使用
-
先在github/gitee新建一个仓库
-
通过命令将clone仓库到本地
- git clone git@github.com:zzyybs/springcloud.git
- git基本命令
- git add
- git commit -m “init yml”
- git push origin master
-
创建多个配置文件,保存格式必须为UTF-8
-
新建config服务端工程
-
导包spring-cloud-config-servers
-
配置yml
#新增的配置 cloud: config: server: git: uri: git@github.com:zzyybs/springcfoud-config.git #GitHub 上面的git仓库名字 ####搜索目录 search-paths : - springcloud-config ##读取分支 label: master
-
主启动类加注解@EnableConfigServer
-
-
修改hosts
127.0.0.1 config-3344.com
-
通过浏览器访问路径可看到配置文件
-
配置读取规则
#label为分支 #后面两项则为配置文件的前缀和后缀 /{label)/{application)-{profile}.yml /{application}-{profile}.yml #返回的为json字符串 /{application}/{profile}[/{label}]
-
总结
- yml会配置默认分支
- 指定了分支则访问指定分支的配置文件,未指定则反问默认分支
- 若分支放后边则返回json数据
-
-
新建客户端config工程
-
导包spring-cloud-starter-config
-
配置bootstrap.yml
- application.yml和bootstrap.yml
- applicaiton. yml是用户级的资源配置项
- bootstrap. yml是系统级的,优先级更加高
- 相关原理和注意事项
- Spring Clould会创建一个"Bootstrap Context”, 作为Spring应用的Application Context的父上下文。
- 初始化的时候,'Bootstrap Context’负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的 Environment。
- Bootstrap属性有高优先级,默认情况下,它们不会被本地配置覆盖。Bootstrap context和Application Context有着不同的约定,所以新增了一个bootstrap.yml文件, 保证Bootstrap Context和Application Context配置的分离。
- 要将Client模块下的application.yml文件改为bootstrap.yml,这是很关键的,因为bootstrap.ymI是比application.yml先加载的。bootstrap.yml优先级高于application.yml
- application.yml和bootstrap.yml
-
配置yml
cloud: #Config客户端配置 config: 1abel: master #分支名称 name: config #配置文件名称 profile: dev #读取后级名称. 上述3个综合: master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.yml uri: http://localhost:3344 #配置中心地址
-
测试是否能读取到服务端的配置文件
@RestController public class ConfigClientController { @Value("${config.info}") private String configInfo; @GetMapping("/configInfo") public String getConfigInfo() { return configInfo; } }
此时能读取到文件,但是并不能动态刷新,client需要重启才能重新读取配置文件
-
动态刷新
-
修改客户端模块
-
pom引入监控
-
修改yml,配置暴露监控端口
#暴露监控端点 management : endpoints : web: exposure : include : "*"
-
加注解@RefreshScope
-
运维人员请求刷新3355
- curl -X POST “http://localhost:3355/actuator/refresh”
- 必须为post请求
服务总线
相关概念
- 总线
- 在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个共用的消息主题,并让系统中所有微服务实例都连接上来。由于该主题中产生的消息会被所有实例监听和消费,所以称它为消息总线。
- 在总线上的各个实例,都可以方便地广播一些需要让其他连接在该主题 上的实例都知道的消息。
- 基本原理.
- ConfigClient实例都监所MQ中同一个topic(默认是springCloudBus)。当-个服务刷新数据的时候,它会把这个信息放入到Topic中, 这样其它监听同一Topic的服务就能得到通知,然后去更新自身的配置。
Spring Cloud Bus
概述
-
Spring Cloud Bus是用来将分布式系统的节点与轻量级消息系统链接起来的框架
-
它整合了Java的事件处理机制和消息中间件的功能。
-
Spring Cloud Bus目前支持RabbitMQ和Kafka.
作用
- Spring Cloud Bus能管理和传播分布式系统间的消息,就像一个分布式执行器,可用于广播状态更改、事件推送等,也可以当作微服务间的通信通道。
设计思想
-
利用消息总线触发一个客户端/bus/refresh,然后客户端继续传播,而刷新所有客户端的配置。
-
利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,而刷新所有客户端
-
第二种更加适合
基本使用
-
需要有rabbitmq先
-
给3344config服务端增加消息总线的支持
-
导包 spirng-cloud-starter-bug-amqp
-
配置yml
- 暴露bug刷新配置的端点
- 配置rabbitmq
##rabbitmq相关配置,暴露bus刷新配置的端点 management: endpoints: #暴露bus刷新配置的端点 web: exposure: include: ' bus-refresh' #rabbitmq相关配置15672是Web管理界面的端口; 5672 是MQ访问的端口 rabbitmq: host: localhost port: 5672 username: guest password: guest
-
ps:
- 凡是要暴露刷新的,都要导入actuator
-
-
给3355,3366config客户端增加消息总线的支持
-
导包 spirng-cloud-starter-bug-amqp
-
配置yml
- 暴露监控端点
- 配置rabbitmq
spring: cloud: rabbitmq: host: localhost port: 5672 username: guest password: guest #暴露监控端点 management : endpoints : web: exposure : include : "*"
-
-
发送请求
- curl -X POST “http://localhost:3344/actuator/bus-refresh”
- 一次发送,处处生效
定点通知
- 指定具体某一个实例生效而不是全部
- 公式:
- http://localhost:3344/actuator/bus-refresh/{destination}
- destination为 微服务名:端口号
流程图
Spring Cloud Stream
概述
解决的痛点:
是什么
- 官方定义Spring Cloud Stream是一个构建消息驱动微服务的框架。
- 应用程序通过inputs或者outputs来与Spring Cloud Stream中binder对象交互。
- 通过我们配置来binding(绑定),而Spring Cloud Stream的binder对象负责与消息中间件交互。
- 所以,我们只需要搞清楚如何与Spring Cloud Stream交互就可以方便使用消息驱动的方式。
- 通过使用Spring Integration来连接消息代理中间件以实现消息事件驱动。
- Spring Cloud Stream为一些供应商的消息中间件产品提供了个性化的自动化配置实现,引用了发布-订阅、消费组、分区的三个核心概念。
- 目前仅支持RabbitMQ、Kafka.
总结成一句话
- 屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型
标准MQ
- 生产者/消费者之间靠消息媒介传递信息内容
- message
- 消息必须走特定的通道
- channel
- 消息通道里的消息如何被消费呢,谁负责收发处理
- messageHandler
作用
- 解决消息中间件的差异
- 比方说我们用到了RabbitMQ和Kafka
- 由于这两个消息中间件的架构上的不同
- 像RabbitMQ有exchange, kafka有 Topic和Partitions分区
- 这些中间件的差异性导致我们实际项目开发给我们造成了一定的困扰,我们如果用了两个消息队列的其中一 种,后面的业务需求,我想往另外一种消息队列进行迁移,这时候无疑就是一 个灾难性的,一大堆东西都要重新推倒重新做,因为它跟我们的系统耦合了,这时候springcloud Stream给我们提供了一种解耦合的方式。
为什么能统一底层差异
- 在没有绑定器这个概念的情况下,我们的SpringBoot应用要 直接与消息中间件进行信息交互的时候,由于各消息中间件构建的初衷不同,它们的实现细节上会有较大的差异性
- 通过定义绑定器作为中间层,完美地实现了应用程序与消息中间件细节之间的隔离。通过向应用程序暴露统一的Channel通道, 使得应用程序不需要再考虑各种不同的消息中间件实现。
- 通过定义绑定器Binder作为中间层,实现了应用程序与消息中间件细节之间的隔离。
- 在没有绑定器这个概念的情况下,我们的SpringBoot应用要直接与消息中间件进行信息交互的时候,由于各消息中间件构建的初衷不同,它们的实现细节上会有较大的差异性,通过定义绑定器作为中间层,完美地实现了应用程序与消息中间件细节之间的隔离。Stream对消息中间件的进一步封装
可以做到代码层面对中间件的无感知,甚至于动态的切换中间件(rabbitmq切换为kafka),使得微服务开发的高度解耦,服务可以关注更多自己的业务流程.
处理架构
遵循发布订阅模式
- Topic主题进行广播
- 在RabbitMQ就是Exchange
- 在Kakfa中就是Topic
基本概念
- Destination Binder (目标绑定器)
- 与消息中间件通信的组件
- Destination Bindings (目标绑定)
- Binding是连接应用程序跟消息中间件的桥梁,用于消息的消费和生产,由binder创建
- Message (消息)
- Binder
- 很方便的连接中间件,屏蔽差异
- Channel
- 通道,是队列Queue的一种抽象,在消息通讯系统中是实现存储和转发的媒介,通过Channe对队列进行配置
- Source和Sink
- 可理解为参照对象是Spring Cloud Stream自身。
- 从Stream发布消息就是输出,接受消息就是输入。
基本使用
-
需要有rabbitmq环境
-
编写发送端
-
导包spring-cloud- starter-stream-rabbit
-
配置yml
#新增配置 cloud: stream: binders: #在此处配置要绑定的服务信息; name-server: 127.0.0.1:9876 bindings: #服务的整合处理 input: #这个名字是一个通道的名称 destination: studyExchange #如果是rocketMQ,group一定要配置 group: binder-group
#新增配置 cloud: stream: binders: #在此处配置要绑定的rabbitmq的服务信息; defaultRabbit: #表示定义的名称,用于Fbinding整合 type: rabbit #消息组件类型 environment: #设置rabbitmq的相关的环境配置 spring: rabbitmq: host: localhost port: 5672 username: guest password: guest bindings: #服务的整合处理 output: #这个名字是一个通道的名称 destination: studyExchange #表示要使用的Exchange名称定义 content-type: application/json #设置消息类型,本次为json, 文本则设置“text/plain" binder: defaultRabbit #设置要绑定的消息服务的具体设置
-
编写推送的类
- 加上@EnableBinding(Source.class)指明是个消息推送管道
- 注入消息发送管道MessageChannel
@EnableBinding(Source.class) //定义消息的推送管道 public class MessageProviderImpl implements IMessageProvider{ @Resource private MessageChannel output; //消息发送管道 @Override public String send(){ String serial = UUID.randomUUID().tostring(); output.send(MessageBuilder.withPayload(serial).build()); System.out.println("******serial: "+serial); return null; } }
-
-
编写消费端
-
导包spring-cloud- starter-stream-rabbit
-
编写yml
- 与发送端比只是output变成input
#新增配置 cloud: stream: binders: #在此处配置要绑定的rabbitmq的服务信息; defaultRabbit: #表示定义的名称,用于Fbinding整合 type: rabbit #消息组件类型 environment: #设置rabbitmq的相关的环境配置 spring: rabbitmq: host: localhost port: 5672 username: guest password: guest bindings: #服务的整合处理 input: #这个名字是一个通道的名称 destination: studyExchange #表示要使用的Exchange名称定义 content-type: application/json #设置消息类型,本次为json, 文本则设置“text/plain" binder: defaultRabbit #设置要绑定的消息服务的具体设置
-
编写接收消息的类
@Component @EnableBinding(Sink.class) public class ReceiveMessagelistenerController{ @Value("${server.port}") private String serverPort; @StreamListener(Sink.INPUT) public void input(Message<String> message){ System.out.println("消费者1号----->接受到的消息:"+message.getPayload()+"\t port: "+serverPort); } }
-
-
此时会有两个问题
- 有重复消费
- 消息持久化问题
分组
- 不同组是可以全面消费的(重复消费)
- 同一组内会发生竞争关系,只有其中一个可以消费。
- 默认是不同组
配置分组
- 可解决重复消息问题
- 并且消息会得到持久化
配置方法
-
配置yml
bindings: #服务的整合处理 input: #这个名字是一- 个通道的名称 destination: studyExchange #表示要使用的Exchange名称定义 content-type: application/json #设置消息类型,本次为对象json, 如果是文本则设置“text/plain” binder: defaultRabbit #设置要绑定的消息服务的具体设置 group: atguiguB
自定义Input和output
-
创建新接口
pubLic interface MySource { String MY_OUTPUT = "my-output"; @Output (MY_OUTPUT) Mes sageChannel output(); }
-
配置yml
my-output: destination: stream-my-topic
-
加上注解
@EnableBinding ({Source.class, MySource.class})
全局异常处理
/**
*全局异常处理
*
* @param message发生异常的消息
*/
@StreamListener("errorChanneL")
public void error(Message<?> message) {
ErrorMessage errorMessage = (ErrorMessage)message;
log.warn("发生异常,errorMessage = {}", errorMessage);
}
监控
-
actuator多了binding和channel端点
- 里面有output和input 的详细信息
-
配置里加上
health : show-details: always
- 即可在health里监控到
Spring Cloud Sleuth
概述
解决痛点
- 在微服务框架中,一个由客户端发起的请求在后端系统中会经过多个不同的的服务节点调用来协同产生最后的请求结果,每一个前段请求都会形成一条复杂的分布式服务调用链路,链路中的任何一环出现高延时或错误都会引起整个请求最后的失败。
是什么
- Spring Cloud Sleuth提供了一套完整的服务跟踪的解决方案
- 在分布式系统中提供追踪解决方案并且兼容支持了zipkin
名词解释
- Tracq:类似于树结构的Span集合,表示一条调用链路,存在唯一标识
- span:表示调用链路来源,通俗的理解span就是一次请求信息
基本使用
-
使用命令运行zipkin-server
- java - jar zipkin-server-2.12.9-exec. jar
-
给项目工程导包
- spring-cloud- starter-zipkin
-
yml新增配置
zipkin: base-url: http://localhost :9411 sleuth: sampler : #采样率值介于0到1之间,1则表示全部采集 probability: 1
-
打开浏览器访问:http://localhost:9411
-
即可看到相关调用链路
-
进阶篇:Spring Cloud Alibaba
概述
出现原因
-
Spring Cloud Netflix进入维护
- 意味着Spring Cloud Netflix将不再开发新的组件
- 我们都知道Spring Cloud版本迭代算是比较快的,因而出现了很多重大ISSUE都还来不及Fix就又推另一个Release 了。进入维护模式意思就是目前一直以后一 段时间Spring Cloud Netflix提供的服务和功能就这么多了,不在开发新的组件和功能了。以后将以维护和Merge分支Full Request为主
-
新组建功能将以其他替代品替代方式实现
诞生
- 2018.10.31, Spring Cloud Alibaba正式入驻了Spring Cloud官方孵器,并在Maven中央库发布了第一个版本。
提供功能
- 服务限流降级
- 默认支持Servlet、 Feign、 RestTemplate、 Dubbo 和RocketMQ限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级Metrics监控。
- 服务注册与发现
- 适配Spring Cloud服务注册与发现标准,默认集成了Ribbon的支持。
- 分布式配置管理
- 支持分布式系统中的外部化配置,配置更改时自动刷新。
- 消息驱动能力
- 基于Spring Cloud Stream为微服务应用构建消息驱动能力。
- 阿里云对象存储
- 阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
- 分布式任务调度
- 提供秒级、精准、可靠可用的定时(基于Cron表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有Worker (schedulerx-client)上执行。
jar包
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
相关组件
- Sentinel
- 阿里巴巴开源产品,把流量作为切入点,从流量控制、熔断障级、系统负载保护等多个维度保护服务的稳定性。
- Nacos
- 阿里巴巴开源产品,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台.
- RocketMQ
- Apache RocketMQ"基于Java的高性能、高吞吐量的分布式消息和流计算平台。
- Dubbo
- Apache Dubbo"是-款高性能Java RPC框架。
- Seata
- 阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案.
- Alibaba Cloud Oss
- 阿里云对象存储服务(Object Storage Service,简称OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。
- 您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
- Alibaba Cloud SchedulerX
- 阿里中间件团队开发的一款分布式任务调度产品,支持周期性的任务与固定时间点触发任务。
Nacos
概述
名字含义
- 前四个字母分别为Naming和Configuration的前两个字母,最后的s为Service.
是什么
- 一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
- Nacos就是注册中心+配置中心的组合
- 等价于 Nacos = Eureka+ Config+Bus
作用
- 替代Eureka做服务注册中心
- 替代Config做服务配置中心
与其他注册中心对比
基本架构
nacos可以在AP和CP之间进行切换
- C是所有节点在同一时间看到的数据是一致的; 而A的定义是所有的请求都会收到响应。
何时选择使用何种模式?
-
一般来说,如果不需要存储服务级别的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。当前主流的服务如Spring cloud和Dubbo服务,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性, 因此AP模式下只支持注册临时实例。
-
如果需要在服务级别编辑或者存储配置信息,那么CP是必须,K8S服务和DNS服务则适用于CP模式。CP模式下则支持注册持久化实例,此时则是以Raft协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误.
-
切换命令
curl -X PUT '$NACOS.SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
注册中心基本使用
-
安装启动nacos
- 本地Java8 + Maven环境已经OK
- 安装并运行Nacos
- 解压安装包,直接运行bin目录下的startup.cmd
- 命令运行成功后直接访问http://localhost:8848/nacos
-
将工程注册到nacos
-
导包
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
-
改yml
spring: cloud: nacos: discovery: server-addr: localhost:8848 #配置Nacos地址 management: endpoints: web: exposure: include: "*"
-
主启动类加上@EnableDiscoveryClient
-
配置中心
基本配置
-
导包spring-cloud-starter-alibaba-nacos-config
-
配置两个yml(application.yml和bootstrap.yml)
bootstrap.yml
cloud: nacos : discovery: server-addr: localhost :8848 #Nacos 服务注册中心地址 config: server-addr: localhost:8848 #Nacos 作为配置中心地址 file-extension: yaml #指定yaml 格式的配置
application.yml
spring: profiles: active: dev #表示开发环境
-
加@RefreshScope
- nacos自带动态刷新
- 加上此注解则会动态刷新
-
在配置列表新增配置
-
dataId格式
${spring.application.name}-${spring.profile.cative}.${spring.cloud.nacos.config.file-extension} #也可以配置${spring.cloud.nacos.config.prefix}就会变成 ${spring.cloud.nacos.config.prefix}-${spring.profile.cative}.${spring.cloud.nacos.config.file-extension}
-
元信息(Metadata)
概念
- Nacos数据(如配置和服务)描述信息如服务版本、权重、容灾策略、负载均衡策略、鉴权配置、各种自定义标签(label),从作用范围来看,分为服务级别的元信息、集群的元信息及实例的元信息。
级别
- 服务级别、集群级别、实例级别
作用
- 提供描述信息
- 让微服务调用更加灵活
配置方法
控制台配置
配置文件配置
cloud:
nacos:
discovery:
metadata:
instance: C
haha: hehe
version: 1
分类配置
解决问题
-
问题1:实际开发中,通常一个系统会准备
- dev开发环境
- test测试环境
- prod生产环境。
- 如何保证指定环境启动时服务能正确读取到Nacos.上相应环境的配置文件呢?
-
问题2:一个大型分布式微服务系统会有很多微服务子项目,
- 每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境… .
- 那怎么对这些微服务配置进行管理呢?
命名空间和组名
概念
- 类似Java里面的package名和类名
- 最外层的namespace是可以用于区分部署环境的,Group和DatalD逻辑上区分两个目标对象。
说明
- Nacos默认的命名空间是public, Namespace主要用来实现隔离。
- 比方说我们现在有三个环境:开发、测试、生产环境,我们就可以创建三个Namespace,不同的Namespace之间是隔离的。
- Group默认是DEFAULT_ GROUP, Group可以把不同的微服务划分到同一个分组里面去
- Service就是微服务; 一个Service可以包含多个Cluster (集群),Nacos默认Cluster是DEFAULT, Cluster是对指定微服务的一个虚拟划分。
- 比方说为了容灾,将Service微服务分别部署在了杭州机房和广州机房,这时就可以给杭州机房的Service微服务起一个集群名称(HZ) ,
- 给广州机房的Service微服务起一个集群名称(GZ), 还可以尽量让同一个机房的微服务互相调用,以提升性能。
- 最后是Instance,就是微服务的实例。
默认情况
- Namespace= public, Group= DEFAULT GROUP,默认Cluster是DEFAULT
基本使用
-
在nacos中创建一个命名空间和组
-
在yml中进行配置
cloud: nacos: discovery: server-addr: localhost :8848 #Nacos 服务注册中心地址 config: server-addr: localhost :8848 #Nacos 作为配置中心地址 file-extension: yaml #指定yaml格式的配置 group: DEV_ GROUP namespace: 7d8f0f5a-6a53-4785-9686-dd460158e5d4
集群部署和数据持久化
持久化
-
概述
- Nacos默认自带的是嵌入式数据库derby
- 但要切换到mysql以实现集群的数据一致性
- 以及持久化
-
配置步骤
-
nacos-server-1.1. 4\nacos\conf目录下找到sql脚本nacos-mysql.sql
-
nacos- server- 1.1.4\nacos\conf目录下找到application.properties 加上以下配置
spring.datasource.platform=mysql db.num= 1 db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout= 1000&socketTimeout=3000&autoReconnect=true db.user= root db.password= 123456
-
集群
推荐使用linux
需要nginx进行反向代理
基本架构
配置步骤
-
Linux服务器上将数据切换到mysql
-
Linux服务器上nacos的集群配置cluster.conf
192.168.111.144:3333 192.168.111.144:4444 192.168.111.144:5555
-
改变启动脚本
-
达到效果
- 集群启动,我们希望可以类似其它软件的shell命令,传递不同的端口号启动不同的nacos实例。
- 命令: ./startup.sh -p 3333表示启动端口号为3333的nacos服务器实例,和上一步的cluster.conf配置的一 -致。
-
修改部分
-
-
配置nginx,有他作为负载均衡器
架构图
Sentinel
基本概述
概念
- 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
特征
-
丰富的应用场景
- Sentinel 承接了阿里巴巴近10年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、 集群流量控制、实时熔断下游不可用应用等。
-
完备的实时监控
- Sentinel同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至500台以下规模的集群的汇总运行情况。
-
广泛的开源生态
- Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。
-
完善的SPI扩展点
-
Sentinel提供简单易用、完善的SPI扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
-
解决的各种问题
- 服务雪崩
- 服务降级
- 服务熔断
- 服务限流
与hystrix对比
两个部分
-
核心库(Java客户端)不依赖任何框架/库,能够运行于所有Java运行时环境,同时对Dubbo/Spring Cloud等框架也有较好的支持。
-
控制台(Dashboard) 基于Spring Boot开发,打包后可以直接运行,不需要额外的Tomcat等应用
容器。
基本使用
-
运行命令
java -jar sentinel-dashboard-1.7.0.jar
-
监控微服务模块
-
导包
<!--SpringCloud ailibaba sentinel --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
-
配置yml
cloud: nacos: discovery: #Nacos服务注册中心地址 server-addr: localhost: 8848 sentinel: transport: #配-Sent inel dashboard地址 dashboard: localhost:8080 #期认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口 port: 8719
-
执行一次访问
-
-
访问localhost:8080/sentinel
流控规则
相关解释
- 资源名: 唯一名称,默认请求路径
- 针对来源: Sentine可以针对调用者进行限流,填写微服务名,默认default (不区分来源)
- 阈值类型单机阈值:
- QPS (每秒钟的请求数量) : 当调用该api的QPS达到阈值的时候, 进行限流
- 线程数: 当调用该api的线程数达到阈值的时候,进行限流
- 是否集群: 不需要集群
- 流控模式:
- 直接 :api达到限流条件时,直接限流
- 关联 :当关联的资源达到阈值时,就限流自己
- 链路 :只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流) [api级别的针对来源]
- 流控效果:
- 快速失败: 直接失败,抛异常
- Warm Up: 根据codeFactor (冷加载因子,默认3)的值,从阈值/codeFactor, 经过预热时长,才达到设置的QPS阈值
- 排队等待: 匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效
配置预热
ps:
- 默认coldFactor为3,即请求QPS从(threshold / 3)开始,经多少预热时长才逐渐升至设定的QPS阈值。
- 案例,阀值为10+预热时长设置5秒。
- 系统初始化的阀值为10/ 3约等于3.即阀值刚开始为3;然后过了5秒后阀值才慢慢升高恢复到10
匀速排队
概念
降级
概述
- Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。
- 当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException) 。
- Sentinel的断路器是没有半开状态的
配置
相关解释
- RT (平均响应时间,秒级)
- 平均响应时间超出阈值且在时间窗口内通过的请求> =5,两个条件同时满足后触发降级
窗口期过后关闭断路器 - RT最大4900 (更大的需要通过-Dcsp.sentinel.statistic.max.rt= XXXX才能生效)
- 平均响应时间超出阈值且在时间窗口内通过的请求> =5,两个条件同时满足后触发降级
- 异常比列(秒级)
- QPS >= 5且异常比例(秒级统计)超过阈值时,触发降级;时间窗结束后,关闭降级
-
异常数
-
分钟统计的,所以时间窗口一定要大于60秒
-
热点规则
作用
- 设置当有某一参数时的限流设置
配置方法
- 页面新增规则
-
controller
@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) { return "----deal_ testHotkey,o(π_ π)o"; //sentinel 系统默认的提示: Blocked by Sentinel (flow limiting) }
参数例外项
概述
- 我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样
配置
- 注意点
- 参数必须是基本类型或者String
系统规则
-
Sentinel系统自适应限流从整体维度对应用入口流量进行控制,结合应用的Load、CPU 使用率、总体平均RT、入口QPS和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
-
配置
相关说明
-
Load自适应(仅对Linux/Unix-like机器生效)
- 系统的load1作为启发指标,进行自适应系统保护。当系统load1超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR阶段)。 系统容量由系统的maxQps * minRt 估算得出。设定参考值一般是CPU cores * 2.5 。
-
CPU usage (1.5.0+版本)
- 当系统CPU使用率超过阈值即触发系统保护(取值范围0.0-1.0) ,比较灵敏。
-
平均RT
- 当单台机器上所有入口流量的平均RT达到阈值即触发系统保护,单位是毫秒。
-
并发线程数
- 当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
-
入口QPS
- 当单台机器上所有入口流星的QPS达到阈值即触发系统保护。
-
-
源码
- com.alibaba.csp.sentinel.slots.system.SystemRuleManager#checkSystem
授权规则
@SentinelResource
基本使用
主要属性
- blockHandler
- 违背规则时的回滚方法
- fallbackHandler
- 抛出异常的回滚方法
@RequestMapping("/consumer/fallback/{id}")
//@SentinelResource(value = "fallback") //没有配置
//@SentinelResource(value = "fallback", fallback = "handlerFallback") //fallback只负责业务异常
//@SentinelResource(value = "fallback", blockHandler = "blockHandler") //blockHandler 只负责sentinel控制台配置违规
@SentinelResource(value = "fallback",fallback = "handlerFallback" ,blockHandler="blockHandler")
配置规则时
- 可以用请求url配置
- 也可以用@SentinelResource的value值来配置
自定义处理方法解耦合
- 创建一个新的类,在里面写所有的兜底方法
- 在@SentinelResource里将兜底方法配置为这个
@GetMapping( "/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class ,
blockHandler = "handlerException2")
其他属性
- value:资源名称,必需项(不能为空)
- entryType:entry 类型,可选项(默认为 EntryType.OUT)
- blockHandler / blockHandlerClass:blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
- fallback:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。
- fallback 函数签名和位置要求:
- 返回值类型必须与原函数返回值类型一致;
- 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
- fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
- fallback 函数签名和位置要求:
- defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。+
- defaultFallback 函数签名要求:
- 返回值类型必须与原函数返回值类型一致。
- 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
- defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析
- defaultFallback 函数签名要求:
- exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
原理剖析
sentinel如何获得微服务的相关信息
主机:接口/api 可以得到api接口的类型和和规则
主机:接口/acturator/sentinel 可以访问到sentinel的相关信息
相关源码
-
注册/心跳发送
- com.alibaba.csp.sentinel.transport.heartbeat.SimpleHttpHeartbeatSender
-
通信API
- com.alibaba.csp.sentinel.command.CommandHandler的实现类
控制台的其他配置项
spring.cloud.sentinel.transport:
#指定控制台的地址
dashboard: localhost:8080
#指定和控制台通信的IP
#如不配置,会自动选择一个IP注册
client-ip: 127.0.0.1
#指定和控制台通信的端口,默认值8719
#如不设置,会自动从8719开始扫描,依次+1,直到找到未被占用的端口
port: 8719
#心跳发送周期,默认值null
#但在SimpleHttpHeartbeatSender会用默认值10秒
heartbeat-interval-ms: 10000
代码配置
-
先关闭监控
sentinel: transport: dashboard: localhost:8080 filter: enabled: false
-
使用到了三大核心API
- SphU 监控和保护资源
- Trace 统计异常数量
- ContextUtil 实现调用来源 标记调用
@GetMapping("/test-sentinel-api") public String testSentinelAPI( @RequestParam(required = false) String a) { // 定义一个sentinel保护的资源, 名称是test-sentinel-ap Entry entry = null; try { entry = SphU.entry("test-sentinel-api") //被保护的业务逻辑 return a; //如果被保护的资源被限流或者降级了,就会抛BlockExcepti() } catch (BlockException e) { Log.warn("限流,或者降级了",e); return "限流,或者降级了"; } finally { if (entry != null) { //退出entry了 entry.exit(); } } }
现在情况:代码只会统计BlockException
解决方法
} catch (IllegaLArgumentException e2) { // 统计IllegalArgumentException [发生的次数、发生占比...] Tracer. trace(e2) ; } finally {
设置来源
String resourceName = "test-sentineL-api" ; ContextUtil.enter( resourceName,"test-wfw") ;
整合
整合restTemplate
单个配置
- @SentinelRestTemplate
- 可以指定fallback和blockHander
配置开关
- resttemplate.sentinel.enabled (false则@SentinelRestTemplate不生效)
相关源码
- org.springframework.cloud.alibaba.sentinel.custom.SentinelBeanPostProcessor
整合fegin
配置文件配置
- feign.sentinel.enabled = true
如何得到异常
@Component
@Slf4j
pubLic class UserCenterFeignClientFallbackFactory implements FallbackFactory{
@Override
pubLic User CenterFeignClient create(Throwable cause) {
return new UserCenterFeignClient() {
@Override
pubLic UserDTO findById(Integer id) {
Log.warn( marker: "远程调用被限流/降级了",e) ;
UserDT0 userDTO = new UserDT0( ) ;
use rDTO. setWxNickname("一个默认用户") ;
return userDTO;
};
}
}
使用姿势总结
自定义
自定义异常处理
public class MyUrlBlockHandler implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest request, HttpServletResponse response,
BlockException ex){
if(ex instanceof FlowException) {
}else if(ex instanceof DegradeException){
}else if(ex instanceof ParamFlowException){
}
else if(ex instanceof SystemBlockException){
}
else if(ex instanceof AuthorityException){
}
}
}
自定义识别处理来源
@Component
public class MyRequestQriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
//从请求参数中获取名为origin 的参数并返回
//如果获取不到origin参数,那么就抛异常
String origin = request.getParameter("origin");
if (StringUtils.isBlank(origin)) {
throw new IllegalArgumentException("origin must be specified");
}
return origin;
}
}
自定义处理资源名称
@Component
public class MyUrlCleaner implements UrlCleaner {
@Override
public String clean(String originUrl) {
//让/shares/I 与/shares/2的返回值相同
//返回/ shares/ {number}
String[] split = originUrl.split("/");
Arrays.stream(split)
.map(string -> {
if (NumberUtils.isNumber(string)) {
return " {number}";
}
return string;
})
.reduce((a, b) -> a + " /" + b)
.orElse("");
return originUrl;
}
}
总结
- UrlBlockHander :提供Sentinel异常处理
- RequestOriginHander :来源支持
- UrlCleaner :重新定义资源名称
- 本质: CommonFilter
- 可以通过commonFilter源码来扩展
持久化
推拉模式
。。。
集成阿里云AHAS
- 修改pom
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>spring-boot-starter-ahas-sentinel-client</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-cloud-alibaba-sentinel</artifactId>
<groupId>com.alibaba.cloud</groupId>
</exclusion>
</exclusions>
</dependency>
- 修改yml
ahas.namespace: default
project.name: content-center
ahas.license: f9b19cf6e26f4fca9595726a1cd7e403
Seata
概述
是什么
Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
相关概念
- Transaction Coordinator (TC)
- 事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚;
- Transaction Manager ™
- 控制全局事务的边界,负责开启一一个全局事务,并最终发起全局提交或全局回滚的决议; .
- Resource Manager (RM)
- 控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚
执行步骤
- TM向TC申请开启一个全局事务,全局事务创建成功并生成一 个全局唯一的XID;
- XID在微服务调用链路的上下文中传播;
- RM向TC注册分支事务,将其纳入XID对应全局事务的管辖;
- TM向TC发起针对XID的全局提交或回滚决议;
- TC调度XID下管辖的全部分支事务完成提交或回滚请求。
使用步骤
-
修改file.conf
- 先备份原始file.conf文件
- 自定义事务组名称+事务日志存储模式为db +数据库连接信息
service { #修改部分 vgroup.mapping.my_test._tx.group ="p_tx_group" default.grouplist = "127.0.0.1:8091" enableDegrade = false disable = false max.commit.retry.timeout = "-1" max.rollback.retry.timeout= "-1" }
## transaction log store store { ## store mode: file、db #修改处 mode = "db" ## database store db { ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc. datasource = " dbcp" ## mysq/oracle/h2/oceanbase etc. db-type = "mysql" #修改处 driver-class-name = *com.mysql.jdbc.Driver" url = "jdbc:mysql://127.0.0.1:3306/seata” user = "root" password =“你自己密码" min-conn = 1 max-conn = 3 global.table = *global. _table* branch.table = "branch_ _table" lock-table = "lock. _table" query-limit = 100 }
-
新建数据库表
- 建表db_ store.sql在\seata-server-0.9.0\seata\conf目 录里面
- 涉及到数据库全部要建
-
修改registry.conf
- 指明注册中心为nacos,及修改nacos连接信息
registry { # file、nacos 、eureka、 redis、 zk、 consul、 etcd3、 sofa #修改部分 type = "nacos" nacos { #修改部分 serverAddr = "localhost:8848" namespace = "" cluster = "default" }
-
分布式的模块设置
-
导jar包
<!--seata--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>1.0.0</version> </dependency>
-
配置yml
cloud: alibaba: seata: #自定义事务组名称需要seatg- server中的对应 tx-service-group: fspl_tx_group
-
将file.conf和register.conf拷贝到项目中
- 修改file.conf
-
主启动类取消数据源的自动配置
- 自行配置数据源
-
在事务的入口方法上加上注解@GlobalTransational
-
补充
saeta有四大模式
- AT 模式(默认)
- TCC模式
- SAGA 模式(商业模式)
- XA 模式(开发中)
原理剖析(AT模式)
概述
- 前提
- 基于支持本地ACID事务的关系型数据库。
- Java应用,通过JDBC访问数据库。
- 整体机制
- 两阶段提交协议的演变:
- 一阶段
- 业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
- 二阶段:
- 提交异步化,非常快速地完成。
- 回滚通过一阶段的回滚日志进行反向补偿。
执行流程
-
概览图
-
一阶段加载
-
在一阶段, Seata 会拦截“业务SQL"
- 解析SQL语义,找到“业务SQL"要更新的业务数据,在业务数据被更新前,将其保存成"before image”
- 执行“业务SQL"更新业务数据,在业务数据更新之后
- 其保存成"after image” ,最后生成行锁。
-
以上操作全部在一个数据库事务内完成, 这样保证了一阶段操作的原子性。
-
-
二阶段提交
-
顺利提交
-
因为“业务SQL"在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉, 完成数据清理即可。
-
-
二阶段回滚
- 二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务SQL",还原业务数据。
- 回滚方式便是用"before image"还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和"after image”,
- 如果两份数据完全一致就说明没有脏写, 可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。
总流程
Gitee 实践代码地址
eout = “-1”
max.rollback.retry.timeout= “-1”
}
```properties
## transaction log store
store {
## store mode: file、db
#修改处
mode = "db"
## database store
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
datasource = " dbcp"
## mysq/oracle/h2/oceanbase etc.
db-type = "mysql"
#修改处
driver-class-name = *com.mysql.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata”
user = "root"
password =“你自己密码"
min-conn = 1
max-conn = 3
global.table = *global. _table*
branch.table = "branch_ _table"
lock-table = "lock. _table"
query-limit = 100
}
-
新建数据库表
- 建表db_ store.sql在\seata-server-0.9.0\seata\conf目 录里面
- 涉及到数据库全部要建
-
修改registry.conf
- 指明注册中心为nacos,及修改nacos连接信息
registry { # file、nacos 、eureka、 redis、 zk、 consul、 etcd3、 sofa #修改部分 type = "nacos" nacos { #修改部分 serverAddr = "localhost:8848" namespace = "" cluster = "default" }
-
分布式的模块设置
-
导jar包
<!--seata--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>1.0.0</version> </dependency>
-
配置yml
cloud: alibaba: seata: #自定义事务组名称需要seatg- server中的对应 tx-service-group: fspl_tx_group
-
将file.conf和register.conf拷贝到项目中
- 修改file.conf
-
主启动类取消数据源的自动配置
- 自行配置数据源
-
在事务的入口方法上加上注解@GlobalTransational
-
补充
saeta有四大模式
- AT 模式(默认)
- TCC模式
- SAGA 模式(商业模式)
- XA 模式(开发中)
原理剖析(AT模式)
概述
- 前提
- 基于支持本地ACID事务的关系型数据库。
- Java应用,通过JDBC访问数据库。
- 整体机制
- 两阶段提交协议的演变:
- 一阶段
- 业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
- 二阶段:
- 提交异步化,非常快速地完成。
- 回滚通过一阶段的回滚日志进行反向补偿。
执行流程
-
概览图
[外链图片转存中…(img-hxUnTCNf-1586497723971)]
-
一阶段加载
-
在一阶段, Seata 会拦截“业务SQL"
- 解析SQL语义,找到“业务SQL"要更新的业务数据,在业务数据被更新前,将其保存成"before image”
- 执行“业务SQL"更新业务数据,在业务数据更新之后
- 其保存成"after image” ,最后生成行锁。
-
以上操作全部在一个数据库事务内完成, 这样保证了一阶段操作的原子性。
[外链图片转存中…(img-TyVQAGlh-1586497723972)]
-
-
二阶段提交
-
顺利提交
-
因为“业务SQL"在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉, 完成数据清理即可。
[外链图片转存中…(img-OYvlpwxZ-1586497723972)]
-
-
二阶段回滚
- 二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务SQL",还原业务数据。
- 回滚方式便是用"before image"还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和"after image”,
- 如果两份数据完全一致就说明没有脏写, 可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。
[外链图片转存中…(img-Pao8pUwu-1586497723973)]
总流程
[外链图片转存中…(img-KWAOc9ab-1586497723974)]
[外链图片转存中…(img-1qt701KB-1586497723975)]
Gitee 实践代码地址
https://gitee.com/skyfeacher/springCloud