Hystrix 学习 2 : 模拟远程调用成功/超时失败

本文通过实例演示了如何使用Hystrix封装远程调用并处理超时情况,包括调整执行参数、观察异常抛出及成功执行的不同场景。

本文是本人学习Hystrix总结的第二篇,通过一个例子程序学习使用Hystrix模拟一个远程调用,这里调整Hystrix的执行参数和远程调用的参数观察成功和超时发生时抛出异常的情况。本应用特征如下 :

  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 类型
 */
public class MockRemoteServiceCommand extends HystrixCommand<String> {

    private MockRemoteService remoteService;

    public MockRemoteServiceCommand(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 {
    /**
     * 使用 HystrixCommand 模拟封装一个远程调用(远程调用运行耗时可以模拟调节)
     *
     */
    @Test
    public void testMockRemoteServiceCallSuccessUsingDefaultConfig() {
        // 使用缺省配置执行 HystrixCommand
        HystrixCommand.Setter config = HystrixCommand.Setter.
                withGroupKey(
                HystrixCommandGroupKey.Factory.asKey("默认可容忍的超时时间大于远程方法的执行时间"));

        // 模拟一个会耗时100毫秒的远程调用
        assertThat(new MockRemoteServiceCommand(config, new MockRemoteService(100)).execute(), 
        	equalTo("SUCCESS"));
    }    
    
    /**
     * 远程调用耗时超过了 HystrixCommand 所能容忍的时间限度,预期会抛出异常 HystrixRuntimeException
     *
     */
    @Test(expected = HystrixRuntimeException.class)
    public void testMockRemoteServiceCallButHRE() {

        HystrixCommand.Setter config = HystrixCommand.Setter
                .withGroupKey(
                	HystrixCommandGroupKey.Factory.asKey("设置可容忍的超时时间小于远程方法的执行时间"));

        HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter();
        commandProperties.withExecutionTimeoutInMilliseconds(5_000);
        config.andCommandPropertiesDefaults(commandProperties);

        new MockRemoteServiceCommand(config, new MockRemoteService(15_000)).execute();
    }
    
    /**
     * 远程调用耗时增加,但是 HystrixCommand 客户端执行允许超时时间更大,所以远程调用可以成功执行完成
     *
     */
    @Test
    public void testMockRemoteServiceCallSuccessUsingBiggerTimeout() {
        HystrixCommand.Setter config = HystrixCommand.Setter
                .withGroupKey(
                	HystrixCommandGroupKey.Factory.asKey("设置可容忍的超时时间大于远程方法的执行时间"));

        HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter();
        commandProperties.withExecutionTimeoutInMilliseconds(20_000);
        config.andCommandPropertiesDefaults(commandProperties);

        assertThat(new MockRemoteServiceCommand(config, new MockRemoteService(15_000)).execute(), 
        	equalTo("SUCCESS"));
    }    
}

这里进行了三个测试,这三个测试运行都应该执行通过。他们的测试点如下 :

  1. testMockRemoteServiceCallSuccessUsingDefaultConfig

    使用缺省配置执行远程调用,并且远程调用所耗时间不太长,为100毫秒,此耗时低于缺省配置中可容忍的远程调用超时设置,所以该由MockRemoteServiceCommand封装的远程调用会成功执行。

  2. testMockRemoteServiceCallButHRE

    执行配置设置可容忍的远程调用超时为5秒,但是远程调用所耗时间为15秒,所以该由MockRemoteServiceCommand封装的远程调用会抛出异常HystrixRuntimeException。但请注意,该异常是该测试预期之内的,所以该测试会通过(换句话讲,如果不抛出该异常,则该测试不能通过)。

  3. testMockRemoteServiceCallSuccessUsingBiggerTimeout

    远程调用所耗时间为15秒,但执行配置中设置可容忍的远程调用超时为20秒,所以即使远程调用耗时很长,由MockRemoteServiceCommand封装的远程调用仍然成功返回,不会抛出异常。

<think>好的,我现在需要帮助用户解决在SpringCloud Alibaba中使用Feign进行远程调用时的超时处理配置问题。用户之前的问题是关于时间单位转换的,但当前的问题转向了微服务中的Feign配置,所以需要切换思维。 首先,我应该回忆一下用户提供的四个引用内容,尤其是关于Feign的超时配置和Spring Cloud Alibaba的相关信息。引用1提到Feign调用的超时问题及配置方法,引用2和3讨论了Feign在Spring Cloud Alibaba中的使用,包括契约配置,引用4则介绍了Feign的基本概念和集成组件。 用户明确询问的是超时处理方法,所以需要结合这些引用来组织答案。首先,Feign的超时配置通常涉及Ribbon和Hystrix,但根据Spring Cloud版本的不同,可能有所变化。特别是,在Spring Cloud Alibaba中,可能还有特定的配置方式需要考虑。 接下来,我需要确认Feign超时配置的关键点。根据引用1,超时配置可以通过Ribbon的参数来设置,比如连接超时和读取超时。此外,可能还需要考虑Hystrix超时配置,但需要确保Hystrix超时时间大于Ribbon的总时间,以避免冲突。 另外,引用2和3提到Feign的契约配置,即是否使用Spring MVC注解或原生Feign注解。这可能影响配置的方式,但超时处理主要是底层HTTP客户端的配置,与契约关系不大,但可能需要提到不同的HTTP客户端(如OkHttp或Apache HttpClient)的配置方式,如引用4提到的HttpClient。 然后,用户可能需要具体的配置示例,包括配置文件(如application.yml)中的设置,以及代码层面的配置,比如自定义配置类。需要分点说明,结构清晰。 此外,需要考虑Spring Cloud Alibaba与其他组件的集成,比如Nacos作为注册中心,是否需要额外的配置,但根据引用内容,可能不需要,所以主要聚焦在Feign和Ribbon的配置上。 最后,用户可能想知道常见问题及解决方法,比如超时时间不生效的可能原因,如Hystrix未正确配置或版本差异。需要给出验证配置的方法,比如日志或测试接口。 总结回答的结构应该是:配置方法(Ribbon、Hystrix、HTTP客户端)、代码示例、常见问题及验证,最后相关问题。需要确保引用正确的来源,比如引用1和4关于超时配置和HTTP客户端的部分,引用2和3关于契约的内容可能不直接相关,但可以忽略,除非用户的问题涉及注解的使用影响超时,但用户的问题集中在超时处理,所以重点在引用1和4。 需要检查是否有遗漏的点,比如是否要提到Feign的日志级别配置帮助调试超时问题,或者重试机制,但用户的问题明确是超时处理,可能不需要展开重试。同时,确保使用中文,术语准确,比如“连接超时”和“读取超时”的正确表述,以及配置项的正确格式,比如feign.client.config.*.loggerLevel。 最后,生成的相关问题需要围绕Feign的配置、超时处理的其他方面,可能涉及与其他组件的整合,如Sentinel的熔断,或者性能优化等。</think>### Spring Cloud Alibaba 中 Feign 远程调用超时处理配置 在 Spring Cloud Alibaba 中,Feign 的远程调用超时处理主要依赖 **Ribbon** 和 **HTTP 客户端配置**(如 Apache HttpClient 或 OkHttp)。以下是具体的配置方法及原理分析: --- #### 一、Ribbon 超时配置 Feign 默认集成 Ribbon 实现负载均衡,超时控制主要通过 Ribbon 参数实现: ```yaml # application.yml ribbon: ConnectTimeout: 5000 # 建立连接的超时时间(毫秒) ReadTimeout: 10000 # 读取响应的超时时间(毫秒) MaxAutoRetries: 0 # 同一实例的重试次数 MaxAutoRetriesNextServer: 1 # 切换实例的重试次数 ``` - **ConnectTimeout**:与服务提供方建立 TCP 连接的最大等待时间,超时则抛出 `ConnectTimeoutException`[^1]。 - **ReadTimeout**:从服务提供方读取响应的最大等待时间,超时则抛出 `SocketTimeoutException`[^1]。 --- #### 二、HTTP 客户端配置 Feign 底层支持多种 HTTP 客户端,需显式配置超时参数: 1. **Apache HttpClient**(默认): ```yaml feign: httpclient: enabled: true connection-timeout: 5000 # 连接池请求超时时间(毫秒) max-connections: 200 # 最大连接数 ``` 2. **OkHttp**: ```yaml feign: okhttp: enabled: true connectTimeout: 5000 # 连接超时 readTimeout: 10000 # 读取超时 ``` --- #### 三、全局与特定服务的超时配置 可通过 `@FeignClient` 注解为不同服务单独配置超时: ```yaml feign: client: config: default: # 全局默认配置 connectTimeout: 5000 readTimeout: 10000 service-provider: # 针对特定服务(service-provider 是服务名) connectTimeout: 8000 readTimeout: 15000 ``` --- #### 四、Hystrix 熔断超时(可选) 若启用了 Hystrix 熔断,需确保 Hystrix 超时时间大于 Ribbon 总超时时间(包括重试): ```yaml hystrix: command: default: execution: timeout: enabled: true isolation: thread: timeoutInMilliseconds: 15000 # Hystrix 超时时间(需 > Ribbon总耗时) ``` --- #### 五、代码示例 自定义 Feign 配置类(如需要更细粒度控制): ```java @Configuration public class FeignConfig { @Bean public Request.Options options() { return new Request.Options(5000, 10000); // 连接超时和读取超时(毫秒) } } ``` --- #### 六、常见问题与验证 1. **超时配置不生效**: - 检查是否依赖了正确的 HTTP 客户端(如 `feign-httpclient`)。 - 确认 Hystrix 超时时间是否覆盖 Ribbon 超时2. **验证方法**: - 通过日志查看请求耗时:设置 `logging.level.feign=DEBUG`。 - 模拟服务端延迟,测试超时触发逻辑。 --- ### 相关问题 1. 如何通过 Sentinel 实现 Feign 调用的熔断与降级? 2. Feign 和 Dubbo 在 Spring Cloud Alibaba 中的性能差异? 3. 如何配置 Feign 支持原生的注解(如 `@RequestLine`)?[^2][^3] 4. Ribbon 与 LoadBalancer 在 Spring Cloud Alibaba 中的替代关系?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值