2 -【 高并发 - 服务 】- 1 Hystrix

1 Hystrix 产生的背景

伴随着业务复杂性的提高,系统的不断拆分,一个面向用户端的 API,其内部的 RPC 调用层层嵌套,调用链条可能会非常长。这会造成以下几个问题:

  • API 接口可用性降低
    引用 Hystrix 官方的一个例子,假设 tomcat 对外提供的一个 application,其内部依赖了30个服务,每个服务的可用性都很高,为99.99%。那整个 applicatiion 的可用性就是:99.99% 的 30 次方 = 99.7%,即 0.3% 的失败率。
    这也就意味着,每 1 亿个请求,有 30万 个失败;按时间来算,就是每个月的故障时间超过 2 小时。

  • 服务熔断
    为了解决上述问题,服务熔断的思想被提出来。类似现实世界中的“保险丝“,当某个异常条件被触发,直接熔断整个服务,而不是一直等到此服务超时。
    熔断的触发条件可以依据不同的场景有所不同,比如统计一个时间窗口内失败的调用次数。

  • 服务降级
    有了熔断,就得有降级。所谓降级,就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值。
    这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强,当然这也要看适合的业务场景。

微服务高并发场景

减少服务与服务之间的依赖关系(不是在业务上的依赖),防止服务雪崩效应,最终以服务降级、熔断、限流。

  • 服务雪崩效应:当一个服务突然受到高并发请求, Tomcat 服务器如果承受不了的情况下,产生服务堆积,可能会导致其他服务不可用

  • 容错:服务发生不可用的时候,出错之后的处理方案。

2 测试环境搭建

需求:搭建一套分布式 rpc 远程通讯案例 - 订单服务调用会员服务实现 服务隔离防止雪崩效应 案例。

