Original blog address: pjmike’s blog

preface

This article mainly introduces a Netty client and server side of the example code, have an intuitive feeling of Netty, see how to use Netty, the subsequent article will be Netty each component of the detailed analysis

Introduction of Netty

Netty is an asynchronous, event-driven network application framework that enables rapid development of maintainable, high-performance protocol-oriented servers and clients. Netty mainly encapsulates the NIO package of Java

Why use Netty

In other words, what are the advantages of Netty that make it worth using? Why not use native Java Socket programming or use Java NIO introduced in Java 1.4? Next, analyze Java Socket programming and Java NIO.

Java Network Programming

Let’s start with an example of Java network programming:

public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8888);
            Socket socket = serverSocket.accept();
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String line = "";
            while((line = reader.readLine()) ! =null) {
                System.out.println("received: "+ line); }}catch(IOException e) { e.printStackTrace(); }}Copy the code

This code can only handle one connection at a time. To manage multiple concurrent clients, you need to create a new Thread for each new client Socket. This concurrent scheme for the number of small and medium-sized client can also accept, if it is in view of the high concurrency, more than 100000 concurrent connections for this scheme is not an option, the thread resources it need too much, and at any time are likely to have a large number of threads in the blocking state, waiting for input or output data ready, poor performance of the whole scheme. Therefore, high concurrency scenarios, the general Java network programming scheme is not desirable.

Java NIO

Let’s start with an example of Java NIO:

public class ServerSocketChannelDemo {
    private ServerSocketChannel serverSocketChannel;
    private Selector selector;

    public ServerSocketChannelDemo(int port) throws IOException {
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    public void listener(a) throws IOException {
        while (true) {
            int n = selector.select();
            if (n == 0) {
                continue;
            }
            Iterator iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = (SelectionKey) iterator.next();
                if (selectionKey.isAcceptable()) {
                    ServerSocketChannel server_channel = (ServerSocketChannel) selectionKey.channel();
                    SocketChannel socketChannel = server_channel.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                }
                if (selectionKey.isReadable()) {
                    // If the channel is in a read-ready state
                    / / read operation
                    //TODO
                }
            }
        }
    }
}
Copy the code

The core parts of NIO are:

  • Channel is the Channel
  • Buffer
  • A multiplexer Selector

The Selector, which is the key to Java’s non-blocking I/O implementation, registers a Channel with a Selector, and if a Channel sends a read or write event, that Channel is in a ready state and polled by the Selector, Then perform subsequent I/O operations. This approach to I/O multiplexing provides better resource management than the blocking I/O model above:

  • Many connections can be handled with fewer threads, thus reducing the overhead of memory management and context switching
  • Threads can also be used for other tasks when there are no I/O operations to handle.

While using Java NIO allows us to handle many connections with fewer threads, reliably and efficiently processing and scheduling I/O operations under high loads is a tedious and error-prone task that led to Netty, the expert in high-performance network programming

Netty features

Netty has a number of great features that are worth using:

design

  • Unified API for different protocols (blocking and non-blocking)
  • Based on a flexible, extensible event-driven model
  • Highly customizable threading model
  • Reliable connectionless data Socket support (UDP)

performance

  • Better throughput, lower latency
  • Lower resource consumption
  • Minimal memory replication

Robustness,

  • Outofmemoryerrors are no longer caused by fast, slow, or overloaded connections
  • There are no longer inconsistent NIO read/write frequencies on high-speed networks

Security:

  • Full SSL/TLS and STARTTLS support
  • Can be used in constrained environments, such as applets and OSGI

Easy to use:

  • Verbose Javadoc and a large sample set
  • No more than JDK 1.6+ dependencies are required

Netty sample code

Here are examples of server and client code, starting with a look at what the Netty code looks like, and later articles looking at each module in detail.

The Server code

public class EchoServer {
    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws InterruptedException {
        new EchoServer(8888).start();
    }

    public void start(a) throws InterruptedException {
        final EchoServerHandler serverHandler = new EchoServerHandler();
        // Create an EventLoopGroup to process events
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(boss,worker)
                    // Specify the NIO transport Channel to use
                    .channel(NioServerSocketChannel.class)
                    // Sets the socket address with the specified port
                    .localAddress(new InetSocketAddress(port))
                    // Add an EchoServerHandler to the sub-channel's ChannelPipeline
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            //EchoServerHandler is marked @shareable, so we can always use the same instancesocketChannel.pipeline().addLast(serverHandler); }});// Asynchronous binding server, calling sync() to block and wait until binding is complete
            ChannelFuture future = b.bind().sync();
            future.channel().closeFuture().sync();
        } finally {
            // Close the EventLoopGroup to release all resourcesgroup.shutdownGracefully().sync(); worker.shutdownGracefully().sync(); }}}Copy the code

EchoServerHandler

@ChannelHandler.Sharable // Identifies a ChannelHandler that can be safely shared by multiple channels
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buffer = (ByteBuf) msg;
        // Log the message to the console
        System.out.println("Server received: " + buffer.toString(CharsetUtil.UTF_8));
        // Write the received message back to the sender
        ctx.write(buffer);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        // Flush the unsent messages to the remote node and close the Channel
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
                .addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // Prints the exception stack trace
        cause.printStackTrace();
        // Close the Channelctx.close(); }}Copy the code

Code Points:

  • ServerBootStrapIs the boot class, help service start auxiliary class, can set Socket parameters
  • EventLoopGroupIs a thread pool that handles I/O operations and is used to allocate I/ OS and events serving channelsEventLoopAnd theNioEventLoopGroupisEventLoopGroupAn implementation class of. Two of them are instantiated hereNioEventLoopGroup, aboss, mainly used to handle client connections, oneworkerIt is used to process client data read and write
  • EchoServerHandlerThe business logic is implemented
  • By calling theServerBootStrap.bind()Method to bind the server

The Client code

public class EchoClient {
    private final String host;
    private final int port;


    public EchoClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void start(a) throws InterruptedException {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .remoteAddress(new InetSocketAddress(host, port))
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(newEchoClientHandler()); }}); ChannelFuture channelFuture = b.connect().sync(); channelFuture.channel().closeFuture().sync(); }finally{ group.shutdownGracefully().sync(); }}public static void main(String[] args) throws InterruptedException {
        new EchoClient("127.0.0.1".8888).start(); }}Copy the code

EchoClientHandler

@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
        System.out.println("Client received: "+byteBuf.toString());
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks",CharsetUtil.UTF_8));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); }}Copy the code

Code Points:

  • To initialize the client, create a BootStrap instance withServerBootStrapAlso a bootstrap class that assists the client
  • Assign aNioEventLoopGroupInstance, insideEventLoopHandle events that occur during the lifetime of the connection
  • EchoClientHandlerClass is responsible for handling the business logic and the server sideEchoSeverHandlerSimilar effects.

References & acknowledgements

  • Netty In Action