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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM