Java NIO(四) Netty 核心组件

1,049 阅读4分钟

一.前言

在介绍NIO 核心组件之前,先了解下比较常见的socket编程客户端和服务端的模式。

1.1 服务端

1.1.2 Server 服务端入口


import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

    private ServerSocket SERVER=null;

    private Integer PORT;

    public Server(int port){
        this.PORT=port;
        try {
            SERVER=new ServerSocket(port);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 异步线程启动
     * @throws Exception
     */
    public void doStart()throws Exception{
        //异步线程获取客户端socket连接,不阻塞主线程
        new Thread(()->{
            while (true){
                try {
                    //等待新连接接入,并获取客户端socket连接
                    Socket socket= SERVER.accept();
                    System.out.println("新客户端接入啦");
                    //处理新连接
                    new ClientHandler(socket).doStart();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public static void main(String[] args) throws Exception{
        Server server=new Server(8001);
        server.doStart();
        System.out.println("服务端已启动.");
    }
}

1.1.2 服务端消息处理类


import java.io.InputStream;
import java.net.Socket;

/**
 * 客户端消息处理
 */
public class ClientHandler {

    private Socket socket;

    /**
     * 处理每个新连接的client 端的socket .
     * @param socket
     */
    public ClientHandler(Socket socket) {
        this.socket=socket;
    }

    /**
     * 异步处理获取客户端消息
     * @throws Exception
     */
    public void doStart() {
        //异步线程处理客户端消息读取不阻塞主线程接收新的客户端连接
        new Thread(()->{
                try {
                    //读取客户端输入流
                    InputStream inputStream= this.socket.getInputStream();
                    while (true) {
                        //指定总结长度读取
                        byte[] maxData = new byte[1024];
                        int len;
                        //一直读取消息
                        while ((len = inputStream.read(maxData)) != -1) {
                            String message = new String(maxData, 0, len);
                            System.out.println("客户端传来消息: " + message);
                            //回执消息
                            socket.getOutputStream().write(maxData);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
        }).start();

    }
}

1.2 客户端连接或直接telnet命令连接

1.2.1 客户端 socket 连接


import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.TimeUnit;


/**
 * client
 */
public class Client {

    private Socket socket;

    public void start(){
        new Thread(()->{
            try {
                //连接指定端口
                socket=new Socket("127.0.0.1",8001);
                int i=0;
                while (true) {
                    socket.getOutputStream().write(("你好,我是客户端"+i++).getBytes());
                    TimeUnit.SECONDS.sleep(1);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }

    public static void main(String[] args)throws Exception {
        System.out.println("客户端启动了");
        new Client().start();
    }


}


1.2.2 telnet 连接方式

telnet 127.0.0.1 8001

telnet 命令方式发送消息 小结以上的流程服务端和客户端流程.

服务端->1.建立serverSocket 服务端服务.通过serverSocket构造方法绑定服务端口。 服务端->2.调用serverSocket accept 方法,当前线程被阻塞等待客户端连接接入 客户端-> 3.客户端建立socket连接,通过socket构造函数绑定指定服务端端口 客户端->4.客户端发送客户端消息字节流 服务端->5.服务端通过accept方法分配新线程处理客户端socket。 服务端->6.服务端通过handler异步处理socket字节流,并回执消息给客户端.

以上就是利用传统IO的方式来处理的标准socket。从以上的编码和总结过程可以知道。在第五步:服务端在处理每个客户端连接时都会new一个线程来处理客户端的连接。这也是传统IO在处理IO的一个特点,在面对高并发的场景下不断的创建线程难以满足。 带着这个问题我们继续后面的学习。

二.Netty 核心组件

2.1 NioEventLoop

在上述传统IO中我们通过异步线程的方式来处理客户端的连接以及处理客户端写入输出流的的处理过程。 对应到Netty中我们这里用到的组件是NioEventLoop这个循环类。类似于整个Netty的Bootstrap的角色,所以我们经常看到如下的这种方式。通过声明两个NioEventLoop来处理整个服务端的Netty动作。

 EventLoopGroup bossGroup=new NioEventLoopGroup();
EventLoopGroup workerGroup=new NioEventLoopGroup();
 
//2.启动nio对象bootstrap
ServerBootstrap serverBootstrap=new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup)

对应的传统IO是如下这段代码 bossGroup 在这里插入图片描述 worker loop 在这里插入图片描述

2.2 Channel

无论是服务端的serverSocket和客户端的socket都是围绕这socket对象来进行交换。而对应到NIO中则是Channel对象,Netty通过底层的对于Channel的封装,提供了几个常见的channel。最常用的则是NioServerSocketChannel.后续继续学习到。

2.4 PipeLine&ChannelHandler

Netty 通过PipeLine的来定义在整个服务处理过程中的逻辑链,类似于springmvc中的链式处理,具体每个逻辑代码则是ChannelHandler

2.5 Bytebuf

传统IO中我们通过简单的输入,输出流的方式发送客户端数据和解析服务端收到的数据。对应到Netty中的组件就是Bytebuf,定义的Netty自有的字节码模式。

传统Java 序列化

  • 字节流占用太大的空间,增加资源的负担
  • 序列化性能占用更高的CPU资源
  • Java 序列化字节流无法跨语言使用

三 .小结

上文我们首先了解了整个传统IO下socket的常见编程方式,了解传统IO服务端和客户端的整个流程。 然后针对每个步骤我们了解了对应过程在Netty中是哪些组件与之对应,方便了后续我们在学习每个组件时能够更好的和现有的传统IO方式联系起来,更好的去对比和使用。 具体的组件:

在这里插入图片描述