本篇文章为系列文章,未读第一集的同学请猛戳这里:Spring Cloud 系列之 Netflix Hystrix 服务容错(一)
本篇文章讲解 Hystrix 服务隔离中的线程池隔离与信号量隔离。
  没有线程池隔离的项目所有接口都运行在一个 ThreadPool 中,当某一个接口压力过大或者出现故障时,会导致资源耗尽从而影响到其他接口的调用而引发服务雪崩效应。我们在模拟高并发场景时也演示了该效果。
通过每次都开启一个单独线程运行。它的隔离是通过线程池,即每个隔离粒度都是个线程池,互相不干扰。线程池隔离方式,等于多了一层的保护措施,可以通过 hytrix 直接设置超时,超时后直接返回。
隔离前


隔离后


优点:
缺点:
服务消费者 pom.xml 添加 hystrix 依赖。
<!-- spring-cloud netflix hystrix 依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
服务消费者业务层代码添加线程隔离规则。
package com.example.service.impl;
import com.example.pojo.Product;
import com.example.service.ProductService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@Service
public class ProductServiceImpl implements ProductService {
    @Autowired
    private RestTemplate restTemplate;
    /**
     * 查询商品列表
     *
     * @return
     */
    // 声明需要服务容错的方法
    // 线程池隔离
    @HystrixCommand(groupKey = "order-productService-listPool",// 服务名称,相同名称使用同一个线程池
            commandKey = "selectProductList",// 接口名称,默认为方法名
            threadPoolKey = "order-productService-listPool",// 线程池名称,相同名称使用同一个线程池
            commandProperties = {
                    // 超时时间,默认 1000ms
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",
                            value = "5000")
            },
            threadPoolProperties = {
                    // 线程池大小
                    @HystrixProperty(name = "coreSize", value = "6"),
                    // 队列等待阈值(最大队列长度,默认 -1)
                    @HystrixProperty(name = "maxQueueSize", value = "100"),
                    // 线程存活时间,默认 1min
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                    // 超出队列等待阈值执行拒绝策略
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "100")
            }, fallbackMethod = "selectProductListFallback")
    @Override
    public List<Product> selectProductList() {
        System.out.println(Thread.currentThread().getName() + "-----selectProductList-----");
        // ResponseEntity: 封装了返回数据
        return restTemplate.exchange(
                "http://product-service/product/list",
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<Product>>() {
                }).getBody();
    }
    
    // 托底数据
    private List<Product> selectProductListFallback() {
        System.out.println("-----selectProductListFallback-----");
        return Arrays.asList(
                new Product(1, "托底数据-华为手机", 1, 5800D),
                new Product(2, "托底数据-联想笔记本", 1, 6888D),
                new Product(3, "托底数据-小米平板", 5, 2020D)
        );
    }
    /**
     * 根据主键查询商品
     *
     * @param id
     * @return
     */
    // 声明需要服务容错的方法
    // 线程池隔离
    @HystrixCommand(groupKey = "order-productService-singlePool",// 服务名称,相同名称使用同一个线程池
            commandKey = "selectProductById",// 接口名称,默认为方法名
            threadPoolKey = "order-productService-singlePool",// 线程池名称,相同名称使用同一个线程池
            commandProperties = {
                    // 超时时间,默认 1000ms
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", 
                            value = "5000")
            },
            threadPoolProperties = {
                    // 线程池大小
                    @HystrixProperty(name = "coreSize", value = "3"),
                    // 队列等待阈值(最大队列长度,默认 -1)
                    @HystrixProperty(name = "maxQueueSize", value = "100"),
                    // 线程存活时间,默认 1min
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                    // 超出队列等待阈值执行拒绝策略
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "100")
            })
    @Override
    public Product selectProductById(Integer id) {
        System.out.println(Thread.currentThread().getName() + "-----selectProductById-----");
        return restTemplate.getForObject("http://product-service/product/" + id, Product.class);
    }
}
  @HystrixCommand 注解各项参数说明如下:

服务消费者启动类开启熔断器注解。
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
// 开启熔断器注解 2 选 1,@EnableHystrix 封装了 @EnableCircuitBreaker
// @EnableHystrix
@EnableCircuitBreaker
@SpringBootApplication
public class OrderServiceRestApplication {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceRestApplication.class, args);
    }
}
  服务提供者接口添加 Thread.sleep(2000),模拟服务处理时长。
