六.Hystrix
Hystrix官方宣布,停止更新,进入维护阶段,但Hystrix非常优秀,属“业界标杆”
1.Hystrix断路器知识点
能做的事:服务降级、服务熔断、接近实时的监控等。
①服务降级fallback
服务器忙,稍后再试。不让客户端等待并立刻返回一个友好提示。
②服务熔断break
达到最大服务访问后,直接拒绝访问,调用服务降级的方法并返回友好提示。
③服务限流flowlimit
秒杀高并发等操作,严禁一窝蜂拥挤,大家排队,一秒钟限制N个,有序进行
2.构建项目
先确定7001为单机版。
①创建module
②编写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>
<!--引入自定义的api通用包 可以使用公用的entities-->
<dependency>
<groupId>com.hry.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: 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
④创建主启动类
package com.hry.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class, args);
}
}
⑤业务代码
service层
package com.hry.springcloud.service;
public interface PaymentService {
public String paymentInfo_OK(Integer id);
public String paymentInfo_TimeOut(Integer id);
}
package com.hry.springcloud.service.impl;
import com.hry.springcloud.service.PaymentService;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class PaymentServiceImpl implements PaymentService {
/**
* 正常访问
* @param id
* @return
*/
@Override
public String paymentInfo_OK(Integer id) {
String s = "线程池:"+Thread.currentThread().getName()+ " paymentInfo_OK,id:"+id+"\t";
return s;
}
/**
* 超时访问
* @param id
* @return
*/
@Override
public String paymentInfo_TimeOut(Integer id) {
int time = 3;
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
String s = "线程池:"+Thread.currentThread().getName()+ " paymentInfo_TimeOut,id:"+id+"\t"+"耗时"+time+"s";
return s;
}
}
controller层
package com.hry.springcloud.controller;
import com.hry.springcloud.service.PaymentService;
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 javax.annotation.Resource;
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@Value("{server.port}")
private String serverPort;
@GetMapping(value = "/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentService.paymentInfo_OK(id);
log.info("*****result:"+result);
return result;
}
@GetMapping(value = "/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
String result = paymentService.paymentInfo_TimeOut(id);
log.info("*****result:"+result);
return result;
}
}
⑥测试
依次启动7001和新8001
这里ok正常是秒刷新的。
⑦压力测试
使用Jmeter
当我们使用压力测试对timeout施压时,ok也会受到影响。
这时我们刷新ok看效果
发现当高线程访问timeout时ok也会受到影响,从原来的秒刷新到现在的需要等待。
由此,8001自测都不能通过。你以为这就完了?下面再建一个order80消费者访问这个8001服务提供者。
3.构建项目
①创建module
②编写pom文件
<dependencies>
<!-- openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 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>
<!--引入自定义的api通用包 可以使用公用的entities-->
<dependency>
<groupId>com.hry.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: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka
④创建主启动类
package com.hry.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class OrderHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain80.class, args);
}
}
⑤业务代码
service层
package com.hry.springcloud.service;
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;
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {
@GetMapping(value = "/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping(value = "/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
controller层
package com.hry.springcloud.controller;
import com.hry.springcloud.service.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
@Slf4j
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping(value = "/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_OK(id);
return result;
}
@GetMapping(value = "/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
}
⑥测试
启动新80
把Jmeter跑起来
访问发现也需要等待了,甚至多访问几次出现error page页面报错
4.服务降级
①修改8001
(1)修改service层
添加
public String PaymentInfo_TimeOutHandler(Integer id);
修改与添加
/**
* 超时访问
* fallbackMethod:指定方法名:PaymentInfo_TimeOutHandler 来处理
* value指定时间为2s
*/
@HystrixCommand(fallbackMethod = "PaymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
})
@Override
public String paymentInfo_TimeOut(Integer id) {
int time = 3;
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
String s = "线程池:"+Thread.currentThread().getName()+ " paymentInfo_TimeOut,id:"+id+"\t"+"耗时"+time+"s";
return s;
}
/**
* 用于处理fallback
* @param id
* @return
*/
@Override
public String PaymentInfo_TimeOutHandler(Integer id) {
String s = "线程池:"+Thread.currentThread().getName()+ " TimeOutHandler,id:"+id+"\t"+"o(╥﹏╥)o";
return s;
}
这里的"execution.isolation.thread.timeoutInMilliseconds",value = “2000” 设置了超时2s,而下面int time还是3s.
(2)修改主启动类
添加注解
@EnableCircuitBreaker
(3)测试
测试发现当超时3s超过我们设置的2s时就会走我们的Handler方法。
运行异常和超时异常都会走这个方法
②修改80
(1)修改yml文件
添加配置
feign:
hystrix:
enabled: true
(2)修改主启动类
添加注解
@EnableHystrix
(3)添加和修改controller
/**
* 超时访问
* fallbackMethod:指定方法名:PaymentInfo_TimeOutHandler 来处理
* value指定时间为2s
*/
@HystrixCommand(fallbackMethod = "PaymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
})
@GetMapping(value = "/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String s = paymentHystrixService.paymentInfo_TimeOut(id);
return s;
}
/**
* 用于处理fallback
* @param
* @return
*/
public String PaymentInfo_TimeOutHandler() {
String s = "消费者80出错啦!!!!!!!! 支付系统8001繁忙,请稍后再试";
return s;
}
(4)将8001改回正常状态
80设置了超时为2s,而8001休眠了3s显然 802s后不会再等待8001了,因此自身出错走下面的fallback方法打印消费者80出错啦!
在测试之前需要回到8001service层修改注解上的超时时间 改成大于3s 比如设置为5000
(5)测试
当我们准备通过客户端访问支付服务端时,报了一个错,提示我们没有找到我们的handler,原因是:fallback方法参数列表必须与加了注解的方法参数列表一致
我们加入参数
public String PaymentInfo_TimeOutHandler(@PathVariable("id") Integer id)
修改后重启80继续测试。
看到成功打印了handler中的语句。
这就是客户端80访问服务端8001时由于响应超过期待的2s的情况。
同理运行时出错也会直接跳到自己的fallback方法中。例如在代码最前面加一行
int a = 1/0;
还没去调用服务端8001,也不涉及超时,就已经报错,也会进入我们的兜底方法。