Spring 集成 - 每当我创建会话时出现异常

Spring integration - Exception whenever I create a session

我的问题与我之前描述的问题中描述的解决方案有关:。快速总结一下我正在尝试做的事情,我想将一个 HTTP REST 请求传输到我的服务器,另一个 websocket 客户端,当从 websocket 客户端收到答案时,我将它传输到 HTTP REST响应。

前面link中描述的解决方案没有任何问题。我试图通过添加一个创建会话的服务激活器 lightOnStoringActivator 来稍微修改它,每当我在入站 websocket 网关上收到一条消息时(见下面我的新配置文件)。这样修改后,我有一个异常,说从weboscket客户端接收到的消息无法传递到reply通道。我确定问题出在创建会话的行,因为如果我只删除创建会话的行,问题就会消失。

知道为什么会发生这种情况,以及如何解决它吗?

org.springframework.messaging.MessageHandlingException: ; nested exception is org.springframework.messaging.MessageHandlingException: ; nested exceptionan actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still equestContextListener or RequestContextFilter to expose the current request.
    at org.springframework.integration.websocket.inbound.WebSocketInboundChannelAdapter.handleMessage(WebSocketInboundChannelAdapter.java:122)
    at org.springframework.integration.channel.FixedSubscriberChannel.send(FixedSubscriberChannel.java:70)
    at org.springframework.integration.channel.FixedSubscriberChannel.send(FixedSubscriberChannel.java:64)
    at org.springframework.integration.websocket.support.PassThruSubProtocolHandler.handleMessageFromClient(PassThruSubProtocolHandler.java:73)
    at org.springframework.integration.websocket.inbound.WebSocketInboundChannelAdapter.onMessage(WebSocketInboundChannelAdapter.java:232)
    at org.springframework.integration.websocket.IntegrationWebSocketContainer$IntegrationWebSocketHandler.handleMessage(IntegrationWebSocketContain
    at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75)
    at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56)
    at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:72)
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleBinaryMessage(StandardWebSocketHandlerAdapter.java:122)
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access0(StandardWebSocketHandlerAdapter.java:42)
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.onMessage(StandardWebSocketHandlerAdapter.java:88)
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.onMessage(StandardWebSocketHandlerAdapter.java:85)
    at org.apache.tomcat.websocket.WsFrameBase.sendMessageBinary(WsFrameBase.java:549)
    at org.apache.tomcat.websocket.WsFrameBase.processDataBinary(WsFrameBase.java:514)
    at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:274)
    at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:116)
    at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:54)
    at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onDataAvailable(WsHttpUpgradeHandler.java:192)
    at org.apache.coyote.http11.upgrade.AbstractServletInputStream.onDataAvailable(AbstractServletInputStream.java:178)
    at org.apache.coyote.http11.upgrade.AbstractProcessor.upgradeDispatch(AbstractProcessor.java:92)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:601)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.messaging.MessageHandlingException: ; nested exception is java.lang.IllegalStateException: No thread-bound request found:nally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of Disp request.
    at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:78)
    at org.springframework.integration.handler.ServiceActivatingHandler.handleRequestMessage(ServiceActivatingHandler.java:71)
    at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:
    at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78)
    at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
    at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:101)
    at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:97)
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:277)
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:239)
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
    at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:95)
    at org.springframework.integration.router.AbstractMessageRouter.handleMessageInternal(AbstractMessageRouter.java:164)
    at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78)
    at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
    at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:101)
    at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:97)
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:277)
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:239)
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
    at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:95)
    at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:248)
    at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:171)
    at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:119)
    at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:
    at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78)
    at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
    at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:101)
    at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:97)
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:277)
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:239)
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
    at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:95)
    at org.springframework.integration.endpoint.MessageProducerSupport.sendMessage(MessageProducerSupport.java:101)
    at org.springframework.integration.websocket.inbound.WebSocketInboundChannelAdapter.handleMessageAndSend(WebSocketInboundChannelAdapter.java:279
    at org.springframework.integration.websocket.inbound.WebSocketInboundChannelAdapter.access[=11=]0(WebSocketInboundChannelAdapter.java:63)
    at org.springframework.integration.websocket.inbound.WebSocketInboundChannelAdapter.handleMessage(WebSocketInboundChannelAdapter.java:119)
    ... 25 more
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or  still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or R
    at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
    at com.transacteleurope.service.activator.FingerVeinWebsocketActivator.createHttpSession(FingerVeinWebsocketActivator.java:64)
    at com.transacteleurope.service.activator.FingerVeinWebsocketActivator.onEnrollmentCaptureForEnrollResponse(FingerVeinWebsocketActivator.java:15
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:72)
    at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:129)
    at org.springframework.expression.spel.ast.MethodReference.access[=11=]0(MethodReference.java:49)
    at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.getValue(MethodReference.java:347)
    at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:87)
    at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:126)
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:327)
    at org.springframework.integration.util.AbstractExpressionEvaluator.evaluateExpression(AbstractExpressionEvaluator.java:164)
    at org.springframework.integration.util.MessagingMethodInvokerHelper.processInternal(MessagingMethodInvokerHelper.java:276)
    at org.springframework.integration.util.MessagingMethodInvokerHelper.process(MessagingMethodInvokerHelper.java:142)
    at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:75)
    ... 66 more

