这篇文章,主要介绍微服务组件之Hystrix实现线程池隔离和信号量隔离。

目录

一、线程池隔离

1.1、线程池隔离介绍

1.2、实现线程池隔离

(1)引入依赖

(2)编写Service层代码

(3)编写Controller层代码

(4)运行测试

二、信号量隔离

2.1、信号量隔离介绍

2.2、实现信号量隔离

(1)引入依赖

(2)编写Service层代码

(3)编写Controller层代码

(4)运行测试


一、线程池隔离

1.1、线程池隔离介绍

在一个微服务中,所有的接口如果都是运行在同一个线程池里面,那么如果其中某一个接口出现大量的请求,并且这个接口又没办法处理过来,这个时候服务就可能会出现资源耗尽的情况,从而导致整个服务崩溃。所以,Hystrix为了解决这个问题,提出了线程池隔离的方案,将不同的接口放到不同的线程池里面进行管理,并且不同的线程池互不干扰,这样即使其中一个线程池发生故障,也不会影响其他的线程池,也就降低了服务崩溃的可能性。

微服务 还需要多线程台下 微服务多线程使用_线程池隔离

线程池隔离虽然解决了服务崩溃的问题,但是同时也带来了一个问题,那就是线程的创建和销毁是需要消耗系统资源的,如果采用线程池隔离,当这个微服务的请求越来越大,对应的线程也就会创建的越来越多,这就会导致线程的切换非常频繁,从而也就降低了系统的一个性能。线程池隔离适用于隔离实例较少的场景里面

1.2、实现线程池隔离

(1)引入依赖

<!--hystrix 依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

(2)编写Service层代码

在service服务层中,我们在需要开启线程池隔离的方法上面,使用【@HystrixCommand】注解进行标记。

package com.gitcode.hystrix.service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;

/**
 * @version 1.0.0
 * @Date: 2023/4/9 19:49
 * @Copyright (C) ZhuYouBin
 * @Description:
 */
@Service
public class HystrixThreadService {


    @HystrixCommand(
            // 命令key,默认是当前方法名称
            commandKey = "firstCommandKey",
            // 线程池分组的key
            groupKey = "queryList-thread-pool",
            // 线程池的key,可以和 groupKey 设置相同
            threadPoolKey = "queryList-thread-pool",
            // 设置 hystrix 命令的属性
            commandProperties = {
                    // 设置线程池隔离的超时时间,默认是1000ms
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
            },
            // 设置线程池的属性
            threadPoolProperties = {
                    // 核心线程大小
                    @HystrixProperty(name = "coreSize", value = "4"),
                    // 队列大小
                    @HystrixProperty(name = "maxQueueSize", value = "100"),
                    // 存活时间,默认是 1 分钟,单位是分钟
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                    // 超出队列执行拒绝策略的阈值
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "100"),
            },
            // 设置快速失败时候调用的方法,可以写,也可以不写,写了就会执行快速失败,不写就阻塞等待
            fallbackMethod = "fallbackDemo"
    )
    public String queryList() {
        // TODO 这里模拟调用其他微服务
        try {
            Thread.sleep(6000);
            System.out.println("微服务调用成功:" + Thread.currentThread().getName());
        } catch (Exception e) {}
        return "success";
    }

    @HystrixCommand(
            // 命令key,默认是当前方法名称
            commandKey = "secondCommandKey",
            // 线程池分组的key
            groupKey = "getDataById-thread-pool",
            // 线程池的key,可以和 groupKey 设置相同
            threadPoolKey = "getDataById-thread-pool",
            // 设置 hystrix 命令的属性
            commandProperties = {
                    // 设置线程池隔离的超时时间,默认是1000ms
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
            },
            // 设置线程池的属性
            threadPoolProperties = {
                    // 核心线程大小
                    @HystrixProperty(name = "coreSize", value = "2"),
                    // 队列大小
                    @HystrixProperty(name = "maxQueueSize", value = "100"),
                    // 存活时间,默认是 1 分钟,单位是分钟
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                    // 超出队列执行拒绝策略的阈值
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "100"),
            }
    )
    public String getDataById() {
        // TODO 这里模拟调用其他微服务
        System.out.println("微服务调用成功:" + Thread.currentThread().getName());
        return "success";
    }

    public String fallbackDemo() {
        return "接口调用失败,请稍后重试!";
    }
}

