【问题标题】:JaxRsClient (OkHttpClient) is much slower than Feign (HttpURLConnection) or cURLJaxRsClient (OkHttpClient) 比 Feign (HttpURLConnection) 或 cURL 慢得多
【发布时间】:2020-05-12 22:41:14
【问题描述】:

我有一个 REST 端点,它基本上只是从调用我们的云 VM 的机器上传一个文件。

当我从客户端 VM 使用 cURL 进行调用时,上传一个 100 兆字节的文件需要大约 10 秒。

当我从客户端 VM 使用 jaxrs client 进行调用时,上传一个 100 兆字节的文件大约需要 2 分 50 秒。

客户端代码。我玩过创建一个自定义 SSLSocketFactory,它允许我打开 SocketOptions 的旋钮,但这不在下面的 sn-p 中。

        SslConfiguration sslConfig = SslConfiguration.of(Paths.get(trustStorePath));
        ClientConfiguration config = ClientConfigurations.of(
                ImmutableList.of(<uri>),
                SslSocketFactories.createSslSocketFactory(sslConfig),
                SslSocketFactories.createTrustManagers(sslConfig));

        MyService service = JaxRsClient.create(
                MyService.class,
                UserAgent.of(UserAgent.Agent.of("my-user-agent", "1.0.0")),
                new HostMetricsRegistry(),
                config);

       service.fooBar(...);

主机端点(抽象定义)

    @POST
    @Path("path/foo")
    @Consumes(MediaType.WILDCARD)
    void fooBar(
            @HeaderParam(HttpHeaders.AUTHORIZATION) AuthHeader authHeader,
            InputStream fileData);

我的问题是:什么可能导致网络吞吐量出现如此巨大的差异?有没有我可以在 Java 端打开 SocketOptions 的表盘来帮助解决这个问题?

任何和所有上下文/帮助将不胜感激!

相关细节:

  • jre11.0.6
  • TLSv1.2(尝试使用 CBC 和 GCM 密码套件,两者均未更改)
  • 我已经修改了 TCP_NODELAY、SO_SNDBUF 和 SO_RCVBUF(通过 SSLSocketFactory)。 TCP_NODELAY 提高了约 10% 的性能,而更改 SO_SNDBUF 和 SO_RCVBUF 没有任何影响。

更新 1

我改用了普通的 Feign 客户端(HttpsURLConnection,在后台):

        MyService service = Feign.builder()
                .client(new Client.Default(new KeepAliveSslSocketFactory(sslContext.getSocketFactory()), null))
                .contract(new JAXRSContract())
                .encoder(
                        new InputStreamDelegateEncoder(
                                new TextDelegateEncoder(
                                        new CborDelegateEncoder(
                                                new ObjectMapper(),
                                                new ConjureFeignJacksonEncoder(objectMapper)))))
                .target(MyService.class, <url>);

并在类的前面设置以下块:

static {
    Security.insertProviderAt(Conscrypt.newProvider(), 1);
}

这两个更改一起(Feign 客户端和 Conscrypt 提供程序)使 java 调用的性能与cURL 相当。但是,如果我将 Conscrypt 提供程序添加到原始 JaxRsClient,它仍然很慢。而且没有 Conscrypt 提供程序的原版 Feign 客户端也仍然很慢。

还可能值得注意的是,我已经尝试过 GCM 和 CBC 密码套件,它们根本不会影响性能(无论设置了哪个客户端或 Conscrypt 提供程序)。

更新 2

所以我已将 JRE 更新到 11.0.6,并且通过该更改,我不再需要启用 Conscrypt 以使 Feign 请求更快。我已经捕获了 feign 请求(快)和 JaxRsClient 请求(慢)的 JFR。

这是 Feign (HttpURLConnection under the hood) 请求上的 Socket I/O

这是 JaxRsClient (OkHttpClient under the hood) 请求上的 Socket I/O

这些是使用 Java Mission Control 查看的。有趣的是,慢速请求只是对套接字进行大量读取(~200),而快速请求是 3 次读取和 15 次写入。出于健全性检查,我还在这两个请求中切换了 CBC 和 GCM 密码套件,两者之间没有真正的区别。

【问题讨论】:

  • 请编辑您的问题并添加您用于 jaxrs 客户端的代码。一般来说,因应用程序而异的网络速度通常是缓冲区/打包问题。缓冲区越大,发送的数据包越大,传输速度越快。例如,一旦我犯了一个错误并将数据单字节发送到服务器 - 因此每个字节都被包装在一个协议中,增加了 10-100 个字节......
  • 客户端代码非常标准,我在上面添加了它(几乎直接来自我提供的 jaxrsclient 链接的文档。我还添加了一些关于我正在使用的环境/协议的更多细节。
  • MyService中返回数据的方法签名是什么?你用StreamingOutput吗?
  • @Robert,文件作为InputStream 传递给MyService 中的方法。在 feign 客户端 + Conscrypt 提供者的情况下(参见更新 1),改变 InputStream 的类型根本不会显着影响性能。我还在内存中加载了一个 100mb 的“文件”(不是从磁盘读取),这也不影响性能。
  • 请贴出实际的客户端代码。声称它“相当标准”是不够的。

标签: java curl networking https


【解决方案1】:

所以,我有点不满意,我对这个答案没有更深入的了解,但就是这样。

JaxRsClient 正在协商 HTTP/2。我不确定幕后发生了什么,但如果没有启用 GCM 密码,那么唯一允许的协议应该是 HTTP1.1。这是纠正该问题的 PR:

https://github.com/palantir/conjure-java-runtime/commit/e52efdc1f66a52c3ee9112ecaae7bb40ac799224

在更改之后,如果未启用 GCM 密码,JaxRsClient 将不会协商 HTTP/2。在 HTTP/1.1 上,它匹配 cURLFeign 以提高速度。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-13
    • 2011-10-27
    • 1970-01-01
    • 2013-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多