我的配置文件如下:

<!-- REST service to turn on the light -->
<int-http:inbound-gateway
        supported-methods="POST"
        request-channel="lightOnRequest"
        reply-channel="lightOnResponse"
        path="rest/lighton/{sessionId}">
    <int-http:header name="{sessionId}" expression="{sessionId}"/>
</int-http:inbound-gateway>

<!-- We add a header SESSION_ID_HEADER to choose the websocket destination client -->
<int:header-enricher 
    input-channel="lightOnRequest" 
    output-channel="lightOnClientRequest">
    <int:header 
        name="#{T(...SimpMessageHeaderAccessor).SESSION_ID_HEADER}"
        expression="headers.sessionId"/>
    <int:header-channels-to-string/>
</int:header-enricher>

<!-- Websocket out to client -->
<int-websocket:outbound-channel-adapter 
    channel="lightOnClientRequest" 
    container="serverWebSocketContainer" />

<!-- Response reception from the Websocket client -->
<int-websocket:inbound-channel-adapter 
    channel="lightOnClientResponse" 
    container="serverWebSocketContainer" />

<!-- We store some data in the session -->
<int:service-activator 
    request-channel="lightOnClientResponse"
    reply-channel="lightOnClientStoredResponse"
    ref="lightOnStoringActivator"
    method="onNewRestfullRequest"
    requires-reply="true" />

<!-- The websocket client provides again the reply channel in the headers.
     The bridge connects the response to the reply channel -->
<int:bridge input-channel="lightOnClientStoredResponse"/>

您需要保留入站线程并将回复数据传输给它。

目前,只要您将消息发送到出站 websocket 适配器,您的入站 REST 流就会结束。

当回复进入 websocket 入站适配器时,您正试图将其发送回网关,但该上下文已经消失;此外,您无法在 "foreign" 线程上访问 session 变量。

一个解决方案是将 lightOnClientRequest 设为 <publish-subscribe-channel/> 并订阅 <service-activator/> - 确保它是第二个订阅者(使用 order 属性请确定)。

在该服务中,挂起 REST 线程。然后,不使用桥,而是在服务上调用另一个方法来传输数据并释放线程。该方法应该 return 无效,因此 WS 流在该点结束。

您可以使用 LinkedBlockingQueueMap,使用 replyChannel header(字符串)作为键。从 queue 获取网关 REST 线程 take(),并将 ws 入站线程 put() 发送至 queue。当 take() returns 时,删除映射条目。

请注意,可能会在调用第二个消费者之前收到回复,因此您需要处理那一侧丢失的地图条目。或者,在频道上使用 3 个订阅者

  1. 创建地图条目
  2. 写入 WS
  3. 取自 queue 并删除地图条目

您可能希望使用 poll 超时而不是 take() 以防您没有收到回复。

编辑:(回应您在下方的评论)。

这更多是运气而非设计 - 发送后,http (REST) 线程位于网关中等待回复。只要您不尝试对回复线程上的 HTTP session 做任何事情,它就会起作用。

只有 http 线程可以访问 session-scoped 变量。我无法确切地看到您在做什么,因为您的配置似乎不完整 - 例如,堆栈跟踪中有一个路由器,而您的配置显示没有路由器。

路由器的下游似乎正在尝试访问 session 属性:currentRequestAttributes。如果你需要这样做,你需要从主线程的 session 中提取变量,然后存储在 header 中。您根本无法从 WebSocket 回复线程访问 HTTP 请求上下文。