父工程导入依赖:

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.snow</groupId>
    <artifactId>Studying-2020</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>order</module>
        <module>member</module>
    </modules>

    <packaging>pom</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR1</spring-cloud.version>
        <fastjson.version>1.2.62</fastjson.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- springCloud -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- fastjson -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <!-- 集成commons工具类 -->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>${commons.lang3.version}</version>
            </dependency>
            <dependency>
                <groupId>commons-net</groupId>
                <artifactId>commons-net</artifactId>
                <version>${commons.net.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- SpringBoot 整合Web组件 -->
        <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>
        <!-- 集成lombok 框架 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
    </dependencies>

    <!-- 注意: 这里必须要添加,否者各种依赖有问题 -->
    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/libs-milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

</project>

2.1 创建会员工程

依赖

<dependencies>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
    </dependency>
</dependencies>

配置文件

server:
  port: 8081

启动项

package com.snow.member;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AppMember {

    public static void main(String[] args) {
        SpringApplication.run(AppMember.class, args);
    }

}

controller

package com.snow.member.controller;

import java.util.HashMap;
import java.util.Map;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/member")
public class MemberController {

    @RequestMapping("/memberIndex")
    public Object memberIndex() throws InterruptedException {
        Map<String, Object> hashMap = new HashMap<String, Object>();
        hashMap.put("code", 200);
        hashMap.put("msg", "memberIndex");
        Thread.sleep(1500);
        return hashMap;
    }

}

测试:
在这里插入图片描述

2.2 创建订单工程

依赖

<dependencies>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
    </dependency>
    <!-- Hystix 依赖 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
</dependencies>

配置文件

server:
  port: 8080
  tomcat:
    max-threads: 20 # 设置tomcat最大线程数

启动项

package com.snow.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AppOrder {

    public static void main(String[] args) {
        SpringApplication.run(AppOrder.class);
    }
}

HttpClient 工具类

package com.snow.order.utils;

import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
 * HttpClient 工具类
 *
 * @author hang.luo
 */
public class HttpClientUtils {

    // 日志记录
    private static Logger logger = LoggerFactory.getLogger(HttpClientUtils.class);

    private static RequestConfig requestConfig = null;

    static {
        // 设置请求和传输超时时间
        requestConfig = RequestConfig.custom().setSocketTimeout(2000).setConnectTimeout(2000).build();
    }

    /**
     * post请求传输json参数
     *
     * @param url url地址
     * @param jsonParam 参数
     * @return
     */
    public static JSONObject httpPost(String url, JSONObject jsonParam) {
        // post请求返回结果
        CloseableHttpClient httpClient = HttpClients.createDefault();
        JSONObject jsonResult = null;
        HttpPost httpPost = new HttpPost(url);
        // 设置请求和传输超时时间
        httpPost.setConfig(requestConfig);
        try {
            if (null != jsonParam) {
                // 解决中文乱码问题
                StringEntity entity = new StringEntity(jsonParam.toString(), "utf-8");
                entity.setContentEncoding("UTF-8");
                entity.setContentType("application/json");
                httpPost.setEntity(entity);
            }
            CloseableHttpResponse result = httpClient.execute(httpPost);
            // 请求发送成功,并得到响应
            if (result.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                String str = "";
                try {
                    // 读取服务器返回过来的json字符串数据
                    str = EntityUtils.toString(result.getEntity(), "utf-8");
                    // 把json字符串转换成json对象
                    jsonResult = JSONObject.parseObject(str);
                } catch (Exception e) {
                    logger.error("post请求提交失败:" + url, e);
                }
            }
        } catch (IOException e) {
            logger.error("post请求提交失败:" + url, e);
        } finally {
            httpPost.releaseConnection();
        }
        return jsonResult;
    }

    /**
     * post请求传输String参数 例如:name=Jack&sex=1&type=2
     * Content-type:application/x-www-form-urlencoded
     *
     * @param url url地址
     * @param strParam 参数
     * @return
     */
    public static JSONObject httpPost(String url, String strParam) {
        // post请求返回结果
        CloseableHttpClient httpClient = HttpClients.createDefault();
        JSONObject jsonResult = null;
        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(requestConfig);
        try {
            if (null != strParam) {
                // 解决中文乱码问题
                StringEntity entity = new StringEntity(strParam, "utf-8");
                entity.setContentEncoding("UTF-8");
                entity.setContentType("application/x-www-form-urlencoded");
                httpPost.setEntity(entity);
            }
            CloseableHttpResponse result = httpClient.execute(httpPost);
            // 请求发送成功,并得到响应
            if (result.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                String str = "";
                try {
                    // 读取服务器返回过来的json字符串数据
                    str = EntityUtils.toString(result.getEntity(), "utf-8");
                    // 把json字符串转换成json对象
                    jsonResult = JSONObject.parseObject(str);
                } catch (Exception e) {
                    logger.error("post请求提交失败:" + url, e);
                }
            }
        } catch (IOException e) {
            logger.error("post请求提交失败:" + url, e);
        } finally {
            httpPost.releaseConnection();
        }
        return jsonResult;
    }

    /**
     * 发送get请求
     *
     * @param url 路径
     * @return
     */
    public static JSONObject httpGet(String url) {
        // get请求返回结果
        JSONObject jsonResult = null;
        CloseableHttpClient client = HttpClients.createDefault();
        // 发送get请求
        HttpGet request = new HttpGet(url);
        request.setConfig(requestConfig);
        try {
            CloseableHttpResponse response = client.execute(request);

            // 请求发送成功,并得到响应
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                // 读取服务器返回过来的json字符串数据
                HttpEntity entity = response.getEntity();
                String strResult = EntityUtils.toString(entity, "utf-8");
                // 把json字符串转换成json对象
                jsonResult = JSONObject.parseObject(strResult);
            } else {
                logger.error("get请求提交失败:" + url);
            }
        } catch (IOException e) {
            logger.error("get请求提交失败:" + url, e);
        } finally {
            request.releaseConnection();
        }
        return jsonResult;
    }

}

service

package com.snow.order.service;

