Spring WebSocket简单示例 - java.lang.IllegalStateException:意外使用调度程序

时间:2018-04-09 22:07:10

标签: spring spring-boot websocket spring-websocket

我尝试在没有STOMP的情况下在Spring中实现WebSocket服务器,请遵循以下文章:http://www.devglan.com/spring-boot/spring-websocket-integration-example-without-stomp

WebSocketConfig.java

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Autowired private WebSocketHandler webSocketHandler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(webSocketHandler, "/app");
    }

}

WebSocketHandler

@Component
public class WebSocketHandler extends TextWebSocketHandler {

    private List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        sessions.add(session);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
        // ...
    }

}

但是,我在启动时遇到此异常:

java.lang.IllegalStateException: Unexpected use of scheduler.
at org.springframework.web.socket.config.annotation.WebSocketConfigurationSupport$NoOpScheduler.schedule(WebSocketConfigurationSupport.java:108) ~[spring-websocket-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.scheduling.config.ScheduledTaskRegistrar.scheduleCronTask(ScheduledTaskRegistrar.java:414) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.scheduling.config.ScheduledTaskRegistrar.scheduleTasks(ScheduledTaskRegistrar.java:352) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.scheduling.config.ScheduledTaskRegistrar.afterPropertiesSet(ScheduledTaskRegistrar.java:332) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.finishRegistration(ScheduledAnnotationBeanPostProcessor.java:280) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:211) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:102) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:399) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:353) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:887) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:161) ~[spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:552) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:388) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:327) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at my.package.App.main(App.java:31) [main/:na]

如何使这个简单的WebSocket应用程序工作?

2 个答案:

答案 0 :(得分:9)

https://github.com/spring-projects/spring-boot/issues/12814

“@ EnableWebSocket没有进一步的配置可能会创建一个no op调度程序,它将与任务调度程序的任何其他使用冲突。”

这应该添加到配置中:

@Bean
public TaskScheduler taskScheduler() {
    return new ConcurrentTaskScheduler(Executors.newSingleThreadScheduledExecutor());
}

答案 1 :(得分:0)

根据您的堆栈跟踪(ScheduledAnnotationBeanPostProcessor),您在应用程序@EnableScheduling中的某个位置并尝试依赖于:

@Bean
public TaskScheduler defaultSockJsTaskScheduler() {
    return initTaskScheduler();
}

private TaskScheduler initTaskScheduler() {
    if (this.scheduler == null) {
        ServletWebSocketHandlerRegistry registry = initHandlerRegistry();
        if (registry.requiresTaskScheduler()) {
            ThreadPoolTaskScheduler threadPoolScheduler = new ThreadPoolTaskScheduler();
            threadPoolScheduler.setThreadNamePrefix("SockJS-");
            threadPoolScheduler.setPoolSize(Runtime.getRuntime().availableProcessors());
            threadPoolScheduler.setRemoveOnCancelPolicy(true);
            this.scheduler = threadPoolScheduler;
        }
        else {
            this.scheduler = new NoOpScheduler();
        }
    }
    return this.scheduler;
}

接受NoOpScheduler但在运行时为您的计划任务导致问题。

我建议提供一个SchedulingConfigurer实现,并在TaskScheduler中注入一些ScheduledTaskRegistrar bean。请参阅@EnableScheduling JavaDocs:

 * <p>Implementing {@code SchedulingConfigurer} also allows for fine-grained
 * control over task registration via the {@code ScheduledTaskRegistrar}.
 * For example, the following configures the execution of a particular bean
 * method per a custom {@code Trigger} implementation:
 *
 * <pre class="code">
 * &#064;Configuration
 * &#064;EnableScheduling
 * public class AppConfig implements SchedulingConfigurer {
 *
 *     &#064;Override
 *     public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
 *         taskRegistrar.setScheduler(taskScheduler());
 *         taskRegistrar.addTriggerTask(
 *             new Runnable() {
 *                 public void run() {
 *                     myTask().work();
 *                 }
 *             },
 *             new CustomTrigger()
 *         );
 *     }
 *
 *     &#064;Bean(destroyMethod="shutdown")
 *     public Executor taskScheduler() {
 *         return Executors.newScheduledThreadPool(42);
 *     }
 *
 *     &#064;Bean
 *     public MyTask myTask() {
 *         return new MyTask();
 *     }
 * }</pre>

我在这个问题上找到了几个JIRA:https://jira.spring.io/browse/SPR-16189https://jira.spring.io/browse/SPR-16705