一、 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) 代碼改造
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