资讯专栏INFORMATION COLUMN

从JDK11新增HttpClient谈谈非阻塞模型

pingan8787 / 911人阅读

摘要:是一个倡议,它提倡提供一种带有非阻塞背压的异步流处理的标准。是标准的实现之一。的实现细节请求响应的与请求响应的暴露为是请求的的消费者是响应的的生产者内部的内部

北京时间 9 月 26 日,Oracle 官方宣布 Java 11 正式发布 一、JDK HTTP Client介绍 JDK11中的17个新特性

JDK11中引入HTTP Client的动机

既有的HttpURLConnection存在许多问题

其基类URLConnection当初是设计为支持多协议,但其中大多已经成为非主流(ftp, gopher…)

API的设计早于HTTP/1.1,过度抽象

难以使用,存在许多没有文档化的行为

它只支持阻塞模式(每个请求/响应占用一个线程)

HTTP Client发展史

在JDK11 HTTP Client出现之前

在此之前,可以使用以下工具作为Http客户端

JDK HttpURLConnection

Apache HttpClient

Okhttp

Spring Rest Template

Spring Cloud Feign

将Jetty用作客户端

使用Netty库。还

初探JDK HTTP Client

我们来看一段HTTP Client的常规用法的样例 ——
执行GET请求,然后输出响应体(Response Body)。

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("http://openjdk.java.net/"))
      .build();
client.sendAsync(request, asString())
      .thenApply(HttpResponse::body)
      .thenAccept(System.out::println)
      .join();
第一步:创建HttpClient

一般使用JDK 11中的HttpClient的第一步是创建HttpClient对象并进行配置。

指定协议(http/1.1或者http/2)

转发(redirect)

代理(proxy)

认证(authenticator)

HttpClient client = HttpClient.newBuilder()
      .version(Version.HTTP_2)
      .followRedirects(Redirect.SAME_PROTOCOL)
      .proxy(ProxySelector.of(new InetSocketAddress("www-proxy.com", 8080)))
      .authenticator(Authenticator.getDefault())
      .build();
第二步:创建HttpRequest

从HttpRequest的builder组建request

请求URI

请求method(GET, PUT, POST)

请求体(request body)

Timeout

请求头(request header)

HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("http://openjdk.java.net/"))
      .timeout(Duration.ofMinutes(1))
      .header("Content-Type", "application/json")
      .POST(BodyPublisher.fromFile(Paths.get("file.json")))
      .build()
第三步:send

http client可以用来发送多个http request

请求可以被以同步或异步方式发送

1. 同步发送

同步发送API阻塞直到HttpResponse返回

HttpResponse response =
      client.send(request, BodyHandler.asString());
System.out.println(response.statusCode());
System.out.println(response.body());
2. 异步发送

异步发送API立即返回一个CompletableFuture

当它完成的时候会获得一个HttpResponse

client.sendAsync(request, BodyHandler.asString())
      .thenApply(response -> { System.out.println(response.statusCode());
                               return response; } )
      .thenApply(HttpResponse::body)
      .thenAccept(System.out::println);

※CompletableFuture是在java8中加入的,支持组合式异步编程

二、从提升单机并发处理能力的技术来看HttpClient的实现细节 提升单机并发处理能力的技术

多CPU/多核

多线程

非阻塞(non-blocking)

Java NIO

Java NIO为Java带来了非阻塞模型。

Lambda表达式

Lambda表达式可以方便地利用多CPU。

Lambda表达式让代码更加具有可读性

回调

假设以下代码是一个聊天应用服务器的一部分,该应用以Vert.x框架实现。
(Eclipse Vert.x is a tool-kit for building reactive applications on the JVM.)
向connectHandler方法输入一个Lambda表达式,每当有用户连接到聊天应用时,都会调用该Lambda表达式。这就是一个回调。
这种方式的好处是,应用不必控制线程模型——Vert.x框架为我们管理线程,打理好一切相关复杂性,程序员只考虑和回调就够了。

vertx.createServer()
    .connectHandler(socket -> {
        socket.dataHandler(new User(socket, this));
    }).listen(10_000);

注意,这种设计里,不共享任何状态。对象之间通过向事件总线发送消息通信,根本不需要在代码中添加锁或使用synchronized关键字。并发编程变得更加简单。

Future

大量的回调会怎样?请看以下伪代码

