领先一步
VMware 提供培训和认证,助您加速进步。
了解更多最后更新于 2012 年 11 月 5 日 (Spring MVC 3.2 RC1)
在 之前的 博客 文章 中,我介绍了 Spring MVC 3.2 中基于 Servlet 3 的异步支持,并使用了 spring-mvc-showcase 和 Spring AMQP stocks 示例 来演示。本文将介绍一个聊天示例,其中外部事件不是 AMQP 消息,而是带有聊天消息的 HTTP POST 请求。在本文的第二部分,我将切换到一个分布式聊天,其中事件是 Redis 通知。
聊天并不是 Web 应用程序的常见需求。然而,它是一个只能通过实时通知满足的需求的良好示例。它比电子邮件或状态警报对时间延迟更敏感,而且在浏览器中与朋友聊天,或者在网络研讨会期间与同事聊天,或者与购物网站上的真人聊天的情况并不少见。您可以想象其他类型的在线协作。
示例
spring-mvc-chat 示例在 Github 上可用。虽然不是本文的重点,但客户端使用了 Thymeleaf、knockout.js 和 jQuery。Thymeleaf 是 JSP 的一个极好的替代品,它支持干净的 HTML 模板,并支持预览,允许设计师双击 HTML 模板查看它,而不像 JSP 需要 Servlet 容器。knockout.js 是一个客户端 MVC 框架,非常方便地为 HTML 元素添加行为。要快速了解它,请遵循其出色的 教程 之一。jQuery 用于 DOM 脚本和 Ajax 请求。
ChatController
ChatController 暴露了获取和发布聊天消息的操作。这是获取消息的方法:
@RequestMapping(method=RequestMethod.GET)
@ResponseBody
public DeferredResult<List<String>> getMessages(@RequestParam int messageIndex) {
final DeferredResult<List<String>> deferredResult = new DeferredResult<List<String>>(null, Collections.emptyList());
this.chatRequests.put(deferredResult, messageIndex);
deferredResult.onCompletion(new Runnable() {
@Override
public void run() {
chatRequests.remove(deferredResult);
}
});
List<String> messages = this.chatRepository.getMessages(messageIndex);
if (!messages.isEmpty()) {
deferredResult.setResult(messages);
}
return deferredResult;
}
创建一个新的 DeferredResult,并将其保存在一个 Map 中,注册的 onCompletion 回调将在异步请求完成后将其移除。然后,该方法使用内存中的 ChatRepository 检查新消息。如果找到新消息,DeferredResult 将立即设置。否则,它将在新消息到达时稍后设置。
下面是保存聊天消息并更新所有已保存 DeferredResult 实例的方法:
@RequestMapping(method=RequestMethod.POST)
@ResponseBody
public void postMessage(@RequestParam String message) {
this.chatRepository.addMessage(message);
// Update all chat requests as part of the POST request
// See Redis branch for a more sophisticated, non-blocking approach
for (Entry<DeferredResult<List<String>>, Integer> entry : this.chatRequests.entrySet()) {
List<String> messages = this.chatRepository.getMessages(entry.getValue());
entry.getKey().setResult(messages);
}
}
分布式聊天
上面的聊天示例使用了简单的内存持久化,并且仅在部署到单个服务器时有效。 redis 分支 使用了一个基于 Redis 的 ChatRepository。 Redis 是一个简单的键值存储,借助 Spring Redis 项目,在 Java 中使用起来非常方便。
RedisChatRepository 使用 Spring Redis 的 RedisTemplate 来查找和保存聊天消息。请随时 查看 代码。
保存新聊天消息的控制器方法现在只有一行:
@RequestMapping(method=RequestMethod.POST)
@ResponseBody
public void postMessage(@RequestParam String message) {
this.chatRepository.addMessage(message);
}
接收新消息也非常简单。这涉及到实现 Spring Redis 的 MessageListener 接口,可以直接在控制器中完成:
@Controller
@RequestMapping("/mvc/chat")
public class ChatController implements MessageListener {
// ...
public void onMessage(Message message, byte[] pattern) {
for (Entry<DeferredResult<List<String>>, Integer> entry : this.chatRequests.entrySet()) {
List<String> messages = this.chatRepository.getMessages(entry.getValue());
entry.getKey().setResult(messages);
}
}
}
聊天程序的 Redis 版本可以在集群中运行。消息可以发布到任何服务器,所有其他服务器都将收到 Redis 通知。Spring Redis 项目使得以消息驱动的 POJO 风格接收这些通知变得非常简单。
也可以从非 Java 的 Redis 客户端发布聊天消息。例如,使用 Redis 命令行 shell 连接,输入以下命令,聊天消息将分发到所有订阅的服务器和连接的浏览器:
redis 127.0.0.1:6379> RPUSH chat:archive "hello from the redis cli"
redis 127.0.0.1:6379> PUBLISH chat "a new chat message is available"
以上是关于 Spring MVC 3.2、基于 Servlet 3 的异步支持的博客文章的全部内容。感谢您的阅读!