• netty中的传输


    终于在课设的闲时间把netty实战的四五章给解决了

    这里来记录一下第四章里面所讲的IO

    首先说到IO,我想,必须要先了解阻塞,非阻塞,同步和异步这四个词

    看到一个讲的很易懂的例子:https://www.cnblogs.com/George1994/p/6702084.html

    那么了解完这四个词,就到了IO了

    传统的IO,即阻塞IO

    也就是跟之前用socket编程那样,没有数据写入到来这边的线程就一直等待,直到数据到来然后再对数据进行处理,例如打印

    拿出以前的老代码

    public void bio(int port) throws IOException{
            final ServerSocket socket = new ServerSocket(port);
            try {
                //死循环,直到有连接再用线程将内容写入
                while (true){
                    Socket client = socket.accept();//阻塞
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            OutputStream out;
                            try {
                                out = client.getOutputStream();
                                out.write("hello world".getBytes(CharsetUtil.UTF_8));
                                out.flush();
                                client.close();
                            }catch (IOException e){
                                e.printStackTrace();
                            }finally {
                                try {
                                    client.close();
                                }catch (IOException e){
                                    e.printStackTrace();
                                }
                            }
                        }
                    }).start();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    传统阻塞IO

    代码很容易懂,就是等客户端连接就往里面写入数据,每一个连接对应一个线程去处理,索性采用了多线程,如果是单线程,系统就会阻塞在这里,

    多线程CPU在这里能释放做更多事情,的确是个很好的模型,至少我之前一直是这么觉得的...但是似乎这里忽略了一些问题,那就是连接数量的

    问题,如果是小量的连接数还是好的,但是如果往大了增加连接数,打个比方一万,那么就会有一万个线程,那么问题就显而易见了,内存这上面就

    已经有瓶颈了,而且,线程数量增大,线程之间的切换也会成为一个大问题,因为线程切换的成本很高,有可能线程切换的时间比执行的时间还长,

    导致更严重的问题

    所以可以知道的是,如果连接量小,那么这个模型是没问题的,如果大了,那么这个就无能为力了

    再来说非阻塞IO,即NIO

    这是netty实战上的非阻塞IO代码

    public void serve(int port) throws IOException {
            ServerSocketChannel serverChannel = ServerSocketChannel.open();
            serverChannel.configureBlocking(false);//channel设置为非阻塞模式
            ServerSocket ss = serverChannel.socket();
            InetSocketAddress address = new InetSocketAddress(port);
            ss.bind(address);
            Selector selector = Selector.open();
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
            final ByteBuffer msg = ByteBuffer.wrap("Hi!
    ".getBytes());
            for (;;){
                try {
                    selector.select();//这个函数是阻塞的
                } catch (IOException ex) {
                    ex.printStackTrace();
                    //handle exception
                    break;
                }
                Set<SelectionKey> readyKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = readyKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    try {
                        if (key.isAcceptable()) {
                            ServerSocketChannel server =
                                    (ServerSocketChannel) key.channel();
                            SocketChannel client = server.accept();
                            client.configureBlocking(false);
                            client.register(selector, SelectionKey.OP_WRITE |
                                    SelectionKey.OP_READ, msg.duplicate());//注册到selector中
                            System.out.println(
                                    "Accepted connection from " + client);
                        }
                        if (key.isWritable()) {
                            SocketChannel client =
                                    (SocketChannel) key.channel();
                            ByteBuffer buffer =
                                    (ByteBuffer) key.attachment();
                            while (buffer.hasRemaining()) {
                                if (client.write(buffer) == 0) {
                                    break;
                                }
                            }
                            client.close();
                        }
                    } catch (IOException ex) {
                        key.cancel();
                        try {
                            key.channel().close();
                        } catch (IOException cex) {
                            // ignore on close
                        }
                    }
                }
            }
        }
    View Code

    注册当这几个事件到来的时候所对应的处理器。然后在合适的时机告诉事件选择器:对此事件感兴趣。对于写操作,就是写不出去的时候对

    写事件感兴趣;对于读操作,就是完成连接和系统没有办法承载新读入的数据的时,对于accept,一般是服务器刚启动的时候;而对于connect,

    一般是connect失败需要重连或者异步调用connect的时候,NIO的读写函数可以立刻返回,这就不用另开线程去等待结果了,如果一个连接不能读

    写(socket.read()返回0或者socket.write()返回0),就把这件事记下来,记录的方式通常是在Selector上注册标记位,然后切换到其它就绪的连

    接(channel)继续进行读写。

    这样的优点:相比于阻塞模型,非阻塞不用再等待任务,而是把时间花费到其它任务上,也就是这个当前线程同时处理多个任务,但是也有缺点:

    那就是导致任务完成的响应延迟增大了,因为每隔一段时间才去执行询问的动作,但是任务万一在两次询问之间的时间间隔内完成,就会导致整体

    数据吞吐量的降低。影响用户体验

    Netty中的阻塞和非阻塞

    以上是java 里面的NIO,可以看到从阻塞改写到非阻塞这其中还是有很多地方要改的,势必会给带来很多麻烦,下面继续回到netty

    而在netty中就不会出现这样的事情了,因为只需改变EventLoopGroup的种类,分别对应的OioEventLoopGroup和NioEventLoppGroup

    和channel的种类,对应OioServerSocketChannel和NioServerSocketChannel,其余地方基本不变

    Epool,专用于linux的本地非阻塞传输

    jdk在linuxl中的NOI的实现上采用了不同的办法,就是使用了epoll调用,他有着比select和poll更好的性能,同时也是linux非阻塞网络编程的事实标准

    而且如果程序是运行在linux,也不需要改动很多代码,就跟上面的例子一样,将NioEventLoopGroup和NioServerSocketChannel改成EpollEventLoopGroup

    和EpollServerSocketChannel,这一改动在高负载的情况下能比NIO有更好的表现

    JVM内部的传输

    用于在同一个虚拟机中的客户端和服务端的通信,这个过程中的SocketAddress没有绑定具体的物理网络地址,只要服务器在运行,他就会存储在注册表里,channel关闭

    即从表里注销,这里的传输不接收网络流量,其实也能猜出来,毕竟本地,所以他不能和其他我们使用的socket传输互相操作,所以如果客户端想使用本机,即同JVM中的服

    务端就必须通过他实现

    小结笔记:

    IO访问过程:(网络IO本质上就是socket的读取)

     对于socket流,就是等待网络上的数据到达,然后复制到图中的缓冲区,然后复制到应用进程

     JVM内部通信还有其他几种IO那里还没来得及找例子做一做,找时间再补上,如果错误,还请多多指出

  • 相关阅读:
    [LeetCode] 1898. Maximum Number of Removable Characters
    [LeetCode] 1897. Redistribute Characters to Make All Strings Equal
    [LeetCode] 1400. Construct K Palindrome Strings
    235. 二叉搜索树的最近公共祖先
    349. 两个数组的交集
    海量数据TOPK 问题
    121. 买卖股票的最佳时机
    删除数组中为0元素
    这行字符串中出现频率最高的字符
    50. Pow(x, n)
  • 原文地址:https://www.cnblogs.com/Yintianhao/p/10205990.html
Copyright © 2020-2023  润新知