(https://graph.baidu.com/resou...
handler on netty's pipeline processing chain: Requires IdleStateHandler heartbeat to detect if the channel is valid, and handles UserAuthHandler for login authentication and MessageHandler for message processing
protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(defLoopGroup, //Codec new HttpServerCodec(), //Convert multiple messages into a single message object new HttpObjectAggregator(65536), //Supports asynchronous sending of large bitstreams, typically used to send file streams new ChunkedWriteHandler(), //Check if the link is idle for reading, and check if the channel is working with a heartbeat handler new IdleStateHandler(60, 0, 0), //Handshake and certification handling new UserAuthHandler(), //Handles the sending of messages new MessageHandler() ); }
For all incoming channel s, we need to save them, and future mass messages need to rely on them
public static void addChannel(Channel channel) { String remoteAddr = NettyUtil.parseChannelRemoteAddr(channel); System.out.println("addChannel:" + remoteAddr); if (!channel.isActive()) { logger.error("channel is not active, address: {}", remoteAddr); } UserInfo userInfo = new UserInfo(); userInfo.setAddr(remoteAddr); userInfo.setChannel(channel); userInfo.setTime(System.currentTimeMillis()); userInfos.put(channel, userInfo); }
Once logged in, the channel becomes a valid channel and invalid channels are discarded
public static boolean saveUser(Channel channel, String nick, String password) { UserInfo userInfo = userInfos.get(channel); if (userInfo == null) { return false; } if (!channel.isActive()) { logger.error("channel is not active, address: {}, nick: {}", userInfo.getAddr(), nick); return false; } // Verify user name and password if (nick == null || password == null) { return false; } LambdaQueryWrapper<Account> lambdaQueryWrapper = new LambdaQueryWrapper<>(); lambdaQueryWrapper.eq(Account::getUsername, nick).eq(Account::getPassword, password); Account account = accountMapperStatic.selectOne(lambdaQueryWrapper); if (account == null) { return false; } // Add an Authenticated User userCount.incrementAndGet(); userInfo.setNick(nick); userInfo.setAuth(true); userInfo.setId(account.getId()); userInfo.setUsername(account.getUsername()); userInfo.setGroupNumber(account.getGroupNumber()); userInfo.setTime(System.currentTimeMillis()); // Register the channel through which this user pushes messages offlineInfoTransmitStatic.registerPull(channel); return true; }
When the channel is closed, messages are no longer received.unregisterPull is the logoff information consumer, and the client no longer receives chat messages.Additionally, there is a write lock below to prevent the channel from suddenly closing while it is still sending messages, which can cause errors.
public static void removeChannel(Channel channel) { try { logger.warn("channel will be remove, address is :{}", NettyUtil.parseChannelRemoteAddr(channel)); //Adding a read-write lock guarantees that when a channel is removed, there will be other threads working on it, causing errors when the channel is closed rwLock.writeLock().lock(); channel.close(); UserInfo userInfo = userInfos.get(channel); if (userInfo != null) { if (userInfo.isAuth()) { offlineInfoTransmitStatic.unregisterPull(channel); // Subtract one authenticated user userCount.decrementAndGet(); } userInfos.remove(channel); } } finally { rwLock.writeLock().unlock(); } }
To seamlessly switch between four states, rabbitmq, rocketmq, activemq, and no middleware for storing and forwarding chat messages, the following four interfaces are defined.Send single chat message, group chat message, client start receiving message, client go offline not receiving message.
public interface OfflineInfoTransmit { void pushP2P(Integer userId, String message); void pushGroup(String groupNumber, String message); void registerPull(Channel channel); void unregisterPull(Channel channel); }
Among them, how to use one of the three middleware, rabbitmq, rocketmq and activemq, to store and forward chat messages, its processing flow is as follows:
- The chat model refers to the model of the thread pool, and if the user is online, it is sent directly to the user via channel.If the user is offline, it is sent to the middleware store and the next time the user is online, the message is pulled directly from the middleware.The benefit of doing this compared to sending all messages through the middleware is improved performance
- Group chat forwards messages entirely through the middleware, which sends messages, and clients receive and cancel messages from the middleware.If it's still like chatting, online users send directly through channel s, which is too cumbersome to determine which users of this group are online
- If the user registers the consumer online, the interest is cancelled from the middleware.Otherwise, disconnect the consumer and leave the message in the middleware so that the client can pull it the next time it comes online.This enables offline message reception.
- Regardless of which middleware you use or which you don't use, the process follows the three requirements above to seamlessly switch the four methods above to store and forward messages.Which method do you need to open the corresponding comment?
Project Address: https://github.com/shuangyueliao/netty-chat