(1)->{
    (2)->{
        (3)->{
            (4)->{}
        }
    }
}

大量回调会形成“末日金字塔”。

如何破解? 使用Future

Future1=(1)->{}
Future2=(Future1.get())->{}
Future3=(Future2.get())->{}
Future4=(Future3.get())->{}

但这会造成原本期望的并行处理,变成了串行处理,带来了性能问题。
我们真正需要的是将Future和回调联合起来使用。下面将要讲的CompletableFuture就是结合了Future和回调,其要点是组合不同实例而无需担心末日金字塔问题。

CompletableFuture
(new CompletableFuture()).thenCompose((1)->{})
    .thenCompose((2)->{})
    .thenCompose((3)->{})
    .thenCompose((4)->{})
    .join()
Reactive Streams

Reactive Streams是一个倡议,它提倡提供一种带有非阻塞背压的异步流处理的标准(Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure)。

JDK 9中的java.util.concurrent.Flow中的概念,与Reactive Streams是一对一对等的。java.util.concurrent.Flow是Reactive Streams标准的实现之一。

多CPU的并行机制让处理海量数据的速度更快,消息传递和响应式编程让有限的并行运行的线程执行更多的I/O操作。 HttpClient的实现细节 请求响应的body与reactive streams

请求响应的body暴露为reactive streams

http client是请求的body的消费者

http client是响应的body的生产者

HttpRequest内部
public abstract class HttpRequest {
    ...
    public interface BodyPublisher
                extends Flow.Publisher { ... }
}
HttpResponse的内部
public abstract class HttpResponse {
    ...
    public interface BodyHandler {
        BodySubscriber apply(int statusCode, HttpHeaders responseHeaders);
    }

    public interface BodySubscriber
        extends Flow.Subscriber> { ... }
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/71991.html

相关文章

  • 浅析 jdk11HttpClient 的使用

    摘要:在中也可以直接使用返回的是,然后通过来获取结果阻塞线程,从中获取结果四一点唠叨非常的年轻,网络资料不多,且代码非常精细和复杂,目前来看底层应该是使用了线程池搭配进行异步通讯。 零 前期准备 0 版本 JDK 版本 : OpenJDK 11.0.1 IDE : idea 2018.3 1 HttpClient 简介 java.net.http.HttpClient 是 jdk11 中正式...

    Eminjannn 评论0 收藏0
  • tornado配合celery及rabbitmq实现web request异步阻塞

    摘要:主要是为了实现系统之间的双向解耦而实现的。问题及优化队列过长问题使用上述方案的异步非阻塞可能会依赖于的任务队列长度,若队列中的任务过多,则可能导致长时间等待,降低效率。 Tornado和Celery介绍 1.Tornado Tornado是一个用python编写的一个强大的、可扩展的异步HTTP服务器,同时也是一个web开发框架。tornado是一个非阻塞式web服务器,其速度相当快。...

    番茄西红柿 评论0 收藏0
  • Java11 HttpClient小试牛刀

    序 本文主要研究一下Java11的HttpClient的基本使用。 变化 从java9的jdk.incubator.httpclient模块迁移到java.net.http模块,包名由jdk.incubator.http改为java.net.http 原来的诸如HttpResponse.BodyHandler.asString()方法变更为HttpResponse.BodyHandlers.of...

    Bmob 评论0 收藏0
  • Java11的新特性

    摘要:从版本开始,不再单独发布或者版本了,有需要的可以自己通过去定制官方解读官方细项解读稳步推进系列六的小试牛刀一文读懂的为何如此高效弃用引擎 Java语言特性系列 Java5的新特性 Java6的新特性 Java7的新特性 Java8的新特性 Java9的新特性 Java10的新特性 Java11的新特性 Java12的新特性 Java13的新特性 序 本文主要讲述一下Java11的新...

    April 评论0 收藏0
  • 记一次 JAVA 的内存泄露分析

    摘要:展示如下场景再现经过分析,最后我们定位到是使用产生的内存泄露问题。下面通过一个,来简单讲下具体内存泄露的原因。这一次的内存泄露问题算是解决了。总结关于内存泄露问题在第一次排查时,往往是有点不知所措的。 记一次 JAVA 的内存泄露分析 摘要:本文属于原创,欢迎转载,转载请保留出处:https://github.com/jasonGeng88/blog 当前环境 jdk == 1.8 ...

    Tecode 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<