[英]Spring Boot with AsyncRestTemplate Netty Client Fails
我有一个Spring Boot 1.3.6应用程序,开箱即用并使用嵌入式Tomcat服务器。 应用程序有一个端点执行非常简单的echo请求。
后来我使用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 Bad Request报告我没有通过正文:
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);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.