import com.alibaba.fastjson.JSONObject;
import com.snow.order.utils.HttpClientUtils;
import org.springframework.stereotype.Service;

@Service
public class MemberService {

    public JSONObject getMember() {

        JSONObject result = HttpClientUtils.httpGet("http://127.0.0.1:8081/member/memberIndex");
        return result;
    }

}

controller

package com.snow.order.controller;

import com.alibaba.fastjson.JSONObject;
import com.snow.order.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private MemberService memberService;

    @RequestMapping("/orderIndex")
    public Object orderIndex() throws InterruptedException {
        JSONObject member = memberService.getMember();
        System.out.println("当前线程名称:" + Thread.currentThread().getName() + ", 订单服务调用会员服务:member:" + member);
        return member;
    }

    @RequestMapping("/findOrderIndex")
    public Object findIndex() {
        System.out.println("当前线程:" + Thread.currentThread().getName() + ",findOrderIndex");
        return "findOrderIndex";
    }
}

测试:
在这里插入图片描述
在这里插入图片描述

3 Hystrix 作用

什么是 Hystrix

Hystrix 是一个微服务关于服务保护的框架,是 Netflix 开源的一款针对分布式系统的 延迟容错 解决框架,目的是用来 隔离分布式服务故障。它提供 线程信号量隔离,以减少不同服务之间资源竞争带来的相互影响;提供优雅降级机制;提供熔断机制使得服务可以快速失败,而不是一直阻塞等待服务响应,并能从中快速恢复。Hystrix通过这些机制来阻止级联失败并保证系统弹性、可用。

Hystrix 作用

  • 服务保护:当服务产生堆积的时候,对服务实现保护功能。

  • 服务隔离:保证每个服务互不影响,使用信号量和线程池方式。

  • 服务降级:当服务不可用的时候,不会被等待,直接返回一个友好的提示。
    主要目的:针对用户体验和高并发限流

  • 服务熔断:当服务器达到最大的承受之后,直接拒绝访问服务,然后调用服务降级方式。返回友好的提示,目的是保证服务器不会宕机掉。
    主要目的:保护服务

  • 堆积请求:假设默认 tomcat 最大线程的线程池是 50,尝试第 51 个请求,第 51 个请求阻塞,大量请求正在等待,如果堆积的请求过多,可能会造成服务器瘫痪。

tomcat 底层是 HTTP + 线程池,每个线程都是独立的请求。

3.1 为什么会产生雪崩效应?

tomcat 底层实际上是 线程池技术。线程池管理所有线程请求,假设线程池最大创建 50 个线程,超过 50 个就会等待。

如何查看是否是同一个线程池?

可以获取当前 线程名称线程池名称 + 线程ID

服务雪崩效应

产生服务堆积在同一个线程池中,因为在同一个线程池中,所有请求全部到一个服务进行访问,这时就会导致其他服务没有线程接收请求访问,所以就会产生服务雪崩效应。

3.2 服务隔离

当大多数人在使用 Tomcat 时,多个 HTTP 服务会共享一个线程池,假设其中一个 HTTP 服务访问的数据库响应非常慢,这将造成服务响应时间延迟增加,大多数线程阻塞等待数据响应返回,导致整个 Tomcat 线程池都被该服务占用,甚至拖垮整个 Tomcat。因此,如果我们能把不同 HTTP 服务隔离到不同的线程池,则某个 HTTP 服务的线程池满了也不会对其他服务造成灾难性故障。这就需要线程隔离或者信号量隔离来实现了。

使用线程隔离或信号隔离的目的是为不同的服务分配一定的资源,当自己的资源用完,直接返回失败而不是占用别人的资源。

服务隔离

每个服务接口互不影响

Hystrix 实现服务隔离两种方案:

  • 线程池方式:相同服务接口都有自己独立的线程池,管理运行自己的接口.
    缺点:CPU 内存开销非常大,实现完全隔离,能应用高并发解决雪崩效应。
  • 计数器方式:底层使用原子计数器,针对每个服务都设置自己独立的限制阈值。
    比如:设置每个服务接口最多同时访问 50 次,超出 50 次请求,自己实现拒绝策略。

