领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多信息上次更新于 2012 年 11 月 5 日(Spring MVC 3.2 RC1)
在之前 的博文 中,我介绍了 Spring MVC 3.2 中基于 Servlet 3 的异步功能,并使用spring-mvc-showcase 和 Spring AMQP 股票示例 来演示它。本文介绍了一个聊天示例,其中外部事件不是 AMQP 消息,而是带有聊天消息的 HTTP POST 请求。在本文的第二部分,我将切换到一个分布式聊天,其中事件是Redis 通知。
聊天不是 Web 应用程序的常见需求。但是,它是一个很好的例子,说明只有通过实时通知才能满足的需求。它对时间延迟比电子邮件或状态警报更敏感,并且在浏览器中与朋友聊天、在网络研讨会期间与同事聊天或在购物网站上与真人聊天并不罕见。您可以想象其他类型的在线协作。
示例
该spring-mvc-chat 示例可在 Github 上获得。虽然这不是本文的重点,但客户端使用Thymeleaf、knockout.js 和 jQuery。Thymeleaf 是 JSP 的一个极佳替代方案,它支持干净的 HTML 模板,并支持预览,允许设计人员双击 HTML 模板并查看它,这与需要 Servlet 容器的 JSP 不同。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);
}
}
}
Redist 版本的聊天将在集群中工作。可以在任何服务器上发布消息,所有其他服务器都将接收 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 的异步支持的博文系列。感谢阅读!