首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >关于外部WebClient事件的Spring onTerminate

关于外部WebClient事件的Spring onTerminate
EN

Stack Overflow用户
提问于 2018-07-25 11:15:07
回答 1查看 704关注 0票数 1

我正在运行一个Spring v2.0.3tomcat嵌入的webserver 8.5.31,以服务SpringWeb通量REST服务。其中一个REST服务调用另一个外部。

代码语言:javascript
复制
public Mono<ServerResponse> select(ServerRequest request) {
  return request.principal().cast(Authentication.class)
      .flatMap(principal ->
          client.get().uri(f -> buildUri(request, principal, request.queryParams(), f))
              .exchange())
      .flatMap((ClientResponse mapper) ->
                  ServerResponse.status(mapper.statusCode())
                      .headers(c -> mapper.headers().asHttpHeaders().forEach(c::put))
                      .body(mapper.bodyToFlux(DataBuffer.class)
                          .delayElements(Duration.ofSeconds(10))
                          .doOnCancel(() -> log.error("Cancelled client"))
                          .doOnTerminate(() -> log.error("Terminated client")), DataBuffer.class))
      .doOnTerminate(() -> log.error("Termination called"));
}

如果浏览器调用了我的REST-Service,并且在不久之后取消了连接,我可以看到外部“终止称为”事件,并且客户机也被终止了。但是,客户端终止似乎在tomcat中触发了一个错误:

代码语言:javascript
复制
2018-07-25 12:50:42.860 DEBUG 12084 --- [      elastic-3] org.example.search.security.UserManager       : Authorizing org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@809aec11: Principal: cn=dv dbsearch client, ou=dbsearch, o=example, l=eb, st=unknown, c=de; Credentials: [PROTECTED]; Authenticated: false; Details: null; Not granted any authorities
2018-07-25 12:50:42.864 DEBUG 12084 --- [      elastic-3] org.example.search.security.UserManager       : Successfully authorized: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@c03925ec: Principal: org.springframework.security.core.userdetails.User@809aec0e: Username: cn=dv dbsearch client, ou=dbsearch, o=example, l=eb, st=unknown, c=de; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_ADMIN
2018-07-25 12:50:45.470 ERROR 12084 --- [ctor-http-nio-4] c.d.s.s.h.SolrSelectRequestHandler       : Termination called
2018-07-25 12:51:15.562 ERROR 12084 --- [     parallel-3] c.d.s.s.h.SolrSelectRequestHandler       : Terminated client
2018-07-25 12:51:15.625 ERROR 12084 --- [nio-8443-exec-2] o.s.w.s.adapter.HttpWebHandlerAdapter    : Unhandled failure: Eine bestehende Verbindung wurde softwaregesteuert durch den Hostcomputer abgebrochen, response already set (status=200)
2018-07-25 12:51:15.628  WARN 12084 --- [nio-8443-exec-2] o.s.h.s.r.ServletHttpHandlerAdapter      : Handling completed with error: Eine bestehende Verbindung wurde softwaregesteuert durch den Hostcomputer abgebrochen
2018-07-25 12:51:15.652 ERROR 12084 --- [nio-8443-exec-2] o.a.catalina.connector.CoyoteAdapter     : Exception while processing an asynchronous request

  java.lang.IllegalStateException: Calling [asyncError()] is not valid for a request with Async state [DISPATCHING]
  at org.apache.coyote.AsyncStateMachine.asyncError(AsyncStateMachine.java:424)
  at org.apache.coyote.AbstractProcessor.action(AbstractProcessor.java:470)
  at org.apache.coyote.Request.action(Request.java:431)
  at org.apache.catalina.core.AsyncContextImpl.setErrorState(AsyncContextImpl.java:388)
  at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:176)
  at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:232)
  at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:53)
  at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
  at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
  at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
  at java.lang.Thread.run(Thread.java:748)

对不起德语错误消息,它的意思是“客户端中止连接”。

我对这条错误消息本身并没有什么问题,只是我在spring的Webclient中的缓冲区似乎没有被清除(我没有在本地复制日志,所以它有不同的时间戳):

代码语言:javascript
复制
2018-07-23 08:44:36.892 ERROR 22707 — [reactor-http-nio-5] io.netty.util.ResourceLeakDetector       : LEAK: ByteBuf.release() was not called before it's garbage-collected. See http://netty.io/wiki/reference-counted-objects.html for more information.

Recent access records:

Created at:
   io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:331)      io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:185)

因此,这里的问题是:在取消对REST的请求时,我如何干净地结束WebClient连接?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-07-25 12:45:20

我不能确切地说出异常消息,但我知道Tomcat在8.5.x代中改进了这一点。你用的是哪个版本?如果您能够以最小的应用程序提供一种一致的方法来再现这个问题,那么您可以在Spring上的jira.spring.io中创建一个新的问题,或者如果您能够在没有Spring的情况下复制它(尽管它应该很难再现)。

现在,关于发布DataBuffer实例-- DataBuffer实例可以池化,具体取决于实现。在这里,WebClient正在使用Netty,它是池缓冲区。因此,当它们不再被使用时,它们需要被释放。

查看您的实现,我认为那些未释放的缓冲区来自于以下内容:

  • WebClient从远程端点获取数据并创建DataBuffer实例。
  • 在此过程中,不同的反应堆操作人员正在缓冲那些使用内部队列的反应堆(根据预取和使用的操作符,排队缓冲区的数量可能有所不同)。
  • 当订阅服务器失败或取消时,处于内部队列中的缓冲区不会按其应有的方式释放。

目前,在这些错误情况下,反应堆不提供到达这些对象的挂钩点。但这是一个全新的特性,它被添加到反应堆核心3.2.0中。使用SPR-17025的Spring将在内部利用这一点。请遵循这个问题--当涉及到测试修复时,您的用例可能很方便。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51517531

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档