目录
7.2.2 Sentinel与Hystrix的区别Sentinel
1 服务调用Feign入门
@GetMapping("/buy/{id}")
public Product order() {
Product product = restTemplate.getForObject("http://shop-service-product/product/1",Product.class);
return product;
}
由代码可知,我们是使用拼接字符串的方式构造
URL
的,该
URL
只有一个参数。但是,在现实中,
URL中往往含有多个参数。这时候我们如果还用这种方式构造URL
,那么就会非常痛苦。那应该如何解决?
1.1 Feign简介
Feign
是
Netflix
开发的声明式,模板化的
HTTP
客户端,其灵感来自
Retrofit,JAXRS-2.0
以及
WebSocket。
- Feign可帮助我们更加便捷,优雅的调用HTTP API。
- 在SpringCloud中,使用Feign非常简单——创建一个接口,并在接口上添加一些注解,代码就完成了。
- Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等。
- SpringCloud对Feign进行了增强,使Feign支持了SpringMVC注解,并整合了Ribbon和Eureka,从而让Feign的使用更加方便。
1.2 基于Feign的服务调用
(
1
)引入依赖
<!--springcloud整合的openFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
@SpringBootApplication
@EntityScan("cn.awen.order.entity")
//激活Feign
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
}
(3)启动类激活FeignClient
创建一个
Feign
接口,此接口是在
Feign
中调用微服务的核心接口。在服务消费者 shop_service_order
添加一个
ProductFeginClient
接口定义各参数绑定时,@PathVariable
、
@RequestParam
、
@RequestHeader
等可以指定参数属性,在Feign
中绑定参数必须通过
value
属性来指明具体的参数名,不然会抛出异常。
@FeignClient:注解通过
name
指定需要调用的微服务的名称,用于创建
Ribbon
的负载均衡器。 所以Ribbon
会把
shop
-
service
-
product
解析为注册中心的服务。
/**
* 声明需要调用的微服务名称
* @FeignClient
* * name : 服务提供者的名称
*/
@FeignClient(name="service-product")
public interface ProductFeignClient {
/**
* 配置需要调用的微服务接口
*/
@RequestMapping(value="/product/{id}",method = RequestMethod.GET)
public Product findById(@PathVariable("id") Long id);
}
(4)配置请求提供者的调用接口
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private ProductFeignClient productFeignClient;
/**
*/
@RequestMapping(value = "/buy/{id}",method = RequestMethod.GET)
public Product findById(@PathVariable Long id) {
Product product = null;
product = productFeignClient.findById(id);
return product;
}
}
1.3 Feign和Ribbon的联系
- Ribbon是一个基于HTTP和TCP客户端的负载均衡的工具。它可以在客户端配置RibbonServerList(服务端列表),使用 HttpClient 或 RestTemplate 模拟http请求,步骤相当繁琐。
- Feign是在Ribbon的基础上进行了一次改进,是一个使用起来更加方便的 HTTP 客户端。采用接口的方式,只需要创建一个接口,然后在上面添加注解即可,将需要调用的其他服务的方法定义成抽象方法即可,不需要自己构建http请求。然后就像是调用自身工程的方法调用,而感觉不到是调用远程方法,使得编写客户端变得非常容易。
1.4 负载均衡
Feign
中本身已经集成了
Ribbon
依赖和自动配置,因此我们不需要额外引入依赖,也不需要再注册RestTemplate 对象。另外,我们可以像上节课中讲的那样去配置
Ribbon
,可以通过
ribbon.xx
来进行全局配置。也可以通过 服务名
.ribbon.xx
来对指定服务配置。
2 服务调用Feign高级
2.1 Feign的配置
从
Spring Cloud Edgware
开始,
Feign
支持使用属性自定义
Feign
。对于一个指定名称的
FeignClient(例如该
Feign Client
的名称为
feignName
),
Feign
支持如下配置项:
2.2 请求压缩
Spring Cloud Feign
支持对请求和响应进行
GZIP
压缩,以减少通信过程中的性能损耗。通过下面的参数即可开启请求与响应的压缩功能。
同时,我们也可以对请求的数据类型,以及触发压缩的大小下限进行设置,且上面的数据类型、压缩大小下限均为默认值。
feign:
compression:
request: enabled: true # 开启请求压缩
mime-types: text/html,application/xml,application/json # 设置压缩的数据类型
min-request-size: 2048 # 设置触发压缩的大小下限
response: enabled: true # 开启响应压缩
2.3 日志级别
在开发或者运行阶段往往希望看到
Feign
请求过程的日志记录,默认情况下
Feign
的日志是没有开启的。要想用属性配置方式来达到日志效果,只需在 application.yml
中添加如下内容即可。
日志级别:
- NONE【性能最佳,适用于生产】:不记录任何日志(默认值)
- BASIC【适用于生产环境追踪问题】:仅记录请求方法、URL、响应状态代码以及执行时间
- HEADERS:记录BASIC级别的基础上,记录请求和响应的header。
- FULL【比较适用于开发及测试环境定位问题】:记录请求和响应的header、body和元数据。
#配置feign日志的输出
#日志配置 NONE : 不输出日志(高) BASIC: 适用于生产环境追踪问题
#HEADERS : 在BASIC的基础上,记录请求和响应头信息 FULL : 记录所有
feign:
client:
config:
service-product: #需要调用的服务名称
loggerLevel: FULL
logging:
level:
cn.itcast.order.feign.ProductFeignClient: debug
2.4 源码分析
通过上面的使用过程,
@EnableFeignClients
和
@FeignClient
两个注解就实现了
Feign
的功能,那我们从@EnableFeignClients注解开始分析
Feign
的源码。
(
1
)
EnableFeignClients
注解
(
5
)
发送请求
3 服务注册与发现总结
3.1 组件的使用方式
3.1.1 注册中心
(
1
)
Eureka
搭建注册中心
- 引入依赖 spring-cloud-starter-netflix-eureka-server
- 配置EurekaServer
- 通过 @EnableEurekaServer 激活Eureka Server端配置
服务注册
- 服务提供者引入 spring-cloud-starter-netflix-eureka-client 依赖
- 通过 eureka.client.serviceUrl.defaultZone 配置注册中心地址
(
2
)
consul
搭建注册中心
- 下载安装consul
- 启动consul consul agent -dev
服务注册
- 服务提供者引入 spring-cloud-starter-consul-discovery 依赖
- 通过 spring.cloud.consul.host 和 spring.cloud.consul.port 指定Consul Server的请求地址
3.1.2 服务调用
(
1
)
Ribbon
通过
Ribbon
结合
RestTemplate
方式进行服务调用只需要在声明
RestTemplate
的方法上添加注解@LoadBalanced即可。可以通过 {
服务名称
}.ribbon.NFLoadBalancerRuleClassName
配置负载均衡策略。
(
2
)
Feign
服务消费者引入
spring
-
cloud
-
starter
-
openfeign
依赖通过 @FeignClient
声明一个调用远程微服务接口。启动类上通过 @EnableFeignClients
激活
Feign。
4 微服务架构的高并发问题
4.1 性能工具Jmetter
Apache JMeter
是
Apache
组织开发的基于
Java
的压力测试工具。用于对软件做压力测试,它最初被设计用于Web
应用测试,但后来扩展到其他测试领域。 它可以用于测试静态和动态资源,例如静态文件、Java 小服务程序、
CGI
脚本、
Java
对象、数据库、
FTP
服务器等等。JMeter
可以用于对服务器、网络或对象模拟巨大的负载,来自不同压力类别下测试它们的强度和分析整体性能。另外JMeter
能够对应用程序做功能/
回归测试,通过创建带有断言的脚本来验证你的程序返回了你期望的结果。为了最大限度的灵活性,JMeter
允许使用正则表达式创建断言。
4.1.1 安装Jmetter
4.1.2 配置Jmetter
(
1
)创建新的测试计划
- 可以配置请求的线程数
- 以及每个请求发送的请求次数
(
3
)创建
http
请求模板
(
4
)配置测试的接口信息
4.2 系统负载过高存在的问题
4.2.1 问题分析
在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用,由于网络原因或者自身的原因,服务并不能保证服务的100%
可用,如果单个服务出现问题,调用这个服务就会出现网络延迟,此时若有大量的网络涌入,会形成任务累计,导致服务瘫痪。在SpringBoot
程序中,默认使用内置
tomcat
作为
web
服务器。单
tomcat
支持最大的并发请求是有限的,如果某一接口阻塞,待执行的任务积压越来越多,那么势必会影响其他接口的调用。
4.2.2 线程池的形式实现服务隔离
(
1
)
配置坐标
为了方便实现线以线程池的形式完成资源隔离,需要引入如下依赖
<!--hystrix-->
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-metrics-event-stream</artifactId>
<version>1.5.12</version>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>1.5.12</version>
</dependency>
(2)配置线程池
配置
HystrixCommand
接口的实现类,再实现类中可以对线程池进行配置。
package cn.itcast.order.command;
import cn.itcast.order.entity.Product;
import com.netflix.hystrix.*;
import org.springframework.web.client.RestTemplate;
public class OrderCommand extends HystrixCommand<Product> {
private RestTemplate restTemplate;
private Long id;
public OrderCommand(RestTemplate restTemplate, Long id) {
super(setter());
this.restTemplate = restTemplate;
this.id = id;
}
private static Setter setter() {
// 服务分组
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("order_product");
// 服务标识
HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("product");
// 线程池名称
HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("order_product_pool");
/**
* 线程池配置
* withCoreSize : 线程池大小为10
* withKeepAliveTimeMinutes: 线程存活时间15秒
* withQueueSizeRejectionThreshold :队列等待的阈值为100,超过100执行拒绝策略
*/
HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter().withCoreSize(50)
.withKeepAliveTimeMinutes(15).withQueueSizeRejectionThreshold(100);
// 命令属性配置Hystrix 开启超时
HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
// 采用线程池方式实现服务隔离
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
// 禁止
.withExecutionTimeoutEnabled(false);
return Setter.withGroupKey(groupKey).andCommandKey(commandKey).andThreadPoolKey(threadPoolKey)
.andThreadPoolPropertiesDefaults(threadPoolProperties).andCommandPropertiesDefaults(commandProperties);
}
@Override
protected Product run() throws Exception {
System.out.println(Thread.currentThread().getName());
return restTemplate.getForObject("http://127.0.0.1/product/"+id, Product.class);
}
/**
* 降级方法
*/
@Override
protected Product getFallback(){
Product product = new Product();
product.setProductName("不好意思,出错了");
return product;
}
}
/**
* 使用OrderCommand调用远程服务
*/
@RequestMapping(value = "/buy/{id}",method = RequestMethod.GET)
public Product findById(@PathVariable Long id) {
return new OrderCommand(restTemplate,id).execute();
}
5 服务熔断Hystrix入门
5.1 服务容错的核心知识
5.1.1 雪崩效应
在微服务架构中,一个请求需要调用多个服务是非常常见的。如客户端访问
A
服务,而
A
服务需要调用
B服务,B
服务需要调用
C
服务,由于网络原因或者自身的原因,如果
B
服务或者
C
服务不能及时响应,
A
服务将处于阻塞状态,直到B
服务
C
服务响应。此时若有大量的请求涌入,容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,造成连锁反应,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“
雪崩
”
效应。
雪崩是系统中的蝴蝶效应导致其发生的原因多种多样,有不合理的容量设计,或者是高并发下某一个方法响应变慢,亦或是某台机器的资源耗尽。源头上我们无法完全杜绝雪崩源头的发生,但是雪崩的根本原因来源于服务之间的强依赖,所以我们可以提前评估,做好熔断,隔离,限流。
5.1.2 服务隔离
顾名思义,它是指将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖。当有故障发生时,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其它模块,不影响整体的系统服务。
5.1.3 熔断降级
熔断这一概念来源于电子工程中的断路器(
Circuit Breaker
)。在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。所谓降级,就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值。 也可以理解为兜底。
5.1.4 服务限流
限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已达到保护系统的目的。一般来说系统的吞吐量是可以被测算的,为了保证系统的稳固运行,一旦达到的需要限制的阈值,就需要限制流量并采取少量措施以完成限制流量的目的。比方:推迟解决,拒绝解决,或者者部分拒绝解决等等。
5.2 Hystrix介绍
- 包裹请求:使用HystrixCommand包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用了设计模式中的“命令模式”。
- 跳闸机制:当某服务的错误率超过一定的阈值时,Hystrix可以自动或手动跳闸,停止请求该服务一段时间。
- 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等待,从而加速失败判定。
- 监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等。
- 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑由开发人员自行提供,例如返回一个缺省值。
- 自我修复:断路器打开一段时间后,会自动进入“半开”状态。
5.3 Rest实现服务熔断
(
1
)复制
shop_service_order
项目并命名为
shop_service_order_rest_hystrix
略
(
2
)配置依赖
<!--引入hystrix依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
(
3
)开启熔断
@SpringBootApplication
//激活hystrix
@EnableCircuitBreaker
@EntityScan("cn.itcast.order.entity")
public class RestOrderApplication {
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(RestOrderApplication.class,args);
}
}
(
4
)
配置熔断降级业务逻辑
有代码可知,为
findProduct
方法编写一个回退方法
fifindProductFallBack
,该方法与
findProduct
方法具有相同的参数与返回值类型,该方法返回一个默认的错误信息。在 Product
方法上,使用注解
@HystrixCommand
的
fallbackMethod
属性,指定熔断触发的降级方法是 findProductFallBack
。
- 因为熔断的降级逻辑方法必须跟正常逻辑方法保证:相同的参数列表和返回值声明。
- 在 findProduct 方法上 HystrixCommand(fallbackMethod = "findProductFallBack") 用来声明一个降级逻辑的方法
当
shop
-
service
-
product
微服务正常时,浏览器访问
http://localhost:9001/order/product/1
可以正常调用服务提供者获取数据。当将商品微服务停止时继续访问此时Hystrix
配置已经生效进入熔断降级方法。
@RestController
@RequestMapping("/order")
/**
* @DefaultProperties : 指定此接口中公共的熔断设置
* 如果过在@DefaultProperties指定了公共的降级方法
* 在@HystrixCommand不需要单独指定了
*/
//@DefaultProperties(defaultFallback = "defaultFallBack")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
/**
* 使用注解配置熔断保护
* fallbackmethod : 配置熔断之后的降级方法
*/
@HystrixCommand(fallbackMethod = "orderFallBack")
@RequestMapping(value = "/buy/{id}",method = RequestMethod.GET)
public Product findById(@PathVariable Long id) {
if(id != 1) {
throw new RuntimeException("服务器异常");
}
return restTemplate.getForObject("http://service-product/product/1",Product.class);
}
/**
* 降级方法
* 和需要收到保护的方法的返回值一致
* 方法参数一致
*/
public Product orderFallBack(Long id) {
Product product = new Product();
product.setProductName("触发降级方法");
return product;
}
}
默认的
Fallback
@RestController
@RequestMapping("/order")
/**
* @DefaultProperties : 指定此接口中公共的熔断设置
* 如果过在@DefaultProperties指定了公共的降级方法
* 在@HystrixCommand不需要单独指定了
*/
//@DefaultProperties(defaultFallback = "defaultFallBack")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
/**
* 使用注解配置熔断保护
* fallbackmethod : 配置熔断之后的降级方法
*/
@HystrixCommand(fallbackMethod = "orderFallBack")
@RequestMapping(value = "/buy/{id}",method = RequestMethod.GET)
public Product findById(@PathVariable Long id) {
if(id != 1) {
throw new RuntimeException("服务器异常");
}
return restTemplate.getForObject("http://service-product/product/1",Product.class);
}
/**
* 指定统一的降级方法
* * 参数 : 没有参数
*/
public Product defaultFallBack() {
Product product = new Product();
product.setProductName("触发统一的降级方法");
return product;
}
}
超时设置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000 #默认的连接超时时间1秒,若1秒没有返回数据,自动的触发降级逻辑
5.4 Feign实现服务熔断
SpringCloud Fegin
默认已为
Feign
整合了
hystrix
,所以添加
Feign
依赖后就不用在添加
hystrix
,那么怎么才能让Feign
的熔断机制生效呢,只要按以下步骤开发:
(
1)
配置依赖
<!--引入hystrix依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
feign:
#开启对hystrix的支持
hystrix:
enabled: true
(
3
)配置
FeignClient
接口的实现类
@Component
public class ProductFeignClientCallBack implements ProductFeignClient {
/**
* 熔断降级的方法
*/
public Product findById(Long id) {
Product product = new Product();
product.setProductName("feign调用触发熔断降级方法");
return product;
}
}
/**
* 声明需要调用的微服务名称
* @FeignClient
* * name : 服务提供者的名称
* * fallback : 配置熔断发生降级方法
* 实现类
*/
@FeignClient(name="service-product",fallback = ProductFeignClientCallBack.class)
public interface ProductFeignClient {
/**
* 配置需要调用的微服务接口
*/
@RequestMapping(value="/product/{id}",method = RequestMethod.GET)
public Product findById(@PathVariable("id") Long id);
}
6 服务熔断Hystrix高级
6.1 Hystrix的监控平台
除了实现容错功能,
Hystrix
还提供了近乎实时的监控,
HystrixCommand
和HystrixObservableCommand在执行时,会生成执行结果和运行指标。比如每秒的请求数量,成功数量等。这些状态会暴露在Actuator
提供的
/health
端点中。只需为项目添加
spring
-
boot
-
actuator
依赖,重启项目,访问
http://localhost:9001/actuator/hystrix.stream
,
即可看到实时的监控数据。
(
1
)导入依赖
<!--引入hystrix的监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
(
2
)在启动类使用@EnableHystrixDashboard
注解激活仪表盘项目
//激活hystrix
@EnableCircuitBreaker
(
3
)暴露所有actuator监控的端点
#暴露所有端点
management:
endpoints:
web:
exposure:
include: '*'
6.1.1 搭建Hystrix DashBoard监控
刚刚讨论了
Hystrix
的监控,但访问
/hystrix.stream
接口获取的都是已文字形式展示的信息。很难通过文字直观的展示系统的运行状态,所以Hystrix
官方还提供了基于图形化的
DashBoard
(仪表板)监控平台。Hystrix
仪表板可以显示每个断路器(被
@HystrixCommand
注解的方法)的状态。
(
1
)导入依赖
<!--引入hystrix的监控信息-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
(
2
)在启动类使用@EnableHystrixDashboard
注解激活仪表盘项目
//激活hystrix
@EnableCircuitBreaker
//激活hytrix的web监控平台
@EnableHystrixDashboard
(
3
)访问测试
localhost:9003/hytrix
6.1.2断路器聚合监控turbine
在微服务架构体系中,每个服务都需要配置
Hystrix DashBoard
监控。如果每次只能查看单个实例的监控数据,就需要不断切换监控地址,这显然很不方便。要想看这个系统的Hystrix Dashboard
数据就需
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
(
2
)配置多个微服务的hystrix
监控
eureka
相关配置 :
server:
port: 8031
spring:
application:
name: hystrix-turbine
eureka:
client:
service-url:
defaultZone: http://localhost:9000/eureka/
instance:
prefer-ip-address: true
turbine:
# 要监控的微服务列表,多个用,分隔
appConfig: service-order
clusterNameExpression: "'default'"
(
3
)配置启动类
@SpringBootApplication
//trubin配置
@Enableturbine
@EnableHystrixDashboard
public class TurbinAppliation {
public static void main(String[] args) {
SpringApplication.run(TurbinAppliation.class,args);
}
}
(
4
)测试
浏览器访问
http://localhost:8031/hystrix
展示
HystrixDashboard
。并在
url
位置输入
http://localhost:8031/turbine.stream
,动态根据
turbine.stream
数据展示多个微服务的监控数据
6.2 熔断器的状态
熔断器有三个状态
CLOSED
、
OPEN
、
HALF_OPEN
熔断器默认关闭状态,当触发熔断后状态变更为OPEN ,
在等待到指定的时间,
Hystrix
会放请求检测服务是否开启,这期间熔断器会变为
HALF_OPEN
半开启状态,熔断探测服务可用则继续变更为 CLOSED
关闭熔断器。
- Closed:关闭状态(断路器关闭),所有请求都正常访问。代理类维护了最近调用失败的次数,如果某次调用失败,则使失败次数加1。如果最近失败次数超过了在给定时间内允许失败的阈值,则代理类切换到断开(Open)状态。此时代理开启了一个超时时钟,当该时钟超过了该时间,则切换到半断开(Half-Open)状态。该超时时间的设定是给了系统一次机会来修正导致调用失败的错误。
- Open:打开状态(断路器打开),所有请求都会被降级。Hystix会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全关闭。默认失败比例的阈值是50%,请求次数最少不低于20次。
- Half Open:半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5S)。随后断路器会自动进入半开状态。此时会释放1次请求通过,若这个请求是健康的,则会关闭断路器,否则继续保持打开,再次进行5秒休眠计时。
为了能够精确控制请求的成功或失败,我们在
shop_service_product
的调用业务中加入一段逻辑:
@Autowired
private RestTemplate restTemplate;
/**
* 使用注解配置熔断保护
* fallbackmethod : 配置熔断之后的降级方法
*/
@HystrixCommand(fallbackMethod = "orderFallBack")
@RequestMapping(value = "/buy/{id}",method = RequestMethod.GET)
public Product findById(@PathVariable Long id) {
if(id != 1) {
throw new RuntimeException("服务器异常");
}
return restTemplate.getForObject("http://service-product/product/1",Product.class);
}
这样如果参数是
id
为
1
,一定失败,其它情况都成功。
我们准备两个请求窗口:
- requestVolumeThreshold:触发熔断的最小请求次数,默认20
- errorThresholdPercentage:触发熔断的失败请求最小占比,默认50%
- sleepWindowInMilliseconds:熔断多少秒后去尝试请求
hystrix:
command:
default:
circuitBreaker:
requestVolumeThreshold: 5 #触发熔断的最小请求次数,默认20 /10秒
sleepWindowInMilliseconds: 10000 #熔断多少秒后去尝试请求 默认 5 打开状态的时间
errorThresholdPercentage: 50 #触发熔断的失败请求最小占比,默认50%
当我们疯狂访问
id
为
1
的请求时(超过
10
次),就会触发熔断。断路器会端口,一切请求都会被降级处理。
此时你访问
id
为
2
的请求,会发现返回的也是失败,而且失败时间很短,只有
20
毫秒左右。
6.3 熔断器的隔离策略
- 线程池隔离策略:使用一个线程池来存储当前的请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求堆积入线程池队列。这种方式需要为每个依赖的服务申请线程池,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢处理)
- 信号量隔离策略:使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃改类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务)。
hystrix:
command:
default:
execution:
isolation:
strategy: ExecutionIsolationStrategy.SEMAPHORE #信号量隔离
#strategy: # ExecutionIsolationStrategy.THREAD 线程池隔离
7 服务熔断Hystrix的替换方案
18
年底
Netflflix
官方宣布
Hystrix
已经足够稳定,不再积极开发
Hystrix
,该项目将处于维护模式。就目前来看Hystrix
是比较稳定的,并且
Hystrix
只是停止开发新的版本,并不是完全停止维护,
Bug
什么的依然会维护的。因此短期内,Hystrix
依然是继续使用的。但从长远来看,
Hystrix
总会达到它的生命周期,那么Spring Cloud
生态中是否有替代产品呢?
7.1 替换方案介绍
Alibaba Sentinel
Sentinel 是阿里巴巴开源的一款断路器实现,目前在Spring Cloud的孵化器项目Spring Cloud Alibaba中的一员Sentinel本身在阿里内部已经被大规模采用,非常稳定。因此可以作为一个较好的替代品。
Resilience4J
Resilicence4J 一款非常轻量、简单,并且文档非常清晰、丰富的熔断工具,这也是Hystrix官方推荐的替代产品。不仅如此,Resilicence4j还原生支持Spring Boot 1.x/2.x,而且监控也不像Hystrix一样弄Dashboard/Hystrix等一堆轮子,而是支持和Micrometer(Pivotal开源的监控门面,Spring Boot 2.x中的Actuator就是基于Micrometer的)、prometheus(开源监控系统,来自谷歌的论文)、以及Dropwizard metrics(Spring Boot曾经的模仿对象,类似于Spring Boot)进行整合。
7.2 Sentinel概述
7.2.1 Sentinel简介
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 SpringCloud、dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。
- 完善的SPI扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel
的主要特性:
7.2.2 Sentinel与Hystrix的区别Sentinel
7.2.3 迁移方案
7.2.4 名词解释
使用
Sentinel
来进行熔断保护,主要分为几个步骤
:
- 1. 定义资源
- 2. 定义规则
- 3. 检验规则是否生效
规则
:
Sentinel
支持以下几种规则:流量控制规则、熔断降级规则、系统保护规则、来源访问控制规则和热点参数规则。Sentinel
的所有规则都可以在内存态中动态地查询及修改,修改之后立即生效先把可能需要保护的资源定义好,之后再配置规则。也可以理解为,只要有了资源,我们就可以在任何时候灵活地定义各种流量控制规则。在编码的时候,只需要考虑这个代码是否需要保护,如果需要保护,就将之定义为一个资源。
7.3 Sentinel中的管理控制台
7.3.1 下载启动控制台
您可以从官方
网站中
下载最新版本的控制台
jar
包,下载地址如下:
https://github.com/alibaba/Sentinel/releases/download/1.6.3/sentinel-dashboard-1.6.3.jar
(
2
)启动
使用如下命令启动控制台:
- 其中 -Dserver.port=8080 用于指定 Sentinel 控制台端口为 8080 。
- 从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel 。可以参考鉴权模块文档配置用户名和密码。
- 启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本。
7.3.2 客户端能接入控制台
控制台启动后,客户端需要按照以下步骤接入到控制台。
(
1
)
引入
JAR
包
客户端需要引入
Transport
模块来与
Sentinel
控制台进行通信。可以通过
pom.xml
引入
JAR
包
:
(
2
)配置启动参数
7.3.3 查看机器列表以及健康情况
sentinel.eager=true
,
取消
Sentinel
控制台懒加载。
打开浏览器即可展示
Sentinel
的管理控制台
7.4 基于Sentinel的服务保护
7.4.1
通用资源保护
(
1
)
案例准备
复制工程
shop_service_order
并命名为
shop_service_order_rest_sentinel
(
2
)引入依赖
需要注意
SpringCloud-Alibaba
与
SpringCloud
的版本关系。父工程引入alibaba实现的
SpringCloud。
<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
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
(
3
)
在客户端配置启动参数
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080 #sentinel控制台的请求地址
在需要被保护的方法上使用
@SentinelResource
注解进行熔断配置。与
Hystrix
不同的是,
Sentinel
对抛出异常和熔断降级做了更加细致的区分,通过 blockHandler
指定熔断降级方法,通过
fallback
指定触发异常执行的降级方法。对于@SentinelResource
的其他配置如下表:
特别地,若
blockHandler
和
fallback
都进行了配置,则被限流降级而抛出
BlockException
时只会进入 blockHandler
处理逻辑。若未配置
blockHandler
、
fallback
和
defaultFallback
,则被限流降级时会将 BlockException
直接抛出
。
7.4.2 Rest实现熔断
Spring Cloud Alibaba Sentinel 支持对 RestTemplate 的服务调用使用 Sentinel 进行保护,在构造RestTemplate bean的时候需要加上 @SentinelRestTemplate 注解。
- @SentinelRestTemplate 注解的属性支持限流( blockHandler , blockHandlerClass )和降级( fallback , fallbackClass )的处理。
- 其中 blockHandler 或 fallback 属性对应的方法必须是对应 blockHandlerClass 或fallbackClass 属性中的静态方法。
- 该方法的参数跟返回值跟 org.springframework.http.client.ClientHttpRequestInterceptor#interceptor 方法一致,其中参数多出了一个 BlockException 参数用于获取 Sentinel 捕获的异常。
public class ExceptionUtils {
/**
* 静态方法
* 返回值: SentinelClientHttpResponse
* 参数 : request , byte[] , clientRquestExcetion , blockException
*/
//限流熔断业务逻辑
public static SentinelClientHttpResponse handleBlock(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
return new SentinelClientHttpResponse("abc");
}
//异常降级业务逻辑
public static SentinelClientHttpResponse handleFallback(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution, BlockException ex) {
return new SentinelClientHttpResponse("def");
}
}
Sentinel RestTemplate
限流的资源规则提供两种粒度:
- httpmethod:schema://host:port/path :协议、主机、端口和路径
- httpmethod:schema://host:port :协议、主机和端口
7.4.3 Feign实现熔断
Sentinel
适配了
Feign
组件。如果想使用,除了引入
sentinel
-
starter
的依赖外还需要
2
个步骤:
- 配置文件打开 sentinel 对 feign 的支持: feign.sentinel.enabled=true。
- 加入 openfeign starter 依赖使 sentinel starter 中的自动化配置类生效。
(
1
)
引入依赖
<!--feign对sentinel的支持-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
#激活sentinel的支持
feign:
sentinel:
enabled: true
(
3
)配置
FeignClient
/**
* 声明需要调用的微服务名称
* @FeignClient
* * name : 服务提供者的名称
* * fallback : 配置熔断发生降级方法
* 实现类
*/
@FeignClient(name="service-product",fallback = ProductFeignClientCallBack.class)
public interface ProductFeignClient {
/**
* 配置需要调用的微服务接口
*/
@RequestMapping(value="/product/{id}",method = RequestMethod.GET)
public Product findById(@PathVariable("id") Long id);
}