Netty implements synchronous "request-response" communication mechanism

demand

Implementation of Netty-based "request-response" synchronous communication mechanism.

Design thinking

Netty provides a unified implementation of asynchronous IO and synchronous IO, but our requirements have nothing to do with the synchronous asynchronization of IO. The key is to realize the typical question-and-answer interaction of request-response. To meet this need, two problems need to be solved: 

The correct match between request and response.

After the client sends the data, when the server returns the response result, how does it match the client's request correctly (that is, a request corresponds to its own response)?
Solution: Through the unique RequestId of the client, the response returned by the server needs to include the RequestId, so that the client can match the request response correctly through the RequestId.

Communication between request threads and response threads.

The request thread will wait for the server to return synchronously after the request is made. Therefore, it needs to be resolved how the Netty client notifies the request thread of the result after receiving the response.
Solution: After sending the request, the client thread enters to wait. After the server returns the response, it wakes up the request thread of the client according to RequestId, and returns the result to the request thread.

Solution

The CountDownLatch class in Java is used to synchronize Future.
The specific process is: after the client sends the request, the < request ID, Future > key value pair is saved in a cache, and then the request thread is hung by Future waiting for the result; when the Netty client receives the response from the server, the response thread takes Future from the cache according to the request ID, and then sets the response result to Future. At this point, the CountDownLatch notification mechanism is used to notify the requesting thread. The request thread gets the response result from Future and then does business processing.
Caching guava using google

Specific code

First introduce dependencies

        <!-- guava -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>28.0-jre</version>
        </dependency>

        <!-- Netty -->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.39.Final</version>
        </dependency>

SyncFuture

SyncFuture: Synchronized Future. This is the core, through this tool class to achieve thread waiting.

package com.topinfo.ci.netty.client;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class SyncFuture<T> implements Future<T> {

    // Because the request and response are one-to-one, the CountDownLatch value is initialized to 1.
    private CountDownLatch latch = new CountDownLatch(1);
    
    // Response results that need to respond to thread settings
    private T response;
    
    // Futrue request time, used to calculate whether Future timeout
    private long beginTime = System.currentTimeMillis();

    public SyncFuture() {
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        return false;
    }

    @Override
    public boolean isCancelled() {
        return false;
    }

    @Override
    public boolean isDone() {
        if (response != null) {
            return true;
        }
        return false;
    }

    // Get the response result, and do not return until there is a result.
    @Override
    public T get() throws InterruptedException {
        latch.await();
        return this.response;
    }

    // Get the response result, and return it until the result is available or beyond the specified time.
    @Override
    public T get(long timeout, TimeUnit unit) throws InterruptedException {
        if (latch.await(timeout, unit)) {
            return this.response;
        }
        return null;
    }

    // Used to set the response result and do countDown to notify the requesting thread
    public void setResponse(T response) {
        this.response = response;
        latch.countDown();
    }

    public long getBeginTime() {
        return beginTime;
    }
}

Client Code

NettyClient

NettyClient: Synchronized and asynchronous methods of messages, as follows:

package com.topinfo.ci.netty.client;

import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.CharsetUtil;


/**
 *@Description: Netty Client
 *@Author:Yang pan
 *@Since:2019 September 26, 8:54:59 p.m.  
 */
@Component
public class NettyClient {

    private static final Logger LOGGER = LoggerFactory.getLogger(NettyClient.class);
    
    private EventLoopGroup group = new NioEventLoopGroup();

    /** 
     *@Fields DELIMITER : Custom separators, server and client alignment
     */ 
    public static final String DELIMITER = "@@";
    
    /**
     * @Fields hostIp : Server ip
     */
    private String hostIp = "192.168.90.96";

    /**
     * @Fields port : Server Port
     */
    private int port= 8888;

    /**
     * @Fields socketChannel : passageway
     */
    private SocketChannel socketChannel;

    
    /** 
     *@Fields clientHandlerInitilizer : Initialization
     */ 
    @Autowired
    private NettyClientHandlerInitilizer clientHandlerInitilizer;
    
    /**
     * @Description: Start the client
     * @Author:Yang pan
     * @Since: 2019 September 12, 4:43:21 p.m.
     */
    @SuppressWarnings("unchecked")
    @PostConstruct
    public void start() {

        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
            // Specify Channel
            .channel(NioSocketChannel.class)
            // Server address
            .remoteAddress(hostIp, port)
            
            // Packing small data packets into larger frames for transmission increases the network load, i.e. TCP delay transmission.
            .option(ChannelOption.SO_KEEPALIVE, true)
            // Packing small data packets into larger frames for transmission increases the network load, i.e. TCP delay transmission.
            .option(ChannelOption.TCP_NODELAY, true)
            .handler(clientHandlerInitilizer);
        
        // Connect
        ChannelFuture channelFuture = bootstrap.connect();
         //Client disconnection reconnection logic
        channelFuture.addListener(new ChannelFutureListener() {

            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if(future.isSuccess()) {
                    LOGGER.info("Connect Netty Server Success...");
                }else {
                    
                    LOGGER.info("Connect Netty Server failed, disconnected and reconnected...");
                    final EventLoop loop =future.channel().eventLoop();
                    loop.schedule(new Runnable() {
                        @Override
                        public void run() {
                            LOGGER.info("Connection is being retried...");
                            start();
                        }
                    }, 20, TimeUnit.SECONDS);
                }
            }

             
        });
        
        socketChannel = (SocketChannel) channelFuture.channel();
    }

    
    /**
     *@Description: message sending
     *@Author:Yang pan
     *@Since: 2019 September 12, 5:08:47 p.m.
     *@param message
     */
    public void sendMsg(String  message) {
        
        String msg = message.concat(NettyClient.DELIMITER);

        ByteBuf byteBuf = Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8);
        ChannelFuture future = socketChannel.writeAndFlush(byteBuf);

        future.addListener(new ChannelFutureListener() {

            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                
                if(future.isSuccess()) {
                    System.out.println("===========Send successfully");
                }else {
                    System.out.println("------------------fail in send");
                }
            }
        });
    }

    

    /**
     *@Description: Send Synchronized Messages
     *@Author:Yang pan
     *@Since: 2019 September 12, 5:08:47 p.m.
     *@param message
     */
    public String sendSyncMsg(String  message, SyncFuture<String> syncFuture) {
        
        String result = "";
        
        String msg = message.concat(NettyClient.DELIMITER);

        ByteBuf byteBuf = Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8);
        
        try {
            
            ChannelFuture future = socketChannel.writeAndFlush(byteBuf);
            future.addListener(new ChannelFutureListener() {

                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    
                    if(future.isSuccess()) {
                        System.out.println("===========Send successfully");
                    }else {
                        System.out.println("------------------fail in send");
                    }
                }
            });
            
            // Wait for 8 seconds.
            result = syncFuture.get(8, TimeUnit.SECONDS);
            
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        return result;
    }

    public String getHostIp() {
        return hostIp;
    }


    public void setHostIp(String hostIp) {
        this.hostIp = hostIp;
    }


    public int getPort() {
        return port;
    }


    public void setPort(int port) {
        this.port = port;
    }
    
    
}

NettyClientHandlerInitilizer

NettyClientHandler Initilizer: Initialization

package com.topinfo.ci.netty.client;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

@Component
public class NettyClientHandlerInitilizer extends ChannelInitializer<Channel> {

    /** 
     *@Fields clientHandler : Client Processing 
     */ 
    @Autowired
    private NettyClientHandler clientHandler;
    
    @Override
    protected void initChannel(Channel ch) throws Exception {
        
        // Get the corresponding pipeline through socket Channel
                ChannelPipeline channelPipeline = ch.pipeline();
                
                /*
                 * channelPipeline There will be many handler classes (also known as interceptor classes)
                 * Once you get pipeline, you can add handler directly to. addLast
                 */
                ByteBuf buf = Unpooled.copiedBuffer(NettyClient.DELIMITER.getBytes());
                channelPipeline.addLast("framer", new DelimiterBasedFrameDecoder(1024*1024*2, buf));
                //channelPipeline.addLast("decoder",new StringDecoder(CharsetUtil.UTF_8));
                //channelPipeline.addLast("encoder",new StringEncoder(CharsetUtil.UTF_8));
                channelPipeline.addLast(clientHandler);
        
    }

}

