For an instant messaging server, a single server is sufficient to provide all the services when the number of users is low. This architecture is also the simplest. For example, user A and user B are friends of each other. User A sends A message to user B. However, when the number of users is increasing, a server cannot meet the needs of all users. At this time, service expansion and distributed deployment must be carried out


As shown in the figure, different users may log in to different servers. When user A sends A message to user B, the server receives the message and determines whether USER B also logs in to the server. If so, the server can directly forward the message. If B is not on the server, where should he forward the message? The simplest thing to do is to broadcast the message to the other servers in the server cluster, and for each server that receives the message, first determine whether the intended user of the message is logged in to it, and if not, ignore the message. If so, the message is forwarded to the destination user. Of course, this brute force approach is the simplest and most direct, but it can result in a lot of invalid message forwarding, which can have a big impact on server performance. I have seen the code of Teamtalk, the open source instant messaging software of Mogujie, and this is how the server is implemented. Its server architecture is as follows:



Different MSG servers are connected to the same Route Server, and all forwarding between MSG servers goes through route Server. This undoubtedly increases the load on the Route Server. No matter how many MSG Servers are deployed, according to the bucket theory, the performance of a system is determined by its weakest link. Therefore, the system capacity of such architecture is also limited. So how to improve this system, clearly server message forwarding cannot directly between all radio, and should have a set of explicit routing system, namely the server in the forwarded message, which should know that this message should be forwarded to the server, so there is no need for each message is broadcast between all servers.

So how to achieve such a set of by the system?

In a simple way, when each user goes online, he/she broadcasts his/her login information to all other MSG servers through the MSG server he/she is connected to, and tells other servers which server he/she is logged in to. In this way, when a user sends a message to his friend, he first checks the MSG server he logs in through his friend ID. If the friend and oneself are the same server, then directly forward can be; If not, the server forwards the message to the Route Server with the ID of the target MSG Server. In this way, after the Route Server receives the message, it can parse out the target MSG server and forward it once, saving a lot of broadcast messages. Although this method solves the problem of broadcast messages, routing information of all users must be saved on each MSG server. When all users are logged in, it almost degenerates into a single point model that MSG Server can’t afford.

So how to solve this problem? Imagine, since all MSG servers store the same routing information, we can separate the data from the MSG server and store it in a separate server for the MSG Server to query. Let’s call this server route Info Server for now. The data to be stored for a user is





Assuming that both data are 32 bytes, the memory required to store 100 million users is 3.2 GB. The better servers now have 50GB of ram, so memory is obviously not an issue. That leaves the question of traffic. If all MSG servers read data from this server, it will definitely affect the performance of the whole system. So the routing information server cannot adopt this single point model. Given the nature of this routing information, it is obvious that the data is read too much and written too little. A user writes routing information only when he logs in, and reads routing information when he forwards messages. In this case, we can adopt a database – like active/standby model, where the primary server writes routing information and the secondary server queries routing information. In addition, multiple standby servers can be set to share the read pressure of MSG server. In fact, we can also use some mature cache systems to complete the function of the routing information server, such as Redis. Redis has a ready-made master/slave solution, but such a general cache server will consume more memory when storing data. Most people who have studied the redis source code can understand it. The key values are stored in the structure of a RedisObject, with some additional information, so it will consume more memory than writing such a service yourself.

OK, with routing information servers out of the way, let’s go back to MSG Server. Then, when forwarding messages, the MSG server first searches for the MSG Server based on the ID of the destination user on the routing information server for message forwarding. However, there is also a routing information that needs to be queried every time a message is forwarded, which will undoubtedly affect the forwarding speed of the message and increase the access pressure of the routing information server. If the routing information is saved locally after the message is sent, the routing information server does not need to query the message again the next time. However, all routes cannot be saved locally, which will seriously consume the memory of the MSG server. Therefore, we came up with a compromise scheme, using an LRU cache queue. When new routing information needs to be saved, first check whether the cache queue is full. If not, directly insert the queue into the head, if the queue is full, eliminate the data at the end of the queue. Cache queue size can be flexibly set according to memory size. Considering that when we usually use QQ, most people are logged in, but not many people send messages. For routing information, the router queries a route from the routing information server for the first time when it forwards a message. The routing information is cached locally during the entire reply process. After the session ends, the latest and longest unused routing data is weeded out. This method takes into account the memory usage and greatly reduces the number of server accesses, which is a good compromise. After completing the routing information system, Route Server can also be extended horizontally. All the Route Server needs to do is forward messages without storing data, which is very convenient to be extended. The final system architecture is as follows:




1. The im server architecture described in this paper focuses on how to route messages, but it does not represent a complete IM server system, such as registration, login, offline messages, files and other functions are not discussed in this paper and so on

2. The scheme proposed in this paper is also an idea, which has not really been implemented, and there must be many details that have not been taken into account. Please leave comments and discuss

3. The system described in this article can be called a service cluster. A system with as many users as QQ is spread across the country in many clusters. This article is limited to the design of communication within a cluster, and how communication between clusters communicates. Routing data for each cluster is not optimal if it is fully synchronized to other clusters. If you have better ideas, feel free to leave comments