例如:下面的 OrderController 里面有两个服务:orderIndexfindIndex

那么他们有自己独立的线程池,所以一个服务阻塞不会导致另外一个服务等待。

但是,由于每个服务都有自己独立的线程池,所以对 CPU 的开销非常大。
在这里插入图片描述

3.3 服务降级

服务降级

当服务不可用(相当于当服务正在等待的时候、网络延迟、服务器响应慢),客户端一直等待,应该直接返回一个错误提示给客户端,不让客户端继续等待,使用 fallback 方法返回当前服务不用提示。

服务降级作用

目的是为了提高用户体验,防止雪崩效应。

3.4 服务熔断

服务熔断

微服务中服务熔断产生的原因是:服务发生请求过多(属于高并发),设置一个限制,比如最多只能同时 100 请求访问(100 个线程), 超出的请求存放在缓存队列中,如果缓存队列中线程满了的话,直接拒绝访问,直接访问不了服务。

服务熔断和服务降级一起使用。

服务熔断能实现效果:

防止服务不会被挂掉,保护服务。

4 使用 Hystrix

引言

启动服务,使用 jmeter 测试:

1、创建一个线程组:
在这里插入图片描述

2、创建一个 HTTP 请求:
在这里插入图片描述

测试:
点击开始,然后浏览器访问:http://127.0.0.1:8080/order/findOrderIndex

在这里插入图片描述

可以看到,上面的请求会长时间卡顿,这就是因为高并发导致的。

使用 Hystrix

导入依赖:

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

4.1 方式一:线程池方式

  1. 使用线程池隔离可以完全隔离第三方应用,请求线程可以快速放回;
  2. 请求线程可以继续接受新的请求,如果出现问题线程池隔离是独立的不会影响其他应用
  3. 当失败的应用再次变得可用时,线程池将清理并可立即恢复,而不需要一个长时间的恢复
  4. 独立的线程池提高了并发性。

缺点:

线程池隔离的主要缺点是它们增加计算开销(CPU)。每个命令的执行涉及到排队、调度和上 下文切换都是在一个单独的线程上运行的。

创建 OrderHystrixCommand

package com.snow.order.hystrix;

import com.snow.order.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.fastjson.JSONObject;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixThreadPoolKey;
import com.netflix.hystrix.HystrixThreadPoolProperties;

/**
 * 功能说明: 使用线程池方式 <br>
 *     继承 HystrixCommand<JSONObject> ,泛型里面的值应该与controller里面的返回的类型一致,都是JSONObject
 *
 */
@SuppressWarnings("rawtypes")
public class OrderHystrixCommand extends HystrixCommand<JSONObject> {

    @Autowired
    private MemberService memberService;

    public OrderHystrixCommand(MemberService memberService) {
        super(setter());
        this.memberService = memberService;
    }

    protected JSONObject run() throws Exception {
        JSONObject member = memberService.getMember();
        System.out.println("当前线程名称:" + Thread.currentThread().getName() + ",订单服务调用会员服务:member:" + member);
        return member;
    }

    /**
     * 配置服务的隔离机制
     *
     * @return
     */
    private static Setter setter() {

        // 服务分组
        HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("orders");
        // 服务标识
        HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("order");
        // 线程池名称(为什么指定线程池名称?因为每个服务都有自己的线程池)
        HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("order-pool");
        // #####################################################
        // 线程池配置 线程池大小为10,线程存活时间15秒 队列等待的阈值为100,超过100执行拒绝策略 ------ 配置服务熔断
        HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter().withCoreSize(10)
                .withKeepAliveTimeMinutes(15).withQueueSizeRejectionThreshold(100);
        // ########################################################
        // 命令属性配置Hystrix 开启超时
        HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
                // 采用线程池方式实现服务隔离
                .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
                // 禁止
                .withExecutionTimeoutEnabled(false);
        return HystrixCommand.Setter.withGroupKey(groupKey).andCommandKey(commandKey).andThreadPoolKey(threadPoolKey)
                .andThreadPoolPropertiesDefaults(threadPoolProperties).andCommandPropertiesDefaults(commandProperties);

    }

