java9系列(六)HTTP/2 Client (Incubator)

本文主要研究下JEP 110: HTTP/2 Client (Incubator)

基本实例

sync get

    /**
     * --add-modules jdk.incubator.httpclient
     * @throws IOException
     * @throws InterruptedException
     * @throws URISyntaxException
     */
    @Test
    public void testGet() throws IOException, InterruptedException, URISyntaxException {
        HttpClient httpClient = HttpClient.newHttpClient();
        HttpRequest httpRequest = HttpRequest.newBuilder()
                .uri(new URI("https://www.baidu.com"))
                .header("User-Agent", "jdk 9 http client")
                .GET()
                .build();
        HttpResponse<String> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandler.asString());
        System.out.println(httpResponse.statusCode());
        System.out.println(httpResponse.body());
    }

由于jdk9模块化了,junit这里没有模块化,需要在javac编译时添加--add-modules jdk.incubator.httpclient,否则报错如下:

Error:(3, 21) java: 程序包 jdk.incubator.http 不可见
  (程序包 jdk.incubator.http 已在模块 jdk.incubator.httpclient 中声明, 但该模块不在模块图中)

在java运行时也要添加--add-modules jdk.incubator.httpclient,否则报NoClassDefFoundError

java.lang.NoClassDefFoundError: jdk/incubator/http/HttpResponse

    at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
    at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3139)
    at java.base/java.lang.Class.getDeclaredMethods(Class.java:2266)
    at org.junit.internal.MethodSorter.getDeclaredMethods(MethodSorter.java:54)
    at org.junit.runners.model.TestClass.scanAnnotatedMembers(TestClass.java:65)
    at org.junit.runners.model.TestClass.<init>(TestClass.java:57)
    at org.junit.runners.ParentRunner.createTestClass(ParentRunner.java:88)
    at org.junit.runners.ParentRunner.<init>(ParentRunner.java:83)
    at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:65)
    at org.junit.internal.builders.JUnit4Builder.runnerForClass(JUnit4Builder.java:10)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
    at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
    at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33)
    at org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:36)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:49)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.ClassNotFoundException: jdk.incubator.http.HttpResponse
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)
    ... 19 more

async get

    @Test
    public void testAsyncGet() throws URISyntaxException, InterruptedException, ExecutionException {
        HttpClient client = HttpClient.newHttpClient();

        HttpRequest request = HttpRequest.newBuilder()
                .uri(new URI("https://www.baidu.com"))
                .GET()
                .build();

        CompletableFuture<HttpResponse<String>> response = client.sendAsync(request, HttpResponse.BodyHandler.asString());

        response.whenComplete((resp,t) -> {
            if(t != null){
                t.printStackTrace();
            }else{
                System.out.println(resp.body());
                System.out.println(resp.statusCode());
            }
        }).join();
    }

post form

    @Test
    public void testPostForm() throws URISyntaxException {
        HttpClient client = HttpClient.newHttpClient();

        HttpRequest request = HttpRequest.newBuilder()
                .uri(new URI("http://www.w3school.com.cn/demo/demo_form.asp"))
                .header("Content-Type","application/x-www-form-urlencoded")
                .POST(HttpRequest.BodyProcessor.fromString("name1=value1&name2=value2"))
                .build();
        client.sendAsync(request, HttpResponse.BodyHandler.asString())
                .whenComplete((resp,t) -> {
                    if(t != null){
                        t.printStackTrace();
                    }else{
                        System.out.println(resp.body());
                        System.out.println(resp.statusCode());
                    }
        }).join();
    }

HTTP/2

curl http2

  • 安装