(3)编写Controller层代码

package com.gitcode.hystrix.controller;

import com.gitcode.hystrix.service.HystrixThreadService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @version 1.0.0
 * @Date: 2023/4/9 19:47
 * @Copyright (C) ZhuYouBin
 * @Description: Hystrix 线程池隔离
 */
@RestController
@RequestMapping("/hystrix/thread")
public class HystrixThreadController {

    @Autowired
    private HystrixThreadService hystrixThreadService;

    @GetMapping("/queryList")
    public String queryList() {
        return hystrixThreadService.queryList();
    }

    @GetMapping("/getDataById")
    public String getDataById() {
        return hystrixThreadService.getDataById();
    }
}

(4)运行测试

启动应用,浏览器访问两个接口。

微服务 还需要多线程台下 微服务多线程使用_微服务_02

二、信号量隔离

2.1、信号量隔离介绍

采用线程池隔离,可能会导致线程过多而降低系统的性能,所以为了解决这个问题,Hystrix又提出了一个信号量隔离。信号量隔离是采用同步阻塞等待的思想,它通过设置一个信号量,这个信号量用于表示可以同时访问的最大请求数量,当系统中到达的请求数量超过这个信号量之后,超出的请求就会处于阻塞状态,或者直接采用快速失败机制,返回一个错误提示。

微服务 还需要多线程台下 微服务多线程使用_信号量隔离_03

信号量隔离,不会产生新的线程,所以也就不会存在大量线程之间的来回切换;但是信号量隔离也存在缺点,它不支持超时设置,所以它适用于非网络请求的。

2.2、实现信号量隔离

(1)引入依赖

<!--hystrix 依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

(2)编写Service层代码

信号量隔离,也是使用【@HystrixCommand】注解,指定隔离策略是【信号量隔离】即可。

package com.gitcode.hystrix.service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;

/**
 * @version 1.0.0
 * @Date: 2023/4/9 19:49
 * @Copyright (C) ZhuYouBin
 * @Description: 信号量隔离
 */
@Service
public class HystrixSemaphoreService {

    @HystrixCommand(
            // 设置 hystrix 命令的属性
            commandProperties = {
                    // 设置线程池隔离的超时时间,默认是1000ms
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"),
                    // 设置隔离策略是【信号量隔离】,默认是线程池隔离
                    @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"),
                    // 设置信号量最大请求数, 超过最大信号量数量,如果写了 fallbackMethod 属性,则会调用快速失败方法
                    @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "4")
            },
            // 设置快速失败时候调用的方法,可以写,也可以不写,写了就会执行快速失败,不写就阻塞等待
            fallbackMethod = "fallbackDemo"
    )
    public String queryList() {
        // TODO 这里模拟调用其他微服务
        System.out.println("微服务调用成功:" + Thread.currentThread().getName());
        return "success";
    }

    public String fallbackDemo() {
        System.out.println("接口调用失败,请稍后重试!");
        return "接口调用失败,请稍后重试!";
    }
}

(3)编写Controller层代码

package com.gitcode.hystrix.controller;

import com.gitcode.hystrix.service.HystrixSemaphoreService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @version 1.0.0
 * @Date: 2023/4/9 19:47
 * @Copyright (C) ZhuYouBin
 * @Description: Hystrix 信号量隔离
 */
@RestController
@RequestMapping("/hystrix/semaphore")
public class HystrixSemaphoreController {

    @Autowired
    private HystrixSemaphoreService hystrixSemaphoreService;

    @GetMapping("/queryList")
    public String queryList() {
        return hystrixSemaphoreService.queryList();
    }
}

(4)运行测试

启动应用,使用jemeter模拟并发请求,查看控制台输入日志。

微服务 还需要多线程台下 微服务多线程使用_信号量隔离_04

到此,Hystrix实现线程池隔离和信号量隔离就介绍完啦。

综上,这篇文章结束了,主要介绍微服务组件之Hystrix实现线程池隔离和信号量隔离。