Netty总结

📚Linux系统IO线程模型

在Linnux系统级别,提供了5种IO线程模型,分别是同步阻塞IO、同步非阻塞IO、多路服务用IO、信号驱动IO和异步IO。

在了解IO模型之前,我们要对用户空间和内核空间有一定的了解,在进行IO的时候,用户线程它不是直接操作的数据,而是把请求交给了操作系统,由操作系统与磁盘或网卡进行交互获取数据,之后,数据会被操作系统放入内核缓冲区,然后用户线程会从内核缓冲区服务数据到用户缓冲区。

对于同步阻塞IO,在数据被从内核缓冲区成功复制到用户空间缓冲区前,用户线程是被阻塞的,也就是说,直到数据被准备好以前,用户线程什么也做不了。

对于同步非阻塞线程,当数据读取请求交给操作系统后,用户线程就会返回,并不会一直阻塞,一般在这种情况下,需要不断轮询来确定数据是否已经在内核空间了,如何是,则会同步的将数据从内核空间复制到用户空间缓冲区。

以上两种IO模型,都是一个线程对应一个读写的,在并发量比较大的情况下,会造成线程数量暴增,而IO多路复用技术就是为了解决这个问题,在这种模型下,一个线程可以监听多个线程对应的IO数据是否已经就绪,如果就绪再进行后续处理,一般被用在网络IO上,而磁盘IO并不会用到这种技术。

对于信号驱动IO,可以与多路服务用IO进行对比着理解,多路复用IO在检查IO状态时是通过轮询的方式实现的,这种方式在被监听线程数量比较多时,效率比较低,而信号驱动IO指的是,不需要用户线程不断轮询,而是当数据被读取到内核缓冲区后,系统会自动通知用户线程,这样用户线程就能被动的知道哪些数据已经准备好了,从而避免不断轮询,目前Java NIO底层就是基于这种信号驱动IO实现的IO多路复用。

而异步IO把数据从内核缓冲区到用户缓冲区也进行了异步化,就是,当数据已经从内核缓冲区到用户缓冲区时,系统才会通知用户线程,这是真正意义的异步IO。

Java中的NIO是采用多路复用的IO模型,底层基于调用的是基于信号驱动实现的多路服务用IO,也就是我们常说的epoll。

📚Java NIO概述

Java目前支持的IO模型有BIO、NIO和AIO,NIO是在jdk1.4中被引入的,主要为了解决BIO服务无法应对高性能网络编程的问题,Java NIO底层基于epoll模型实现,编程模型中主要涉及Buffer、Channel和Selector等概念,其中Buffer是数据的载体在Channel中传递,Channel类似于BIO中流的概念,但Channel是全双工的,Selector是选择器,主要用于监听注册在他上面的Channel,Selector只适用于网络IO,对于文件IO,并没有Selector的概念。

下面看两个基于BIO和NIO开发的Demo

  • BIO
package test.netty.bio;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;

public class TimeServer {

    public static void main(String[] args) throws Exception {

        int port = 8080;

        ServerSocket serverSocket = null ;
        try {
            serverSocket = new ServerSocket(port) ;
            System.out.println("Time server is started in port " + port);
            Socket socket = null ;
            while (true) {
                socket = serverSocket.accept();
                new Thread(new TimeServerHanler(socket)).start();
            }
        }finally {

        }

    }

    private static class TimeServerHanler implements Runnable {

        private Socket socket ;
        public TimeServerHanler(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            BufferedReader in = null;
            PrintWriter out = null;
            try {
                in = new BufferedReader(new InputStreamReader(socket.getInputStream())) ;
                out = new PrintWriter(socket.getOutputStream());

                String currentTime ;
                String body ;

                //while (true) {
                    body = in.readLine() ;
                    /*if(Objects.isNull(body)) {
                        break;
                    }*/
                //}
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
                currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? dateFormat.format(new Date()) : "BAD ORDER";
                out.println(currentTime);
            }catch (Exception ex) {
                try {
                    in.close();
                    out.close();
                    socket.close();
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}



package test.netty.bio;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;

public class TimeClient {

    public static void main(String[] args) throws Exception {

        Socket socket = new Socket("127.0.0.1", 8080);
        //socket.bind(new InetSocketAddress(8080));

        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())) ;
        PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
        printWriter.write("QUERY TIME ORDER");
        //printWriter.ne
        printWriter.flush();
        System.out.println("send order 2 server success.");
        String response = bufferedReader.readLine();
        System.out.println("time server response : " + response);
    }
}

  • NIO
package test.netty.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;

public class TimeServer2 {


    /**
     * NIO Server
     * 1. 打开ServerSocketChannel,监听客户端连接
     * 2. 绑定监听端口,设置为非阻塞
     * 3. 创建Reactor线程,创建多路复用器,并启动线程
     * 4. 将ServerSocketChannel注册到Reactor线程 多路复用器上,监听accept事件
     * 5. 多路复用器在while中无限循环 accept事件
     * 6. 拿到客户端连接对应的SocketChannel
     * 7. 将SocketChannel也设置为非阻塞模式
     * 8. 将SocketChannel注册到Reactor线程的Selector上,并监听read事件
     * 9. 读取数据到bytebuffer
     * 10. 把数据交给业务线程池进行处理
     * 11. 将encode后的pojo通过SocketChannel写回给客户端
     * */

    public static void main(String[] args) {
        MultiplexerTimeServer multiplexerTimeServer = new MultiplexerTimeServer(8080);
        //该线程为Reactor线程
        new Thread(multiplexerTimeServer,"MultiplexerServer").start();
    }


    private static class MultiplexerTimeServer implements Runnable {

        //Reactor Selector
        private Selector selector ;
        //Server Main Thread
        private ServerSocketChannel serverSocketChannel ;

        private volatile boolean stop;

        public MultiplexerTimeServer(int port) {
            init(port);
        }

        //init server
        public void init(int port) {
            try {
                /**
                 *
                 * */
                serverSocketChannel = ServerSocketChannel.open();
                selector = Selector.open();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值