NettyClientHandler

Netty ClientHandler: Client Processing Class for Receiving

package com.topinfo.ci.netty.client;

import java.util.concurrent.TimeUnit;

import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.topinfo.ci.netty.service.NettyClientService;
import com.topinfo.ci.netty.utils.ExceptionUtil;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoop;
import io.netty.channel.SimpleChannelInboundHandler;

@Component
@ChannelHandler.Sharable // Annotating a channel handler can be safely shared by multiple channels
public class NettyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

    private static final Logger LOGGER = LoggerFactory.getLogger(NettyClientHandler.class);

    @Autowired
    private NettyClientService service;

    @Autowired
    private NettyClient nettyClient;

    /**
     * @Description: When the server sends a message to the client, it triggers the method to receive the message.
     * @Author:Yang pan
     * @Since: 2019 September 12, 5:03:31 p.m.
     * @param ctx
     * @param byteBuf
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {

        String msg = byteBuf.toString(CharsetUtil.UTF_8);

        LOGGER.info("The client receives a message:{}", msg);

        //service.ackMsg(msg);
        service.ackSyncMsg(msg); // Synchronized message return
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        LOGGER.info("Successful request connection...");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {

        LOGGER.info("Connection disconnected...");

        // Disconnecting and reconnecting during use
        final EventLoop eventLoop = ctx.channel().eventLoop();
        eventLoop.schedule(new Runnable() {

            @Override
            public void run() {
                // Reconnect
                nettyClient.start();
            }
        }, 20, TimeUnit.SECONDS);
        super.channelInactive(ctx);
    }

    /**
     * Handling exceptions, typically putting Andler, which implements exception handling logic, at the end of Channel Pipeline
     * This ensures that all inbound messages are always processed, regardless of where they occur. Here's just a simple way to close Channel and print exception messages
     * 
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();

        // Output to log
        ExceptionUtil.getStackTrace(cause);

        Channel channel = ctx.channel();
        if (channel.isActive()) {
            ctx.close();
        }
    }

}

NettyClientServiceImpl

NettyClientService Impl: The client encapsulates the implementation class and its interface is not pasted out.

package com.topinfo.ci.netty.service.impl;

import com.topinfo.ci.netty.bean.RealDataInfo;

import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import com.topinfo.ci.netty.bean.Message;
import com.topinfo.ci.netty.client.NettyClient;
import com.topinfo.ci.netty.client.SyncFuture;
import com.topinfo.ci.netty.service.NettyClientService;
import com.topinfo.ci.netty.utils.AESUtil;

@Service
public class NettyClientServiceImpl implements NettyClientService {

    private static final Logger LOGGER = LoggerFactory.getLogger(NettyClientServiceImpl.class);
    

    //The cache interface here is LoadingCache, which automatically loads the cache when the cache item does not exist.
    private static LoadingCache<String, SyncFuture> futureCache = CacheBuilder.newBuilder()
            //Setting the initial capacity of the cache container to 10
            .initialCapacity(100)
            // maximumSize Sets Cache Size
            .maximumSize(10000)
            //Set the concurrency level to 20, which refers to the number of threads that can write caches at the same time.
            .concurrencyLevel(20)
            // expireAfterWrite expires 8 seconds after the write cache is set
            .expireAfterWrite(8, TimeUnit.SECONDS)
            //Setting Cache Removal Notification
            .removalListener(new RemovalListener<Object, Object>() {
                @Override
                public void onRemoval(RemovalNotification<Object, Object> notification) {
                    LOGGER.debug("LoadingCache: {} was removed, cause is {}",notification.getKey(), notification.getCause());
                }
            })
            //CacheLoader can be specified in the build method, and caching can be automatically loaded through CacheLoader when caching does not exist.
            .build(new CacheLoader<String, SyncFuture>() {
                @Override
                public SyncFuture load(String key) throws Exception {
                    // When the cache to get the key does not exist, there is no need to add it automatically.
                    return null;
                }
            });
    
    
    @Autowired
    private NettyClient nettyClient;
    
    @Autowired
    private CacheManager cacheManager;
 
    @Override
    public boolean sendMsg(String text, String dataId, String serviceId) {
        
        LOGGER.info("Contents sent:{}", text);

        //TODO        
        //nettyClient.sendMsg(json);
        return true;
    }
 

    @Override
    public String sendSyncMsg(String text, String dataId, String serviceId) {
        
        SyncFuture<String> syncFuture = new SyncFuture<String>();
        // Put it in the cache
        futureCache.put(dataId, syncFuture);
        
        // Encapsulated data
        JSONObject object = new JSONObject();
        object.put("dataId", dataId);
        object.put("text", text);
        
        // Send Synchronized Messages
        String result = nettyClient.sendSyncMsg(object.toJSONString(), syncFuture);
        
        return result;
    }

    @Override
    public void ackSyncMsg(String msg) {
        
        LOGGER.info("ACK Confirmation information: {}",msg);
        
        JSONObject object =JSON.parseObject(msg);
        String dataId = object.getString("dataId");
        
        // Getting data from the cache
        SyncFuture<String> syncFuture = futureCache.getIfPresent(dataId);
        
        // If it is not null, the notification returns
        if(syncFuture != null) {
            syncFuture.setResponse(msg);
        }
    }

}

TestController

TestController: Test TestController.

package com.topinfo.ci.netty.controller;

import com.alibaba.fastjson.JSON;
import com.topinfo.ci.netty.bean.CmwSensoralert;
import com.topinfo.ci.netty.bean.Equip;
import com.topinfo.ci.netty.bean.JsonResult;
import com.topinfo.ci.netty.bean.RealDataInfo;
import com.topinfo.ci.netty.mapper.SensorAlertMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.topinfo.ci.netty.service.NettyClientService;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/test")
public class TestController {
    
    @Autowired
    private NettyClientService clientService;
    @Autowired
    private SensorAlertMapper sensorAlertMapper;

    @RequestMapping("/sendSyncMsg")
    public String sendSyncMsg(String dataId, String text) {
        
        String serviceId = "mmmm";
        
        String result = clientService.sendSyncMsg(text, dataId, serviceId);
        
        return "result:"+result ;
    }
     
}

The test perfectly achieves the effect of "request-response".

Server-side code

NettyServer

package com.topinfo.ju.ccon.netty.server;

import java.net.InetSocketAddress;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

@Component
public class NettyServer {

    private static final Logger LOGGER = LoggerFactory.getLogger(NettyServer.class);
    
    /** 
     *@Fields DELIMITER : Custom separators, server and client alignment
     */ 
    public static final String DELIMITER = "@@";
    
    /**
     * @Fields boss : boss Thread groups are used to handle connection work. By default, they are twice the number of CPU s in the system. They can also be specified according to the actual situation.
     */
    private EventLoopGroup boss = new NioEventLoopGroup();

    /**
     * @Fields work : work Thread groups are used for data processing. By default, they are twice the number of CPU s in the system. They can also be specified according to the actual situation.
     */
    private EventLoopGroup work = new NioEventLoopGroup();

    /**
     * @Fields port : Monitor port
     */
    private Integer port = 8888;
    
    @Autowired
    private NettyServerHandlerInitializer handlerInitializer;

    /**
     * @throws InterruptedException 
     * @Description: Start Netty Server
     * @Author:Yang pan
     * @Since: 2019 September 12, 4:21:35 p.m.
     */
    @PostConstruct
    public void start() throws InterruptedException {

        ServerBootstrap bootstrap = new ServerBootstrap();

        bootstrap.group(boss, work)
                // Specify Channel
                .channel(NioServerSocketChannel.class)
                // Set the socket address using the specified port
                .localAddress(new InetSocketAddress(port))

                // The number of queues that the server can connect corresponds to the backlog parameter in the listen function of TCP/IP protocol
                .option(ChannelOption.SO_BACKLOG, 1024)

                // Setting up a long TCP connection, TCP will automatically send an active probe data message if there is no data communication within two hours.
                .childOption(ChannelOption.SO_KEEPALIVE, true)

                // Packing small data packets into larger frames for transmission increases the network load, i.e. TCP delay transmission.
                .childOption(ChannelOption.TCP_NODELAY, true)
                .childHandler(handlerInitializer);

        ChannelFuture future = bootstrap.bind().sync();
        
        if (future.isSuccess()) {
            LOGGER.info("start-up Netty Server...");
        }
    }
    
    @PreDestroy
    public void destory() throws InterruptedException {
        boss.shutdownGracefully().sync();
        work.shutdownGracefully().sync();
        LOGGER.info("Close Netty...");
    }
}

