HTTP/2服务端推送

翻译:shihuaping0918@163.com

【译者注:本文需要有一定的HTTP协议知识和浏览器解析页面过程知识】

原文 :https://blog.golang.org/h2push

前言

HTTP/2被设计为解决HTTP/1.x的许多缺陷。当代网页使用了许多资源:HTML、样式表、脚本、图片等等。在HTTP/1.x中这些资源每一个都必须明确地请求。这可能是一个很慢的过程。浏览器从获取HTML开始,然后在它解析和评估页面的时候,增量地获取更多的资源。因为服务器必须等待浏览器做每一个请求,网络经常是空闲的和未充分使用的。

为了改善延迟,HTTP/2引入了server push,它允许服务端推送资源给浏览器,在浏览器明确地请求之前。一个服务器经常知道一个页面需要很多附加资源,在它响应浏览器第一个请求的时候,可以开始推送这些资源。这允许服务端去完全充分地利用一个可能空闲的网络,改善页面加载时间。


serverpush.svg.png

在协议层,HTTP/2 server push被push_promise 帧所驱动,一个PUSH_PROMISE描述了一个请求,即服务端预测浏览器将马上要发出的请求。浏览器一收到PUSH_PROMISE,它马上知道服务端将要传输这个资源。如果浏览器后续发现它需要这个资源,它会等待这个推送完成,而不是发送一个新的请求。这减少了浏览器花费在网络等待上的时间 。

net/http包中的服务端推送

go1.8引入了来自http.Server对推送响应的支持。如果运行的server是一个HTTP/2的服务,并且进来的连接是使用HTTP/2的时候,这个特性是可用的。在任意一个HTTP handler中,你可以判断http.ResponseWriter是否支持服务端推送,通过检查它是否实现了新的http.Pusher接口。

例如,如果server知道app.js将要被请求来渲染页面,如果http.Pusher可用,handler可以初始化一个push。

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        if pusher, ok := w.(http.Pusher); ok {
            // Push is supported.
            if err := pusher.Push("/app.js", nil); err != nil {
                log.Printf("Failed to push: %v", err)
            }
        }
        // ...
    })

因时间关系,今天晚上先译到这……

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会尽力回答你的问题。HTTP/2是一个二进制协议,而且它是建立在TLS之上的,所以我们需要先创建一个TLS连接,然后才能使用HTTP/2进行通信。 以下是一个简单的HTTP/2服务端推送数据至客户端的示例: ```java import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import jdk.incubator.http.HttpRequest; import jdk.incubator.http.HttpResponse; import jdk.incubator.http.HttpResponse.PushPromiseHandler; import jdk.incubator.http.HttpServer; import jdk.incubator.http.HttpsConfigurator; import jdk.incubator.http.HttpsParameters; public class Http2Server { private static final int PORT = 8080; private static final String KEYSTORE_PATH = "/path/to/keystore"; private static final String KEYSTORE_PASSWORD = "password"; private final SSLContext sslContext; private final ConcurrentMap<AsynchronousSocketChannel, HttpResponse.BodySubscriber<?>> subscribers = new ConcurrentHashMap<>(); public Http2Server() throws Exception { KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keystore(KEYSTORE_PATH), KEYSTORE_PASSWORD.toCharArray()); sslContext = SSLContext.getInstance("TLS"); sslContext.init(keyManagerFactory.getKeyManagers(), null, null); } public void start() throws Exception { HttpsConfigurator configurator = new HttpsConfigurator(sslContext) { public void configure(HttpsParameters params) { InetSocketAddress remote = params.getClientAddress(); if (remote != null) { params.setNeedClientAuth(false); } } }; HttpServer.create(new InetSocketAddress(PORT), 0) .setHttpsConfigurator(configurator) .createContext("/", (request) -> { if (request.getMethod().equals("GET")) { HttpResponse.BodyPublisher publisher = HttpResponse.BodyPublishers.ofString("Hello, HTTP/2!"); return HttpResponse.newBuilder() .header("Content-Type", "text/plain") .body(publisher) .pushPromise("style.css", HttpRequest.newBuilder() .uri(request.getRequestURI().resolve("style.css")) .header("Content-Type", "text/css") .build(), new PushPromiseHandler() { public void applyPushPromise(HttpRequest pushedRequest, HttpRequest initiatingRequest, HttpResponse initiatingResponse) { HttpResponse.BodyPublisher publisher = HttpResponse.BodyPublishers.ofString("body { background-color: yellow; }"); HttpResponse response = HttpResponse.newBuilder() .header("Content-Type", "text/css") .body(publisher) .build(); initiatingResponse.pushPromise(pushedRequest, response, new CompletionHandler<Void, Void>() { public void completed(Void result, Void attachment) { System.out.println("Pushed " + pushedRequest.uri()); } public void failed(Throwable exc, Void attachment) { System.err.println("Failed to push " + pushedRequest.uri() + ": " + exc); } }); } }) .build(); } else { return HttpResponse.newBuilder() .status(405) .header("Allow", "GET") .build(); } }) .setExecutor(null) .start(); } public static void main(String[] args) throws Exception { Http2Server server = new Http2Server(); server.start(); } private static ByteBuffer keystore(String path) throws IOException { try (InputStream in = new FileInputStream(path)) { byte[] bytes = new byte[in.available()]; in.read(bytes); return ByteBuffer.wrap(bytes); } } } ``` 这个示例创建了一个HTTP/2服务端,它会响应所有GET请求,并且会在响应中推送一个样式表文件。当客户端请求这个样式表文件时,服务端推送这个文件,以便客户端可以更快地加载它。 我们在这个示例中使用了Java 9中引入的HTTP/2 API,这个API可以让我们方便地创建HTTP/2服务端和客户端。我们还使用了Java 9中引入的AsynchronousServerSocketChannel和AsynchronousSocketChannel API来处理连接。我们也使用了Java 9中引入的CompletableFuture API来处理异步操作的结果。 注意,这个示例使用了TLS进行加密,这意味着我们需要提供一个密钥库(keystore)来存储服务端的私钥和证书。我们还需要提供一个密码来保护这个密钥库。在这个示例中,我们使用了Java 9中引入的KeyManagerFactory API来加载密钥库。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值