Spring Cloud学习day100:Hystrix

一、雪崩效应和解决方案

1.什么是灾难性的雪崩效应 ?

在微服务架构中,一个请求需要调用多个服务是非常常见的。若有大量的请求涌入,容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,造成连锁反应,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。

服务T出现故障后

导致的连锁反应

导致整个系统瘫痪

2.出现雪崩的原因:

(1)服务提供者不可用(硬件故障,程序bug,缓存击穿,用户的大量请求);
(2)重试加大流量(用户重试,代码逻辑重试);
(3)服务调用者不可用(同步等待造成的资源耗尽);
最终的结果就是一个服务不可用,导致一系列服务的不可用,而往往这种后果是无法预料的。

3.如何解决雪崩效应:

  • 降级 :

超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。 实现一个 fallback 方法, 当请求后端服务出现异常的时候, 可以使用 fallback 方法返回的值. 隔离(线程池隔离和信号量隔离) 限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用。

  • 熔断:

当失败率(如因网络故障/超时造成的失败率高)达到阀值自动触发降级,熔断器触发的快 速失败会进行快速恢复。

  • 缓存:

提供了请求缓存。

  • 请求合并:

提供请求合并。


二、服务降级

1.什么是服务降级?

就是对不怎么重要的服务进行低优先级的处理。尽可能的把系统资源让给优先级高的服务。资源有限,而请求是无限的。如果在并发高峰期,不做服务降级处理,一方面肯定会影响整体服务的性能,严重的话可能会导致宕机某些重要的服务不可用。

  • 什么时候会触发getFallback调用:

(1) 方法抛出非 HystrixBadRequestException 异常。
(2) 方法调用超时。
(3) 熔断器开启拦截调用。
(4) 线程池/队列/信号量是否跑满。

2.如何使用服务降级:

  • 创建简单测试降级实现:

使用Eureka注册中心注册服务,下面测试都使用这个Provider提供服务。

Consumer——服务的消费者

Provider服务的生产者
1.1Consumer-hystrix:
  • 修改 pom 文件添加 hystrix 的坐标:
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  • 修改全局配置文件:
spring.application.name=product-provider-hystix
server.port=9010

eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
  • 添加实体类:
public class Product {
    private int id;
    private String name;

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Product(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
    public Product() {
        super();
    }
}
  • 编写启动类开启熔断器:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableCircuitBreaker // 开启熔断器 断路器
@EnableEurekaClient
@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
}
  • 编写ProductService:
@Service
public class ProductService {
    @Autowired
    private LoadBalancerClient loadBalancerClient;// ribbon负载均衡器

    @HystrixCommand(fallbackMethod = "fallback")//当下面方法服务出现异常停止会返回托底数据
    public List<Product> getProduct() {
        // 选择调用的服务的名称
        // ServiceInstance 封装了服务的基本信息,如 IP,端口
        ServiceInstance si = this.loadBalancerClient.choose("product-provider-hystix");
        // 拼接访问服务的URL
        StringBuffer sb = new StringBuffer();
        // http://localhost:9001/product/findAll
        sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll");
        System.out.println(sb.toString());
        // springMVC RestTemplate
        RestTemplate rt = new RestTemplate();

        ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {
        };

        // ResponseEntity:封装了返回值信息
        ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);
        List<Product> list = response.getBody();
        return list;
    }
    //返回托底数据的方法
    public List<Product> fallback(){
        List<Product> list = new ArrayList<>();
        list.add(new Product(-1,"托底数据!"));
        return list;
    }
}
  • 编写Controller:
@RestController
public class ProductController {
    
    @Autowired
    private ProductService productService;
    
    @RequestMapping("/list")
    public List<Product> list(){
        return productService.getProduct();
    }
}
1.2Service-hystrix:
  • 修改POM文件:
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
    </dependencies>
  • 创建实体类:
public class Product {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • 编写Service:
@RequestMapping("/product")
public interface ProductService {
    // 查询所有
    @RequestMapping(value = "/findAll", method = RequestMethod.GET)
    public List<Product> findAll();

    // ID查询
    @RequestMapping(value = "/findById", method = RequestMethod.GET)
    public Product getProductById(@RequestParam("id") Integer id);

    // 传递多个参数 方式一 :GET方式
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public Product addProduct(@RequestParam("id") Integer id, @RequestParam("name") String name);

