preface

First of all, LET’s talk about what IM (instant messaging) technology can be used for: chat: QQ, wechat live: Douyu live, Douyin real-time location sharing, multiplayer game interaction and so on. It can be said that almost all high real-time application scenarios need IM technology.

This article will take you to build a lightweight IM server from scratch. Although it is small, it has all the five components. The IM server we built realizes the following functions:

  1. One-to-one text message, file message communication
  2. Each message has a “sent”/” delivered “/” read “return receipt
  3. Storing offline messages
  4. Support user login, friend relationship and other basic functions.
  5. It is easy to scale horizontally

What can be learned through this project?

This project covers a lot of back-end essentials:

  • RPC communication
  • The database
  • The cache
  • The message queue
  • Distributed, highly concurrent architecture design
  • Docker deployment

Message communication

A text message

Let’s start with the simplest feature: a normal message is sent in the following format:

message ChatMsg{ id = 1; // Message id fromId = Alice // userId destId = Bob // receiver userId msgBody = hello // message body}Copy the code

As shown above, we now have two users: Alice and Bob connected to the server when Alice sends a messagemessage(hello)After receiving the message, the server forwards it to Bob according to the destId of the message.

Send the receipt

So how do we send the receipt? MsgType: SENT (delivered), delivered (delivered), read (read)

message AckMsg { id; // Message id fromId; // Sender id destId; // Receiver id msgType; // Message type ackMsgId; Enum MsgType {DELIVERED; READ; }Copy the code

When a server receives a message from Alice:

  1. Send one to Alicesent(hello)Indicates that the message has been sent to the server.
message AckMsg {
    id = 2;
    fromId = Alice;
    destId = Bob;
    msgType = SENT;
    ackMsgId = 1;
}
Copy the code

2. The serverhelloAfter it is forwarded to Bob, it is immediately sent to Alicedelivered(hello)Indicates that the message has been sent to Bob.

message AckMsg {
    id = 3;
    fromId = Bob;
    destId = Alice;
    msgType = DELIVERED;
    ackMsgId = 1;
}
Copy the code

3. After Bob reads the message, the client sends the message to the serverread(hello)Indicates that the message has been read

message AckMsg {
    id = 4;
    fromId = Bob;
    destId = Alice;
    msgType = READ;
    ackMsgId = 1;
}
Copy the code

This message is processed by the server like a normal chat message and eventually sent to Alice.

There is no distinction in the serverChatMsgandAckMsgThe process is the same: parsing the messagedestIdAnd forward it.

Horizontal scaling

As the number of users grows, the number of servers must increase, and users’ connections are scattered across different machines. At this point, you need to store which machine the user is connected to. We introduce a new module to manage user connection information.

Managing User Status

The module is called User Status and has three interfaces:

Public interface UserStatusService {/** * Store the relationship between userId and machine ID * * @param userId * @param connectorId * @return If the current user is online, return the id of the machine he is connected to, Otherwise null */ String online(String userId, String connectorId) is returned; /** * @param userId */ void offline(String userId); ** @param userId * @return */ String getConnectorId(String userId); }Copy the code

In this way, we can manage the user connection state. The specific implementation should consider the number of users of the service and the expected performance. Here we use Redis to store the relationship between userId and connectorId as a key-value.

forward

In addition, you need a module to forward messages on different machines, with the following structure:

At this point, our service is split into connector and Transfer modules. The Connector module is used to maintain long links between users, while the transfer function is to forward messages between multiple connectors. Now that Alice and Bob are connected to both connectors, how will messages be transferred?

  1. Alice goes online and connects toMachine [1]when
    • Store Alice and its connection into memory.
    • calluser statustheonlineMethod Alice goes online.
  2. Alice sends a message to Bob
    • Machine [1]After receiving the message, destId is parsed to see if Bob exists in memory.
    • If not, Bob is not connected to the machine and is forwarded totransfer.
  3. transfercalluser statusthegetConnectorId(Bob)Method finds the Connector Bob is connected to, and returnsMachine [2]“Is forwarded toMachine [2].

Flow chart:

Conclusion:

  • The introduction ofuser statusThe module manages user connections,transferModules are forwarded between different machines, allowing services to scale horizontally.
  • In order to meet real-time forwarding,transferNeed and eachconnectorMachines keep long links.

Offline message

If the user is not online, the message must be persisted and pushed until the next time the user is online. Here, mysql is used to store offline messages. For easy horizontal scaling, message queues are used for decoupling.

  • transferIf the user is not online after receiving the message, the user is sent to the message queue for archiving.
  • When a user logs in, the server pulls offline messages from the database and pushes them.

User login, friend relationship

The functions of user registration and login, account management, friend relationship chain and so on are more suitable for using HTTP protocol. Therefore, we make this module into a restful service, exposing THE HTTP interface externally for the client to call.

The basic architecture of the server side is now complete:

conclusion

That’s all for this blog post. This post has helped you build the IM server architecture, but there are more details to consider. For example:

  • How can messages be ordered and unique
  • How to ensure message consistency when multiple devices are online
  • How to handle message sending failure
  • Message security
  • What do I do if I want to store chat records
  • The database is divided into tables and libraries
  • High service availability

More details will be left for the next article

Github link: github.com/yuanrw/IM feel helpful to you please click a star bar ~!