为什么要有Hystrix呢 ?
因为多个微服务的调用,一个如果超时,会让其他的微服务都需要等待,这样一来,一个微服务的性能下降,就影响了与之关联的很多个服务,这不是我们想看到的。所以Hystrix出现了,将服务降级,熔断,限流。来保证每个服务的正常使用。
现实场景:10086 坐席忙
家里的电闸。保险。
什么时候派上用场呢
1.服务发生异常
2。超时
3,线程池满了。
为了测试方便,我们先将集群变成单机
新建一个8001 的项目
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud</artifactId>
<groupId>com.home</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hystrix8001</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</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-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>
mybatis-spring-boot-starter
</artifactId>
</dependency>
<dependency>
<artifactId>common</artifactId>
<groupId>com.home</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--<dependency>-->
<!--<groupId>com.alibaba</groupId>-->
<!--<artifactId>druid-spring-boot-starter</artifactId>-->
<!--<version>1.1.9</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>mysql</groupId>-->
<!--<artifactId>mysql-connector-java</artifactId>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-starter-jdbc</artifactId>-->
<!--</dependency>-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
</project>
加了一个hystrix的依赖
controller
package home.controller;
import home.entity.MyResult;
import home.entity.Pay;
import home.service.PaySer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
@RestController
@Slf4j
public class PayCon {
@Autowired
PaySer paySer;
@Autowired
DiscoveryClient discoveryClient;
@GetMapping(value = "/hystrix/ok/{id}")
public String ok(@PathVariable(value = "id") int id) {
return paySer.ok(id);
}
@GetMapping(value = "/hystrix/notok/{id}")
public String notok(@PathVariable(value = "id") int id) {
return paySer.notok(id);
}
}
service
package home.service;
import home.entity.MyResult;
import home.entity.Pay;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
@Service
public class PaySer {
@Value("${server.port}"
)
String port;
public String ok(int id) {
return "线程:"+Thread.currentThread().getName()+"ok:"+id+"o(∩_∩)o 哈哈";
}
public String notok(int id) {
int n = 5;
try {
TimeUnit.SECONDS.sleep(n);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程:"+Thread.currentThread().getName()+"notok:"+id+"o(∩_∩)o 哈哈"+"耗时"+n+"秒";
}
}
在自己测试的情况下,一切都是正常的,如果我们用jmeter 设置每秒200 个线程,循环100次,访问notok的,同时访问ok的,ok的也会变成转圈的状态。在高并发的情况下,原来的方式是有问题的。客户体验差
我们建了一个新的项目hystrix的80,这个80是复制的之前的项目open80
The bean ‘PAY-SERVICE.FeignClientSpecification’ could not be registered. A b 会报出这样一个错误 怎么处理?
其原因是idea中,如果复制项目,要将 .iml文件删除。
80 改造 controller
package home.controller;
import com.netflix.discovery.converters.Auto;
import home.entity.MyResult;
import home.entity.Pay;
import home.lb.Lb;
import home.service.FiegnService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@Slf4j
public class OpenCon {
@Autowired
FiegnService fiegnService;
@GetMapping(value = "/consumer/hystrix/notok/{id}")
public String notok(@PathVariable(value = "id") int id){
return fiegnService.notok(id);
}
@GetMapping(value = "/consumer/hystrix/ok/{id}")
public String ok(@PathVariable(value = "id") int id){
return fiegnService.ok(id);
}
}
service
package home.service;
import home.entity.MyResult;
import home.entity.Pay;
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;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@Component
@FeignClient(value = "PAY-SERVICE")
public interface FiegnService {
@GetMapping(value = "/hystrix/ok/{id}")
public String ok(@PathVariable(value = "id") int id);
@GetMapping(value = "/hystrix/notok/{id}")
public String notok(@PathVariable(value = "id") int id);
}
8001 改造controller
package home.controller;
import home.entity.MyResult;
import home.entity.Pay;
import home.service.PaySer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
@RestController
@Slf4j
public class PayCon {
@Autowired
PaySer paySer;
@Autowired
DiscoveryClient discoveryClient;
@GetMapping(value = "/hystrix/ok/{id}")
public String ok(@PathVariable(value = "id") int id) {
return paySer.ok(id);
}
@GetMapping(value = "/hystrix/notok/{id}")
public String notok(@PathVariable(value = "id") int id) {
return paySer.notok(id);
}
}
service 8001
package home.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import home.entity.MyResult;
import home.entity.Pay;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.naming.Name;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
@Service
public class PaySer {
@Value("${server.port}"
)
String port;
public String ok(int id) {
return "线程:" + Thread.currentThread().getName() + "ok:" + id + "o(∩_∩)o 哈哈";
}
@HystrixCommand(fallbackMethod = "notokHandler", commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")})
public String notok(int id) {
int n = 5;
try {
TimeUnit.SECONDS.sleep(n);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程:" + Thread.currentThread().getName() + "notok:" + id + "o(∩_∩)o 哈哈" + "耗时" + n + "秒";
}
public String notokHandler(int id) {
return "线程:" + Thread.currentThread().getName() + "notokhandler:" + id + "(┬_┬)";
}
}
最重要的就是这一步,我们要在service的方法上加上@HystrixCommand(fallbackMethod = “notokHandler”, commandProperties = {@HystrixProperty(name = “execution.isolation.thread.timeoutInMilliseconds”, value = “3000”)})
注解
在主方法上也要做配合的注解
package home;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class})
@EnableEurekaClient
@EnableDiscoveryClient
@EnableCircuitBreaker
public class
Hystrix8001 {
public static void main(String[] args) {
SpringApplication.run(Hystrix8001.class, args);
}
}
@EnableHystrix 这个注解也行。
@EnableCircuitBreaker 这个注解让@hystrixconmmond注解生效。
发生异常的时候测试
@HystrixCommand(fallbackMethod = "notokHandler", commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")})
public String notok(int id) {
// int n = 5;
// try {
// TimeUnit.SECONDS.sleep(n);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
int num = 10/0;
// return "线程:" + Thread.currentThread().getName() + "notok:" + id + "o(∩_∩)o 哈哈" + "耗时" + n + "秒";
return "线程:" + Thread.currentThread().getName() + "notok:" + id + "o(∩_∩)o 哈哈" + "耗时";
}
也会走fallback方法。
因为以上每个方法都要配置一个fallback 这样代码太多了
可以在类上配置一个默认的fallback ,但是这个fallback方法有个特点,不要有入参。否则找不到。方法。
package home.controller;
import com.netflix.discovery.converters.Auto;
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 home.entity.MyResult;
import home.entity.Pay;
import home.lb.Lb;
import home.service.FiegnService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "allerrer")
public class OpenCon {
@Autowired
FiegnService fiegnService;
@GetMapping(value = "/consumer/hystrix/notok/{id}")
@HystrixCommand(fallbackMethod = "notokHandler", commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")})
public String notok(@PathVariable(value = "id") int id){
return fiegnService.notok(id);
}
public String notokHandler(int id) {
return "客户端controller比你更早的超时了(┬_┬)";
}
@GetMapping(value = "/consumer/hystrix/ok/{id}")
@HystrixCommand
public String ok(@PathVariable(value = "id") int id){int n = 10/0;
return fiegnService.ok(id);
}
public String allerrer() {
return "all超时了(┬_┬)";
}
}
这样就解决了代码的冗余,而且可以和单独的定制共存。
解决了冗余的问题又引出了一个问题
业务代码和fallback
代码在一起混合,有没有解决方法呢?
因为你要用feignclient客户端来通过接口的方式调用。我们可以利用接口。
其实这种方法主要是利用feign 集成的hystrix 来处理。
这种是熔断:
我们需要建立一个实现类
package home.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
public class FiegnServiceimpl implements FiegnService {
@Override
public String ok(int id) {
return "ok method fallback (┬_┬)";
}
@Override
public String notok(int id) {
return "ok method fallback (┬_┬)";
}
}
还需要接口上配置一下这个类
package home.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import home.entity.MyResult;
import home.entity.Pay;
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;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@Component
@FeignClient(value = "PAY-SERVICE",fallback = FiegnServiceimpl.class)
public interface FiegnService {
@GetMapping(value = "/hystrix/ok/{id}")
public String ok(@PathVariable(value = "id") int id);
@GetMapping(value = "/hystrix/notok/{id}")
public String notok(@PathVariable(value = "id") int id);
}
主要是fallback的配置。
在yml中配置
feign:
hystrix:
enabled: true
#开启熔断
feign.hystrix.enabled=true
#是否开启超时熔断, 如果为false, 则熔断机制只在服务不可用时开启
hystrix.command.default.execution.timeout.enabled=true
# 设置超时熔断时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000
上面一些是参数的说明,其实是熔断,并不是直接的降级。测试需要将8001关闭,模拟宕机。