JMeter 开启 20 线程循环 50 次访问:http://localhost:9090/order/1/product/list
浏览器访问:http://localhost:9090/order/1/product 控制台打印结果如下:
hystrix-order-productService-listPool-1-----selectProductList-----
hystrix-order-productService-listPool-4-----selectProductList-----
hystrix-order-productService-listPool-2-----selectProductList-----
hystrix-order-productService-listPool-3-----selectProductList-----
hystrix-order-productService-singlePool-1-----selectProductById-----
hystrix-order-productService-listPool-5-----selectProductList-----
hystrix-order-productService-listPool-6-----selectProductList-----
  每次调用线程,当前请求通过计数信号量进行限制,当信号量大于了最大请求数 maxConcurrentRequests 时,进行限制,调用 fallback 接口快速返回。信号量的调用是同步的,也就是说,每次调用都得阻塞调用方的线程,直到结果返回。这样就导致了无法对访问做超时(只能依靠调用协议超时,无法主动释放)。

服务消费者 pom.xml 添加 hystrix 依赖。
<!-- spring-cloud netflix hystrix 依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
服务消费者业务层代码添加信号量隔离规则。
package com.example.service.impl;
import com.example.pojo.Product;
import com.example.service.ProductService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.Arrays;
import java.util.List;
@Service
public class ProductServiceImpl implements ProductService {
    @Autowired
    private RestTemplate restTemplate;
    /**
     * 查询商品列表
     *
     * @return
     */
    // 声明需要服务容错的方法
    // 信号量隔离
    @HystrixCommand(commandProperties = {
            // 超时时间,默认 1000ms
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",
                    value = "5000"),
            // 信号量隔离
            @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY,
                    value = "SEMAPHORE"),
            // 信号量最大并发,调小一些方便模拟高并发
            @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS,
                    value = "6")
    }, fallbackMethod = "selectProductListFallback")
    @Override
    public List<Product> selectProductList() {
        // ResponseEntity: 封装了返回数据
        return restTemplate.exchange(
                "http://product-service/product/list",
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<Product>>() {
                }).getBody();
    }
    // 托底数据
    private List<Product> selectProductListFallback() {
        System.out.println("-----selectProductListFallback-----");
        return Arrays.asList(
                new Product(1, "托底数据-华为手机", 1, 5800D),
                new Product(2, "托底数据-联想笔记本", 1, 6888D),
                new Product(3, "托底数据-小米平板", 5, 2020D)
        );
    }
}
  @HystrixCommand 注解各项参数说明如下:

服务消费者启动类开启熔断器注解。
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
// 开启熔断器注解 2 选 1,@EnableHystrix 封装了 @EnableCircuitBreaker
// @EnableHystrix
@EnableCircuitBreaker
@SpringBootApplication
public class OrderServiceRestApplication {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceRestApplication.class, args);
    }
}
  服务提供者接口添加 Thread.sleep(2000),模拟服务处理时长。
  服务消费者信号量最大并发设置为 6,方便模拟高并发。
JMeter 开启 20 线程循环 50 次访问:http://localhost:9090/order/1/product/list
浏览器也访问:http://localhost:9090/order/1/product/list 结果如下:

| 隔离方式 | 是否支持超时 | 是否支持熔断 | 隔离原理 | 是否是异步调用 | 资源消耗 | 
|---|---|---|---|---|---|
| 线程池隔离 | 支持 | 支持 | 每个服务单独用线程池 | 支持同步或异步 | 大 | 
| 信号量隔离 | 不支持 | 支持 | 通过信号量的计数器 | 同步调用,不支持异步 | 小 | 
请求线程和调用 Provider 线程不是同一条线程;
支持超时,可直接返回;
支持熔断,当线程池到达最大线程数后,再请求会触发 fallback 接口进行熔断;
隔离原理:每个服务单独用线程池;
支持同步和异步两种方式;
资源消耗大,大量线程的上下文切换、排队、调度等,容易造成机器负载高;
无法传递 Http Header。
maxConcurrentRequests 后。再请求会触发 fallback 接口进行熔断;
下一篇我们讲解 Hystrix 的服务熔断和服务降级以及基于 Feign 的服务熔断处理,记得关注噢~

  本文采用 知识共享「署名-非商业性使用-禁止演绎 4.0 国际」许可协议。
  大家可以通过 分类 查看更多关于 Spring Cloud 的文章。
  ?? 您的点赞和转发是对我最大的支持。
  ?? 扫码关注 哈喽沃德先生「文档 + 视频」每篇文章都配有专门视频讲解,学习更轻松噢 ~

Spring Cloud 系列之 Netflix Hystrix 服务容错(三)
原文:https://www.cnblogs.com/mrhelloworld/p/hystrix2.html