java异步io_Java中的异步IO与异步请求处理

java异步io

In this article, I am trying to explain the difference between Async-IO and Async-Request processing in the HTTP request in the Java world.

在本文中,我试图解释Java世界中HTTP请求中Async-IO和Async-Request处理之间的区别。

In the pre-Java 1.4 world, Java provides an API to send/receive data over the network socket. The original authors of JVM mapped this API behavior to OS socket API, almost one to one.

在Java 1.4之前的版本中,Java提供了一个API,用于通过网络套接字发送/接收数据。 JVM的原始作者将此API行为几乎一对一地映射到OS套接字API。

So, what is the OS socket behaviour? OS provides Socket programming api, which has send/recv blocking call. Since java is just a process running on top of linux(OS), hence this java program has to use this blocking api provided by OS.

那么,操作系统套接字的行为是什么? OS提供了Socket编程api ,该API具有send / recv 阻止调用 。 由于Java只是在linux(OS)之上运行的进程,因此该Java程序必须使用OS提供的此阻塞api。

The world was happy and java developers started using the API to send/receive the data. But they had to keep one java thread for every socket(client).

全世界都很高兴,Java开发人员开始使用API​​发送/接收数据。 但是他们必须为每个套接字(客户端)保留一个Java线程。

Everybody was writing their own flavor of HTTP servers. Code sharing was becoming hard, the java world demanded a standardization.Enters the java servlet Spec.

每个人都在编写自己的HTTP服务器。 代码共享变得越来越困难,Java世界要求实现标准化。 输入Java Servlet规范。

Before moving on lets define few terms:

在继续之前,让我们先定义几个术语:

Java Server Developer: People who are using the java socket api and implementing http protocol like tomcat.

Java Server Developer :正在使用Java套接字api并实现诸如tomcat之类的http协议的人们。

java Application Developer: People who are building buisness application on top of tomcat.

Java应用程序开发人员:在Tomcat之上构建商务应用程序的人们。

GETTING BACK NOW

现在回来

Once the java servlet spec entered the world, it said:

Java Servlet规范进入世界后,它说:

Dear java server developers, please provide a method like below:

尊敬的Java服务器开发人员,请提供以下方法:

doGet(inputReq, OutPutRes)

so that java application developer can implement doGet and they can write their business logic. Once “application developer” wants to send the response, he can call OutPutRes.write().

这样Java应用程序开发人员就可以实现doGet并编写自己的业务逻辑。 一旦“应用程序开发人员”想要发送response ,他就可以调用OutPutRes.write().

A thing to Note:Since socket api is blocking, hence OutPutRes.write() is also blocking. Also, the additional limitation was that the response object is committed on doGet method exit.

注意事项:由于套接字api被阻塞,因此OutPutRes.write()也被阻塞。 另外,另一个限制是响应对象在doGet方法退出时提交。

Due to these limitations, people had to use one thread for processing one request.

由于这些限制,人们不得不使用一个线程来处理一个请求。

Time passed and the internet took over the world. one Thread per Request started to show limitations.

时间流逝,互联网占领了世界。 每个请求一个线程开始显示限制。

问题一: (Problem 1:)

The thread-per-request model fails when there are long pauses during the processing of each request.

当每个请求的处理过程中出现长时间的停顿时,每个请求线程模型将失败。

For Example: fetching data from sub-service take long time.

例如:从子服务中获取数据需要很长时间。

Under such a situation, the thread is mostly sitting idle and JVM can run out of thread easily.

在这种情况下,线程通常处于空闲状态,JVM可以很容易地用完线程。

问题2: (Problem 2:)

Things got even worse with http1.1 persistent connection. As with persistent connection, the underlying TCP connection will be kept alive and the server has to block one thread per connection.

使用http1.1持久连接,情况变得更糟。 与持久连接一样,基础TCP连接将保持活动状态,并且服务器必须为每个连接阻止一个线程。

But why does the server have to block one thread per connection?

但是,为什么服务器必须为每个连接阻塞一个线程?

But why does the server have to block one thread per connection?Since OS provides a blocking socket Recv api, the jvm has to call the OS blocking Recv method in order to listen for more requests on same tcp connection from the client.

但是,为什么服务器必须为每个连接阻塞一个线程? 由于OS提供了阻塞套接字Recv api,因此jvm必须调用OS阻塞Recv方法,以便在来自客户端的同一tcp连接上侦听更多请求。

世界要求解决方案! (The world demanded a solution!)

The First Solution came from the creator of JVM. They introduced NIO(ASYNC-IO). Nio is the non-blocking API for sending/receiving data over socket.

第一个解决方案来自JVM的创建者。 他们介绍了NIO( ASYNC-IO ) 。 Nio是用于通过套接字发送/接收数据的非阻塞API。

Some background: the OS along with blocking socket api also provides a non-blocking version of the socket api.

一些背景: 操作系统以及阻止套接字api也提供了套接字api的非阻止版本。

But how does the OS provide that .. Does it fork a thread internally and that thread gets blocked???

但是,操作系统如何提供该功能呢?它是否在内部派生了一个线程并且该线程被阻塞了?

The ANSWER is no… the OS instruct the hardware to interupt when there is data to read or write.

