Hystrix 学习 4 : 调用失败率对熔断器开关的影响

本文通过一个Java示例程序深入探讨Hystrix熔断器的使用,演示如何通过调整参数来观察调用失败对熔断器的影响,特别是在未启用fallback机制的情况下。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文是本人学习Hystrix总结的第四篇,这次通过一个例子程序学习使用Hystrix模拟远程调用,并调整参数观察调用失败对熔断器的影响,这次的例子中没有使用fallback机制,也就是说,调用失败的时候会抛出异常而不是执行fallback逻辑。本应用特征如下 :

  1. java 项目 ;
    • 暂不涉及结合Spring使用,以便专注于Hystrix自身;
  2. 使用junit 观察运行效果 ;
  3. 使用 maven 管理依赖;

这里所模拟的远程调用通过sleep阻塞线程一段时间模拟一个耗时的远程调用,并无真正的远程调用发生。

1. pom.xml

<?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>tut.zero</groupId>
    <artifactId>hystrix</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-core</artifactId>
            <version>1.5.18</version>
        </dependency>
        <!-- test -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. 模拟一个远程服务调用

package tut.zero.middle;

/**
 * 模拟一个远程服务调用
 */
public class MockRemoteService {

    private long wait;

    /**
     * @param wait 模拟远程调用所消耗的时间,单位 : 毫秒
     * @throws InterruptedException
     */
    public MockRemoteService(long wait)   {
        this.wait = wait;
    }

    String execute() throws InterruptedException {
        Thread.sleep(wait);
        return "SUCCESS";
    }
}

这是一个纯粹通过Java模拟远程调用的类,通过Thread.sleep()来模拟远程调用的执行耗时,而并不真正发生远程调用。该类和Hystrix还没有任何关系。

2. 定义封装远程调用的Hystrix Command

package tut.zero.middle;

import com.netflix.hystrix.HystrixCommand;

/**
 * 封装一个模拟进行远程调用的 HystrixCommand, 返回结果设计为 String 类型,
 * 没有fallback实现
 */
public class MockRemoteServiceCommandWithFallback extends HystrixCommand<String> {

    private MockRemoteService remoteService;

    public MockRemoteServiceCommandWithFallback(Setter config, MockRemoteService remoteService) {
        super(config);
        this.remoteService = remoteService;
    }

    @Override
    protected String run() throws Exception {
        return remoteService.execute();
    }
}

3. 测试运行定义的Hystrix Command

package tut.zero;

import tut.zero.middle.MockRemoteService;
import tut.zero.middle.MockRemoteServiceCommand;
import tut.zero.simple.CommandHelloWorld;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixThreadPoolProperties;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import org.junit.Test;

import java.util.concurrent.Future;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;

public class HystrixTest {    
    @Test
    public void testMockRemoteServiceCallWithCircuitBreakerButNoFallback() {
        HystrixCommand.Setter config = HystrixCommand.Setter
                .withGroupKey(
                        HystrixCommandGroupKey.Factory.asKey("MockRemoteServiceCircuitBreaker"));

        HystrixCommandProperties.Setter properties = HystrixCommandProperties.Setter();
        properties.withExecutionTimeoutInMilliseconds(1_000); // 每次任务执行超过1秒就认为是超时失败
        final int circuitBreakerSleepWindowInMills = 4_000; // 设置熔断之后多长时间恢复调用:4秒
        properties.withCircuitBreakerSleepWindowInMilliseconds(circuitBreakerSleepWindowInMills);
        // 1. 属性 errorThresholdPercentage 表示错误百分比,默认值50%,例如一段时间(10s)内有100个请求,
        // 其中有55个超时或者异常返回了,那么这段时间内的错误百分比是55%,大于了默认值50%,这种情况下触发
        // 熔断器打开。
        // 2. 属性 circuitBreakerRequestVolumeThreshold 默认为 20, 意思是至少有20个请求才进行 
        // errorThresholdPercentage 错误百分比计算。
        // 这里设置为 1, 表示累计调用1时次开始进行 errorThresholdPercentage 错误百分比计算。
        properties.withCircuitBreakerRequestVolumeThreshold(1);

        config.andCommandPropertiesDefaults(properties);

        // 第一次触发远程调用,但会触发超时失败
        assertThat(invokeRemoteService(config, 1, 2_000), containsString("timed-out"));
        // 第二次触发远程调用,但会触发超时失败
        assertThat(invokeRemoteService(config, 2, 2_000), containsString("timed-out"));
        // 第三次不再触发远程调用,因为熔断器已经打开,且没有fallback实现,所以抛出HystrixRuntimeException
        assertThat(invokeRemoteService(config, 3, 2_000), containsString("short-circuited"));
        // 第四次不再触发远程调用,因为熔断器已经打开,且没有fallback实现,所以抛出HystrixRuntimeException
        assertThat(invokeRemoteService(config, 4, 2_000), containsString("short-circuited"));

        // 以上4次远程调用每次都需要2秒,但实际上因为超时容忍为1秒,所以前两次远程调用都超时失败了。
        // 第二次之后失败次数2开始变成大于1(基于  错误百分比 errorThresholdPercentage 的熔断器开关打开),
        // 所以导致第3,4次远程调用根本不会发生。

        // 因为 breaker sleep window 是 4 秒, 所以此时 4 秒后 断路器才会放行新的请求。
        // 下面休眠了 4秒 多一点点(10毫秒)是为了确保尽量不受线程调度的抖动影响
        try {
            Thread.sleep(circuitBreakerSleepWindowInMilliseconds + 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 现在经过休眠,熔断器已经恢复关闭状态, 允许远程调用继续进行。
        // 以下模拟的调用都是在足够短的时间内完成,所以都会成功返回。
        assertThat(invokeRemoteService(config, 5, 500), equalTo("SUCCESS"));
        assertThat(invokeRemoteService(config, 6, 800), equalTo("SUCCESS"));
        assertThat(invokeRemoteService(config, 7, 950), equalTo("SUCCESS"));
    }
}

    /**
     * 工具方法,调用远程服务,成功时返回结果,失败时返回异常消息
     *
     * @param config
     * @param count
     * @param timeout
     * @return
     */
    public static String invokeRemoteService(HystrixCommand.Setter config, int count, int timeout) {
        try {
            MockRemoteServiceCommand command = new MockRemoteServiceCommand(config, 
                new MockRemoteService(timeout));
            final boolean isCircuitBreakerOpen = command.isCircuitBreakerOpen();
            final String result = command.execute();
            System.out.println("第" + count + "次调用成功 :" + result);
            return result;
        } catch (HystrixRuntimeException ex) {
            System.out.println("第" + count + "次调用失败 :  ex = " + ex);
            return ex.getMessage();
        } catch (Exception e) {
            return e.getMessage();
        }
    }

该测试方法执行时控制台输出为 :

第1次调用失败 :  ex = com.netflix.hystrix.exception.HystrixRuntimeException: MockRemoteServiceCommand timed-out and no fallback available.
第2次调用失败 :  ex = com.netflix.hystrix.exception.HystrixRuntimeException: MockRemoteServiceCommand timed-out and no fallback available.
第3次调用失败 :  ex = com.netflix.hystrix.exception.HystrixRuntimeException: MockRemoteServiceCommand short-circuited and no fallback available.
第4次调用失败 :  ex = com.netflix.hystrix.exception.HystrixRuntimeException: MockRemoteServiceCommand short-circuited and no fallback available.
第5次调用成功 :SUCCESS
第6次调用成功 :SUCCESS
第7次调用成功 :SUCCESS
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值