1.hystrix是什么
hystrix官宣停更
2.能做什么
3.重要概念
3.1 服务降级
3.2 服务熔断
3.3服务限流
4.服务降级 案例
4.1流程
创建一个单机版eureka服务注册中心 一个支付微服务(消费端) 和 一个订单微服务(客户端)
支付微服务注册进eureka 并且 订单微服务 通过openfeign来调用支付微服务
4.2创建eureka服务注册中心
pom
(版本控制都在父项目中,springboot版本 2.2.2.RELEASE springcloud版本 Hoxton.SR1 其他的自行选择)
<dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--boot web actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--一般通用配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
yml
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#设置与Eureka server 交互的地址查询服务和注册服务都需要依赖这个地址。
defaultZone: http://localhost:7001/eureka/ #单机版 指向自己
主启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @author Xkuna
* @date 2020/7/22 15:32.
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class, args) ;
}
}
4.3创建支付微服务
4.3.1创建项目基础
pom
<dependencies>
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yml
server:
port: 8001
spring:
application:
name: cloud-provider-hystrix-payment
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
主启动类
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
/**
* @author Xkuna
* @date 2020/8/9 18:20.
*/
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker //启动熔断降级
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class, args) ;
}
}
4.3.2 创建service 层 并且 完成服务降级配置
服务降级配置 无非是给 service层方法发生服务降级时 配置一个“兜底”的方法 以节省访问发生错误时的时间和资源消耗
在这里我们简写 service层 直接写 实现类
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* @author Xkuna
* @date 2020/8/9 18:21.
*/
@Service
public class PaymentService {
public String paymentInfo_ok(Integer id){
return "线程池: "+Thread.currentThread().getName()+" paymentInfo_OK,id: "+id+"\t"+"O(∩_∩)O哈哈~" ;
}
//fallbackMethod 就是发生服务降级时的 “兜底”方法
//commandProperties 是相关配置 是个数组
//HystrixProperty 是具体的单个配置 在这里我们配置的是超时时间 值为 3000 ms
@HystrixCommand(fallbackMethod = "paymentInfoHandler", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
public String paymentInfo_timeout(Integer id){
// int num = 10 / 0 ;
try {
TimeUnit.SECONDS.sleep(5); //设置暂停 5s
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池: "+Thread.currentThread().getName()+" id: "+id+"\t"+"O(∩_∩)O哈哈~" ;
}
//服务降级 “兜底”方法
public String paymentInfoHandler(Integer id) {
return "线程池: "+Thread.currentThread().getName()+", 8001系统访问超时!! id: "+id+"\t"+"O(∩_∩)O哈哈~" ;
}
}
4.3.3 创建controller
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import top.xkuna.springboot.service.PaymentService;
import javax.annotation.Resource;
/**
* @author Xkuna
* @date 2020/8/9 18:24.
*/
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService ;
@Value("${server.port}")
private String port ;
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_ok(@PathVariable("id") Integer id){
String rs = paymentService.paymentInfo_ok(id);
log.info(rs);
return rs ;
}
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_timeout(@PathVariable("id") Integer id){
String rs = paymentService.paymentInfo_timeout(id);
log.info(rs);
return rs ;
}
}
4.3.4 测试
我们首先启动 eureka7001 在启动 支付微服务8001
然后我们访问 正常的接口
http://localhost:8001/payment/hystrix/ok/1
没有问题
我们接着访问设置了 超时的接口
**发现 我们访问到的是 服务降级后的结果 **
支付微服务 hystrix 服务降级搭建成功
4.4创建订单微服务
4.4.1 搭建项目基础
pom
<dependencies>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--一般基础通用配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yml
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://localhost:7001/eureka/
#开启openfeign 服务熔断
feign:
hystrix:
enabled: true
#开启openfeign 服务熔断 必须配置 hystrix超时时间 Hystrix的熔断时间必须大于Ribbon的 ( ConnectTimeout + ReadTimeout )
hystrix:
command:
default:
execution:
timeout:
enabled: true
isolation:
thread:
timeoutInMilliseconds: 10000
#开启openfeign服务熔断 必须配置 ribbon超时时间
ribbon:
ConnectTimeout: 5000
ReadTimeout: 5000
主启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author Xkuna
* @date 2020/8/9 19:45.
*/
@SpringBootApplication
@EnableFeignClients //开启openfeign
public class OrderHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain80.class, args) ;
}
}
4.4.2 创建service业务类 (openfeign 及 解决支付微服务宕机关闭问题)
踩坑~
——————————————————————————
订单服务 调用 支付微服务的超时接口,需要注意一下三点
1. 开启openfeign的熔断
在yml添加
2.配置ribbon超时时间
因为 ribbon默认调用服务 超时时间为 1000ms
支付服务 超时接口 服务降级 时限为 3000 ms
所以当我们访问 支付服务超时接口时 会超时报错
修改为ribbon超时时间 > 支付服务超时接口降级时间
在yml添加
3.配置 熔断时间
如此, 完成支付微服务的yml配置, 便在订单服务调用 支付微服务时, 若支付微服务发生了服务降级,那么订单服务也会获取到 支付微服务的服务降级信息了。
上面的yml配置 已经在搭建基础时配置完成了
——————————————————————————
创建openfeign调用服务接口
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author Xkuna
* @date 2020/8/9 19:46.
*/
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT", fallback = PaymentFallbackService.class)
public interface PaymentServiceClient {
@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) ;
}
我们可以看到与之前的openfeign服务调用有一点不同
多了一个fallback
,这个是用来做什么的呢?
我们现在思考一个问题:支付服务8001 我们完成了服务降级,如果订单服务访问支付服务时,支付服务发生了服务降级,订单服务可以获取到 服务降级的信息,
但是 如果订单服务访问支付服务时, 支付服务宕机或者关机了呢?
订单服务还能获取到 支付服务返回的 服务降级信息吗?
肯定获取不到,所以此时 支付服务要自己为防止这种情况的发生,我们需要 添加一个服务降级处理的实现类
实现类实现 该openfeign调用接口即可
import org.springframework.stereotype.Component;
/**
* @author Xkuna
* @date 2020/8/10 10:09.
*/
@Component //必须注入ioc容器 否则 openfeign调用接口的fallbcak获取不到
public class PaymentFallbackService implements PaymentServiceClient{
@Override
public String paymentInfo_ok(Integer id) {
return "80调用8001失败 (┬_┬)" ;
}
@Override
public String paymentInfo_timeout(Integer id) {
return "80调用8001失败 (┬_┬)" ;
}
}
如此再给openfeign接口 添加fallback 即可
4.4.3创建controller
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.xkuna.springcloud.service.PaymentServiceClient;
import javax.annotation.Resource;
/**
* @author Xkuna
* @date 2020/8/9 19:50.
*/
@RestController
@Slf4j
public class OrderController {
@Resource
private PaymentServiceClient paymentServiceClient ;
@RequestMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_ok(@PathVariable("id") Integer id) {
return paymentServiceClient.paymentInfo_ok(id) ;
}
@RequestMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_timeout(@PathVariable("id") Integer id) {
return paymentServiceClient.paymentInfo_timeout(id) ;
}
}
4.4.5测试
我们的eureka7001 和 支付微服务8001 已经启动
接下来启动我们的 订单微服务80
访问正常接口 http://localhost/consumer/payment/hystrix/ok/1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pyVYATwB-1597123879668)(C:\Users\xkuna\AppData\Roaming\Typora\typora-user-images\image-20200811123759828.png)]
服务调用成功
访问超时接口 http://localhost/consumer/payment/hystrix/timeout/1
浏览器转了一会儿圈 访问到了支付微服务的服务降级信息 这个也ok
接下来我们将 支付微服务 关闭 再测试
上面两个接口
说明我们 配置订单服务调用支付服务的 fallback也成功了
4.5服务降级 全局配置
支付微服务中
如果一个方法调用失败 要配置一个fallback方法 代码相对臃肿
此时 我们可以使用 全局配置
修改 支付服务的service
在类上面添加@DefaultProperties 及其配置
需要配置服务降级的方法上面添加@HystrixCommand即可
注意fallback方法去掉参数
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* @author Xkuna
* @date 2020/8/9 18:21.
*/
@Service
//全局配置服务降级
@DefaultProperties(defaultFallback = "paymentInfoHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
public class PaymentService {
public String paymentInfo_ok(Integer id){
return "线程池: "+Thread.currentThread().getName()+" paymentInfo_OK,id: "+id+"\t"+"O(∩_∩)O哈哈~" ;
}
//fallbackMethod 就是发生服务降级时的 “兜底”方法
//commandProperties 是相关配置 是个数组
//HystrixProperty 是具体的单个配置 在这里我们配置的是超时时间 值为 3000 ms
@HystrixCommand
public String paymentInfo_timeout(Integer id){
// int num = 10 / 0 ;
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池: "+Thread.currentThread().getName()+" id: "+id+"\t"+"O(∩_∩)O哈哈~" ;
}
public String paymentInfoHandler() {
return "线程池: "+Thread.currentThread().getName()+", 8001系统访问超时!! "+"\t"+"O(∩_∩)O哈哈~" ;
}
}
此时我们重启 eureka注册中心 支付微服务 订单微服务
订单服务访问 支付微服务 超时接口 http://localhost/consumer/payment/hystrix/timeout/1
获取到了 支付服务的服务降级信息 全局配置成功~~
如果我们给个别方法配置了单独的 服务降级 那么 发生服务降级时, 调用的是 单独配置的fallback
5.服务熔断 案例
5.1 修改 支付微服务service
添加
这里我们设置发生服务降级的条件是 id <0 抛出异常
本地上出现 访问超时 从而发生服务降级 的可能性很小
//----断路器
@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(Integer id) {
if(id < 0){
throw new RuntimeException("*****id < 0") ;
}
String uuid = UUID.randomUUID().toString().replace("-", "") ;
return Thread.currentThread().getName() + "\t 调用成功, 流水号:" + uuid ;
}
public String paymentCircuitBreaker_fallback(Integer id) {
return "id不能小于0" ;
}
5.2 修改 支付服务controller
//====服务熔断
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id)
{
String result = paymentService.paymentCircuitBreaker(id);
log.info("****result: "+result);
return result;
}
5.3 测试
在这里我们就不写 订单服务 来调用 支付服务的熔断接口了( 跟上面的一样 openfeign接口 写调用方法 实现类实现fallback controller写个接口就行了)
我们直接用支付微服务测试
重启 支付微服务8001
访问 http://localhost:8001/payment/circuit/1
访问成功 多刷新几次 也是没毛病的
访问 http://localhost:8001/payment/circuit/-1
访问成功 返回服务降级信息
我们接着快速多次刷新访问 http://localhost:8001/payment/circuit/-1
然后立马访问 http://localhost:8001/payment/circuit/1
发现 还是返回了 服务降级信息
过一会 在访问 http://localhost:8001/payment/circuit/1
访问正常了
如此,hystrix服务熔断成功~~
6. hystrix的仪表盘
6.1创建项目基础
pom
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yml
server:
port: 9001
启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
/**
* @author Xkuna
* @date 2020/8/10 20:46.
*/
@SpringBootApplication
@EnableHystrixDashboard//启动hystrix仪表盘
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class, args) ;
}
}
6.2 当前版本改动
当前版本需要在被监控的服务 主启动类 添加 指定的监控路径
我们在这里监控的是 支付微服务8001
所以修改 支付微服务8001 主启动类
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
/**
* @author Xkuna
* @date 2020/8/9 18:20.
*/
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class, args) ;
}
/**
*此配置是为了服务监控而配置,与服务容错本身无关,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;
}
}
6.3 测试
启动 仪表盘 9001
浏览器打开 http://localhost:9001/hystrix
快速多次刷新访问 http://localhost:8001/payment/circuit/-1
然后多次访问 http://localhost:8001/payment/circuit/1
7.结束语
到这里 hystrix的服务降级 服务熔断 还有仪表盘 就介绍完了
很可惜的是hystrix停更了 所以 新的技术将逐步替换它
阿里爸爸 的 sentinel是不二之选
真的是学不完啊~~😢 😢