目录
1.简单介绍❤️❤️❤️
2.主要功能 ❤️❤️❤️
3.正确案例❤️❤️❤️
4.使用jmeter压测 ❤️❤️❤️
5.建模块 80❤️❤️❤️
6.如何解决上面问题 ❤️❤️❤️
7.对8001进行服务降级❤️❤️❤️
8.对80进行服务降级 ❤️❤️❤️
9.通用降级方法❤️❤️❤️
10.在Feign接口实现降级 ❤️❤️❤️
1.简单介绍❤️❤️❤️
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
2.主要功能 ❤️❤️❤️
- 服务隔离:通过将不同的依赖服务调用分配给不同的线程池来隔离服务之间的调用,防止一个服务的故障导致整个系统的崩溃。
- 服务降级:当依赖服务出现延迟或故障时,Hystrix可以提供一个备用的响应,避免用户等待超时或出现错误。
- 服务熔断:Hystrix可以根据依赖服务的错误率和延迟来决定是否打开断路器,当断路器打开时所有的请求将直接返回,避免对依赖服务的继续调用。
- 服务限流:Hystrix可以限制对依赖服务的并发调用数量,避免因过多的请求导致依赖服务的崩溃。
- 实时监控和报警:Hystrix提供了实时的监控仪表盘,可以监控依赖服务的调用情况和错误率,并提供报警机制。
3.正确案例❤️❤️❤️
1.建模块
在父工程下创建,注意jdk和maven版本
2.写pom
1.springboot依赖
2.mysql依赖
3.mybatis依赖
4.eureka依赖
5.hystrix依赖
<dependencies>
<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.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</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>
<!--引入自己的api-->
<dependency>
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--eureka的client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
3.写yml
1.服务端口
2.服务名称
3.数据库连接池
4.mybatis配置
5.入住到服务注册中心(eureka)
server:
port: 8001
spring:
application:
name: cloud-provider-hystrix-payment
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud
username: root
password: 123456
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.xz.springcloud.entity
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
4.主启动类
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class);
}
}
5.编写业务
创建两个方法:
1.正常方法没有延迟
2.错误方法有延时,Thread.sleep()模拟程序执行时间
@RestController
public class PaymentController {
@Autowired
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
@GetMapping("/payment/hystrix/ok/{id}")
public CommonResult paymentInfo_Ok(@PathVariable("id") Integer id) {
Payment result = paymentService.getById_Ok(id);
if (result != null) {
return new CommonResult(200, "查寻成功,端口:" + serverPort+",线程池:"+Thread.currentThread().getName(), result);
} else {
return new CommonResult(404, "查询失败,端口:" + serverPort+",线程池:"+Thread.currentThread().getName());
}
}
@GetMapping("/payment/hystrix/timeout/{id}")
public CommonResult paymentInfo_TimeOut(@PathVariable("id") Integer id) throws InterruptedException {
int timeOut = 3;
Payment result = paymentService.getById_TimeOut(id);
if (result != null) {
TimeUnit.SECONDS.sleep(3);
return new CommonResult(200, "查寻成功,端口:"+ serverPort+",线程池:"+Thread.currentThread().getName() + ",超时时间:" + timeOut, result);
} else {
return new CommonResult(404, "查询失败,端口:"+ serverPort+",线程池:"+Thread.currentThread().getName() + ",超时时间:" + timeOut);
}
}
}
6.测试:
1.paymentInfo_ok,没有延迟,直接查询到
2.paymentInfo_TimeOut,有延迟,三秒后查询到
4.使用jmeter压测 ❤️❤️❤️
- 1.创建线程组-取样器-HTTP请求
- 2.设置压测数据
- 3.启动压测,同时刷新正常的请求Ok
发现之前没有设置延迟的接口,访问时也变得十分缓慢。。。
原因:tomcat的默认的工作线程数被打满了,没有多余的线程来分解压力和处理。
这就导致,我们访问同一服务下的其他接口地址也变得缓慢
5.建模块 80❤️❤️❤️
1.建模块
在父工程下创建,注意jdk版本和maven版本
2.写pom
1.springboot依赖
2.通用依赖
3.eureka依赖
4.OpenFeign依赖
5.Hystri依赖
<dependencies>
<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.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>
<dependency>
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--eureka的Client端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--openFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
3.加yml
1.服务端口
2.入住eureka配置信息
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
4.主启动
使用OpenFeign,添加@EnableFeignClients
@SpringBootApplication
@EnableFeignClients
public class OrderHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain80.class);
}
}
5.@FeignClient接口
1.使用@Component,交给spring管理
2.添加@FeignClient(value="要访问服务的名称")
3.要访问服务的哪个接口
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
public CommonResult paymentInfo_Ok(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public CommonResult paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
6.编写业务
@RestController
public class OrderHystrixController {
@Autowired
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public CommonResult paymentInfo_Ok(@PathVariable("id") Integer id) {
return paymentHystrixService.paymentInfo_Ok(id);
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public CommonResult paymentInfo_TimeOut(@PathVariable("id") Integer id) {
return paymentHystrixService.paymentInfo_TimeOut(id);
}
}
7.压测
当正常访问ok方法时,浏览器返回基本是刹那间
当我们压测timeout方法时,再去使用80访问ok方法,结果发现直接返回错误页面。
8001同一层次的其它接口服务被困死,因为tomcat线程池里面的工作线程已经被挤占完毕
6.如何解决上面问题 ❤️❤️❤️
- 对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级
- 对方服务(8001)down机了,调用者(80)不能一直卡死等待,必须有服务降级
- 对方服务(8001)OK,调用者(80)自己出故障或有自我要求(自己的等待时间小于服务提供者)自己处理降级
7.对8001进行服务降级❤️❤️❤️
1.添加@HystrixCommand注解
属性:
1.fallbackMethod:当超时后,要返回的兜底方法
2.@HystrixProperty:设置多少秒没有响应,返回兜底方法
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
@GetMapping("/payment/hystrix/timeout/{id}")
public CommonResult paymentInfo_TimeOut(@PathVariable("id") Integer id) throws InterruptedException {
int timeOut = 5;
Payment result = paymentService.getById_TimeOut(id);
if (result != null) {
TimeUnit.SECONDS.sleep(3);
return new CommonResult(200, "查寻成功,端口:"+ serverPort+",线程池:"+Thread.currentThread().getName() + ",超时时间:" + timeOut, result);
} else {
return new CommonResult(404, "查询失败,端口:"+ serverPort+",线程池:"+Thread.currentThread().getName() + ",超时时间:" + timeOut);
}
}
2.创建兜底方法
public CommonResult paymentInfo_TimeOutHandler(@PathVariable("id") Integer id){
Payment result = paymentService.getById_TimeOut(id);
if (result != null) {
return new CommonResult(200, "系统繁忙!稍后重试~");
} else {
return new CommonResult(404, "备用~查询失败,端口:" + serverPort+",线程池:"+Thread.currentThread().getName());
}
}
3.主启动类添加@EnableCircuitBreaker
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class);
}
}
4.测试
1.当调用的接口超时,返回兜底数据
2.放调用的接口中存在运行时错误,也会返回兜底数据
8.对80进行服务降级 ❤️❤️❤️
1.改yml
启用Hystrix作为Feign的断路器
feign:
hystrix:
enabled: true
2.添加@HystrixCommand注解
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")
})
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public CommonResult paymentInfo_TimeOut(@PathVariable("id") Integer id) {
return paymentHystrixService.paymentInfo_TimeOut(id);
}
3.创建兜底方法
注意方法的返回值类型必须原方法一致!
public CommonResult paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
return new CommonResult(200,"我是消费80,系统繁忙,稍后重试~");
}
4.主启动添加@Enablehystrix
@SpringBootApplication
@EnableFeignClients
@EnableHystrix
public class OrderHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain80.class);
}
}
5.测试
1.当使用feign调用的方法超时,返回兜底数据
2.当feign调用的方法存在运行时错误,返回兜底数据
9.通用降级方法❤️❤️❤️
1.问题
配置降级服务时,每个方法都要有降级的方法。显然代码膨胀,不好管理
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")
})
2.解决(@DefaultProperties)
使用@DefaultProperties(defaultFallback="全局降级的方法")
需要服务降级的方法,仍需要添加@HystrixCommand
@RestController
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHystrixController {
@HystrixCommand
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public CommonResult paymentInfo_TimeOut(@PathVariable("id") Integer id) {
return paymentHystrixService.paymentInfo_TimeOut(id);
}
//全局fallback
public CommonResult payment_Global_FallbackMethod(){
return new CommonResult(200,"全局配置处理,系统繁忙,稍后重试");
}
}
3.测试
10.在Feign接口实现降级❤️❤️❤️
1.改yml
启用Hystrix作为Feign的断路器
feign:
hystrix:
enabled: true
2.创建实现Feign接口类
1.创建一个类,实现有@FeignClient标记的接口
2.添加@Component注解,交给spring管理
@Component
public class PaymentFallbackService implements PaymentHystrixService{
@Override
public CommonResult paymentInfo_Ok(Integer id) {
return new CommonResult(200,"payment_ok,系统繁忙,稍后重试~");
}
@Override
public CommonResult paymentInfo_TimeOut(Integer id) {
return new CommonResult(200,"payment_TimeOut,系统繁忙,稍后重试~");
}
}
3.在@FeignClien中添加属性:fallback
fallback:返回的兜底类(实现当前接口的类)
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
public CommonResult paymentInfo_Ok(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public CommonResult paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
4.测试
当服务8001宕机之后,访问正常接口ok时,返回兜底数据