Spring cloud day(5) Hystrix
一、Hystrix
1.1 分布式系统面临的问题
1.2 服务雪崩
1.3 Hystrix
1.4 服务降级
- 服务不可用,启动背备方案,就是服务降级
1.5 服务熔断
达到最大服务访问,直接拒绝访问
1. 6 服务限流
限制特定时间段或者特定条件下的流量
二、项目搭建
2.1 pom
<dependencies>
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>CommModule</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<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>
2.2 yml
server:
port: 8001
spring:
application:
name: cloud-provider-hystrix-payment
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
#defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
defaultZone: http://eureka7001.com:7001/eureka
2.3 services
package com.pack.example.Services;
import cn.hutool.core.util.IdUtil;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import java.util.concurrent.TimeUnit;
public class PaymentServices {
/**
* 正常访问,肯定OK
*
* @param id
* @return
*/
public String paymentInfoOK(Integer id) {
return "线程池: " + Thread.currentThread().getName() + " paymentInfoOK,id: " + id + "\t"
+ "O(∩_∩)O哈哈~";
}
/**
* 超时访问,设置自身调用超时的峰值,峰值内正常运行,超过了峰值需要服务降级 自动调用fallbackMethod 指定的方法
* 超时异常或者运行异常 都会进行服务降级
*
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "paymentInfoTimeOutHandler", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
})
public String paymentInfoTimeOut(Integer id) {
// int age = 10/0;
int second = 3;
long start = System.currentTimeMillis();
try {
TimeUnit.SECONDS.sleep(second);
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
return "线程池: " + Thread.currentThread().getName() + " paymentInfoTimeOut,id: " + id + "\t"
+ "O(∩_∩)O哈哈~" + " 耗时(秒): " + second;
}
/**
* paymentInfoTimeOut 方法失败后 自动调用此方法 实现服务降级 告知调用者 paymentInfoTimeOut 目前无法正常调用
*
* @param id
* @return
*/
public String paymentInfoTimeOutHandler(Integer id) {
return "线程池: " + Thread.currentThread().getName() + " paymentInfoTimeOutHandler8001系统繁忙或者运行报错,请稍后再试,id: " + id + "\t"
+ "o(╥﹏╥)o";
}
/**
* 服务熔断 超时、异常、都会触发熔断
* 1、默认是最近10秒内收到不小于10个请求,<br/>
* 2、并且有60%是失败的<br/>
* 3、就开启断路器<br/>
* 4、开启后所有请求不再转发,降级逻辑自动切换为主逻辑,减小调用方的响应时间<br/>
* 5、经过一段时间(时间窗口期,默认是5秒),断路器变为半开状态,会让其中一个请求进行转发。<br/>
* 5.1、如果成功,断路器会关闭,<br/>
* 5.2、若失败,继续开启。重复4和5<br/>
*
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "paymentCircuitBreakerFallback", 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"),// 失败率达到多少后跳闸
// @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")// 超时处理
})
public String paymentCircuitBreaker(Integer id) {
if (id < 0) {
throw new RuntimeException("******id 不能负数");
}
//测试异常
// int age = 10 / 0;
// int second = 3;
// try {
// TimeUnit.SECONDS.sleep(second);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName() + "\t" + "调用成功,流水号: " + serialNumber;
}
/**
* paymentCircuitBreaker 方法的 fallback,<br/>
* 当断路器开启时,主逻辑熔断降级,该 fallback 方法就会替换原 paymentCircuitBreaker 方法,处理请求
*
* @param id
* @return
*/
public String paymentCircuitBreakerFallback(Integer id) {
return Thread.currentThread().getName() + "\t" + "id 不能负数或超时或自身错误,请稍后再试,/(ㄒoㄒ)/~~ id: " + id;
}
}
2.4 主启动类
@SpringBootApplication
@EnableEurekaClient
public class hystrixMain {
public static void main(String[] args) {
SpringApplication.run(hystrixMain.class, args);
}
}
2.5 controller
package com.atguigu.springcloud.controller;
import com.pack.example.Services.PaymentServices;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* (Payment)表控制层
*
* @author lixiaolong
* @date 2022/01/13 16:05
*/
@RestController
@RequestMapping("payment")
@Slf4j
public class myController {
/**
* 服务对象
*/
@Resource
private PaymentServices paymentService;
@Value("${server.port}")
private String serverPort;
/**
* 正常访问
*
* @param id
* @return
*/
@GetMapping("/hystrix/ok/{id}")
public String paymentInfoOK(@PathVariable("id") Integer id) {
String result = paymentService.paymentInfoOK(id);
log.info("result: " + result);
return result;
}
/**
* 超时或者异常
*
* @param id
* @return
*/
@GetMapping("/hystrix/timeout/{id}")
public String paymentInfoTimeOut(@PathVariable("id") Integer id) {
String result = paymentService.paymentInfoTimeOut(id);
log.info("result: " + result);
return result;
}
/**
* 服务熔断
*
* @param id
* @return
*/
@GetMapping("/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
String result = paymentService.paymentCircuitBreaker(id);
log.info("****result: " + result);
return result;
}
}
2.6 请求过多时
2.7 feign consumer
- pom
<dependencies>
<!--新增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-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<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: 9001
- 主启动类
@SpringBootApplication
@EnableHystrixDashboard
public class feignMain {
public static void main(String[] args) {
SpringApplication.run(feignMain.class, args);
System.out.println("启动成功");
}
}
- services 接口,用来调用提供者提供的服务,与提供者提供的服务一致
@Component
// FeignFallback 客户端的服务降级 针对 CLOUD-PROVIDER-HYSTRIX-PAYMENT 该服务 提供了一个 对应的服务降级类
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT", fallback = PaymentFallbackServiceImpl.class)
//@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfoOK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
String paymentInfoTimeOut(@PathVariable("id") Integer id);
}
- controller
package com.pcak.feign.controller;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.pcak.feign.services.PaymentHystrixService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
//@RequestMapping("consumer")
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")// hystrix 全局fallback方法
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfoOK(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfoOK(id);
return result;
}
@GetMapping("/payment/hystrix/timeout/{id}")
// @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
// @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
// })
@HystrixCommand
public String paymentInfoTimeOut(@PathVariable("id") Integer id) {
int age = 10 / 0;
String result = paymentHystrixService.paymentInfoTimeOut(id);
return result;
}
/**
* 超时访问,设置自身调用超时的峰值,峰值内正常运行,超过了峰值需要服务降级 自动调用fallbackMethod 指定的方法
* <br/>
* 超时异常或者运行异常 都会进行服务降级
*
* @param id
* @return
*/
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
}
/**
* hystrix 全局fallback方法
*
* @return
*/
public String payment_Global_FallbackMethod() {
return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
}
}
2.8 解决的问题
- 超时导致服务器变慢
- 宕机或者程序出错
- 解决
三、Hystrix 应用
3.1 @HystrixCommand
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
}))
3.2 服务提供端使用服务降级
- service 中的paymentInfoTimeOut方法,服务降级限定的是6s若无响应则调用·paymentInfoTimeOutHandler()方法
@HystrixCommand(fallbackMethod = "paymentInfoTimeOutHandler", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
})
public String paymentInfoTimeOut(Integer id) {
// int age = 10/0;
int second = 8;
long start = System.currentTimeMillis();
try {
TimeUnit.SECONDS.sleep(second);
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
return "线程池: " + Thread.currentThread().getName() + " paymentInfoTimeOut,id: " + id + "\t"
+ "O(∩_∩)O哈哈~" + " 耗时(秒): " + second;
}
- 启动类变化:@EnableCircuitBreaker
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class hystrixMain {
public static void main(String[] args) {
SpringApplication.run(hystrixMain.class, args);
}
}
- 运行结果
- 当service中出错,比如:int i = 10 / 0,也会服务降级
3.3 服务消费者端进行服务降级
- yml
feign:
hystrix:
enabled: true
- 主启动类
@SpringBootApplication
//@EnableHystrixDashboard
@EnableHystrix
@EnableFeignClients
public class feignMain {
public static void main(String[] args) {
SpringApplication.run(feignMain.class, args);
System.out.println("启动成功");
}
}
- controller
@GetMapping("/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
})
public String paymentInfoTimeOut(@PathVariable("id") Integer id) {
int age = 10 / 0;
String result = paymentHystrixService.paymentInfoTimeOut(id);
return result;
}
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
}
public String payment_Global_FallbackMethod() {
return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
}
3.4 全局服务降级@DefaultProperties
- 在controller层上进行处理
- 需要兜底的业务接口加上@HystrixCommand
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")// hystrix 全局fallback方法
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfoOK(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfoOK(id);
return result;
}
public String payment_Global_FallbackMethod() {
return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
}
......
}
3.5 对远程调用的接口进行同意的降级处理
-
这里主要针对的是服务提供方宕机的情况,且没有单独的服务降级处理会调用此处的类服务降级方法。
-
@FeignClient(value = “CLOUD-PROVIDER-HYSTRIX-PAYMENT”, fallback = PaymentFallbackServiceImpl.class)
指定服务降级类
@Component
// FeignFallback 客户端的服务降级 针对 CLOUD-PROVIDER-HYSTRIX-PAYMENT 该服务 提供了一个 对应的服务降级类
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT", fallback = PaymentFallbackServiceImpl.class)
//@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfoOK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
String paymentInfoTimeOut(@PathVariable("id") Integer id);
}
- 服务降级类
@Component
public class PaymentFallbackServiceImpl implements PaymentHystrixService {
@Override
public String paymentInfoOK(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_OK ,o(╥﹏╥)o";
}
@Override
public String paymentInfoTimeOut(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_TimeOut ,o(╥﹏╥)o";
}
}
四、服务熔断
4.1 熔断机制
4.2 熔断机制状态转换
4.3 熔断机制案例
4.3.1 服务层
- 保险丝一般是关闭的
@HystrixCommand(fallbackMethod = "paymentCircuitBreakerFallback", 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"),// 失败率达到多少后跳闸
// @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")// 超时处理
})
public String paymentCircuitBreaker(Integer id) {
if (id < 0) {
throw new RuntimeException("******id 不能负数");
}
//测试异常
// int age = 10 / 0;
// int second = 3;
// try {
// TimeUnit.SECONDS.sleep(second);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName() + "\t" + "调用成功,流水号: " + serialNumber;
}
4.3.2 controller层调用
@GetMapping("/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
String result = paymentService.paymentCircuitBreaker(id);
log.info("****result: " + result);
return result;
}
4.3.3 上述案例说明
- 当输入正数时,正确输出,当输入负数时,抛出异常,相当于出错,当10s内10次输入,出错率达到60%时,保险丝开启,当下一次输入正确的数时,还是显示错误,只有过段时间保险丝关闭,才会得到正确的响应。
4.4 熔断类型
4.5 断路器打开
4.6 服务限流
4.7 服务监控HystrixDashBoard
4.7.1 pom
<dependencies>
<!--新增hystrix dashboard-->
<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>
4.7.2 yml
server:
port: 9001
4.7.3 主启动类
@SpringBootApplication
@EnableHystrixDashboard
public class hystrixMain {
public static void main(String[] args) {
SpringApplication.run(hystrixMain.class, args);
System.out.println("启动成功");
}
}
4.7.4 启动9001/hystrix
4.8 9001监控者监控8001
4.8.1 被监控者必须有的pom文件
4.8.2 8001 主启动类配置
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class hystrixMain {
public static void main(String[] args) {
SpringApplication.run(hystrixMain.class, args);
}
/**
* 注意:新版本Hystrix需要在主启动类中指定监控路径
* 此配置是为了服务监控而配置,与服务容错本身无关,spring cloud升级后的坑
* ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
* 只要在自己的项目里配置上下面的servlet就可以了
*
* @return ServletRegistrationBean
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
// 一启动就加载
registrationBean.setLoadOnStartup(1);
// 添加url
registrationBean.addUrlMappings("/hystrix.stream");
// 设置名称
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}
4.8.3 填写监控地址
五、Other
5.1 hutool工具类
5.2 yml没有变成小叶子
- project structer
点中所需的模块,add spring
- 选中没变成小叶子的配置文件