    // ----------------------Httpclient----------------------------------------------
    //传递多个参数 方式二 :POST方式
    @RequestMapping(value = "/add2", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
    public Product addProduct2(@RequestBody Product product);

    // 使用HttpClient工具 传递多个参数 :基于GET方式
    @RequestMapping(value = "/add3", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE)
    public Product addProduct3(Product product);

}
1.3Provider- hystrix:
  • 修改POM文件:
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <!-- 添加product-service坐标 -->
        <dependency>
            <groupId>com.zlw</groupId>
            <artifactId>springcloud-eureka-ribbon-hystrix-service</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  • 修改全局配置文件:
spring.application.name=product-provider-hystix
server.port=9011

eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
  • 编写启动类:
@EnableEurekaClient
@SpringBootApplication
public class ProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }
}
  • 编写ProviderController:
/**
 * Product-Provider服务
 * @author zhang
 *
 */
@RestController
public class ProductController implements  ProductService{

    @Override
    public List<Product> findAll() {
        List<Product> list = new ArrayList<Product>();
        list.add(new Product(1,"手机"));
        list.add(new Product(2,"电脑"));
        list.add(new Product(3,"电视"));
        return list;
    }

    @Override
    public Product getProductById(Integer id) {
        return new Product(id,"Product Provider");
    }

    @Override
    public Product addProduct(Integer id, String name) {
        return new Product(id,name);
    }
    @Override
    public Product addProduct2(@RequestBody Product product) {
        return product;
    }
    @Override
    public Product addProduct3(@RequestBody Product product) {
        return product;
    }
}
  • 测试:


    示例

    示例

三、请求缓存

Hystrix 为了降低访问服务的频率,支持将一个请求与返回结果做缓存处理。如果再次 请求的 URL 没有变化,那么 Hystrix 不会请求服务,而是直接从缓存中将结果返回。这样可 以大大降低访问服务的压力。

  • Hystrix 自带缓存。有两个缺点:

(1)是一个本地缓存。在集群情况下缓存是不能同步的。
(2)不支持第三方缓存容器。Redis,memcache 不支持的。 可以使用 spring 的 cache。

1.使用Redis

安装Redis:https://www.jianshu.com/p/c3f2e374e62f

1.1创建Consumer-cache:
示例
  • 修改POM文件添加 springCache 坐标 :
        <!-- springCache Redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
  • 修改配置文件添加redis链接信息:
spring.application.name=product-consumer-redis
server.port=9012

#设置服务注册中心地址,指向另一个注册中心 
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/

#Redis
spring.redis.datasource=0
#Redis服务器地址
spring.redis.host=192.168.226.130
#Reids服务器连接的端口
spring.redis.port=6379
#Redis 服务器连接密码(默认为空) 
spring.redis.password=
#连接池最大连接数(负值表示没有限制) 
spring.redis.pool.max-active=100
#连接池最大阻塞等待时间(负值表示没有限制) 
spring.redis.pool.max-wait=3000
#连接池最大空闭连接数 
spring.redis.pool.max-idle=200
#连接汉最小空闲连接数 
spring.redis.pool.min-idle=50
#连接超时时间(毫秒)
spring.redis.pool.timeout=600 
  • 修改启动类开启缓存:
@EnableCaching
@EnableEurekaClient
@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
}
  • 创建实体类(get和set方法):
    private int id;
    private String name;
  • 修改ProductService:
@CacheConfig(cacheNames = { "product.consumer" })
@Service
public class ProductService {
    @Autowired
    private LoadBalancerClient loadBalancerClient;// ribbon负载均衡器

    public List<Product> getProduct() {
        // 选择调用的服务的名称
        // ServiceInstance 封装了服务的基本信息,如 IP,端口
        ServiceInstance si = this.loadBalancerClient.choose("product-provider-hystix");
        // 拼接访问服务的URL
        StringBuffer sb = new StringBuffer();
        // http://localhost:9001/product/findAll
        sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll");
        System.out.println(sb.toString());
        // springMVC RestTemplate
        RestTemplate rt = new RestTemplate();

        ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {
        };

        // ResponseEntity:封装了返回值信息
        ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);
        List<Product> list = response.getBody();
        return list;
    }

    // 根据ID查询
    @Cacheable(key = "'product'+#id")
    public Product findById(Integer id) {
        System.out.println("------Find------" + id);
        return new Product(id, "Product Redis");
    }

    // 根据ID删除
    @CacheEvict(key = "'product'+#id")
    public void delProduct(Integer id) {
        System.out.println("-------Delete--------"+id);
    }
}
  • 测试:


    控制台打印
