Redis网络通信模型

news2024/9/21 0:43:06

1.1. Java IO读写原理

不管是Socket的读写仍是文件的读写,在Java层面的应用开发或者是linux系统底层开发,都属于输入input和输出output的处理,简称为IO读写。在原理上和处理流程上,都是一致的。区别在于参数的不一样。用户程序进行IO的读写,基本上会用到read&write两大系统调用。可能不一样操做系统,名称不彻底同样,可是功能是同样的。
先强调一个基础知识:read系统调用,并非把数据直接从物理设备,读数据到内存。write系统调用,也不是直接把数据,写入到物理设备。read系统调用,是把数据从内核缓冲区复制到进程缓冲区;而write系统调用,是把数据从进程缓冲区复制到内核缓冲区。这个两个系统调用,都不负责数据在内核缓冲区和磁盘之间的交换。底层的读写交换,是由操做系统kernel内核完成的。

1.1.1. 内核缓冲与进程缓冲区

缓冲区的目的,是为了减小频繁的系统IO调用。你们都知道,系统调用须要保存以前的进程数据和状态等信息,而结束调用以后回来还须要恢复以前的信息,为了减小这种损耗时间、也损耗性能的系统调用,因而出现了缓冲区。
有了缓冲区,操做系统使用read函数把数据从内核缓冲区复制到进程缓冲区,write把数据从进程缓冲区复制到内核缓冲区中。等待缓冲区达到必定数量的时候,再进行IO的调用,提高性能。至于何时读取和存储则由内核来决定,用户程序不须要关心。
在linux系统中,系统内核也有个缓冲区叫作内核缓冲区。每一个进程有本身独立的缓冲区,叫作进程缓冲区。
因此,用户程序的IO读写程序,大多数状况下,并无进行实际的IO操做,而是在读写本身的进程缓冲区。

1.1.2. java IO读写的底层流程

用户程序进行IO的读写,基本上会用到系统调用read&write,read把数据从内核缓冲区复制到进程缓冲区,write把数据从进程缓冲区复制到内核缓冲区,它们不等价于数据在内核缓冲区和磁盘之间的交换。
image.png
首先看看一个典型Java 服务端处理网络请求的典型过程:
(1)客户端请求
Linux经过网卡,读取客户断的请求数据,将数据读取到内核缓冲区。
(2)获取请求数据
服务器从内核缓冲区读取数据到Java进程缓冲区。
(1)服务器端业务处理
Java服务端在本身的用户空间中,处理客户端的请求。
(2)服务器端返回数据
Java服务端已构建好的响应,从用户缓冲区写入系统缓冲区。
(3)发送给客户端
Linux内核经过网络 I/O ,将内核缓冲区中的数据,写入网卡,网卡经过底层的通信协议,会将数据发送给目标客户端。

1.2. 四种主要的IO模型

服务器端编程常常须要构造高性能的IO模型,常见的IO模型有四种:
(1)同步阻塞IO(Blocking IO)
首先,解释一下这里的阻塞与非阻塞:
阻塞IO,指的是须要内核IO操做完全完成后,才返回到用户空间,执行用户的操做。阻塞指的是用户空间程序的执行状态,用户空间程序需等到IO操做完全完成。传统的IO模型都是同步阻塞IO。在java中,默认建立的socket都是阻塞的。
其次,解释一下同步与异步:
同步IO,是一种用户空间与内核空间的调用发起方式。同步IO是指用户空间线程是主动发起IO请求的一方,内核空间是被动接受方。异步IO则反过来,是指内核kernel是主动发起IO请求的一方,用户线程是被动接受方。
(2)同步非阻塞IO(Non-blocking IO)
非阻塞IO,指的是用户程序不须要等待内核IO操做完成后,内核当即返回给用户一个状态值,用户空间无需等到内核的IO操做完全完成,能够当即返回用户空间,执行用户的操做,处于非阻塞的状态。
简单的说:阻塞是指用户空间(调用线程)一直在等待,并且别的事情什么都不作;非阻塞是指用户空间(调用线程)拿到状态就返回,IO操做能够干就干,不能够干,就去干的事情。
非阻塞IO要求socket被设置为NONBLOCK。
强调一下,这里所说的NIO(同步非阻塞IO)模型,并不是Java的NIO(New IO)库。
(3)IO多路复用(IO Multiplexing)
即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。
(4)异步IO(Asynchronous IO)
异步IO,指的是用户空间与内核空间的调用方式反过来。用户空间线程是变成被动接受的,内核空间是主动调用者。
这一点,有点相似于Java中比较典型的模式是回调模式,用户空间线程向内核空间注册各类IO事件的回调函数,由内核去主动调用。

