jetty 和 springboot 应用如何从http1.x升级至http2

 

一、 why http2 (引用

 a)http2 相比于http1.x 有以下优点:

  • 二进制格式:非明文协议,将数据分为数据帧,更利于组织和传输;

  • 多路复用:允许使用单个连接同时发起多个请求,不受数量限制;

  • 请求优先级:高优先级的请求可以更快地获得相应;

  • 流量控制:类似 TCP 的流量控制机制,使用 “窗口” 避免拥塞;

  • 头部压缩:使用专用的 HPACK 算法压缩冗余的头部信息;

  • 支持服务端推送:服务器可以主动向客户端发送 “可能” 需要的资源;

  • 强安全性:禁用了数百种不再安全的算法,减少了被攻破的可能;

  • 非强制加密:允许用户在安全与性能间做出自己的选择。

 

b)http2 同 http1.x 的性能对比,参考链接

c)http2 的请求示例

curl -i --http2 https://cn.bing.com

HTTP/2 200
cache-control: private, max-age=0
content-length: 112737
content-type: text/html; charset=utf-8
p3p: CP="NON UNI COM NAV STA LOC CURa DEVa PSAa PSDa OUR IND"

 

d)http2 with ALPN (引用

  The development of new web protocols such as HTTP/2 raised the need of protocol negotiation within a Transport Layer Security (TLS) handshake. A protocol negotiation called ALPN (Application Layer Protocol Negotiation - RFC7301) has been defined to accomplish this.

      HTTP/2协议的开发在TLS的握手层面需要进行协议协商,进行协议协商的`协议`被称作ALPN(应用层协议协商)。

 

Application Layer Protocol Negotiation (ALPN) is a TLS extension that allows client and server to negotiate the application protocol that they will use to communicate within the encryption provided by TLS.

  ALPN是TLS的扩展支持,使得客户端服务端在TLS的加密通讯层可进行协议约定,举个例子:甲同乙沟通,甲用英文向乙说,你会说中文吗? 

乙说我会讲,甲说那我们用中文来沟通吧,乙说可以的。如过乙说不会讲,甲乙继续保持英文交流。

 

Any protocol can be negotiated by ALPN within a TLS connection; the protocols that are most commonly negotiated are HTTP/2 and HTTP/1.1.

  在TLS链接层面ALPN支持任意协议协商,通常最广泛进行协商的协议是 HTTP/2、HTTP/1.1。

 

Browsers only support HTTP/2 over TLS by negotiating the HTTP/2 protocol via ALPN. You need to configure the server to support TLS and ALPN if you want browsers to use the HTTP/2 protocol, otherwise they will default to HTTP/1.1.

      浏览器仅支持HTTP/2 over TLS,还有一种HTTP/2 overcleartext,前一种简称h2、后一种简称h2c。over TLS是加密链接,后一种未加密明文。如果你想让你的服务在浏览器层面支持HTTP/2,你需要配置你的服务器端支持TLS 和 ALPN。

 

二、 基于jetty的升级

 

a) ALPN 支持

When using Jetty embedded, the jetty-alpn-client and jetty-alpn-server artifacts must be included in the classpath, respectively for client and server use cases.

基于嵌入jetty的应用,服务端和客户端需要分别引入jetty-alpn-server 和 jetty-alpn-client jar包。

The ALPN implementation is provided to these two artifacts with the following three options:

ALPN的支持有三种方案可选

  • For JDK 8 only, a provider based on modified OpenJDK classes

    • Only works with JDK 8, pure Java implementation
    • Requires the -Xbootclasspath/p option on command line

        仅jdk8, 通过修改OpenJDK的类来支持,需要在启动命令里上 java -Xbootclasspath/p:yourdir/alpn-boot-8.1.13.v20181017.jar 选项。

  • For JDK 8 or later, a provider based on the Conscrypt security provider

    • Works with JDK 8 or later and provides improved performance
    • Binds to the OpenSSL native library shipped by Conscrypt and is therefore only available on the platforms supported by Conscrypt

        对jdk8及以后,借助native library Conscrypt的支持,因此仅对支持Conscrypt的平台可用,提供最佳的性能。

  • For JDK 9 or later, a provider based on the ALPN APIs present in the JDK

    • Works with JDK 9 or later, pure Java implementation
    • Lower performance than Conscrypt

     对jdk9及以后,ALPN的支持在JDK层面实现,不需要引入额外的依赖,性能相比于Conscrypt略低。

 

