当前位置: 首页 > 知识库问答 >
问题:

如何应对传统Java蔚来汽车的缓慢消费?

侯善
2023-03-14

所以,我一直在复习我对传统Java非阻塞应用编程接口的理解。我对应用编程接口的几个方面有点困惑,这些方面似乎迫使我手动处理背压。

例如,WritableByteChannel.write(ByteBuffer)上的留档说明如下:

除非另有说明,写入操作只有在写入所有请求的字节后才会返回。某些类型的通道,根据其状态,可能只写入部分字节,也可能根本不写入。例如,处于非阻塞模式的套接字通道不能写入超过套接字输出缓冲区中空闲字节的任何字节。

现在,考虑一下Ron Hitchen的书中的例子:Java NIO。

在下面的代码中,Ron试图演示如何在非阻塞套接字应用程序中实现回显响应(对于上下文,这里是完整示例的要点)。

//Use the same byte buffer for all channels. A single thread is
//servicing all the channels, so no danger of concurrent access.
private ByteBuffer buffer = ByteBuffer.allocateDirect(1024);

protected void readDataFromSocket(SelectionKey key) throws Exception {
    var channel = (SocketChannel) key.channel();
    buffer.clear(); //empty buffer

    int count;
    while((count = channel.read(buffer)) > 0) {
        buffer.flip(); //make buffer readable

        //Send data; don't assume it goes all at once
        while(buffer.hasRemaining()) {
            channel.write(buffer);
        }

        //WARNING: the above loop is evil. Because
        //it's writing back to the same nonblocking
        //channel it read the data from, this code
        //can potentially spin in a busy loop. In real life
        //you'd do something more useful than this.

        buffer.clear(); //Empty buffer
    }

    if(count < 0) {
        //Close channel on EOF, invalidates the key
        channel.close();
    }
}

我的困惑在于while循环写入输出通道流:

//Send data; don't assume it goes all at once
while(buffer.hasRemaining()) {
   channel.write(buffer);
}

我真的很困惑NIO是如何在这里帮助我的。当然,根据可写字节通道的描述,代码可能没有阻塞。write(ByteBuffer),因为如果输出通道由于其缓冲区已满而无法接受更多字节,则此写入操作不会阻塞,它只会不写任何内容,返回,并且缓冲区保持不变。但是——至少在本例中——当我们等待客户端处理这些字节时,没有简单的方法可以将当前线程用于更有用的内容。尽管如此,如果我只有一个线程,其他请求将堆积在选择器中,而这个while循环浪费了宝贵的cpu周期“等待”客户端缓冲区打开一些空间。没有明显的方法在输出通道中注册就绪。还是有?

因此,假设我试图实现一个需要将大量字节发送回客户端的响应(例如文件下载),并且假设客户端的带宽非常低,或者与服务器缓冲区相比,输出缓冲区非常小,那么这个文件的发送可能需要很长时间。似乎我们需要使用我们宝贵的cpu周期来处理其他客户端,而我们缓慢的客户端正在咀嚼我们的文件下载字节。

如果我们在输入通道上准备就绪,但在输出通道上没有准备就绪,那么这个线程似乎可以免费使用宝贵的CPU周期。它没有被阻塞,但好像是因为线程在不确定的时间段内做无关紧要的CPU工作是无用的。

为了解决这个问题,Hitchens的解决方案是将这段代码移动到一个新线程——这只是将问题移动到另一个地方——然后我想知道,如果我们每次需要处理一个长时间运行的请求时都必须打开一个线程,那么在处理这种请求时,NIOJava比普通IO更好吗?

我还不清楚如何使用传统的JavaNIO来处理这些场景。在这种情况下,用更少的资源做更多事情的promise似乎会被打破。如果我正在实现一个HTTP服务器,我不知道向客户端提供响应需要多长时间,该怎么办?

这个例子似乎有很大的缺陷,一个好的解决方案设计也应该考虑监听输出通道上的就绪情况,例如:

registerChannel(selector, channel, SelectionKey.OP_WRITE);

但是这个解决方案看起来会是什么样子呢?我一直在试图找到解决方案,但我不知道如何恰当地实现它。

我不是在寻找像Netty这样的其他框架,我的目的是理解核心JavaAPI。我很感激任何人能够分享任何见解,任何关于使用传统Java NIO处理这种背压场景的正确方法的想法。

共有1个答案

赵越
2023-03-14

NIO的非阻塞模式使线程能够请求从通道读取数据,如果当前没有数据可用,则只获取当前可用的数据,或者什么都不获取。线程可以继续处理其他事情,而不是在数据可供读取之前保持阻塞。

非阻塞写入也是如此。线程可以请求将一些数据写入通道,但不能等待它完全写入。然后线程可以继续同时做其他事情。

当未在IO调用中阻塞时,线程将空闲时间花在什么上,通常是同时在其他通道上执行IO。也就是说,一个线程现在可以管理多个输入和输出通道。

因此,我认为您需要依赖于解决方案的设计,使用设计模式来处理这个问题,也许**任务或策略设计模式**是很好的候选者,根据您使用的框架或应用程序,您可以决定解决方案。

但在大多数情况下,您不需要自己实现它,因为它已经在Tomcat、Jetty等中实现了。

参考:非阻塞IO

 类似资料:
  • #非技术2023笔面经# 蔚来 面试经验贴! 岗位:交付运营 面试时长:电话面试了大概几分钟左右,通过后 会收到线下面试的邀请,大概一个小时左右嗷。 面试官:部门leader 和两个工作时间较久的部门员工 具体流程:1.电话面试 会相对简单一点嗷,在求职软件或公司官网上投递了简历或者意向,一般很快会有HR联系你大概1到3天的样子 。电话里先会问一些简单的问题大家不用很担心,一般是先让你做个简单的自

  •  #蔚来笔试##蔚来#蔚来汽车# 22/08/02 笔试:计算机视觉岗位 内容: 27道单选题+2道编程题 时间:90mins 单选题涉及C++、C语言、python、计算机网络基础等内容,范围较广,不会的比较多。花了很多时间思考。 第一道编程题比较简单,是求数组的转置数组,然后对应位置之差绝对值再求和。通过100%。 第二道设计十进制与二进制的转化,还有其他操作。时间不够了,没调试出来,通过0%

  • 业务(一面) hr自我介绍 自我介绍 之前一份实习做了什么事情,为什么离职? sql学的怎么样? 可以长时间实习吗?最少要求6个月时间?

  • 9.27蔚来汽车测开一面(20min) 自我介绍 能实习多久,每周几天 手撕代码:反转数组 手撕代码:创建链表并初始化 struct和class的区别是什么 常用的Linux命令 查进程的命令是什么 反问环节: (这么快?严重怀疑面试官对我不感兴趣或者KPI) 部门日常工作 什么是自动驾驶感知 需要加强的地方 #蔚来##蔚来汽车实习##测开##测开实习##测试开发#

  • 问的问题都比较深入,要求有项目基础,祝好🥰 1、自我介绍 2、你的产品上线后有没有关注用户反馈? 3、给客户交付时,如果产品能力还没ready,你会怎么办? 4、你们团队需求一般来源于哪里? 5、你们内容接入的音源都有哪些?如何做音源的选择? 6、你有参与过项目投标吗? 7、SOA架构你了解么,与传统EEA在开发流程、方法上有哪些区别? 8、为什么要做可见即可说,价值在哪?什么叫可见即可说? 9

  • 电话面,周一上午给我打电话约的周二下午 面试官拷打的挺多的,我还是第一次遇到这么不温柔的面试官 问题一个接着一个的抛 我太菜了给我问蒙了都 对深蓝汽车了解吗 接受工作地点在重庆吗 ==和equals的区别 Integer a=200和Integer b=200用equals比较和用==比较分别是true还是false等等一系列(接连拷打) 迭代器模式,为啥不用for循环 工厂模式和抽象工厂模式 r