This article is shared by the author "Chinese Cabbage", and there are many revisions and changes. Note: This series is an article for IM beginners, IM old fritters still look to Haihan, don't spray!
1 Introduction
The first two articles "Coding Practice (Single Chat Function)" and "Coding Practice (Group Chat Function)" respectively implement the functions of the console version of IM single chat and group chat. What I have experienced through the first two small cases is the real development practice of Netty in the IM system, but compared to the real Netty application development, the cases in this series are very simple, and the main purpose is to let everyone You can better understand how it works, so you can write higher quality Netty code. However, although Netty's performance is high, it cannot guarantee that projects written at will have high performance, so this article will mainly explain several optimization and practical technical points of Netty-based IM system.
Learning and communication: - Introduction to mobile IM development article: "One entry for beginners is enough: developing mobile IM from scratch" - Open source IM framework source code: https://github.com/JackJiang2011/MobileIMSDK (click here for the alternate address) ( This article is simultaneously published at: http://www.52im.net/thread-3988-1-1.html )
2. Write in front
It is recommended that you read the first three articles in this series, "IM System Design", "Coding Practice (Single Chat Function)", and "Coding Practice (Group Chat Function)" before reading this article. Finally, before starting this article, please be sure to understand the basic knowledge of Netty in advance, starting from the "Knowledge Preparation" chapter in the first "IM System Design" in this series.
3. Series of articles
This article is the third in a series of articles. The following is a series of catalogues: "Based on Netty, Developing IM from Scratch (1): IM System Design" "Based on Netty, Developing IM from Zero (2): Coding Practice (Single Chat Function) )" "Based on Netty, developing IM from scratch (3): coding practice (group chat function)" "Based on Netty, developing IM from scratch (4): coding practice (system optimization)" (* this article)
4. Common optimization directions of Netty-based IM systems
Common optimization direction brain map:
We explain the purpose of these optimizations in detail one by one: 1) Heartbeat detection: mainly to avoid the phenomenon of suspended connection; 2) Connection disconnection: delete the channel binding attribute and delete the corresponding mapping relationship, all of which are stored in memory , if it is not deleted, it will cause waste of resources; 3) Performance problems: the relationship between user ID and Channel is bound in memory, for example: Map, key is user ID, value is Channel, if the number of users is too large (the number of clients is too large) more), then the memory of the server will be exhausted; 4) Performance problem: every time the server pushes a message to the client, it needs to find the corresponding Channel in the Map, if the number is large and the query is frequent, how to ensure Query performance; 5) Security issues: HashMap is thread-unsafe, how can we ensure thread safety under concurrent conditions; 6) Identity verification: How LoginHandler is the business Handler responsible for login authentication, and AuthHandler is responsible for correcting each request Check whether the request has been authenticated. These Handlers have been added to the Pipeline pipeline when the link is ready. In fact, we can use hot swapping to remove some Handlers that are not used in business operations. The above are the technical optimization points that need to be paid attention to in the development of the IM system based on Netty. Of course, there are many other details, such as the thread pool, which needs to be accumulated from actual combat slowly.
5. This article is optimized
Direction The main optimization content of this article is to continue to improve a few points on the basis of the second single chat function and the third group chat function. The specific optimization directions are as follows: 1) No matter the client or the server, there is only one Handler. In this case, the business will become more and more, and the code in the Handler will become more and more bloated. We should find a way to split the Handler into various Independent Handler; 2) If there are many split Handlers, every time a connection comes in, the initChannel () method will be triggered, and all Handlers have to be new again, we should change these Handlers into singleton mode (no need for every Handler). 3) When sending a message, whether it is a single chat or a group chat, if the other party is not online, the message is cached and pushed to him after waiting for it to go online; 4) When the connection is disconnected, whether it is active And passive, you need to delete the Channel attribute, delete the user and Channel mapping relationship.
6. Business splitting and singleton mode optimization
6.1 Overview The main optimization details are as follows: 1) The custom Handler inherits SimpleChannelInboundHandler, then when decoding, it will automatically transfer to the corresponding Handler for processing according to the data format type; 2) @Shareable modifies the Handler to ensure that the Handler can be shared and avoid every Create an instance each time. 6.2 Login Handler optimization@ChannelHandler.Sharablepublic class ClientLogin2Handler extends SimpleChannelInboundHandler<LoginResBean> { //1. The constructor is private to avoid creating an entity private ClientLogin2Handler(){} //2. Define a static global variable public static ClientLogin2Handler instance=null; //3. Get entity method public static ClientLogin2Handler getInstance(){ if(instance==null){ synchronized(ClientLogin2Handler.class){ if(instance==null){ instance=new ClientLogin2Handler(); } } } return instance; } protected void channelRead0( ChannelHandlerContext channelHandlerContext, LoginResBean loginResBean) throws Exception { //Specific business code, refer to the previous }}6.3 Message sending Handler optimization@ChannelHandler.Sharablepublic class ClientMsgHandler extends SimpleChannelInboundHandler<MsgResBean> { //1. Constructor privatization, Avoid creating entity private ClientMsgHandler(){} //2. Define a static global variable public static ClientMsgHandler instance=null; //3. Get entity method public static ClientMs gHandler getInstance(){ if(instance==null){ synchronized(ClientMsgHandler.class){ if(instance==null){ instance=new ClientMsgHandler(); } } } return instance; } protected void channelRead0( ChannelHandlerContext channelHandlerContext, MsgResBean msgResBean) throws Exception { //Specific business code, refer to before}}6.4 initChannel method optimization.handler(newChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) { //1. Unpacker ch.pipeline( ).addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,5,4)); //2. Decoder ch.pipeline().addLast(new MyDecoder()); //3. Log in Handler and use singleton to get ch. pipeline().addLast(ClientLogin2Handler.getInstance()); //4. Message sending Handler, use singleton to get ch.pipeline().addLast(ClientMsgHandler.getInstance()); //5. Encoder ch.pipeline( ).addLast(new MyEncoder()); }});6.5 Summary This kind of business splitting and singleton mode optimization are very commonly used in Netty development. To better maintain Netty-based code and improve application performance.
7. Data cache optimization
In order to improve the user experience, when sending a message (push message), if the receiver is not online, the message should be cached and pushed to him when the other party is online. 7.1 Cache data to collection //1. Define a collection to store data (real items can be stored in database or redis cache), so that data is relatively safe. private List<Map<Integer,String>> datas=new ArrayList<Map<Integer,String>>(); //2. Server push message private void pushMsg(MsgReqBean bean,Channel channel){ Integer touserid=bean.getTouserid (); Channel c=map.get(touserid); if(c==null){//The other party is not online//2.1 Store in the list collection Map<Integer, String> data=new HashMap<Integer, String>() ; data.put(touserid,bean.getMsg()); datas.add(data); //2.2. Response MsgResBean to message "sender" res=new MsgResBean(); res.setStatus(1); res.setMsg (touserid+">>>Not online"); channel.writeAndFlush(res); }else{//The other party is online//2.3. Response MsgResBean to the message "sender" res=new MsgResBean(); res.setStatus(0) ; res.setMsg("Successfully sent); channel.writeAndFlush(res); //2.4. Push message MsgRecBean to receiver res=new MsgRecBean(); res.setFromuserid(bean.getFromuserid()); res.setMsg(bean .getMsg()); c.writeAndFlush(res); }}7.2 Online push private void login(LoginReqBean bean, Channel channel){ Channel c=map.get(bean.getUserid( )); LoginResBean res=new LoginResBean(); if(c==null){ //1.Add to map map.put(bean.getUserid(),channel); //2.Assign channel.attr( AttributeKey.valueOf("userid")).set(bean.getUserid()); //3. Login response res.setStatus(0); res.setMsg("Login successful"); res.setUserid(bean.getUserid( )); channel.writeAndFlush(res); //4. According to the user to find out whether there is a message that has not been pushed yet//Ideas: Go to the lists to find according to the userid....... }else{ res.setStatus(1); res. setMsg("The account is currently online"); channel.writeAndFlush(res); }}
8. Connection disconnection event processing optimization
If the client's network failure causes the connection to be disconnected (non-active offline), then the server should be able to monitor the disconnection, and the corresponding map mapping relationship should be deleted at this time. However, if the mapping relationship is not deleted, the server resources will not be released, which will affect the client's next login with the same account and the performance when a large number of clients are offline. 8.1 Example of correct writing: public class ServerChatGroupHandler extends ChannelInboundHandlerAdapter { //Mapping relationship private static Map<Integer, Channel> map=new HashMap<Integer, Channel>(); //The connection is disconnected and the event is triggered @Override public void channelInactive( ChannelHandlerContext ctx) throws Exception { //1. Get Channel Channel channel=ctx.channel(); //2. From the map, find the corresponding userid according to the Channel Integer userid=null; for(Map.Entry<Integer, Channel> entry : map.entrySet()){ Integer uid=entry.getKey(); Channel c=entry.getValue(); if(c==channel){ userid=uid; } } //3. If userid is not empty , you need to do the following if(userid!=null){ //3.1. Delete the mapping map.remove(userid); //3.2. Remove the identifier ctx.channel().attr(AttributeKey.valueOf("userid") ).remove(); } }}8.2 Wrong way of writing Channel is disconnected, the server listens to the disconnection event, but the attributes bound to the Channel have been removed at this time, so the userid cannot be directly obtained here. Example: public class ServerChatGroupHandler extends ChannelInboundHandlerAdapter { //Mapping relationship private static Map<Integer, Channel> map=new HashMap<Integer, Channel>(); //The connection is disconnected and the event is triggered @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { //1. Get the userid bound to the Channel Object userid=channel.attr(AttributeKey.valueOf("userid")).get(); //2. If userid is not empty if(userid!=null) { //1.Remove the mapping map.remove(userid); //2.Remove the identity ctx.channel().attr(AttributeKey.valueOf("userid")).remove(); } }}
9. Summary of this article
The content of this article is relatively easy to understand. It is mainly to optimize the IM chat function implemented in the previous two articles. The optimization content is the splitting of business handlers and the use of singleton mode. If the receiver is not online, the data will be cached, and then push and monitor when it is online. When the connection is disconnected, the corresponding mapping relationship is deleted. Due to space limitations, this series of articles cannot really explain all aspects involved in developing a complete IM system. If you are interested, you can continue to read more targeted IM development articles, such as IM architecture design, IM communication protocol, IM communication security, Group chat optimization, weak network optimization, network keep-alive, etc.
10. References
[1] Getting Started for Novices: The most thorough analysis of Netty's high-performance principles and framework architecture so far
[2] Combining theory with practice: a detailed explanation of a typical IM communication protocol design
[3] Talking about the architecture design of IM system
[4] Briefly describe the pits of mobile IM development: architecture design, communication protocol and client
[5] A set of practice sharing of mobile IM architecture design for massive online users (including detailed pictures and texts)
[6] A set of original distributed instant messaging (IM) system theoretical architecture scheme
[7] A set of high-availability, easy-to-scale, high-concurrency IM group chat, single chat architecture design practice
[8] A set of IM architecture technology dry goods for hundreds of millions of users (Part 1): overall architecture, service splitting, etc.
[9] From novice to expert: how to design a distributed IM system with hundreds of millions of messages
[10] Based on practice: Summary of technical points of a small-scale IM system with one million messages
[11] Tantan's IM long connection technology practice: technology selection, architecture design, performance optimization
[12] Pick up the keyboard and do it, teach you to develop a distributed IM system by hand
[13] 40-character long text, teach you how to use Netty to create IM chat
[14] Implement a distributed IM system based on Netty
[15] SpringBoot integrates the open source IM framework MobileIMSDK to realize the instant messaging IM chat function (this article is simultaneously published at: http://www.52im.net/thread-3988-1-1.html )
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。