java - 带有 Netty4 的 Spring RestTemplate 和 AsyncRestTemplate 永远挂起

标签 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都没有成功。从日志来看,一切正常。

我在这里错过了什么?

编辑: 开了工单https://jira.spring.io/browse/SPR-14744正如 Spring Gitter 上所建议的那样

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

最佳答案

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

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

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

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

关于java - 带有 Netty4 的 Spring RestTemplate 和 AsyncRestTemplate 永远挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39639467/

相关文章:

java - 使用 "mvn verify"而不是 "mvn clean verify"运行连续构建是否安全

java - 从单个图像流有效生成多个缩略图

java - 在 sqlite 表中找到一个名称并返回 id?

java - Spring MVC 中的 HTTP session

java - 如何根据 Spring 配置文件加载属性文件

java - Jetty 升级到 9.4.19.v20190610 后,传入的客户端请求中没有 X509 证书

java - C for Java 程序员?

java - 无法使用 RestTemplate 和 Spring Data REST 发布具有关系的新实体

java - 缺少请求正文的 Spring Boot POST 请求?

spring-boot - Spring Boot Controller 重定向不起作用