    @Override
    protected JSONObject getFallback() {
        // 如果Hystrix发生熔断,当前服务不可用,直接执行Fallback方法
        System.out.println("系统错误!");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", 500);
        jsonObject.put("msg", "系统错误!");
        return jsonObject;
    }
}

OrderController 里面使用

 /**
  * 解决服务的雪崩效应,底层使用服务隔离 - 线程池方式实现
  * 
  * @return
  * @throws InterruptedException
  */
@RequestMapping("/orderIndexHystrix")
public Object orderIndexHystrix() throws InterruptedException {
    return new OrderHystrixCommand(memberService).execute();
}

测试

使用 jmeter 测试,发现访问:http://127.0.0.1:8080/order/findOrderIndex 不会等待了,因为有自己的线程池了。

在这里插入图片描述

4.2 方式二:信号量

使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,当请求进来时先判断计数 器的数值,若超过设置的最大线程个数则拒绝该请求,若不超过则通行,这时候计数器+1,请求返回成功后计数器-1。

与线程池隔离最大不同在于执行依赖代码的线程依然是请求线程

信号量的大小可以动态调整,线程池大小不可以。

创建 OrderHystrixCommand2

package com.snow.order.hystrix;

import com.snow.order.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.fastjson.JSONObject;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixThreadPoolKey;
import com.netflix.hystrix.HystrixThreadPoolProperties;

/**
 * 功能说明: 使用信号量 <br>
 *
 */
@SuppressWarnings("rawtypes")
public class OrderHystrixCommand2 extends HystrixCommand<JSONObject> {

    @Autowired
    private MemberService memberService;

    public OrderHystrixCommand2(MemberService memberService) {
        super(setter());
        this.memberService = memberService;
    }

    protected JSONObject run() throws Exception {

        JSONObject member = memberService.getMember();
        System.out.println("当前线程名称:" + Thread.currentThread().getName() + ",订单服务调用会员服务:member:" + member);
        return member;
    }

    private static Setter setter() {
        
        // 服务分组
        HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("members");
        // 命令属性配置 采用信号量模式
        HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
                .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
                // 使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,当请求进来时先判断计数
                // 器的数值,若超过设置的最大线程个数则拒绝该请求,若不超过则通行,这时候计数器+1,请求返 回成功后计数器-1。
                .withExecutionIsolationSemaphoreMaxConcurrentRequests(50);
        return HystrixCommand.Setter.withGroupKey(groupKey).andCommandPropertiesDefaults(commandProperties);
    }

    @Override
    protected JSONObject getFallback() {
        // 如果Hystrix发生熔断,当前服务不可用,直接执行Fallback方法
        System.out.println("系统错误!");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", 500);
        jsonObject.put("msg", "系统错误!");
        return jsonObject;
    }
}

OrderController 里面使用

/**
 * 解决服务的雪崩效应,底层使用服务隔离 - 信号量方式实现
 *
 * @return
 * @throws InterruptedException
 */
@RequestMapping("/orderIndexHystrix2")
public Object orderIndexHystrix2() throws InterruptedException {
    return new OrderHystrixCommand2(memberService).execute();
}

测试

使用 jmeter 测试,发现访问:http://127.0.0.1:8080/order/findOrderIndex 不会等待了,因为有自己的线程池了。

在这里插入图片描述

4.3 应用场景

线程池隔离:

  • 第三方应用或者接口
  • 并发量大

信号量隔离:

  • 内部应用或者中间件(redis)
  • 并发需求不大
发布了675 篇原创文章 · 获赞 214 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/weixin_42112635/article/details/104986517