1.概述
在本教程中,我们将看到如何使用WebSockets将计划的消息从服务器发送到浏览器。一种替代方法是使用服务器发送的事件(SSE),但我们将不在本文中介绍。
Spring提供了各种调度选项。首先,我们将介绍@Scheduled
注释。然后,我们将看到由Project Reactor提供的‘Flux::interval’方法的示例。该库对于Webflux应用程序是开箱即用的,并且可以在任何Java项目中用作独立库。
此外,还存在更高级的机制,例如Quartz调度程序,但我们不会对其进行介绍。
2.一个简单的聊天应用程序
在上一篇文章中,我们使用WebSockets构建了一个聊天应用程序。让我们用一个新功能扩展它:聊天机器人。这些漫游器是服务器端组件,可将计划的消息推送到浏览器。
2.1。 Maven依赖
让我们从在Maven中设置必要的依赖关系开始。要构建此项目,我们的pom.xml
应该具有:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
2.2。 JavaFaker依赖关系
我们将使用JavaFaker库来生成机器人的消息。该库通常用于生成测试数据。在这里,我们将一个名为“ Chuck Norris
”的访客添加到我们的聊天室。
让我们看一下代码:
Faker faker = new Faker();
ChuckNorris chuckNorris = faker.chuckNorris();
String messageFromChuck = chuckNorris.fact();
Faker将为各种数据生成器提供工厂方法。我们将使用ChuckNorris
生成器。调用chuckNorris.fact()
将显示预定义消息列表中的随机句子。
2.3。消息模型
聊天应用程序使用简单的POJO作为消息包装器:
public class OutputMessage {
private String from;
private String text;
private String time;
// standard constructors, getters/setters, equals and hashcode
}
放在一起,这是我们如何创建聊天消息的示例:
OutputMessage message = new OutputMessage(
"Chatbot 1", "Hello there!", new SimpleDateFormat("HH:mm").format(new Date())));
2.4。客户端
我们的聊天客户端是一个简单的HTML页面。它使用SockJS客户端和STOMP消息协议。
让我们看看客户端如何订阅主题:
<html>
<head>
<script src="./js/sockjs-0.3.4.js"></script>
<script src="./js/stomp.js"></script>
<script type="text/javascript">
// ...
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
// ...
stompClient.subscribe('/topic/pushmessages', function(messageOutput) {
showMessageOutput(JSON.parse(messageOutput.body));
});
});
// ...
</script>
</head>
<!-- ... -->
</html>
首先,我们通过SockJS协议创建了一个Stomp客户端。然后,主题订阅充当服务器与连接的客户端之间的通信通道。
在我们的存储库中,此代码位于webapp/bots.html
。我们在http:// localhost:8080 / bots.html本地运行时访问它。当然,我们需要根据部署应用程序的方式来调整主机和端口。
2.5。服务器端
在上一篇文章中,我们已经了解了如何在Spring中配置WebSocket。让我们对配置进行一些修改:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// ...
registry.addEndpoint("/chatwithbots");
registry.addEndpoint("/chatwithbots").withSockJS();
}
}
为了推送消息,我们使用实用程序类SimpMessagingTemplate
。默认情况下,它在Spring Context中以@Bean
提供。我们可以看到当AbstractMessageBrokerConfiguration
在类路径中时如何通过自动配置声明它。因此,我们可以将其注入到任何Spring组件中。
之后,我们使用它来将消息发布到主题/topic/pushmessages
。我们假设我们的类将那个bean注入了一个名为simpMessagingTemplate
的变量中:
simpMessagingTemplate.convertAndSend("/topic/pushmessages",
new OutputMessage("Chuck Norris", faker.chuckNorris().fact(), time));
如前面的客户端示例所示,客户端订阅该主题以在消息到达时对其进行处理。
3.安排推送消息
在Spring生态系统中,我们可以从多种调度方法中进行选择。如果我们使用Spring MVC,则@Scheduled
注释由于其简单性而成为自然选择。如果我们使用Spring Webflux,我们也可以使用Project Reactor的Flux::interval
方法。我们将看到每个示例。
3.1。配置
我们的聊天机器人将使用JavaFaker的Chuck Norris生成器。我们将其配置为Bean,以便可以将其注入所需的位置。
@Configuration
class AppConfig {
@Bean
public ChuckNorris chuckNorris() {
return (new Faker()).chuckNorris();
}
}
3.2。使用@Scheduled
我们的示例机器人是预定方法。当他们运行时,他们使用SimpMessagingTemplate
通过WebSocket发送我们的OutputMessage
POJO。
顾名思义,@ @Scheduled
批注允许重复执行method 。有了它,我们可以使用基于速率的简单调度或更复杂的“ cron”表达式。
让我们编写第一个聊天机器人的代码:
@Service
public class ScheduledPushMessages {
@Scheduled(fixedRate = 5000)
public void sendMessage(SimpMessagingTemplate simpMessagingTemplate, ChuckNorris chuckNorris) {
String time = new SimpleDateFormat("HH:mm").format(new Date());
simpMessagingTemplate.convertAndSend("/topic/pushmessages",
new OutputMessage("Chuck Norris (@Scheduled)", chuckNorris().fact(), time));
}
}
我们用@Scheduled(fixedRate = 5000).
注释sendMessage
方法@Scheduled(fixedRate = 5000).
这使sendMessage
每五秒钟运行一次。然后,我们使用simpMessagingTemplate
实例将OutputMessage
发送到主题。 simpMessagingTemplate
和chuckNorris
实例从Spring上下文作为方法参数注入。
3.3。使用Flux::interval()
如果使用WebFlux,则可以使用[Flux::interval](https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html#interval-java.time.Duration-)
运算符。它将发布的无限流Long
通过选择分隔的项D
uration
。
现在,让我们在前面的示例中使用Flux。目标是每五秒钟发送一次Chuck Norris
的报价。首先,我们需要实现InitializingBean
接口以在应用程序启动时订阅Flux
:
@Service
public class ReactiveScheduledPushMessages implements InitializingBean {
private SimpMessagingTemplate simpMessagingTemplate;
private ChuckNorris chuckNorris;
@Autowired
public ReactiveScheduledPushMessages(SimpMessagingTemplate simpMessagingTemplate, ChuckNorris chuckNorris) {
this.simpMessagingTemplate = simpMessagingTemplate;
this.chuckNorris = chuckNorris;
}
@Override
public void afterPropertiesSet() throws Exception {
Flux.interval(Duration.ofSeconds(5L))
// discard the incoming Long, replace it by an OutputMessage
.map((n) -> new OutputMessage("Chuck Norris (Flux::interval)",
chuckNorris.fact(),
new SimpleDateFormat("HH:mm").format(new Date())))
.subscribe(message -> simpMessagingTemplate.convertAndSend("/topic/pushmessages", message));
}
}
在这里,我们使用构造函数注入来设置simpMessagingTemplate
和chuckNorris
实例。这次,调度逻辑在afterPropertiesSet(),
我们在实现InitializingBean
时将其重写。服务启动后,该方法将立即运行。
interval
运算符每五秒钟发出一个Long
。然后, map
运算符会丢弃该值,并将其替换为我们的消息。最后,我们subscribe
Flux
来触发每条消息的逻辑。
4。结论
在本教程中,我们已经看到实用程序类SimpMessagingTemplate
使通过WebSocket推送服务器消息变得容易。此外,我们已经看到了两种调度一段代码执行的方式。
0 评论