Spring MVC 3.2 预览:聊天示例

工程 | Rossen Stoyanchev | 2012 年 5 月 16 日 | ...

上次更新于 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 上找到。 尽管不是此博客文章的重点,但客户端使用 Thymeleafknockout.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 中,async 请求完成时,注册的 onCompletion 回调函数会将其从 Map 中移除。 然后,该方法使用内存中的 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。 借助 Spring Redis 项目,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 的异步支持的博客文章。 感谢您的阅读!

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

抢占先机

VMware 提供培训和认证,以加速您的进步。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中为 OpenJDK™、Spring 和 Apache Tomcat® 提供支持和二进制文件。

了解更多

即将发生的活动

查看 Spring 社区中所有即将发生的活动。

查看所有