gpt4 book ai didi

java - 带有 AsyncRestTemplate Netty 客户端的 Spring Boot 失败

转载 作者:IT老高 更新时间:2023-10-28 13:42:47 34 4
gpt4 key购买 nike

我有一个 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 Module以确保正确的构造函数反序列化。

客户

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

如果我在我的客户端中执行以下操作,它会完美运行并且我会获得成功的回显响应。

    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 本身存在问题。

关于java - 带有 AsyncRestTemplate Netty 客户端的 Spring Boot 失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38319599/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com