NettyServerHandlerInitializer

package com.topinfo.ju.ccon.netty.server;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

@Component
public class NettyServerHandlerInitializer extends ChannelInitializer<Channel> {

    /** 
     *@Fields serverHandler : Service processing
     */ 
    @Autowired
    private NettyServerHandler serverHandler;
    
    
    @Override
    protected void initChannel(Channel ch) throws Exception {

        // Get the corresponding pipeline through socket Channel
        ChannelPipeline channelPipeline = ch.pipeline();
        
        /*
         * channelPipeline There will be many handler classes (also known as interceptor classes)
         * Once you get pipeline, you can add handler directly to. addLast
         */
        ByteBuf buf = Unpooled.copiedBuffer(NettyServer.DELIMITER.getBytes());
        channelPipeline.addLast("framer", new DelimiterBasedFrameDecoder(1024*1024*2, buf));
        //channelPipeline.addLast("decoder",new StringDecoder(CharsetUtil.UTF_8));
        //channelPipeline.addLast("encoder",new StringEncoder(CharsetUtil.UTF_8));
        
        // Custom decoder, paste/unpack/unpack
        
        channelPipeline.addLast(serverHandler);
    }

}

NettyServerHandler

package com.topinfo.ju.ccon.netty.server;


import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;
import com.topinfo.ju.ccon.netty.bean.Message;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

