Spring RestTemplate&与Netty4的AsyncRestTemplate永远挂起

时间:2016-09-22 12:46:24

标签: java spring spring-boot netty

非常简单的设置:

的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>com.example</groupId>
    <artifactId>demo-rest-client</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>demo-rest-client</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.5.Final</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-buffer</artifactId>
            <version>4.1.5.Final</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-core</artifactId>
            <version>9.3.1</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-hystrix</artifactId>
            <version>9.3.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

用于演示AsyncRestTemplate的不同用法的测试用例:

SampleTests.java

package com.example;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandProperties;
import feign.RequestLine;
import feign.hystrix.HystrixFeign;
import feign.hystrix.SetterFactory;
import org.junit.Test;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.Netty4ClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.client.RestTemplate;

public class SampleTests {

    private static final String URL = "https://api.github.com/users/octocat";
    private static final int DEFAULT_SLEEP_MILLIS = 20;
    private static final int DEFAULT_TIMEOUT = 10000;

    @Test(timeout = DEFAULT_TIMEOUT)
    public void syncRestNetty() throws Exception {
        RestTemplate restTemplate = new RestTemplate(new Netty4ClientHttpRequestFactory());
        ResponseEntity<String> response = restTemplate.getForEntity(URL, String.class);
        System.out.println("response = " + response);
    }

    @Test(timeout = DEFAULT_TIMEOUT)
    public void asyncRestNetty() throws Exception {
        AsyncRestTemplate restTemplate = new AsyncRestTemplate(new Netty4ClientHttpRequestFactory());
        ListenableFuture<ResponseEntity<String>> listenableFuture = restTemplate.getForEntity(URL, String.class);
        listenableFuture.addCallback(result -> System.out.println("result = " + result), Throwable::printStackTrace);
        while (!listenableFuture.isDone()) {
            Thread.sleep(DEFAULT_SLEEP_MILLIS);
        }
        System.out.println("the end");
    }

    @Test
    public void asyncRestOkHttp() throws Exception {
        AsyncRestTemplate restTemplate = new AsyncRestTemplate(new OkHttp3ClientHttpRequestFactory());
        ListenableFuture<ResponseEntity<String>> listenableFuture = restTemplate.getForEntity(URL, String.class);
        listenableFuture.addCallback(result -> System.out.println("result = " + result), Throwable::printStackTrace);
        while (!listenableFuture.isDone()) {
            Thread.sleep(DEFAULT_SLEEP_MILLIS);
        }
        System.out.println("the end");
    }

    @Test
    public void asyncRestHystrixFeign() throws Exception {
        GitHub gitHub = HystrixFeign.builder()
                .setterFactory((target, method) -> new SetterFactory.Default().create(target, method).andCommandPropertiesDefaults(HystrixCommandProperties.defaultSetter().withExecutionTimeoutInMilliseconds(10000)))
                .target(GitHub.class, "https://api.github.com");
        HystrixCommand<String> command = gitHub.octocatAsync();
        command.toObservable().subscribe(result -> System.out.println("result = " + result), Throwable::printStackTrace);
        while (!command.isExecutionComplete()) {
            Thread.sleep(DEFAULT_SLEEP_MILLIS);
        }
        System.out.println("command.getExecutionTimeInMilliseconds() = " + command.getExecutionTimeInMilliseconds());
        System.out.println("the end");
    }

    interface GitHub {
        @RequestLine("GET /users/octocat")
        HystrixCommand<String> octocatAsync();
    }
}

当尝试运行使用Netty的测试时,他们只是永远挂起。 (要查看此内容,请删除JUnit超时约束)。但是,如果我与其他客户端运行完全相同的代码,一切都按预期工作。

我尝试过不同版本的Spring Boot和Netty,但没有成功。从日志中看,一切都很好。

我在这里缺少什么?

编辑: 按照Spring Gitter的建议打开了一张票https://jira.spring.io/browse/SPR-14744

EDIT-2: Brian Clozel的回答帮助我找到了与Netty没有意识到服务器发送空响应的问题(Github API和普通http的特殊情况)所以我将其标记为已接受。

1 个答案:

答案 0 :(得分:1)

您可以尝试使用Netty Sslcontext配置您的请求工厂吗?

Netty4ClientHttpRequestFactory nettyFactory = new Netty4ClientHttpRequestFactory();
nettyFactory.setSslContext(SslContextBuilder.forClient().build());
AsyncRestTemplate restTemplate = new AsyncRestTemplate(nettyFactory);

没有该上下文,客户端正在尝试向https端点发送明文请求;在这种情况下,您可能会收到HTTP 400响应。

在您的示例代码中,throwable应该是HttpClientErrorException的实例,您可以通过使用exception.getResponseBodyAsString()记录响应状态或其正文来获取该信息。