b) 服务端改造引入jar包

        实验方案采用第一种,仅JDK8。

        <dependency>
            <groupId>org.eclipse.jetty.http2</groupId>
            <artifactId>http2-server</artifactId>
            <version>9.4.24.v20191120</version>
        </dependency>

        <dependency>
            <groupId>org.eclipse.jetty.alpn</groupId>
            <artifactId>alpn-api</artifactId>
            <version>1.1.3.v20160715</version>
        </dependency>

        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-alpn-openjdk8-server</artifactId>
            <version>9.4.24.v20191120</version>
        </dependency>

        <dependency>
            <groupId>org.mortbay.jetty.alpn</groupId>
            <artifactId>alpn-boot</artifactId>
            <version>8.1.13.v20181017</version>
            <scope>test</scope>
        </dependency>

 

c) 代码改造

  参考官方demo

  connector的port参数必须明确设置。

        // HTTP Configuration
        HttpConfiguration httpConfig = new HttpConfiguration();
        httpConfig.setSecureScheme("https");
        httpConfig.setSecurePort(8443);
        httpConfig.setSendServerVersion(true);

        // HTTP Connector
        ServerConnector httpConnector = new ServerConnector(server, new HttpConnectionFactory(httpConfig), new HTTP2CServerConnectionFactory(httpConfig));
        httpConnector.setPort(8080);
        server.addConnector(httpConnector);

        // HTTP/2 Connector
        ServerConnector http2Connector = new ServerConnector(server, ssl, alpn, h2, new HttpConnectionFactory(httpsConfig));
        http2Connector.setPort(8443);
        server.addConnector(http2Connector);

 

以上代码,8080端口既可支持http1.1又支持 http/2, 但8443端口仅支持http/2,且访问地址为https前缀。

 

#h2c http2 over clear text
curl -i --http2 "http://localhost:8080/api?1"

#http1.1
curl -i  "http://localhost:8080/api?1"

#h2 http/2 over tls
curl -i  "https://localhost:8443/api?1"

 

三、 基于Springboot的升级 (引用)

a) 多种webserver方案

  • With undertow 1.4.0+,在jdk8层面不需要任何依赖
  • With jetty,need Conscrypt support
  • With tomcat, if jdk9 or later 不需要任何依赖,if jdk8, 通过JNI 调用libtcnative 来支持,需要在操作系统环境层面安装libtcnative及其关联依赖
  • With Reactor Netty

b) 选择jdk8 tomcat 方案

    原因,tomcat为springboot的默认webserver。

  •  安装libtcnative,参考
    • apr 1.7.0
    • openssl 1.1
    • GNU development environment (libtool g++ gcc make perl linux-headers)
    • tomcat-native 1.2.23

      libtcnative 最好是集成到docker镜像里,以免新装机器的环境都要重新安装。

    

  •  properties配置文件

     要支持http2, 必须配置ssl

# ssl config
server.ssl.key-store=yourdir/your.jks
server.ssl.key-store-password=yourstorepwd
server.ssl.trust-store=yourdir/your.cacerts
server.ssl.trust-store-password=yourtrustpwd

# http2 config
server.port=8443
server.http2.enabled=true

 

c) 代码改造

@Configuration
public class WebServerConfigure implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        // reference `https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-enable-multiple-connectors-in-tomcat`
        Connector httpConnector = new Connector();
        httpConnector.setScheme("http");
        httpConnector.setPort(8080);
        factory.addAdditionalTomcatConnectors(httpConnector);
    }

}

通过properties配置支持https的端口号为8443, 通过以上代码支持http的端口号为8080。需要说明的是,基于tomcat来适配http2,无法支持h2c,只能要么是h2,要么http1.1。遇到一个诡异的问题是,如果不通过配置文件配置http2, 通过以上代码方式添加http2的connector,应用启动会报调用libtcnative的错。

 

d) 应用启动

java -Djava.library.path=/usr/local/opt/tomcat-native/lib , 需要指定libtcnative的路径。

 

四、 结语

   虽然http2有很多好处,但是它有加解密的开销、建立安全连接需握手4次,而常规http握手3次。如果业务请求量不大,可能不会带来性能提升,甚至是性能损耗。http2的常规使用场景是外网至网关层采用http2的安全协议,网关至后端服务可以采用http协议或非加密版本的http2,即h2c。

 

五、 参考链接

https://www.eclipse.org/jetty/documentation/current/http2-configuring-haproxy.html
https://stackoverflow.com/questions/44243764/http-2-issue-with-jetty-invalid-preface
https://www.jianshu.com/p/7ddcdd3847d6
https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-embedded-web-servers
https://www.eclipse.org/jetty/documentation/current/alpn-chapter.html

 

posted @ 2020-02-03 15:51  walle搬砖  阅读(2286)  评论(0编辑  收藏  举报