netty是如何一步一步演化而来的,不断进化成就netty

这是我参与更文挑战的第14天,活动详情查看: 更文挑战java

简介

  • 随着IO类库不断完善,咱们基于Java的网络编程学习成本愈来愈低。以前网络开发都是底层语言编写的,像C语言,C++。其实经过Java也能够编写。Java的io类基于tcp链接提供的socket就是咱们实现网络开发的桥段。可是由于是同步阻塞式,因此效率上来讲就慢了不少。在Java7以后提供了异步阻塞式io。为咱们拉开了一个新天地。

BIO

  • 什么是网络编程?所谓的网络编程其实就是C/S模型。你们都知道Java是开发B/S模型的。C/S实际是client和server端的开发。说白就是两个进程相互通讯。client经过tcp链接server而后交互数据
  • BIO 即同步阻塞是编程。在JDK1.4以前咱们网络链接都是采用的BIO模式,服务端经过ServerSocket(port)构建服务端。而后服务端经过accept方法阻塞式等待客户端的链接。客户端经过Socket(ip,port)构建客户端。而后经过PrintWriter传递消息。
  • 默认状况下BIO模式是一个客户端对应一个线程。这样对于内存的消耗是严重的。慢慢的这种方式也就被抛弃了。

001.jpg

server

  • 为了缓解线程压力。这里构造线程池。初始大小5个
private static final ThreadLocal<ExecutorService> executorService = new ThreadLocal<ExecutorService>() {
    @Override
    protected ExecutorService initialValue() {
        return Executors.newFixedThreadPool(5);
    }
};

复制代码
  • 而后是构建ServerSocket。而后就是一直在阻塞式等待客户端的链接。什么叫阻塞式等待。就是一直在等待客户端链接。没有新的客户端链接就不继续执行。当客户端链接后。accept就会返回当前客户端的链接对象。而后咱们将他进行封装后放到线程池中。线程池中有可用资源就会将可用线程加载这个客户端对象。
public static void start() throws IOException {
    try {
        // 经过构造函数建立ServerSocket
        server = new ServerSocket(HostConstant.PORT);
        System.out.println("服务器已启动,端口号:" + HostConstant.PORT);
        while (true) {
            // 真正处理的仍是Socket
            Socket socket = server.accept();// 阻塞方法
            // 把客户端请求打包成一个任务,放到线程池执行
            executorService.get().execute(new ServerHandler(socket));
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (server != null) {
            server.close();
        }
    }

}

复制代码
public class ServerHandler implements Runnable {
    private Socket socket;

    public ServerHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try (BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
            PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
            String message;
            String result;
            // 经过输入流读取客户端传输的数据
            while ((message = br.readLine()) != null) {
                System.out.println("server receive data:" + message);
                result = response(message);
                // 将业务结果经过输出流返回给客户端
                pw.println(result);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                socket = null;
            }
        }
    }

    // 返回给客户端的应答
    public static String response(String msg) {
        return "Hello," + msg + ",Now is " + new java.util.Date(System.currentTimeMillis()).toString();
    }
}

复制代码

##client编程