网页

redis的存储

四、请求合并

就是把请求合并起来一起发送给服务端(一次性批量发送请求),服务端批量处理完成后再返回给客户端。
请求合并的功能是客户端来控制的,由客户端整合发起,然后由服务端统一处理,但是需要服务端支持批量处理请求的能力。

1.什么情况下使用请求合并:

在微服务架构中,我们将一个项目拆分成很多个独立的模块,这些独立的模块通过远程 调用来互相配合工作,但是,在高并发情况下,通信次数的增加会导致总的通信时间增加, 同时,线程池的资源也是有限的,高并发环境会导致有大量的线程处于等待状态,进而导致 响应延迟,为了解决这些问题,我们需要来了解 Hystrix 的请求合并。

  • 请求合并的缺点:

设置请求合并之后,本来一个请求可能 5ms 就搞定了,但是现在必须再等 10ms 看看还 有没有其他的请求一起的,这样一个请求的耗时就从 5ms 增加到 15ms 了,不过,如果我们 要发起的命令本身就是一个高延迟的命令,那么这个时候就可以使用请求合并了,因为这个 时候时间窗的时间消耗就显得微不足道了,另外高并发也是请求合并的一个非常重要的场 景。

2.创建Consumer-batch测试:

示例
  • 修改POM文件:
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>
  • 修改配置文件:
spring.application.name=product-provider-batch
server.port=9009

#设置服务注册中心地址,指向另一个注册中心 
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
  • 创建实体类:
    private Integer id;
    private String name;
  • 修改启动类:
@EnableCircuitBreaker
@EnableEurekaClient
@SpringBootApplication
public class ProviductApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviductApplication.class, args);
    }
}
  • 修改ProductService:
@Service
public class ProductService {
    // 使用hystrix进行合并请求
    @HystrixCollapser(batchMethod = "productMethod", scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL, collapserProperties = {
            // 请求时间间隔在 20ms 之内的请求会被合并为一个请求,默认 为 10ms
            @HystrixProperty(name = "timerDelayInMilliseconds", value = "20"),
            // 设置触发批处理执行之前,在批处理中允许许的最大请求数。
            @HystrixProperty(name = "maxRequestsInBatch", value = "3") 
            })
    // consumer的Controller调用的方法,该方法返回值必须要返回 Future类型
    public Future<Product> getFuture(Integer id) {
        System.out.println("-------" + id + "---------");
        return null;
    }
    
    //调用Provider服务的方法
    @HystrixCommand
    public List<Product> productMethod(List<Integer> id){
        System.out.println("---------productMethod---------");
        for (Integer num : id) {
            System.out.println("========id:"+num);
        }
        List<Product> list = new ArrayList<Product>();
        list.add(new Product(1,"手机"));
        list.add(new Product(2,"电脑"));
        list.add(new Product(3,"电视"));
        list.add(new Product(4,"音箱"));
        list.add(new Product(99,"...."));
        System.out.println("=================");
        return list;
    }
}
  • 修改Controller:
@RestController
public class ProductController {

    @Autowired
    private ProductService productService;

    @RequestMapping("/list")
    public void list() throws Exception {
        Future<Product> future = productService.getFuture(1);
        Future<Product> future2 = productService.getFuture(2);
        Future<Product> future3 = productService.getFuture(4);
        Future<Product> future4 = productService.getFuture(3);
        System.out.println(future.get().toString());
        System.out.println(future2.get().toString());
        System.out.println(future3.get().toString());
        System.out.println(future4.get().toString());
    }
}
  • 测试:


    示例

五、服务熔断

熔断机制相当于电路的跳闸功能。

示例

1.创建Consumer

示例
  • 修改POM文件:
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>
    </dependencies>
  • 修改全局配置文件:
spring.application.name=product-provider-fuse
server.port=9008

#设置服务注册中心地址,指向另一个注册中心 
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
  • 编写启动类:
@EnableCircuitBreaker//开启熔断器,断路器
@EnableEurekaClient
@SpringBootApplication
public class FuseApplication {
    public static void main(String[] args) {
        SpringApplication.run(FuseApplication.class, args);
    }
}
  • 创建实体类:
public class Product {
    private int id;
    private String name;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Product() {
        super();
    }
    public Product(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
}
  • 编写Service:
@Service
public class ProductService {

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @HystrixCommand(fallbackMethod = "fallback", commandProperties = {
            // 默认 20 个;10s 内请求数大于 20 个时就启动熔断器,当请 求符合熔断条件时将触发 getFallback()
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10"),
            // 默认 20 个;10s 内请求数大于 20 个时就启动熔断器,当请 求符合熔断条件时将触发 getFallback()
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),
            // 默认 5 秒,熔断多少秒后去尝试请求
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "5000") })
    public List<Product> list(int flag) {
        System.out.println("fuse---productService---list:"+flag);
        if (flag == 1) {
            throw new RuntimeException();
        }
        // 选择调用的服务的名称
        // ServiceInstance 封装了服务的基本信息,如 IP,端口
        ServiceInstance si = this.loadBalancerClient.choose("eureka-provider");
        // 拼接访问服务的 URL
        StringBuffer sb = new StringBuffer();
        // http://localhost:9001/product/findAll
        sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll");
        System.out.println(sb.toString());
        // springMVC RestTemplate
        RestTemplate rt = new RestTemplate();
        ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {
        };

        // ResponseEntity:封装了返回值信息
        ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);
        List<Product> list = response.getBody();
        return list;
    }

    public List<Product> fallback(int flag) {
        List<Product> list = new ArrayList<Product>();
        list.add(new Product(-1, "托底数据!"));
        return list;
    }
}
  • Controller:
@RestController
public class ProductController {

    @Autowired
    private ProductService productService;

    @RequestMapping("/list")
    public List<Product> list(@RequestParam("flag") Integer flag) {
        return productService.list(flag);
    }
}
  • 测试:


    示例

    示例

六、线程池隔离

什么是线程隔离,例如Customer通过线程池,访问服务接口,接口A面对的10次的请求量,接口B是10次的访问量,比例是1:10000,此时接口A和接口B的连接是在同一个的线程池中,如果接口A因为的访问量过大的原因出现问题,势必影响线程池的效率,而线程池中的其他线程也会受到影响,从而造成雪崩效应。

示例

1.如何使用线程隔离解决:

那就要用到线程的的隔离技术了。把可能出现问题的服务独立的运行在一个独立的线程池中。

示例

线程池的隔离如下图中两个线程池我们将服务量大的请求单独的运行在一个的独立的线程池中,两个线程池相互之间是不影响的。可以使用线程池隔离的技术将的线程池内的可能预估出现问题的线程和其他的线程的隔开运行在一个独立的线程池中,一旦此线程出现问题,不会影响到其他线程的运行,解决雪崩效应的产生。

示例
  • 使用线程隔离的优缺点:

(1)优点:
使用线程隔离可以完全隔离依赖的服务。
当线程池出现问题时,线程池隔离是独立的,不影响其他服务和接口。
当失败的服务再次可用时,线程池将清理并可立即恢复,不需要长时间的恢复。
独立的线程池提供了并发性。
(2)缺点:
线程池隔离的主要缺点是他们增加计算开销,每个命令的执行涉及到排队,调度和上下文切换都是在一个单独的线程上运行的。

2.创建Consumer测试:

示例
  • 修改POM文件:
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>
  • 修改配置文件:
spring.application.name=product-provider-threadpool
server.port=9007

#设置服务注册中心地址,指向另一个注册中心 
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
  • 修改启动类:
@EnableCircuitBreaker //开启熔断器 断路器 
@EnableEurekaClient 
@SpringBootApplication public class ConsumerApplication {  
    public static void main(String[] args) {   
          SpringApplication.run(ConsumerApplication.class, args);  
      }
 }
  • 修改ProductService:
@Service
public class ProductService {

    @Autowired
    private LoadBalancerClient loadBalancerClient;// ribbon 负 载均衡器

