简体   繁体   English

为什么 Java HTTP 请求这么慢(与 Python 相比),我怎样才能让它们更快?

[英]Why are Java HTTP requests so slow (in comparison to Python), and how can I make them faster?

Java is a beautiful language, and is also supposedly very efficient. Java 是一门漂亮的语言,据说效率也很高。 Coming from a background of having used Python, I wanted to see the difference between the 2 languages- and from the start I was very impressed by the explicitness and clarity of Java's OOP based syntax.来自使用过 Python 的背景,我想看看这两种语言之间的区别——从一开始我就对 Java 基于 OOP 的语法的明确性和清晰度印象深刻。 However, I also wanted to test out the performance differences between the languages.但是,我还想测试语言之间的性能差异。

I started off by trying to test the performance difference between the 2 languages by computation speed.我首先尝试通过计算速度测试两种语言之间的性能差异。 For this, I wrote some code in each language- the program attempted to calculate a mathematical problem, and would iterate many times.为此,我用每种语言编写了一些代码——程序试图计算一个数学问题,并且会迭代很多次。 I won't be adding this code here, but I will say the results- Python was almost 2 times slower (measured by time) than Java.我不会在这里添加此代码,但我会说结果 - Python 几乎比 Java 慢 2 倍(按时间测量)。 Interesting, but it was expected.有趣,但这是意料之中的。 After all, the whole reason I wanted to try using Java is due to the amount of people bragging about the computation speed.毕竟,我想尝试使用 Java 的全部原因是因为有很多人吹嘘计算速度。

Following that, I proceeded to my second test- making HTTP connections to websites, in order to download web pages.之后,我进行了第二次测试——将 HTTP 连接到网站,以下载 web 页面。 For this test, I wrote another test program which would do the same as the last test, except instead of computing a math equation, it would download a web page using an HTTP library.对于这个测试,我编写了另一个测试程序,它的作用与上一个测试相同,除了不计算数学方程式,它将使用 HTTP 库下载 web 页面。

I ended up writing the following script in Python.我最终在 Python 中编写了以下脚本。 It is very straightforward, it iterates 10 times over downloading a web page, then prints the average.这非常简单,它在下载 web 页面时迭代 10 次,然后打印平均值。

from requests import get
from time import time

# Start the timer
start = time()

# Loop 10 times
for i in range(10):
    # Execute GET request
    get("https://httpbin.org/get")

# Stop the timer
stop = time()

# Calculate and print average
avg = (stop - start) / 10

print(avg)
# Prints 0.5385, on my system.

For the Java test, I wrote the following piece of code.对于 Java 测试,我编写了以下代码。 It is the same test as before, but implemented in Java.它与之前的测试相同,但在 Java 中实现。

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;
import java.util.Objects;

public class Test {

    public static String run(String url) throws IOException {
        // Code taken from OKHTTP docs
        // https://square.github.io/okhttp/
        // https://raw.githubusercontent.com/square/okhttp/master/samples/guide/src/main/java/okhttp3/guide/GetExample.java
        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
            .url(url)
            .build();

        try (Response response = client.newCall(request).execute()) {
            return Objects.requireNonNull(response.body()).string();
        }
    }

    public static void main(String[] args) throws IOException {
        // Start the timer
        long startTime = System.nanoTime();

        // Loop 10 times
        for (int i = 0; i < 10; i++) {
            // Execute GET request
            run("https://httpbin.org/get");
        }

        // Stop the timer
        long endTime = System.nanoTime();

        // Calculate the average
        float average = (((float) (endTime - startTime)) / 1000000000) / 10;

        // Print results (1.05035 on my system)
        System.out.println(average);
    }
}

Huh... that's unexpected.呃……没想到。 Alas, isn't Java supposed to be faster than Python?唉,Java 不应该比 Python 快吗? I'm shocked to see that Java is almost 2 times slower than Python in this test, but I'm determined to find a conclusion that favors Java.我很震惊地看到 Java 在这个测试中比 Python 慢了几乎 2 倍,但我决心找到一个有利于 ZD52387880E1EA22817A72D37592138 的结论。 To satisfy this, I decided to re-write the test using the Java default libraries instead of the OkHttp library.为了满足这一点,我决定使用 Java 默认库而不是OkHttp库重新编写测试。 I won't be showing the code here since it's pretty long, but I used HttpURLConnection to assist me.我不会在这里显示代码,因为它很长,但我使用了HttpURLConnection来帮助我。 My result was still the same, but slightly quicker than with the OkHttp library.我的结果还是一样,但比OkHttp库要快一些。

My final test was to do the same as the previous tests, but on http:// websites (in case the slowness occurs due to the SSL connection).我的最终测试与之前的测试相同,但在http://网站上进行(以防由于 SSL 连接而出现缓慢)。 My result was still the same- Python was faster by almost 2 times.我的结果还是一样——Python 快了将近 2 倍。 The only thing I could think of to why this is happening is because the requests Python library would be coded in C, but as you can see from the "Languages" section of their GitHub page , all of the requests library was programmed in pure Python. The only thing I could think of to why this is happening is because the requests Python library would be coded in C, but as you can see from the "Languages" section of their GitHub page , all of the requests library was programmed in pure Python .

I would like to understand why Java is so slow at running HTTP connections, and if I did something wrong with my system setup or Java test code, what should I have written to improve the results?我想了解为什么 Java 在运行 HTTP 连接时如此缓慢,如果我的系统设置或 Java 测试代码有问题,我应该写什么来改进结果? Also, if it's possible, how can a Java HTTP request be sent so that it is faster than its Python requests counterpart?此外,如果可能的话,如何发送 Java HTTP 请求,使其比 Python requests对应的请求更快?