public class BIOClient {
    public void startConnect() {
        try {
            Socket socket = new Socket(HostConstant.IP, HostConstant.PORT);
            new ReadMsg(socket).start();
            PrintWriter pw = null;
            // 写数据到服务端
            pw = new PrintWriter(socket.getOutputStream());
            pw.println(UUID.randomUUID());
            pw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static class ReadMsg extends Thread {
        Socket socket;

        public ReadMsg(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try (BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
                String line = null;
                // 经过输入流读取服务端传输的数据
                while ((line = br.readLine()) != null) {
                    System.out.printf("%s\n", line);
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        Integer time = 6;
        for (Integer i = 0; i < time; i++) {
            new BIOClient().startConnect();
        }
    }
}

复制代码

缺点

  • 上面服务端和客户端其实是伪异步。表面上看起来不会由于客户端的增长致使内存溢出。可是由于实际上仍是accpet同步阻塞等待。因此在链接性能上仍是很差。服务器

  • 咱们在ServerHanndler中是读取客户端传输的数据经过BufferedReader.readLine这个方法。咱们跟踪下去发现实际调用的是InputStream.read这个方法。markdown

002.jpg

/** * Reads the next byte of data from the input stream. The value byte is * returned as an <code>int</code> in the range <code>0</code> to * <code>255</code>. If no byte is available because the end of the stream * has been reached, the value <code>-1</code> is returned. This method * blocks until input data is available, the end of the stream is detected, * or an exception is thrown. * * <p> A subclass must provide an implementation of this method. * * @return the next byte of data, or <code>-1</code> if the end of the * stream is reached. * @exception IOException if an I/O error occurs. */
public abstract int read() throws IOException;

复制代码
  • 咱们在翻阅他们的源码说明,会发现上面注释大意为:当没有字节的时候意味着已经结束了咱们会返回-1。这个方法一直阻塞知道获取到字节或者是结束或者是抛出异常。咱们以前也说了服务端等待链接的时候是阻塞式等待。这会形成客户端链接的一些问题。可是客户端链接上之后开始通讯了。服务端获取客户端的消息也是采用阻塞式等待的。这会直接形成交互等待从而形成交互拥堵。换句话说客户端A发送了100条消息。服务端会一条一条处理。像食堂打菜排队同样。
  • 一样的道理OutputStream也是阻塞式的。这里有兴趣的读者能够自行翻阅源码查看。这也说明咱们链接是阻塞的。通讯也是阻塞的。这就是BIO暴露的缺点。
  • 由于阻塞,会形成后期客户端的接入没法成功,会一直等待。形成链接超时。

NIO

NIO=Non Block IO .即非阻塞式编程。网络

  • 在BIO中咱们经过ServerSocket、Socket实现服务端和客户端的开发。在NIO中对应提供了ServerSocketChannel、SocketChannel两个类实现通讯。这两个类支持阻塞式和非阻塞式模式。阻塞模式这里不作介绍形成的后果由和BIO同样。下面咱们来看看如何实现NIO

##ByteBuffer框架

  • ByteBuffer使咱们NIO通讯的一个缓冲区,咱们的读写都是借助与它传递的。由于他提供了相似指针指向,咱们操做指向就能够获取到字节。

##Channeldom

  • 在NIO中他被看作是一个通道。经过Channel控制读和写。BIO中是经过Stream方式传递的。Channel和Stream相比最大的特色Channel是双向的。简单的理解就是读用Channel,写也用Channel。在BIO中InputStream和OutputStream分别负责读和写。上面提到的ServerSocketChannelSocketChannel都是Channel的子类。

##Selector异步

-多路复用器。这里咱们能够理解为注册中心。全部的handler再向selector注册的时候会带上标签(SelectorKey)。在某个Channel发生读或写的事件时这个Channel会处于就绪状态。在Selector轮询的时候会筛选出来。而后咱们在根据SelectorKey判断监听的是什么事件。从而作出处理。查阅资料得知selector没有链接限制。理论上一个selector能够管理N个Channel。socket

003.jpg

  • 上面是服务器的开发流程。其实就是用一个线程来管理selector。而后不停遍历selector而后捕获事件。服务端和客户端捕获不一样的事件。下面大概列举了下事件

004.jpg

  • 框架搭建就是流程上的,剩下的就是咱们在selectorKey这个事件上进行业务的io处理。在读写期间就是ByteBuffer来实现。NIO的实现和BIO相对比较复杂。

进化

关于NIO2.0 和nettry 他们在次基础上进行提高!不得不说如今基本上是netty的天下了。下章节咱们针对netty来展开讨论!今天端午节不说了我吃粽子去了tcp

记得吃完粽子,回来点个赞哦!!!

相关文章
相关标签/搜索