AsyncRestTemplate Netty客户端的Spring启动失败

我有一个Spring Boot 1.3.6应用程序,它是开箱即用的,并使用嵌入式Tomcat服务器。 该应用程序有一个端点执行一个非常简单的回应请求。

后来我使用AsyncRestTemplate定义了一个相应的客户端调用该简单的端点,但是如果我的客户端使用Netty4ClientHttpRequestFactory ,请求将失败,否则它会成功。

我的例子是在Kotlin中,但它在Java中失败了,所以它不需要用我实现它的语言。

服务器

@SpringBootApplication
open class EchoApplication {

    companion object {
       @JvmStatic fun main(args: Array<String>) {
          SpringApplication.run(EchoApplication::class.java, *args)
       }
    }

    @Bean
    open fun objectMapper(): ObjectMapper {
        return ObjectMapper().apply {
            dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX")
            registerModule(KotlinModule())
        }
    }

    @Bean
    open fun customConverters(): HttpMessageConverters {
        return HttpMessageConverters(listOf(MappingJackson2HttpMessageConverter(objectMapper())))
    }
}

我的端点如下所示:

@RestController
class EchoController {

    @RequestMapping(value="/foo", method = arrayOf(RequestMethod.PUT))
    fun echo(@RequestBody order: Order): Order {
        return order
    }

}

订单数据类是

data class Order(val orderId: String)

注意:由于我使用Kotlin,我还添加了Kotlin Jackson模块以确保正确的构造函数反序列化。

客户端

然后我开始创建一个调用此端点的客户端。

如果我在客户端执行类似以下的操作,它可以很好地工作,并获得成功的回应响应。

    val executor = TaskExecutorAdapter(Executors.newFixedThreadPool(2))
    val restTemplate = AsyncRestTemplate(executor)
    restTemplate.messageConverters = listOf(MappingJackson2HttpMessageConverter(mapper))

    val promise = restTemplate.exchange(URI.create("http://localhost:8080/foo"), HttpMethod.PUT, HttpEntity(Order("b1254"), headers), Order::class.java)
    promise.addCallback(object : ListenableFutureCallback<ResponseEntity<Order>> {
        override fun onSuccess(result: ResponseEntity<Order>) {
            println(result.body)
        }

        override fun onFailure(ex: Throwable) {
            ex.printStackTrace()
            if(ex is HttpStatusCodeException){
                println(ex.responseBodyAsString)
            }
        }
    })

如上所述,上面的代码运行完美,并打印出一个成功的回应响应。

问题

但是如果我决定使用Netty客户端,那么我会收到400个错误请求报告,我没有传递正文:

val nettyFactory = Netty4ClientHttpRequestFactory()
val restTemplate = AsyncRestTemplate(nettyFactory)

当我这样做时,我得到一个HttpMessageNotReadableException与消息说“必需的请求正文丢失”。

我调试了Spring Boot代码,我发现当ServletInputStream被读取时,它总是返回-1,就像它是空的一样。

在我的gradle中,我添加了runtime('io.netty:netty-all:4.1.2.Final') ,所以我使用了现在是Netty的最新版本。 这个Netty版本在与我使用常规Spring的其他项目(即不是Spring Boot)的终端交互时工作得很好。

SimpleClientHttpRequestFactory如何正常工作,但Netty4ClientHttpRequestFactory失败?

我认为这可能与嵌入式Tomcat服务器有关,但是,如果我将这个应用程序打包为war并将其部署到现有Tomcat服务器(即不使用嵌入式服务器),问题仍然存在。 所以,我猜测是与Spring / Spring Boot有关。

我是否在我的Spring Boot应用程序中缺少任何配置? 关于如何使Netty客户端与Spring Boot一起工作的任何建议?


看起来客户端序列化存在问题。 因为此代码完美:

restTemplate.exchange(
        URI.create("http://localhost:8080/foo"),
        HttpMethod.PUT,
        HttpEntity("""{"orderId":"1234"}""", HttpHeaders().apply {
            setContentType(MediaType.APPLICATION_JSON);
        }),
        Order::class.java
).addCallback(object : ListenableFutureCallback<ResponseEntity<Order>> {
    override fun onSuccess(result: ResponseEntity<Order>) {
        println("Result: ${result.body}")
    }

    override fun onFailure(ex: Throwable) {
        ex.printStackTrace()
        if (ex is HttpStatusCodeException) {
            println(ex.responseBodyAsString)
        }
    }
})

我需要更精确地查看转换器中的restTemplate,但现在您可以这样编写这部分:

val mapper = ObjectMapper()

restTemplate.exchange(
        URI.create("http://localhost:8080/foo"),
        HttpMethod.PUT,
        HttpEntity(mapper.writeValueAsString(Order("HELLO")), HttpHeaders().apply {
            setContentType(MediaType.APPLICATION_JSON);
        }),
        Order::class.java
).addCallback(object : ListenableFutureCallback<ResponseEntity<Order>> {
    override fun onSuccess(result: ResponseEntity<Order>) {
        println("Result: ${result.body}")
    }

    override fun onFailure(ex: Throwable) {
        ex.printStackTrace()
        if (ex is HttpStatusCodeException) {
            println(ex.responseBodyAsString)
        }
    }
})

如您所见,我不使用KotlinModule,并且此代码完美工作,因此在配置AsyncRestTemplate本身时显然存在问题。


我的2美分。 这当然不是解决方案。

我用AsyncHttpClientRequestInterceptor配置了asyncRestTemplate ,它神奇地工作。 没有解释,期限!

public class AsyncClientLoggingInterceptor implements AsyncClientHttpRequestInterceptor {

    @Override
    public ListenableFuture<ClientHttpResponse> intercept(HttpRequest request, byte[] body, AsyncClientHttpRequestExecution execution) throws IOException {

        return execution.executeAsync(request, body);
    }
}
链接地址: http://www.djcxy.com/p/35203.html

上一篇: Spring Boot with AsyncRestTemplate Netty Client Fails

下一篇: Angular cli hook