brew install curl --with-nghttp2
==> Installing dependencies for curl: jemalloc, nghttp2
==> Installing curl dependency: jemalloc
==> Downloading https://homebrew.bintray.com/bottles/jemalloc-5.0.1.sierra.bottl
######################################################################## 100.0%
==> Pouring jemalloc-5.0.1.sierra.bottle.tar.gz
🍺  /usr/local/Cellar/jemalloc/5.0.1: 16 files, 1.6MB
==> Installing curl dependency: nghttp2
==> Downloading https://homebrew.bintray.com/bottles/nghttp2-1.31.0.sierra.bottl
######################################################################## 100.0%
==> Pouring nghttp2-1.31.0.sierra.bottle.tar.gz
🍺  /usr/local/Cellar/nghttp2/1.31.0: 33 files, 6.3MB
==> Installing curl --with-nghttp2
==> Downloading https://curl.haxx.se/download/curl-7.58.0.tar.bz2
######################################################################## 100.0%
==> ./configure --disable-silent-rules --prefix=/usr/local/Cellar/curl/7.58.0 --
==> make install
==> Caveats
This formula is keg-only, which means it was not symlinked into /usr/local,
because macOS already provides this software and installing another version in
parallel can cause all kinds of trouble.

If you need to have this software first in your PATH run:
  echo 'export PATH="/usr/local/opt/curl/bin:$PATH"' >> ~/.zshrc

For compilers to find this software you may need to set:
    LDFLAGS:  -L/usr/local/opt/curl/lib
    CPPFLAGS: -I/usr/local/opt/curl/include
For pkg-config to find this software you may need to set:
    PKG_CONFIG_PATH: /usr/local/opt/curl/lib/pkgconfig

==> Summary
🍺  /usr/local/Cellar/curl/7.58.0: 415 files, 3MB, built in 2 minutes 37 seconds
  • 检查
curl -V
curl 7.58.0 (x86_64-apple-darwin16.7.0) libcurl/7.58.0 OpenSSL/1.0.2n zlib/1.2.8 nghttp2/1.31.0
Release-Date: 2018-01-24
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy
  • 访问
curl -I --http2 https://http2.akamai.com/demo
HTTP/2 200
server: Apache
etag: "07ce30bc53aa7834dff55f92a6d05a56:1466062139"
last-modified: Thu, 16 Jun 2016 07:28:59 GMT
accept-ranges: bytes
content-length: 2421
content-type: text/html
rtt: 186
ghost_ip: 23.193.143.145
ghost_service_ip: 107.14.44.207
client_real_ip: 210.21.215.42
client_ip: 210.21.215.42
myproto: h2
protocol_negotiation: h2
expires: Mon, 05 Mar 2018 01:15:17 GMT
cache-control: max-age=0, no-cache, no-store
pragma: no-cache
date: Mon, 05 Mar 2018 01:15:17 GMT
accept-ch: DPR, Width, Viewport-Width, Downlink, Save-Data
access-control-max-age: 86400
access-control-allow-credentials: false
access-control-allow-headers: *
access-control-allow-methods: GET,HEAD,POST
access-control-allow-origin: *
strict-transport-security: max-age=31536000 ; includeSubDomains

HTTP/2实例

    @Test
    public void testHttp2() throws URISyntaxException, IOException, InterruptedException {
        HttpClient.newBuilder()
                .followRedirects(HttpClient.Redirect.SECURE)
                .version(HttpClient.Version.HTTP_2)
                .build()
                .sendAsync(HttpRequest.newBuilder()
                                .uri(new URI("https://http2.akamai.com/demo"))
                                .GET()
                                .build(),
                        HttpResponse.BodyHandler.asString())
                .whenComplete((resp,t) -> {
                    if(t != null){
                        t.printStackTrace();
                    }else{
                        System.out.println(resp.body());
                        System.out.println(resp.statusCode());
                    }
                }).join();
    }

小结

jdk9的httpclient现在还在incubator中,最大的特性便是支持HTTP/2,当然也优化了httpclient的api,同时也支持了异步模式。鉴于它还处在incubator,如果不是着急使用HTTP/2,建议还是使用spring5的webclient,它是遵循reactive-streams规范的,使用起来更加方便。reactor-netty貌似要在0.9.0.RELEASE版本才支持HTTP/2。

doc

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,907评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,546评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,705评论 0 238
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,624评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,940评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,371评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,672评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,396评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,069评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,350评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,876评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,243评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,847评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,004评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,755评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,378评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,266评论 2 259

推荐阅读更多精彩内容