好消息:IM1.0.0版本已经上线啦,支持特性

  • 私聊发送文本/文件
  • 已发送/已送达/已读回执
  • 支持使用ldap登录
  • 支持接入外部的登录认证系统
  • 提供客户端jar包,方便客户端开发

github链接: https://github.com/yuanrw/IM

本篇将带大家从零开始搭建一个轻量级的IM服务端,IM的整体设计思路和架构在我的上篇博客中已经讲过了,没看过的同学请点击从零开始开发IM(即时通讯)服务端 。

这篇将给大家带来更多的细节实现。我将从三个方面来阐述如何构建一个完整可靠的IM系统。

  1. 可靠性
  2. 安全性
  3. 存储设计

可靠性

什么是可靠性?对于一个IM系统来说,可靠的定义至少是不丢消息消息不重复不乱序,满足这三点,才能说有一个好的聊天体验。

不丢消息

我们先从不丢消息开始讲起。

首先复习一下上一篇设计的服务端架构
im-structure.png

我们先从一个简单例子开始思考:当Alice给Bob发送一条消息时,可能要经过这样一条链路:

伪代码如下:

class ProcessMsgNode{     /**      * 接收到的消息      */     private Message message;     /**      * 处理消息的方法      */     private Consumer<Message> consumer; }  public CompletableFuture<Void> offer(Long id,Message     message,Consumer<Message> consumer) {     if (isRepeat(id)) {     //消息重复         sendAck(id);         return null;     }     if (!isConsist(id)) {     //消息不连续         notConsistMsgMap.put(id, new ProcessMsgNode(message, consumer));         return null;     }     //处理消息     return process(id, message, consumer); }  private CompletableFuture<Void> process(Long id, Message message, Consumer<Message> consumer) {     return CompletableFuture         .runAsync(() -> consumer.accept(message))         .thenAccept(v -> sendAck(id))         .thenAccept(v -> lastId.set(id))         .thenComposeAsync(v -> {             Long nextId = nextId(id);             if (notConsistMsgMap.containsKey(nextId)) {                 //队列中有下个消息                 ProcessMsgNode node = notConsistMsgMap.get(nextId);                 return process(nextId, node.getMessage(), consumer);             } else {                 //队列中没有下个消息                 CompletableFuture<Void> future = new CompletableFuture<>();                 future.complete(null);                 return future;             }         })         .exceptionally(e -> {             logger.error("[process received msg] has error", e);             return null;         }); }

安全性

无论是聊天记录还是离线消息,肯定都会在服务端存储备份,那么消息的安全性,保护客户的隐私也至关重要。
因此所有的消息都必须要加密处理。
在存储模块里,维护用户信息和关系链有两张基础表,分别是im_user用户表和im_relation关系链表。

  • im_user表用于存放用户常规信息,例如用户名密码等,结构比较简单。
  • im_relation表用于记录好友关系,结构如下:
CREATE TABLE `im_relation` (   `id` bigint(20) COMMENT '关系id',   `user_id1` varchar(100) COMMENT '用户1id',   `user_id2` varchar(100) COMMENT '用户2id',   `encrypt_key` char(33) COMMENT 'aes密钥',   `gmt_create` timestamp DEFAULT CURRENT_TIMESTAMP,   `gmt_update` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,    PRIMARY KEY (`id`),   UNIQUE KEY `USERID1_USERID2` (`user_id1`,`user_id2`) );
  • user_id1user_id2是互为好友的用户id,为了避免重复,存储时按照