I was really skeptical about the results you got, so I gave it a try with the exact same Python code and main Java method (that uses https) as yours.我真的对你得到的结果持怀疑态度,所以我尝试使用与你完全相同的 Python 代码和main Java 方法(使用 https)。
Here is the Java run method that reads the entire JSON content of the response:这是读取响应的整个 JSON 内容的 Java run方法:

private static String run(String url) throws IOException {
    final URLConnection c = new URL(url).openConnection();
    c.connect();
    try (InputStream is = c.getInputStream()) {
        final byte[] buf = new byte[1024];
        final StringBuilder b = new StringBuilder();
        int read = 0;
        while ((read = is.read(buf)) != -1) {
            b.append(new String(buf, 0, read));
        }
        return b.toString();
    }
}

Results on my system:我的系统上的结果:

  • Python 2.7.12: 0.5117351770401001 Python 2.7.12:0.5117351770401001
  • Python 3.5.2: 0.48344600200653076 Python 3.5.2:0.48344600200653076
  • Java 1.8: 0.19684727 Java 1.8:0.19684727

10 iterations is probably not enough to get a good result, but here, Java is at least 2 times faster. 10 次迭代可能不足以得到一个好的结果,但在这里,Java 至少快了 2 倍。

TLDR: TLDR:

The majority of a request's lifespan is spent out in the actual internet traffic.请求的大部分生命周期都花在了实际的互联网流量中。 Even though Java is faster than Python, it can only shave off a few milliseconds per request, as most of the time that is recorded for 1 request is due to server lag/latency.尽管 Java 比 Python 快,但每个请求只能缩短几毫秒,因为记录 1 个请求的大部分时间是由于服务器延迟/延迟。 Also, reuse the Python Session and Java OkHttpClient objects in order to opt-in to critical optimizations that knock off drastic computation time.此外,重用 Python Session和 Java OkHttpClient对象,以选择加入关键优化,从而缩短计算时间。


There are a few mistakes that I made in my post.我在帖子中犯了一些错误。 The first of which is that I generate a new OkHttpClient object for each request and used the get method directly.第一个是我为每个请求生成一个新的OkHttpClient object 并直接使用get方法。 As Jesse pointed out in a comment, by using these, I miss out on heavy optimizations, and therefore get an unfair result.正如 Jesse 在评论中指出的那样,通过使用这些,我错过了重度优化,因此得到了不公平的结果。

To fix this, I used a Session object to persist my request history, and saved the same OkHttpClient object likewise.为了解决这个问题,我使用了Session object 来保存我的请求历史记录,并同样保存了相同的OkHttpClient object。

My improvement implemented in Python:我在 Python 中实现的改进:

from requests import Session
from time import time

# Start the timer
start = time()

# Create a new Session     <-----
s = Session()

# Loop a few times
for i in range(50):
    # Execute GET request
    s.get("http://httpbin.org/get")

# Stop the timer
stop = time()

# Calculate and print average
avg = (stop - start) / 50

print(avg) # 180ms on my system

Likewise, I implemented the same concept in Java using a basic Singleton class and a few wrapper classes on top of the OkHttp library.同样,我在 Java 中使用基本的 Singleton class 和 OkHttp 库顶部的一些包装类实现了相同的概念。 I won't be posting the entire code here as I decided to expand it across many classes, but the underlying idea is simple.我不会在这里发布整个代码,因为我决定将它扩展到许多类,但基本思想很简单。 After making these changes and logging my newfound statistics, I ended up with the following chart:在进行这些更改并记录我新发现的统计数据后,我得到了以下图表:

统计数据

As shown, Python does in fact have a quicker initialization process for the first request executed.如图所示,Python 实际上对执行的第一个请求有更快的初始化过程。 However, you can also notice that for the Average Request (average of 50 consecutive and synchronous requests) the Java libraries ( URLConnection & OkHttp ) shave off a few milliseconds in comparison to the Python requests library.但是,您还可以注意到,对于平均请求(50 个连续和同步请求的平均值),Java 库( URLConnectionOkHttp )与 Python requests库相比减少了几毫秒。

Summary:概括:

By reusing the Python Session and Java OkHttpClient objects ( initialize the object once and use it for all requests, instead of making a new one for each request ), heavy optimizations are done, and therefore the execution times are drastically lowered. By reusing the Python Session and Java OkHttpClient objects ( initialize the object once and use it for all requests, instead of making a new one for each request ), heavy optimizations are done, and therefore the execution times are drastically lowered. However, when it comes to averages, Java only beats Python by a few measly milliseconds, as most of the time spent during a request is from network transmission time (the time it takes to send data between computers over the Internet).然而,就平均值而言,Java 仅比 Python 高出几毫秒,因为请求期间花费的大部分时间来自网络传输时间(通过 Internet 在计算机之间发送数据所需的时间)。

If anyone would like to comment more information or show their own findings in another answer, I would be ecstatic to read more about it.如果有人想评论更多信息或在另一个答案中展示他们自己的发现,我会欣喜若狂地阅读更多相关信息。 Thank you to those who commented on my question and helped me figure out a few key components to the optimization process.感谢那些对我的问题发表评论并帮助我找出优化过程的一些关键组成部分的人。 Long live Java:)万岁Java :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM