20200523 尚硅谷2020最新版SpringCloud【笔记】2
尚硅谷2020最新版SpringCloud【笔记】2
10.Hystrix断路器
概述
微服务链路调用
扇出
服务雪崩
Hystrix 是一个用于处理分布式系统的延迟和容错的开源库
能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性
向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常
Hystrix官宣,停更进维
主要功能
- 服务降级
- 服务熔断
- 接近实时的监控
Hystrix重要概念
服务降级(fallback)
服务器忙,请稍候再试,不让客户端等待并立刻返回一个友好提示,fallback
一般用于客户端,即服务消费者
哪些情况会触发降级
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池/信号量打满也会导致服务降级
服务熔断(break)
类比保险丝,达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
服务的降级->进而熔断->恢复调用链路
当失败的调用到一定阈值,默认是5秒内20次调用失败,就会启动熔断机制
服务限流(flowlimit)
秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
降级容错解决的要求
- 超时导致服务器变慢(转圈)
超时不再等待 - 出错(宕机或程序运行出错)
出错要有兜底
Hystrix案例
服务生产者
-
建 Module,cloud-provider-hystrix-payment8001
-
改 POM
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
-
改 YML
-
主启动类,添加注解
@EnableCircuitBreaker
-
服务降级,改业务服务类
// 超时时间设置为 3s @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")}) public String paymentInfo_TimeOut(Integer id) { // 程序异常 int age = 10 / 0; // 超时 // try { // TimeUnit.MILLISECONDS.sleep(5000); // } catch (InterruptedException e) { // e.printStackTrace(); // } return "线程池: " + Thread.currentThread().getName() + " id: " + id + "\t" + "O(∩_∩)O哈哈~" + " 耗时(秒): "; } public String paymentInfo_TimeOutHandler(Integer id) { return "线程池: " + Thread.currentThread().getName() + " 8001系统繁忙或者运行报错,请稍后再试,id: " + id + "\t" + "o(╥﹏╥)o"; }
-
服务熔断
@HystrixCommand
注解中可配的属性位于com.netflix.hystrix.HystrixCommandProperties
中@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"),// 失败率达到多少后跳闸 }) public 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 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: " + id; }
localhost:8001//payment/circuit/1
返回正常,localhost:8001//payment/circuit/-1
会触发降级,多次触发降级后,访问正常 URL,发现返回的是降级后的响应,一段时间后,恢复正常。
服务消费者
-
建 Module,cloud-consumer-feign-hystrix-order80
-
改 POM,同上
-
改 YML
feign: hystrix: enabled: true
-
主启动类,添加注解
@EnableHystrix
-
服务降级,Controller 调用 Feign 接口
// 超时时间 2s @GetMapping("/consumer/payment/hystrix/timeout/{id}") @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")}) public String paymentInfo_TimeOut(@PathVariable("id") Integer id) { // 程序异常 // int age = 10 / 0; String result = paymentHystrixService.paymentInfo_TimeOut(id); return result; } public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) { return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o " + id; }
-
服务降级,Controller 全局服务降级
@RestController @Slf4j @DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") public class OrderHystirxController { @Resource private PaymentHystrixService paymentHystrixService; @GetMapping("/consumer/payment/hystrix/timeout/{id}") @HystrixCommand public String paymentInfo_TimeOut(@PathVariable("id") Integer id) { // 程序异常 // int age = 10 / 0; String result = paymentHystrixService.paymentInfo_TimeOut(id); return result; } public String payment_Global_FallbackMethod() { return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~"; } }
-
服务降级,通配服务降级,针对 Feign 接口的调用
- 实现 Feign 接口
@Component public class PaymentFallbackService implements PaymentHystrixService { @Override public String paymentInfo_OK(Integer id) { return "-----PaymentFallbackService fall back-paymentInfo_OK ,o(╥﹏╥)o"; } @Override public String paymentInfo_TimeOut(Integer id) { return "-----PaymentFallbackService fall back-paymentInfo_TimeOut ,o(╥﹏╥)o"; } }
- Feign 接口添加注解属性
org.springframework.cloud.openfeign.FeignClient#fallback
@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); }
服务熔断原理
熔断类型
- 熔断打开
请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入熔断状态 - 熔断关闭
熔断关闭不会对服务进行熔断 - 熔断半开
部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断
断路器开启或者关闭的条件
- 当满足一定阀值的时候(默认10秒内超过20个请求次数)
- 当失败率达到一定的时候(默认10秒内超过50%请求失败)
- 到达以上阀值,断路器将会开启
- 当开启的时候,所有请求都不会进行转发
- 一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4和5
Hystrix工作流程
服务监控 Hystrix Dashboard
监控的微服务需要依赖 pring-boot-starter-actuator
创建步骤
-
Module,cloud-consumer-hystrix-dashboard9001
-
POM
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency>
-
YML
-
主启动类,添加注解
@EnableHystrixDashboard
-
启动程序,访问
http://localhost:9001/hystrix
断路器演示
-
改造被监控的微服务
/** *此配置是为了服务监控而配置,与服务容错本身无关,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; }
-
访问
http://localhost:9001/hystrix
-
填写
http://localhost:8001/hystrix.stream
11.zuul路由网关
跳过,可以看之前的视频:
尚硅谷经典SpringCloud框架开发教程全套完整版从入门到精通(大牛讲授spring cloud)
12.Gateway新一代网关
概述
Spring Cloud Gateway 使用的 Webflux 中的 reactor-netty 响应式编程组件,底层使用了 Netty 通讯框架
官网
主要功能
- 反向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控
对比 Zuul 和 Gateway
Zuul
Zuul 1进入维护阶段,Zuul 2 一直跳票
在 Spring Cloud F 版,推荐使用 Zuul
基于 Servlet 2.5 使用 阻塞 IO 架构
Gateway
Servlet 3.1 之后有了异步非阻塞的支持
Gateway 基于异步非阻塞模型
Spring Cloud Gateway 具有如下特性:
- 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0 构建
- 动态路由:能够匹配任何请求属性
- 可以对路由指定 Predicate(断言)和 Filter(过滤器)
- 集成 Hystrix 的断路器功能
- 集成 Spring Cloud 服务发现功能
- 请求限流功能
- 支持路径重写
三大核心概念
-
Route(路由)
路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由
-
Predicate(断言)
参考的是java8的
java.util.function.Predicate
开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由 -
Filter(过滤)
指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
核心逻辑
路由转发+执行过滤器链
入门案例
-
Module,cloud-gateway-gateway9527
-
POM
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
-
YML
spring: application: name: cloud-gateway cloud: gateway: routes: - id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名 uri: http://localhost:8001 #匹配后提供服务的路由地址 predicates: - Path=/payment/get/** # 断言,路径相匹配的进行路由 - id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名 uri: http://localhost:8001 #匹配后提供服务的路由地址 predicates: - Path=/payment/lb/** # 断言,路径相匹配的进行路由
-
主启动类
-
测试
直接访问原服务路径
localhost:8001/payment/get/1
和localhost:8001/payment/lb
,可以成功通过网关访问服务
localhost:9527/payment/get/1
和localhost:9527/payment/lb
,可以成功
编码方式配置
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("path_route_atguigu", r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
配置动态路由
默认情况下Gateway会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
Predicate的使用
配置的都是 org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory
接口的实现类
Filter的使用
生命周期
-
pre
在业务逻辑之前
-
post
在业务逻辑之后
种类
自定义全局GlobalFilter
/**
* 要求请求中带参数 uname,否则返回 406, "Not Acceptable"
*/
@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("***********come in MyLogGateWayFilter: " + new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if (uname == null) {
log.info("*******用户名为null,非法用户,o(╥﹏╥)o");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
13.SpringCloud Config分布式配置中心
概述
为不同微服务应用的所有环境提供一个中心化的外部配置
SpringCloud Config 分为服务端和客户端
服务端也称为分布式配置中心,是一个独立的微服务应用
主要功能
-
集中管理配置文件
-
不同环境不同配置,动态化的配置更新,分环境部署比如dev/test/prod/beta/release
-
运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息
-
当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置
-
将配置信息以REST接口的形式暴露
post、curl访问刷新均可....
Config服务端配置与测试
-
Module,cloud-config-center-3344
-
POM
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>
-
YML
server: port: 3344 spring: application: name: cloud-config-center #注册进Eureka服务器的微服务名 cloud: config: server: git: # uri: git@github.com:liuxing5yu/config-repo.git #GitHub上面的git仓库名字 uri: https://github.com/liuxing5yu/config-repo #GitHub上面的git仓库名字 ####读取分支 label: master #服务注册到eureka地址 eureka: client: service-url: defaultZone: http://localhost:7001/eureka
-
主启动类,添加注解
@EnableConfigServer
-
测试,访问 URL
http://localhost:3344/master/configclient-dev.yml
配置读取规则
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
Config客户端配置与测试
-
Module,cloud-config-client-3355
-
POM
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
-
bootstrap.yml
server: port: 3355 spring: application: name: config-client cloud: #Config客户端配置 config: label: master #分支名称 name: configclient #配置文件名称 profile: dev #读取后缀名称 上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.yml uri: http://localhost:3344 #配置中心地址 #服务注册到eureka地址 eureka: client: service-url: defaultZone: http://localhost:7001/eureka
application.yml 是用户级的资源配置项
bootstrap.yml 是系统级的,优先级更高
Spring Cloud 会创建一个 Bootstrap Context,作为 Application Context 的父上下文。初始化的时候,Bootstrap Context 负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的 Environment。
Bootstrap 属性有高优先级,默认情况下,它们不会被本地覆盖。Bootstrap Context 和 Application Context 有着不同的约定,所以新增 bootstrap.yml,保证配置分离。
bootstrap.yml 先于 application.yml 加载。
-
业务类
@RestController public class ConfigClientController { @Value("${my.name}") private String configInfo; @GetMapping("/configInfo") public String getConfigInfo() { return configInfo; } }
-
测试,
http://localhost:3355/configInfo
改变Git里配置的内容后,从3344服务端读取到更新后的内容,3355客户端仍然是改变前的内容
Config客户端之动态刷新
-
POM
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
-
YML
# 暴露监控端点 management: endpoints: web: exposure: include: "*"
-
业务类添加注解
@RefreshScope
-
Git 更改后,发送 Post 请求,
localhost:3355/actuator/refresh