说明
Hystrix
提供了服务熔断、降级、限流、服务监控的功能
场景说明:当访问量激增或者某服务发生故障,调用者会一直等待,然后就会发生一连串的连锁反应,倒是服务器的大面积的瘫痪,为了应对此种情况,需要用到Hystrix做系统的保护。
熔断、降级、限流
降级(丢车保帅):在秒杀时,通过服务降级把注册、修改个人信息等非核心功能关闭掉。
熔断:支付依赖第三方服务,要设置熔断策略,熔断后要给出友好提示,比如10分钟后再来支付。
限流:抢购下单接口采用限流方式,如抢购1000件商品,则设置2000大小的队列,请求超过2000后直接拒绝掉。
测试降级
说明: 1、使用到了eureka、openFeign,我以前的博客有相应的搭建细节
2、降级可以配置在生产者或者消费者上,我个人推荐放到消费者上。
搭建生产者
说明: 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>
application.yml
server:
port: 8001
spring:
application:
# 服务名称
name: hystrix-provider
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 集群版
defaultZone: http://localhost:7001/eureka # 单机版
启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
//本服务启动后会自动注册进eureka服务中
@EnableEurekaClient
public class HystrixProvider8001 {
public static void main(String[] args)
{
SpringApplication.run(HystrixProvider8001.class,args);
}
}
业务类(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;
@RestController
@Slf4j
class PaymentController
{
@Value("${server.port}")
private String serverPort;
/**
* create by: ZhangYiXiong
* description: 功能说明→ 正常访问
* create time: 2022/1/26 16:59
* @params
* @return
*/
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id)
{
return serverPort+id;
}
/**
* create by: ZhangYiXiong
* description: 功能说明→ 模拟超时异常
* create time: 2022/1/26 16:59
* @params
* @return
*/
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) throws InterruptedException
{
Thread.sleep(5000);
return serverPort+id;
}
}
搭建消费者
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>
<!--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>
application.ym
server:
port: 80
spring:
application:
# 服务名称消费者
name: hystrx-consumer
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 集群版
defaultZone: http://localhost:7001/eureka # 单机版
# 开启hystrix
feign:
hystrix:
enabled: true
启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
// 开启hystrix
@EnableHystrix
@SpringBootApplication
@EnableFeignClients
public class HystrixConsumer80 {
public static void main(String[] args) {
SpringApplication.run(HystrixConsumer80.class,args);
}
}
业务类(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 = "HYSTRIX-PROVIDER")
public interface ConsumerService {
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
业务类(controller)
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
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 com.xiong.service.ConsumerService;
import javax.annotation.Resource;
@RestController
@Slf4j
//统一异常处理的方法
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class HystrixController {
@Resource
private ConsumerService consumerService;
//正常访问
@GetMapping("/consumer/payment/hystrix/ok/{id}")
@HystrixCommand
public String paymentInfo_OK(@PathVariable("id") Integer id)
{
String result = consumerService.paymentInfo_OK(id);
return result;
}
//通过hystrix保护
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand
public String paymentInfo_TimeOut(@PathVariable("id") Integer id)
{
String result = consumerService.paymentInfo_TimeOut(id);
return result;
}
//报错访问
@GetMapping("/consumer/payment/hystrix/timeout2/{id}")
public String paymentInfo_TimeOut2(@PathVariable("id") Integer id)
{
String result = consumerService.paymentInfo_TimeOut(id);
return result;
}
//统一异常处理方法
public String payment_Global_FallbackMethod()
{
return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
}
}
启动及测试
启动
启动注册中心
启动生产者
启动消费者
测试
分别访问
http://localhost/consumer/payment/hystrix/ok/100
http://localhost/consumer/payment/hystrix/timeout/100
http://localhost/consumer/payment/hystrix/timeout2/100
第一个正常显示、第二个抛出自定义异常、第三个抛出不友好异常
至此测试成功
测试熔断
说明:只修改了8001生产者代码
启动类开启hystrix
@EnableHystrix
新增service
@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(@PathVariable("id") Integer id)
{
if(id < 0)
{
throw new RuntimeException("******id 不能负数");
}
String serialNumber = UUID.randomUUID().toString();
return Thread.currentThread().getName()+"\t"+"调用成功,流水号: " + serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id)
{
return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: " +id;
}
controller中新增方法
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id)
{
String result = providerService.paymentCircuitBreaker(id);
log.info("****result: "+result);
return result;
}
启动并测试
启动
只启动8001服务于注册中心
报错请求
http://localhost:8001/payment/circuit/-100
正常请求
http://localhost:8001/payment/circuit/100
测试
多次访问错误请求,之后再访问正常请求,之后发现正常请求依然报自定义异常,过一段时间后恢复正常
如此则配置成功
测试限流
修改配置文件
1、如果是线程隔离,可以通过线程数+队列大小限制。参数如下:
hystrix.threadpool.default.coreSize
hystrix.threadpool.default.maxQueueSize
hystrix.threadpool.default.queueSizeRejectionThreshold
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
2、如果是信号量隔离,可以设置最大并发请求数。参数如下:
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests
实时监控
……
常见面试题
……