@Component
@ChannelHandler.Sharable //Annotating a channel handler can be safely shared by multiple channels
public class NettyServerHandler extends SimpleChannelInboundHandler<ByteBuf> {

    private static final Logger LOGGER = LoggerFactory.getLogger(NettyServerHandler.class);
    
    public static AtomicInteger nConnection = new AtomicInteger(0);
    
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        
        String txt = msg.toString(CharsetUtil.UTF_8);
        
        LOGGER.info("Receive a message from the client:{}", txt);
    
        
        
        ackMessage(ctx, txt);
    }

    /**
     *@Description: confirmation message 
     *@Author:Yang pan
     *@Since: 2019 September 17, 11:22:27 a.m.
     *@param ctx
     *@param message
     */
    public void ackMessage(ChannelHandlerContext ctx, String message) {
        
        //custom delimiter
        String msg = message+NettyServer.DELIMITER;
        
        ByteBuf byteBuf = Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8);
        
        //Response Client
        ctx.writeAndFlush(byteBuf);
    }
    
    

    /**
     *@Description: Each time a new connection comes, add one to the number of connections
     *@Author:Yang pan
     *@Since: 2019 September 16, 3:04:42 p.m.
     *@param ctx
     *@throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        nConnection.incrementAndGet();
        
        LOGGER.info("Request connection...{}´╝îCurrent number of connections: : {}",  ctx.channel().id(),nConnection.get());
    }
    
    /**
     *@Description: Each time you disconnect from the server, reduce the number of connections by one
     *@Author:Yang pan
     *@Since: 2019 September 16, 3:06:10 p.m.
     *@param ctx
     *@throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        nConnection.decrementAndGet();
        LOGGER.info("Disconnect...Current number of connections: : {}",  nConnection.get());
    }
    
    
    /**
     *@Description: Callback when connection is abnormal 
     *@Author:Yang pan
     *@Since: 2019 September 16, 3:06:55 p.m.
     *@param ctx
     *@param cause
     *@throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        
        // Print error log
        cause.printStackTrace();
        
        Channel channel = ctx.channel();
        
        if(channel.isActive()){
            ctx.close();
        }
        
    }
    
}

This is the core code, I hope to help you.

Tags: Java Netty Google codec

Posted on Thu, 26 Sep 2019 10:15:18 -0400 by Gary King