您好,登錄后才能下訂單哦!
這篇文章主要介紹“怎么開發(fā)基于Netty的HTTP/HTTPS應用程序”,在日常操作中,相信很多人在怎么開發(fā)基于Netty的HTTP/HTTPS應用程序問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么開發(fā)基于Netty的HTTP/HTTPS應用程序”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
一、通過 SSL/TLS 保護應用程序
二、HTTP 編解碼器
三、聚合 HTTP 消息
四、HTTP 壓縮
五、HTTPS
六、WebSocket
SSL 和 TLS 安全協(xié)議層疊在其他協(xié)議之上,用以實現(xiàn)數(shù)據(jù)安全。為了支持 SSL/TLS,Java 提供了 javax.net.ssl 包,它的 SSLContext 和 SSLEngine 類使得實現(xiàn)解密和加密變得相當簡單。Netty 通過一個名為 SsLHandler 的 ChannelHandler 實現(xiàn)了這個 API,其中 SSLHandler 在內(nèi)部使用 SSLEngine 來完成實際工作
Netty 還提供了基于 OpenSSL 工具包的 SSLEngine 實現(xiàn),比 JDK 提供的 SSLEngine 具有更好的性能。如果 OpenSSL 可用,可以將 Netty 應用程序配置為默認使用 OpenSSLEngine。如果不可用,Netty 將會退回到 JDK 實現(xiàn)
下述代碼展示了如何使用 ChannelInitializer 來將 SslHandler 添加到 ChannelPipeline 中
public class SslChannelInitializer extends ChannelInitializer<Channel> { private final SslContext context; private final boolean startTls; public SslChannelInitializer(SslContext context, boolean startTls) { this.context = context; this.startTls = startTls; } @Override protected void initChannel(Channel ch) throws Exception { SSLEngine engine = context.newEngine(ch.alloc()); ch.pipeline().addFirst("ssl", new SslHandler(engine, startTls)); } }
大多數(shù)情況下,Sslhandler 將是 ChannelPipeline 中的第一個 ChannelHandler,這確保了只有在所有其他的 ChannelHandler 將它們的邏輯應用到數(shù)據(jù)之后,才會進行加密
SSLHandler 具有一些有用的方法,如表所示,例如,在握手階段,兩個節(jié)點將相互驗證并且商定一種加密方式,你可以通過配置 SslHandler 來修改它的行為,或者在 SSL/TLS 握手一旦完成之后提供通知,握手階段之后,所有的數(shù)據(jù)都將會被加密
方法名稱 | 描述 |
---|---|
setHandshakeTimeout(long, TimeUnit) setHandshakeTimeoutMillis(long) getHandshakeTimeoutMillis() | 設(shè)置和獲取超時時間,超時之后,握手 ChannelFuture 將會被通知失敗 |
setCloseNotifyTimeout(long, TimeUnit) setCloseNotifyTimeoutMillis(long) getCloseNotifyTimeoutMillis() | 設(shè)置和獲取超時時間,超時之后,將會觸發(fā)一個關(guān)閉通知并關(guān)閉連接,這也會導致通知該 ChannelFuture 失敗 |
handshakeFuture() | 返回一個在握手完成后將會得到通知的 ChannelFuture,如果握手先前已經(jīng)執(zhí)行過,則返回一個包含了先前握手結(jié)果的 ChannelFuture |
close() close(ChannelPipeline) close(ChannelHandlerContext, ChannelPromise) | 發(fā)送 close_notify 以請求關(guān)閉并銷毀底層的 SslEngine |
HTTP 是基于請求/響應模式的,客戶端向服務(wù)器發(fā)送一個 HTTP 請求,然后服務(wù)器將會返回一個 HTTP 響應,Netty 提供了多種多種編碼器和解碼器以簡化對這個協(xié)議的使用
下圖分別展示了生產(chǎn)和消費 HTTP 請求和 HTTP 響應的方法
如圖所示,一個 HTTP 請求/響應可能由多個數(shù)據(jù)部分組成,并且總以一個 LastHttpContent 部分作為結(jié)束
下表概要地介紹了處理和生成這些消息的 HTTP 解碼器和編碼器
名稱 | 描述 |
---|---|
HttpRequestEncoder | 將 HTTPRequest、HttpContent 和 LastHttpContent 消息編碼為字節(jié) |
HttpResponseEncoder | 將 HTTPResponse、HttpContent 和 LastHttpContent 消息編碼為字節(jié) |
HttpRequestDecoder | 將字節(jié)編碼為 HTTPRequest、HttpContent 和 LastHttpContent 消息 |
HttpResponseDecoder | 將字節(jié)編碼為 HTTPResponse、HttpContent 和 LastHttpContent 消息 |
下述代碼中的 HttpPipelineInitializer 類展示了將 HTTP 支持添加到你的應用程序是多么簡單 —— 只需要將正確的 ChannelHandler 添加到 ChannelPipeline 中
public class HttpPipelineInitializer extends ChannelInitializer<Channel> { private final boolean client; public HttpPipelineInitializer(boolean client) { this.client = client; } @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); if (client) { // 如果是客戶端,則添加 HttpResponseDecoder 處理來自服務(wù)器的響應 pipeline.addLast("decoder", new HttpResponseDecoder()); // 如果是客戶端,則添加 HttpRequestEncoder 向服務(wù)器發(fā)送請求 pipeline.addLast("encoder", new HttpRequestEncoder()); } else { // 如果是服務(wù)端,則添加 HttpRequestDecoder 處理來自客戶端的請求 pipeline.addLast("decoder", new HttpRequestDecoder()); // 如果是客戶端,則添加 HttpResponseEncoder 向客戶端發(fā)送響應 pipeline.addLast("encoder", new HttpResponseEncoder()); } } }
在 ChannelInitializer 將 ChannelHandler 安裝到 ChannelPipeline 中之后,你就可以處理不同類型的 HTTPObject 消息了。但由于 HTTP 請求和響應可能由許多部分組成,因此你需要聚合它們以形成完整的消息。Netty 提供了一個聚合器,它可以將多個消息部分合并為 FullHttpRequest 或者 FullHttpResponse 消息
由于消息分段需要被緩沖,直到可以轉(zhuǎn)發(fā)下一個完整的消息給下一個 ChannelInboundHandler,所以這個操作有輕微的開銷,其所帶來的好處就是你可以不必關(guān)心消息碎片了
引入這種自動聚合機制只不過是向 ChannelPipeline 中添加另外一個 ChannelHandler 罷了,下述代碼展示了如何做到這一點:
public class HttpAggregatorInitializer extends ChannelInitializer<Channel> { private final boolean isClient; public HttpAggregatorInitializer(boolean isClient) { this.isClient = isClient; } @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); if (isClient) { // 如果是客戶端,則添加 HttpClientCodec pipeline.addLast("codec", new HttpClientCodec()); } else { // 如果是服務(wù)器,則添加 HttpServerCodec pipeline.addLast("codec", new HttpServerCodec()); } // 將最大的消息大小為 512KB 的 HTTPObjectAggregator 添加到 ChannelPipeline pipeline.addLast("aggregator", new HttpObjectAggregator(512 * 1024)); } }
當使用 HTTP 時,建議開啟壓縮功能以盡可能多地減小傳輸數(shù)據(jù)的大小。雖然壓縮會帶來一些消耗,但通常來說它都是一個好主意,尤其是對于文本數(shù)據(jù)而言
Netty 為壓縮和解壓都提供了 ChannelHandler 實現(xiàn),它們同時支持 gzip 和 deflate 編碼
客戶端可以通過提供以下頭部信息來指示服務(wù)器它所支持的壓縮格式
GET /encrypted-area HTTP/1.1
Host: www.example.com
Accept-Encoding: gzip, deflate
然而,需要注意的是,服務(wù)器沒有義務(wù)壓縮它所發(fā)送的數(shù)據(jù)
public class HttpCompressionInitializer extends ChannelInitializer<Channel> { private final boolean isClient; public HttpCompressionInitializer(boolean isClient) { this.isClient = isClient; } @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); if (isClient) { // 如果是客戶端,則添加 HTTPClientCodec pipeline.addLast("codec", new HttpClientCodec()); // 如果是客戶端,則添加 HttpContentDecompressor 以處理來自服務(wù)器的壓縮內(nèi)容 pipeline.addLast("decompressor", new HttpContentDecompressor()); } else { // 如果是服務(wù)端,則添加 HttpServerCodec pipeline.addLast("codec", new HttpServerCodec()); // 如果是服務(wù)器,則添加 HttpContentDecompressor 來壓縮數(shù)據(jù) pipeline.addLast("decompressor", new HttpContentDecompressor()); } } }
啟用 HTTPS 只需要將 SslHandler 添加到 ChannelPipeline 的 ChannelHandler 組合中
public class HttpsCodecInitializer extends ChannelInitializer<Channel> { private final SslContext context; private final boolean isClient; public HttpsCodecInitializer(SslContext context, boolean isClient) { this.context = context; this.isClient = isClient; } @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); SSLEngine engine = context.newEngine(ch.alloc()); pipeline.addLast("ssl", new SslHandler(engine)); if (isClient) { pipeline.addLast("codec", new HttpClientCodec()); } else { pipeline.addLast("codec", new HttpServerCodec()); } } }
WebSocket 解決了一個長期存在的問題:既然底層協(xié)議(HTTP)是一個請求/響應模式的交互序列,那么如何實時地發(fā)布信息呢?AJAX一定程度上解決了這個問題,但數(shù)據(jù)流仍然是由客戶端所發(fā)送的請求驅(qū)動的
WebSocket 提供了在單個 TCP 連接上提供雙向的通信,它為網(wǎng)頁和遠程服務(wù)器之間的雙向通信提供了一種替代 HTTP 輪詢的方案
要想向你的應用程序添加對于 WebSocket 的支持,你需要將適當?shù)目蛻舳嘶蛘叻?wù)器 WebSocketChannelHandler 添加到 ChannelPipeline 中。這個類將處理由 WebSocket 定義的稱為幀的特殊消息類型,如表所示,WebSocketFrame 可以被歸類為數(shù)據(jù)幀或者控制幀
名稱 | 描述 |
---|---|
BinaryWebSocketFrame | 數(shù)據(jù)幀:二進制數(shù)據(jù) |
TextWebSocketFrame | 數(shù)據(jù)幀:文本數(shù)據(jù) |
ContinuationWebSocketFrame | 數(shù)據(jù)幀:屬于上一個 BinaryWebSocketFrame 或者 TextWebSocketFrame 的文本或者二進制的數(shù)據(jù) |
CloseWebSocketFrame | 控制幀:一個 CLOSE 請求,關(guān)閉的狀態(tài)碼以及關(guān)閉的原因 |
PingWebSocketFrame | 控制幀:請求一個 PongWebSocketFrame |
PongWebSocketFrame | 控制幀:對 PingWebSocketFrame 請求的響應 |
因為 Netty 主要是一種服務(wù)器端技術(shù),所以我們重點創(chuàng)建 WebSocket 服務(wù)器。下述代碼展示了使用 WebSocketChannelHandler 的簡單示例,這個類會處理協(xié)議升級握手,以及三種控制幀 —— Close、Ping 和 Pong,Text 和 Binary 數(shù)據(jù)幀將會被傳遞給下一個 ChannelHandler 進行處理
public class WebSocketServerInitializer extends ChannelInitializer<Channel> { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast( new HttpServerCodec(), new HttpObjectAggregator(65536), // 如果被請求的端點是 /websocket,則處理該升級握手 new WebSocketServerProtocolHandler("/websocket"), // TextFrameHandler 處理 TextWebSocketFrame new TextFrameHandler(), // BinaryFrameHandler 處理 BinaryWebSocketFrame new BinaryFrameHandler(), // ContinuationFrameHandler 處理 Continuation WebSocketFrame new ContinuationFrameHandler()); } public static final class TextFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { @Override protected void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { // do something } } public static final class BinaryFrameHandler extends SimpleChannelInboundHandler<BinaryWebSocketFrame> { @Override protected void messageReceived(ChannelHandlerContext ctx, BinaryWebSocketFrame msg) throws Exception { // do something } } public static final class ContinuationFrameHandler extends SimpleChannelInboundHandler<ContinuationWebSocketFrame> { @Override protected void messageReceived(ChannelHandlerContext ctx, ContinuationWebSocketFrame msg) throws Exception { // do something } } }
到此,關(guān)于“怎么開發(fā)基于Netty的HTTP/HTTPS應用程序”的學習就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。