答案不是……当有数据需要读取或写入时,操作系统会指示硬件中断。

NIO allowed the java server developer” to tackle problem 2 of blocking one thread per TCP connection. With NIO being an HTTP persistent connection, the thread does not require it to block on recv call. Instead, it can now process it only when there is data to be processed. This allowed one thread to monitor/handle a large number of persistent connections.

NIO允许java服务器开发人员” 解决问题2每个TCP连接阻塞一个线程 。 由于NIO是HTTP持久连接,因此该线程不需要它在recv调用时阻塞。 相反,它现在只能在有要处理的数据时进行处理。 这允许一个线程监视/处理大量持久连接。

The Second Solution came from servlet spec. Servlet Spec got an upgrade and they introduced async support (Async Request Processing).

第二个解决方案来自servlet规范。 Servlet Spec进行了升级,并引入了异步支持 (异步请求处理)。

AsyncContext acontext = req.startAsync();

IMPORTANT: This upgrade removed the limitation of committing the response object on doGet method completion.

重要说明: 此升级消除了在doGet方法完成时提交响应对象的限制。

This allowed the “Java Application Developer” to tackle Problem 1, by offloading work to background threads. Now instead of keeping the thread waiting during the long pause, the thread can be used to handle other requests.

这样,“ Java应用程序开发人员”就可以通过将工作卸载到后台线程来解决问题1 。 现在,可以使线程不必处理长时间的暂停,而可以使用该线程来处理其他请求。

结论: (CONCLUSION:)

Async-IO in java is basically using the non-blocking version on OS socket API.

Java中的Async-IO基本上在OS套接字API上使用非阻塞版本。

Async request processing is basically the servlet spec standardization of how to process more requests with one thread.

异步请求处理基本上是servlet规范中的一个规范,该规范规定了如何通过一个线程处理更多请求。

参考资料: (REFERENCES:)

https://www.scottklement.com/rpg/socktut/tutorial.pdfhttps://stackoverflow.com/questions/15217524/what-is-the-difference-between-thread-per-connection-vs-thread-per-request

https://www.scottklement.com/rpg/socktut/tutorial.pd f https://stackoverflow.com/questions/15217524/what-is-the-difference-between-thread-per-connection-vs-thread-每个请求

Motivation of article: Team Learning/Knowledge Sharing

文章动机:团队学习/知识共享

翻译自: https://www.freecodecamp.org/news/java-async-io-async-request-processing-in-http-request-1a04f395d8c7/

java异步io

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java可以通过HTTP协议进行范围请求,以获取指定范围内的数据。在HTTP请求使用Range字段来指定所需范围,服务器在响应头返回Content-Range字段指定的范围内的数据。 在异步任务执行程序启用请求范围,可以使用Java Servlet API的AsyncContext对象。AsyncContext允许在请求处理过程请求对象传递给另一个线程,以便异步处理请求。 在Servlet,可以使用HttpServletRequest的startAsync方法获取AsyncContext对象。然后,可以使用AsyncContext的start方法启动一个新的线程来处理请求,并在处理完成后使用complete方法结束异步请求。 在异步请求使用范围请求,可以在请求设置Range字段,以获取指定范围内的数据。在异步处理线程,可以使用Java IO的RandomAccessFile类读取指定范围内的数据,并将其写回响应。 以下是一个使用AsyncContext实现范围请求的示例代码: ```java @WebServlet("/download") public class DownloadServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取AsyncContext对象 AsyncContext asyncContext = request.startAsync(request, response); // 启动异步处理线程 asyncContext.start(() -> { try { // 获取请求范围 long start = 0; long end = Long.MAX_VALUE; String range = request.getHeader("Range"); if (range != null && range.startsWith("bytes=")) { range = range.substring(6); int dash = range.indexOf('-'); try { if (dash != -1) { start = Long.parseLong(range.substring(0, dash)); end = Long.parseLong(range.substring(dash + 1)); } } catch (NumberFormatException e) { start = 0; end = Long.MAX_VALUE; } } // 读取指定范围内的数据 RandomAccessFile file = new RandomAccessFile("path/to/file", "r"); long contentLength = file.length(); if (end == Long.MAX_VALUE) { end = contentLength - 1; } response.setHeader("Content-Length", Long.toString(end - start + 1)); response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + contentLength); response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); file.seek(start); byte[] buffer = new byte[1024]; int length; while ((length = file.read(buffer)) > 0) { if (start + length > end) { length = (int) (end - start + 1); } response.getOutputStream().write(buffer, 0, length); start += length; if (start > end) { break; } } file.close(); } catch (IOException e) { e.printStackTrace(); } finally { // 结束异步请求 asyncContext.complete(); } }); } } ``` 在这个示例,我们实现了一个Servlet,用于处理下载请求。在doGet方法,我们获取AsyncContext对象,并使用start方法启动异步处理线程。在异步线程,我们首先获取请求范围,并读取指定范围内的数据。然后,我们将读取到的数据写回响应,并在最后使用complete方法结束异步请求。 注意,在响应头设置Content-Range字段时,需要指定范围的总长度。在本例,我们使用文件的长度作为总长度。如果是动态生成的数据,需要在计算范围时先确定总长度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值