Java 频繁发送到spring websocket会话:在传输过程中丢失

Java 频繁发送到spring websocket会话:在传输过程中丢失,java,spring,spring-mvc,websocket,spring-websocket,Java,Spring,Spring Mvc,Websocket,Spring Websocket,我得到了SpringWebSocket服务器(基于Jetty和SpringVersion4.3.2.RELEASE)和客户端的负载测试设置,它生成了许多连接(基于spring的示例JavaWebSocket客户端)。下面的代码将数据发送到给定的websocket会话:该代码段利用了可以使用sessionId而不是用户ID()的情况。我可能经常执行此代码,每2-3毫秒执行一次。我使用SimpleMessageBroker public void publishToSessionUsingTopi

我得到了SpringWebSocket服务器(基于Jetty和SpringVersion4.3.2.RELEASE)和客户端的负载测试设置,它生成了许多连接(基于spring的示例JavaWebSocket客户端)。下面的代码将数据发送到给定的websocket会话:该代码段利用了可以使用sessionId而不是用户ID()的情况。我可能经常执行此代码,每2-3毫秒执行一次。我使用SimpleMessageBroker

 public void publishToSessionUsingTopic(String sessionId, String subscriptionTopic, Map<String, CacheRowModel> payload) {

        String subscriptionTopicWithoutUser = subscriptionTopic.replace(USER_ENDPOINT, "");
        // necessary message headers for per-session send
        SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
        headerAccessor.setSessionId(sessionId);
        headerAccessor.setLeaveMutable(true);          
        simpMessagingTemplate.convertAndSendToUser(sessionId, subscriptionTopicWithoutUser, Collections.singletonList(payload), headerAccessor.getMessageHeaders());

}
public void publishToSessionUsingTopic(字符串sessionId、字符串subscriptionTopic、映射负载){
字符串subscriptionTopicWithoutUser=subscriptionTopic.replace(用户\端点“”);
//每个会话发送所需的消息头
SimpMessageHeaderAccessor headerAccessor=SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(会话ID);
headerAccessor.setLeaveMutable(真);
SimpMessageTemplate.convertAndSendToUser(sessionId,SubscriptionTopicwithout用户,Collections.singletonList(有效负载),headerAccessor.getMessageHeaders());
}

当此代码非常频繁地执行(每2-3毫秒一次)约100个会话时,虽然我在日志中看到它已运行并被称为convertAndSendToUser,但某些会话不会收到消息。我很感激任何关于如何解决这一问题的建议

嗯,我想你的问题在于:

@Bean
public ThreadPoolTaskExecutor clientOutboundChannelExecutor() {
    TaskExecutorRegistration reg = getClientOutboundChannelRegistration().getOrCreateTaskExecRegistration();
    ThreadPoolTaskExecutor executor = reg.getTaskExecutor();
    executor.setThreadNamePrefix("clientOutboundChannel-");
    return executor;
}
其中,它将此配置用于
执行器

protected ThreadPoolTaskExecutor getTaskExecutor() {
    ThreadPoolTaskExecutor executor = (this.taskExecutor != null ? this.taskExecutor : new ThreadPoolTaskExecutor());
    executor.setCorePoolSize(this.corePoolSize);
    executor.setMaxPoolSize(this.maxPoolSize);
    executor.setKeepAliveSeconds(this.keepAliveSeconds);
    executor.setQueueCapacity(this.queueCapacity);
    executor.setAllowCoreThreadTimeOut(true);
    return executor;
}
请参阅,未配置
RejectedExecutionHandler
。默认情况下,它类似于:

private RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
因此,当您有足够多的消息和任务超过
ThreadPool
时,任何额外的消息和任务都将被中止


要解决此问题,您应该实现
WebSocketMessageBrokerConfigure
并覆盖其
configureClientOutboundChannel()
,以提供一些自定义
任务执行器(ThreadPoolTaskExecutor taskExecutor)
,例如使用
新的ThreadPoolExecutor.CallerRunPolicy()

,我认为你的问题在于:

@Bean
public ThreadPoolTaskExecutor clientOutboundChannelExecutor() {
    TaskExecutorRegistration reg = getClientOutboundChannelRegistration().getOrCreateTaskExecRegistration();
    ThreadPoolTaskExecutor executor = reg.getTaskExecutor();
    executor.setThreadNamePrefix("clientOutboundChannel-");
    return executor;
}
其中,它将此配置用于
执行器

protected ThreadPoolTaskExecutor getTaskExecutor() {
    ThreadPoolTaskExecutor executor = (this.taskExecutor != null ? this.taskExecutor : new ThreadPoolTaskExecutor());
    executor.setCorePoolSize(this.corePoolSize);
    executor.setMaxPoolSize(this.maxPoolSize);
    executor.setKeepAliveSeconds(this.keepAliveSeconds);
    executor.setQueueCapacity(this.queueCapacity);
    executor.setAllowCoreThreadTimeOut(true);
    return executor;
}
请参阅,未配置
RejectedExecutionHandler
。默认情况下,它类似于:

private RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
因此,当您有足够多的消息和任务超过
ThreadPool
时,任何额外的消息和任务都将被中止


要解决此问题,您应该实现
WebSocketMessageBrokerConfigure
并覆盖其
configureClientOutboundChannel()
,以提供一些自定义
任务执行器(ThreadPoolTaskExecutor taskExecutor)
,例如,使用
新的ThreadPoolExecutor.CallerRunPolicy()

我提供了public void configureClientOutboundChannel(ChannelRegistration注册){registration.taskExecutor(getCustomTaskExecutor());},其中customExecutor将CallerRunPolicy传递给RejectionHandler(自定义日志记录版本)。但是我仍然有消息丢失的问题,执行器中的rejectedExecution没有达到断点,就像所有的东西都被“执行”一样。我添加了OutgoingChannelInterceptor,对于未发送的消息,我看不到它被触发。若被触发,则发送消息。所以它是convertAndSendToUser和拦截器之间的东西。好(还是坏?)。您使用哪个代理?Spring中的SimpleMessageBroker。不确定发生了什么,但您是否介意打开
org.springframework
类别的
DEBUG
,并在您认为消息丢失时调查日志。例如,我的日志中有这样的内容,例如:
DEBUG[main][org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler]广播到1个会话。
这可能会帮助您开始寻找。我提供了public void configureClientOutboundChannel(ChannelRegistration注册){registration.taskExecutor(getCustomTaskExecutor();}其中customExecutor将CallerRunPolicy传递给RejectionHandler(自定义日志记录版本)。但是,我仍然存在消息丢失问题,并且执行器中的rejectedExecution未命中断点,就像所有内容都已“执行”一样。我添加了OutgoingChannelInterceptor,对于未发送的消息,我看不到它被触发。如果它被触发,消息将被调度。因此它是convertAndSendToUser和拦截器之间的东西。好(坏?)。您使用哪个代理?Spring中的SimpleMessageBroker。不确定发生了什么,但您是否介意打开
org.springframework
类别的
DEBUG
,并在您认为您的消息丢失时调查日志。例如,我的日志中有这样的内容:
DEBUG[main][org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler]向1个会话广播。
这可能会帮助您开始搜索。