    @HystrixCommand(groupKey="ego-product-provider", commandKey = "getUsers",threadPoolKey="ego-product-provider",          
            threadPoolProperties = {               
                    @HystrixProperty(name = "coreSize", value = "30"),
                    //线程池大小               
                    @HystrixProperty(name = "maxQueueSize", value = "100"),
                    //最大队列长度               
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                    //线程存活时间               
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15")//拒绝请求       
                    },
            fallbackMethod = "fallback")  
    public List<Product> getUsers() {   
        System.out.println(Thread.currentThread().getName());   
        // 选择调用的服务的名称   
        // ServiceInstance 封装了服务的基本信息,如 IP,端口   
        ServiceInstance si = this.loadBalancerClient.choose("ego-product-provider");   
        // 拼接访问服务的 URL   
        StringBuffer sb = new StringBuffer();   
        // http://localhost:9001/product/findAll 
        sb.append("http://").append(si.getHost()).append(":").appen d(si.getPort()).append("/product/findAll"); 
        System.out.println(sb.toString());   
        // springMVC RestTemplate   
        RestTemplate rt = new RestTemplate(); 
     
      ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {   }; 
     
      // ResponseEntity:封装了返回值信息   
      ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);
      List<Product> list = response.getBody();   
      return list;  } // 返回托底数据的方法

    public List<Product> fallback() {
        System.out.println(Thread.currentThread().getName());
        List<Product> list = new ArrayList<>();
        list.add(new Product(-1, "我是托底数据"));
        return list;
    }

    public void showThread() {
        System.out.println(Thread.currentThread().getName());
    }
}
  • 修改ProductController:

@RestController
public class ProductController {

    @Autowired
    private ProductService userService;

    @RequestMapping("/consumer")
    public List<Product> getUsers() {
        return this.userService.getUsers();
    }

    @RequestMapping("/consumer1")
    public void getUsers1() {
        this.userService.showThread();
    }
}

七、使用的注解

Hystrix 配置参数全解析

1.降级中使用的注解:

名称 作用
@HystrixCommand 当下面方法服务出现异常停止会返回托底数据
@EnableCircuitBreaker 开启熔断器,断路器

2.请求缓存中使用的注解:

名称 作用
@CacheConfig 一个类中可能会有多个缓存操作,而这些缓存操作可能是重复的。这个时候可以使用@CacheConfig
@Cacheable 根据一定条件对缓存添加数据
@CacheEvict 主要针对方法配置,能够根据一定的条件对缓存进行清空。
@EnableCaching 注解是spring framework中的注解驱动的缓存管理功能,开启缓存。

3.请求合并:

@HystrixCollapser:利用 hystrix 实现请求合并。

属性名称 作用
batchMethod 设置合并请求的方法
scope 设置请求的方式

@HystrixProperty:Hystrix 的命令属性是由@HystrixProperty 注解数组构成的,HystrixProperty 由 name 和 value 两个属性,数据类型都是字符串。

属性名称 作用
timerDelayInMilliseconds 请求时间间隔默认 为 10ms之内的请求会被合并为一个请求
maxRequestsInBatch 设置触发批处理执行之前,在批处理中允许许的最大请求数。

4.服务熔断:

@HystrixProperty中的属性HystrixPropertiesManager参数的作用。

参数名称 作用
circuitBreaker.requestVolumeThreshold 一个统计窗口内熔断触发的最小个数。
circuitBreaker.enabled 是否开启熔断。
circuitBreaker.sleepWindowInMiliseconds 熔断多少秒后去尝试请求。
circuitBreaker.errorThresholdPercentage 失败率达到多少百分比后熔断。
circuitBreaker.forceOpen 是否强制开启熔断。
circuitBreaker.forceClosed 是否强制关闭熔断。

5.线程隔离:

(1) @HystrixCommand注解中的threadPoolProperties属性的作用配置与线程池相关的属性。

参数名称 作用
groupKey 设置服务名(相同的服务用一个名称),默认值getClass().getSimpleName()
commandKey 设置服务下面的接口,默认当前执行方法名
threadPoolKey 设置线程池的名称,全局唯一标识线程池的名称,相同线程池名称的线程池是同一个
coreSize 设置线程池大小
maxQueueSize 设置最大队列长度
queueSizeRejectionThreshold 设置拒绝请求的临界值
keepAliveTimeMinutes 设置线程存活时间,默认为1min
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341