1.3. 同步阻塞IO(Blocking IO)

在linux中的Java进程中,默认状况下全部的socket都是blocking IO。在阻塞式 I/O 模型中,应用程序在从IO系统调用开始,一直到到系统调用返回,这段时间是阻塞的。返回成功后,应用进程开始处理用户空间的缓存数据。
image.png
举个栗子,发起一个blocking socket的read读操做系统调用,流程大概是这样:
(1)当用户线程调用了read系统调用,内核(kernel)就开始了IO的第一个阶段:准备数据。不少时候,数据在一开始尚未到达(好比,尚未收到一个完整的Socket数据包),这个时候kernel就要等待足够的数据到来。
(2)当kernel一直等到数据准备好了,它就会将数据从kernel内核缓冲区,拷贝到用户缓冲区(用户内存),而后kernel返回结果。
(3)从开始IO读的read系统调用开始,用户线程就进入阻塞状态。一直到kernel返回结果后,用户线程才解除block的状态,从新运行起来。
因此,blocking IO的特色就是在内核进行IO执行的两个阶段,用户线程都被block了。
BIO的优势:
程序简单,在阻塞等待数据期间,用户线程挂起。用户线程基本不会占用 CPU 资源。
BIO的缺点:
通常状况下,会为每一个链接配套一条独立的线程,或者说一条线程维护一个链接成功的IO流的读写。在并发量小的状况下,这个没有什么问题。可是,当在高并发的场景下,须要大量的线程来维护大量的网络链接,内存、线程切换开销会很是巨大。所以,基本上,BIO模型在高并发场景下是不可用的。

public class BIOServerSocket {

    public static void main(String[] args) {
        ServerSocket serverSocket=null;

        try {
            serverSocket=new ServerSocket(8080);
            System.out.println("启动服务:监听端口:8080");
            //表示阻塞等待监听一个客户端连接,返回的socket表示连接的客户端信息
            while(true) {
                Socket socket = serverSocket.accept(); //连接阻塞
                System.out.println("客户端:" + socket.getPort());
                //inputstream是阻塞的(***)
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); //表示获取客户端的请求报文
                String clientStr = bufferedReader.readLine();
                System.out.println("收到客户端发送的消息:" + clientStr);
                BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                bufferedWriter.write("receive a message:" + clientStr + "\n");
                bufferedWriter.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(serverSocket!=null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

线程BIO

public class BIOServerSocketWithThread {

    static ExecutorService executorService= Executors.newFixedThreadPool(10);
    public static void main(String[] args) {
        ServerSocket serverSocket=null;
        try {
            serverSocket=new ServerSocket(8080);
            System.out.println("启动服务:监听端口:8080");
            //表示阻塞等待监听一个客户端连接,返回的socket表示连接的客户端信息
            while(true) {
                Socket socket = serverSocket.accept(); //连接阻塞
                System.out.println("客户端:" + socket.getPort());
                //IO变成了异步执行
                executorService.submit(new SocketThread(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(serverSocket!=null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
public class SocketThread implements Runnable{

    private Socket socket;

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

    @Override
    public void run() {
        try {
            //inputstream是阻塞的(***)
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); //表示获取客户端的请求报文
            String clientStr = bufferedReader.readLine();
            System.out.println("收到客户端发送的消息:" + clientStr);
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bufferedWriter.write("receive a message:" + clientStr + "\n");
            bufferedWriter.flush();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //TODO 关闭IO流
        }
    }
}

1.4. 同步非阻塞NIO(None Blocking IO)

在linux系统下,能够经过设置socket使其变为non-blocking。NIO 模型中应用程序在一旦开始IO系统调用,会出现如下两种状况:
(1)在内核缓冲区没有数据的状况下,系统调用会当即返回,返回一个调用失败的信息。
(2)在内核缓冲区有数据的状况下,是阻塞的,直到数据从内核缓冲复制到用户进程缓冲。复制完成后,系统调用返回成功,应用进程开始处理用户空间的缓存数据。image.png
举个栗子。发起一个non-blocking socket的read读操做系统调用,流程是这个样子:
(1)在内核数据没有准备好的阶段,用户线程发起IO请求时,当即返回。用户线程须要不断地发起IO系统调用。
(2)内核数据到达后,用户线程发起系统调用,用户线程阻塞。内核开始复制数据。它就会将数据从kernel内核缓冲区,拷贝到用户缓冲区(用户内存),而后kernel返回结果。
(3)用户线程才解除block的状态,从新运行起来。通过屡次的尝试,用户线程终于真正读取到数据,继续执行。
NIO的特色:
应用程序的线程须要不断的进行 I/O 系统调用,轮询数据是否已经准备好,若是没有准备好,继续轮询,直到完成系统调用为止。
NIO的优势:每次发起的 IO 系统调用,在内核的等待数据过程当中能够当即返回。用户线程不会阻塞,实时性较好。
NIO的缺点:须要不断的重复发起IO系统调用,这种不断的轮询,将会不断地询问内核,这将占用大量的 CPU 时间,系统资源利用率较低。
总之,NIO模型在高并发场景下,也是不可用的。通常 Web 服务器不使用这种 IO 模型。通常不多直接使用这种模型,而是在其余IO模型中使用非阻塞IO这一特性。java的实际开发中,也不会涉及这种IO模型。
再次说明,Java NIO(New IO) 不是IO模型中的NIO模型,而是另外的一种模型,叫作IO多路复用模型( IO multiplexing )。

NIO

public class NIOServerSocket {
    //NIO中的核心
    //channel
    //buffer
    //selector

    public static void main(String[] args) {
        try {
            ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false); //设置连接非阻塞
            serverSocketChannel.socket().bind(new InetSocketAddress(8080));
            while(true){
                //是非阻塞的
                SocketChannel socketChannel=serverSocketChannel.accept(); //获得一个客户端连接
                //                socketChannel.configureBlocking(false);//IO非阻塞
                if(socketChannel!=null){
                    ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
                    int i=socketChannel.read(byteBuffer);
                    Thread.sleep(10000);
                    byteBuffer.flip(); //反转
                    socketChannel.write(byteBuffer);
                }else{
                    Thread.sleep(1000);
                    System.out.println("连接位就绪");
                }
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }

    }
}

1.5. IO多路复用模型(I/O multiplexing)

如何避免同步非阻塞NIO模型中轮询等待的问题呢?这就是IO多路复用模型。(select/poll/epoll)
IO多路复用模型,就是经过一种新的系统调用,一个进程能够监视多个文件描述符,一旦某个描述符就绪(通常是内核缓冲区可读/可写),内核kernel可以通知程序进行相应的IO系统调用。
目前支持IO多路复用的系统调用,有 select,epoll等等。select系统调用,是目前几乎在全部的操做系统上都有支持,具备良好跨平台特性。epoll是在linux 2.6内核中提出的,是select系统调用的linux加强版本。
IO多路复用模型的基本原理就是select/epoll系统调用,单个线程不断的轮询select/epoll系统调用所负责的成百上千的socket链接,当某个或者某些socket网络链接有数据到达了,就返回这些能够读写的链接。所以,好处也就显而易见了——经过一次select/epoll系统调用,就查询到到能够读写的一个甚至是成百上千的网络链接。
在这种模式中,首先不是进行read系统调动,而是进行select/epoll系统调用。固然,这里有一个前提,须要将目标网络链接,提早注册到select/epoll的可查询socket列表中。而后,才能够开启整个的IO多路复用模型的读流程。
(1)进行select/epoll系统调用,查询能够读的链接。kernel会查询全部select的可查询socket列表,当任何一个socket中的数据准备好了,select就会返回。
当用户进程调用了select,那么整个线程会被block(阻塞掉)。
(2)用户线程得到了目标链接后,发起read系统调用,用户线程阻塞。内核开始复制数据。它就会将数据从kernel内核缓冲区,拷贝到用户缓冲区(用户内存),而后kernel返回结果。
(3)用户线程才解除block的状态,用户线程终于真正读取到数据,继续执行。
多路复用IO的特色:
IO多路复用模型,创建在操做系统kernel内核可以提供的多路分离系统调用select/epoll基础之上的。多路复用IO须要用到两个系统调用(system call), 一个select/epoll查询调用,一个是IO的读取调用。
和NIO模型类似,多路复用IO须要轮询。负责select/epoll查询调用的线程,须要不断的进行select/epoll轮询,查找出能够进行IO操做的链接。
另外,多路复用IO模型与前面的NIO模型,是有关系的。对于每个能够查询的socket,通常都设置成为non-blocking模型。只是这一点,对于用户程序是透明的(不感知)。
多路复用IO的优势:
用select/epoll的优点在于,它能够同时处理成千上万个链接(connection)。与一条线程维护一个链接相比,I/O多路复用技术的最大优点是:系统没必要建立线程,也没必要维护这些线程,从而大大减少了系统的开销。
Java的NIO(new IO)技术,使用的就是IO多路复用模型。在linux系统上,使用的是epoll系统调用。
多路复用IO的缺点:
本质上,select/epoll系统调用,属于同步IO,也是阻塞IO。都须要在读写事件就绪后,本身负责进行读写,也就是说这个读写过程是阻塞的。
如何充分的解除线程的阻塞呢?那就是异步IO模型。
多路复用模型

NIO之Selector

public class NIOSelectorServerSocket implements Runnable{

    Selector selector;
    ServerSocketChannel serverSocketChannel;

    public NIOSelectorServerSocket(int port) throws IOException {
        selector=Selector.open();
        serverSocketChannel=ServerSocketChannel.open();
        //如果采用selector模型,必须要设置非阻塞
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    @Override
    public void run() {
        while(!Thread.interrupted()){
            try {
                selector.select(); //阻塞等待事件就绪
                Set selected=selector.selectedKeys(); //事件列表
                Iterator it=selected.iterator();
                while(it.hasNext()){
                    //说明有连接进来
                    dispatch((SelectionKey) it.next());
                    it.remove();//移除当前就绪的事件
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    private void dispatch(SelectionKey key) throws IOException {
        if(key.isAcceptable()){ //是连接事件?
            register(key);
        }else if(key.isReadable()){ //读事件
            read(key);
        }else if(key.isWritable()){ //写事件
            //TODO
        }
    }
    private void register(SelectionKey key) throws IOException {
        ServerSocketChannel channel= (ServerSocketChannel) key.channel(); //客户端连接
        SocketChannel socketChannel=channel.accept(); //获得客户端连接
        socketChannel.configureBlocking(false);
        socketChannel.register(selector,SelectionKey.OP_READ);
    }
    private void read(SelectionKey key) throws IOException {
        //得到的是socketChannel
        SocketChannel channel= (SocketChannel) key.channel();
        ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
        channel.read(byteBuffer);
        System.out.println("Server Receive Msg:"+new String(byteBuffer.array()));
    }

    public static void main(String[] args) throws IOException {
        NIOSelectorServerSocket selectorServerSocket=new NIOSelectorServerSocket(8080);
        new Thread(selectorServerSocket).start();
    }
}

单线程的Reactor模型(Redis6.0前)

Reactor

public class Reactor implements Runnable{

    private final Selector selector;
    private final ServerSocketChannel serverSocketChannel;

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

    @Override
    public void run() {
        while(!Thread.interrupted()){
            try {
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while(iterator.hasNext()){
                    dispatch(iterator.next());
                    iterator.remove();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    private void dispatch(SelectionKey key){
        //可能拿到的对象有两个
        // Acceptor
        // Handler
        Runnable runnable=(Runnable)key.attachment();
        if(runnable!=null){
            runnable.run(); //
        }
    }
}

Acceptor

public class Acceptor implements Runnable{

    private final Selector selector;
    private final ServerSocketChannel serverSocketChannel;

    public Acceptor(Selector selector, ServerSocketChannel serverSocketChannel) {
        this.selector = selector;
        this.serverSocketChannel = serverSocketChannel;
    }

    @Override
    public void run() {
        SocketChannel channel;

        try {
            channel=serverSocketChannel.accept();//得到一个客户端连接
            System.out.println(channel.getRemoteAddress()+":收到一个客户端连接");
            channel.configureBlocking(false);
            channel.register(selector, SelectionKey.OP_READ,new Handler(channel));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

Handler

public class Handler implements Runnable{
    SocketChannel channe;

    public Handler(SocketChannel channe) {
        this.channe = channe;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"------");
        ByteBuffer buffer=ByteBuffer.allocate(1024);
        /*try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
        int len=0,total=0;
        String msg="";
        try {
            do {
                len = channe.read(buffer);
                if(len>0){
                    total+=len;
                    msg+=new String(buffer.array());
                }
            } while (len > buffer.capacity());
            System.out.println("total:"+total);

            //msg=表示通信传输报文
            //耗时2s
            //登录: username:password
            //ServetRequets: 请求信息
            //数据库的判断
            //返回数据,通过channel写回到客户端

            System.out.println(channe.getRemoteAddress()+": Server receive Msg:"+msg);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(channe!=null){
                try {
                    channe.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

MAIN

public class ReactorMain {
    public static void main(String[] args) throws IOException {
        new Thread(new Reactor(8080),"Main-Thread").start();
    }
}

多线程Reactor模型(6.0+)(线程处理采用隔离机制)

MultiReactor

public class MultiReactor implements Runnable{

    private final Selector selector;
    private final ServerSocketChannel serverSocketChannel;

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

    @Override
    public void run() {
        while(!Thread.interrupted()){
            try {
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while(iterator.hasNext()){
                    dispatch(iterator.next());
                    iterator.remove();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    private void dispatch(SelectionKey key){
        //可能拿到的对象有两个
        // Acceptor
        // Handler
        Runnable runnable=(Runnable)key.attachment();
        if(runnable!=null){
            runnable.run(); //
        }
    }
}

MultiAcceptor

public class MultiAcceptor implements Runnable{

    private final Selector selector;
    private final ServerSocketChannel serverSocketChannel;

    public MultiAcceptor(Selector selector, ServerSocketChannel serverSocketChannel) {
        this.selector = selector;
        this.serverSocketChannel = serverSocketChannel;
    }

    @Override
    public void run() {
        SocketChannel channel;

        try {
            channel=serverSocketChannel.accept();//得到一个客户端连接
            System.out.println(channel.getRemoteAddress()+":收到一个客户端连接");
            channel.configureBlocking(false);
            channel.register(selector, SelectionKey.OP_READ,new MutilDispatchHandler(channel));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

MutilDispatchHandler

public class MutilDispatchHandler implements Runnable{

    SocketChannel channel;

    private Executor executor= Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    public MutilDispatchHandler(SocketChannel channel) {
        this.channel = channel;
    }

    @Override
    public void run() {
        processor();
    }
    private void processor(){
        executor.execute(new ReaderHandler(channel));
    }
    static class ReaderHandler implements Runnable{
        private SocketChannel channel;

        public ReaderHandler(SocketChannel channel) {
            this.channel = channel;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+":-----");
            ByteBuffer buffer=ByteBuffer.allocate(1024);
            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int len=0,total=0;
            String msg="";
            try {
                do {
                    len = channel.read(buffer);
                    if(len>0){
                        total+=len;
                        msg+=new String(buffer.array());
                    }
                } while (len > buffer.capacity());
                System.out.println("total:"+total);

                //msg=表示通信传输报文
                //耗时2s
                //登录: username:password
                //ServetRequets: 请求信息
                //数据库的判断
                //返回数据,通过channel写回到客户端
                System.out.println(channel.getRemoteAddress()+": Server receive Msg:"+msg);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if(channel!=null){
                    try {
                        channel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

MultiReactorMain

public class MultiReactorMain {
    public static void main(String[] args) throws IOException {
        new Thread(new MultiReactor(8080),"Main-Thread").start();
    }
}

1.6. 异步IO模型(asynchronous IO)

如何进一步提高效率,解除最后一点阻塞呢?这就是异步IO模型,全称asynchronous I/O,简称为AIO。
AIO的基本流程是:用户线程经过系统调用,告知kernel内核启动某个IO操做,用户线程返回。kernel内核在整个IO操做(包括数据准备、数据复制)完成后,通知用户程序,用户执行后续的业务操做。
kernel的数据准备是将数据从网络物理设备(网卡)读取到内核缓冲区;kernel的数据复制是将数据从内核缓冲区拷贝到用户程序空间的缓冲区。
image.png
(1)当用户线程调用了read系统调用,马上就能够开始去作其它的事,用户线程不阻塞。
(2)内核(kernel)就开始了IO的第一个阶段:准备数据。当kernel一直等到数据准备好了,它就会将数据从kernel内核缓冲区,拷贝到用户缓冲区(用户内存)。
(3)kernel会给用户线程发送一个信号(signal),或者回调用户线程注册的回调接口,告诉用户线程read操做完成了。
(4)用户线程读取用户缓冲区的数据,完成后续的业务操做。
异步IO模型的特色:
在内核kernel的等待数据和复制数据的两个阶段,用户线程都不是block(阻塞)的。用户线程须要接受kernel的IO操做完成的事件,或者说注册IO操做完成的回调函数,到操做系统的内核。因此说,异步IO有的时候,也叫作信号驱动 IO 。
异步IO模型缺点:
须要完成事件的注册与传递,这里边须要底层操做系统提供大量的支持,去作大量的工做。
目前来讲, Windows 系统下经过 IOCP 实现了真正的异步 I/O。可是,就目前的业界形式来讲,Windows 系统,不多做为百万级以上或者说高并发应用的服务器操做系统来使用。
而在 Linux 系统下,异步IO模型在2.6版本才引入,目前并不完善。因此,这也是在 Linux 下,实现高并发网络编程时都是以 IO 复用模型模式为主。

小结一下:

四种IO模型,理论上越日后,阻塞越少,效率也是最优。在这四种 I/O 模型中,前三种属于同步 I/O,由于其中真正的 I/O 操做将阻塞线程。只有最后一种,才是真正的异步 I/O 模型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2097839.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kaggle竞赛——手写数字识别(Digit Recognizer)

目录 1. 数据集介绍2. 数据分析3. 数据处理与封装3.1 数据集划分3.2 将数据转为tensor张量3.3 数据封装 4. 模型训练4.1 定义功能函数4.1 resnet18模型4.3 CNN模型4.4 FCNN模型 5. 结果分析5.1 混淆矩阵5.2 查看错误分类的样本 6. 加载最佳模型7. 参考文献 本次手写数字识别使用…

【书生2.5】XTuner 微调个人小助手认知

XTuner 微调个人小助手认知 【Intern Studio的gpu不足。本实验使用自有服务器】 1 环境安装 # 创建虚拟环境 conda create -n xtuner python3.10 -y# 激活虚拟环境&#xff08;注意&#xff1a;后续的所有操作都需要在这个虚拟环境中进行&#xff09; conda activate xtuner…

同样128个内核,AMD霄龙9755性能翻倍:Zen 5架构下的性能飞跃

近日&#xff0c;AMD在服务器处理器领域再次展示了其强大的技术实力&#xff0c;随着AMD EPYC“Turin”处理器发布日期的临近&#xff0c;其基准测试结果也开始浮出水面。硬件爱好者博主9550pro近期分享了AMD 128核EPYC 9755“Turin”处理器在7zip压缩/解压缩基准测试中的跑分数…

深圳MES系统在电子制造业中的应用体现

深圳是中国电子制造业的重要基地&#xff0c;许多电子制造企业在深圳地区都在应用MES系统来优化生产管理、提高生产效率和产品质量。深圳MES系统在电子制造业中的应用主要体现在以下几个方面&#xff1a; 生产计划管理&#xff1a;电子制造企业通常面临订单量大、产品种类多的情…

【知识图谱】4、LLM大模型结合neo4j图数据库实现AI问答的功能

昨天写了一篇文章&#xff0c;使用fastapi直接操作neo4j图数据库插入数据的例子&#xff0c; 本文实现LLM大模型结合neo4j图数据库实现AI问答功能。 废话不多说&#xff0c;先上代码 import gradio as gr from fastapi import FastAPI, HTTPException, Request from pydantic…

分享使用智狐联创AI助手生成的一个食品选择器网页

先看效果&#xff1a; 使用的是智狐超强模型&#xff0c;只有一个html网页&#xff0c;点击开始会有动画的选择动画。效果很不错&#xff0c;你可以更改成任意类似场景使用&#xff0c;如&#xff1a;抽奖等等。感兴趣的可以去搜索官网试试&#xff0c;也有免费模型。https://w…

【云故事探索】NO.8:揭秘餐饮行业龙头 SaaS 厂商神州商龙的全栈可观测实践

云布道师 天津市神州商龙科技股份有限公司成立于1998年&#xff0c;是一家专为餐饮行业提供数字化整体解决方案及咨询业务的高新技术企业。秉承着“产品是第一生产力”的发展理念&#xff0c;神州商龙凭借过硬的产品与服务质量&#xff0c;为呷哺呷哺、大董、新荣记、刘一手、巴…

需方软件供应链安全保障要求及开源场景对照自评表(下)

国标《信息安全技术 软件供应链安全要求》确立了软件供应链安全目标&#xff0c;规定了软件供应链安全风险管理要求和供需双方的组织管理和供应活动管理安全要求。 开源软件供应链作为软件供应链的一种特殊形式&#xff0c;该国标亦适用于指导开源软件供应链中的供需双方开展组…

完美解决LBP2900打印机安装驱动提示无法识别USB及连接错误等问题(附Win11全新安装支持及卸载方案)

目录 前言驱动获取方法简易全新安装方法安装完成后的验证方法常见驱动卸载方法 前言 LBP2900打印机虽然属于经典老旧款&#xff0c;但依旧好用不过时。老早之前也分享过心相关的解决方案&#xff0c;请戳&#xff1a;&#x1f449;旧版解决方案。但因年代久远还diss部分系统不通…

ssm“最多跑一次”微信小程序论文源码调试讲解

2系统相关技术 2.1 Java语言简介 Java是由SUN公司推出&#xff0c;该公司于2010年被oracle公司收购。Java本是印度尼西亚的一个叫做爪洼岛的英文名称&#xff0c;也因此得来java是一杯正冒着热气咖啡的标识。Java语言在移动互联网的大背景下具备了显著的优势和广阔的前景&…

HarmonyOS开发实战( Beta5版)应用TSJS高性能编程工具最佳实践

概述 本文参考业界标准&#xff0c;并结合应用TS&JS部分的性能优化实践经验&#xff0c;从应用编程指南、高性能编程实践、性能优化调试工具等维度&#xff0c;为应用开发者提供参考指导&#xff0c;助力开发者开发出高性能的应用。 本文主要提供TS&JS高性能编程实践…

什么是短视频矩阵?一个人能做好短视频矩阵营销吗?

很多人认为做短视频矩阵就是多账号、多发视频就可以了&#xff0c;但其实做短视频矩阵&#xff0c;并不仅仅是更多账号更多视频那么简单&#xff0c;它的核心在于搭建一个全方位的内容传播方式。这种方式包括三个方面&#xff1a;账号矩阵、平台矩阵和内容矩阵。 首先是账号矩阵…

TikTok运营:IP地址如何影响TikTok的内容运营?

TikTok作为外贸人宣传推广的重要平台&#xff0c;其运营成效与产品的实际转化率息息相关。然而&#xff0c;在TikTok的运营过程中&#xff0c;一个看似微不足道的元素—IP地址&#xff0c;却扮演着至关重要的角色。本文将深入探讨TikTok运营中IP地址的重要性&#xff0c;揭示其…

炫700头猪!所有长久的关系,都是讲条件的——早读(逆天打工人爬取热门微信文章解读)

A股呀A股你好狠呀 引言Python 代码第一篇 洞见 所有长久的关系&#xff0c;都是讲条件的第二篇 华为小黑子&#xff1f;结尾 &#xff08;这哥们是来炫他家的700头猪的吧&#xff0c;等我有钱了&#xff0c;包圆咯&#xff09; 引言 周末工作一天 休息一天 周六上班 那天晚上…

8Manage PM:掌握高效项目进度跟踪的关键策略

在负责管理众多项目的同时&#xff0c;管理人员最不希望遭遇因项目进度滞后而产生额外负担。 项目可能会因资源分配不当、范围蔓延以及其他意外中断等因素而遭受延误&#xff0c;有时这种延误甚至可能是长期的。 因此&#xff0c;掌握项目进度跟踪的方法相当重要。在整个项目…

Unity(2022.3.41LTS) - UI详细介绍-InputField(输入字段)

目录 零.简介 一、基本功能与用途 二、组件介绍 三、使用方法 四、优化和注意事项 零.简介 在 Unity 中&#xff0c;输入字段&#xff08;Input Field&#xff09;是一个非常实用的用户界面组件&#xff0c;以下是更详细的介绍&#xff0c;包括组件方面的深入分析&#x…

深入理解MySQL慢查询优化(2) -- SQL的执行流程

要优化一条SQL语句&#xff0c;要先理解SQL操作的执行流程 1. 不同SQL操作的执行流程 1.1 order by order by用于排序&#xff0c;如果用于排序的列上没有索引&#xff0c;就需要把整张表加载进内存进行排序&#xff0c;非常耗时。如果有索引&#xff0c;因为B树存储的数据本…

计算机毕业设计选题推荐-高校一卡通系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

史记——我与历史的缘妙

究天人之际&#xff0c;通古今之变&#xff0c;成一家之言。 注解&#xff1a;这句话出自司马迁《史记》之《报任安书》。意思是通过“史实”现象揭示本质,探究自然现象和人类社会之间的相依相对关系。通晓从古到今的社会的各种发展演变,进而寻找历代王朝兴衰成败之道理。通过…

【Unity编辑器扩展】SpriteAltas资源一键转换为TMP_SpriteAsset或Sprite图集

【Unity编辑器扩展】艺术字/自定义图片字体/TextMeshPro艺术字生成工具_unity 艺术字-CSDN博客 博文工具源码见GF_X自动化游戏开发框架&#xff1a;GitHub - sunsvip/GF_X: Unity GameFramework HybridCLR&#xff0c;Includes several automated editor extension tools, an …