溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么開發(fā)基于Netty的HTTP/HTTPS應用程序

發(fā)布時間:2021-06-29 16:05:02 來源:億速云 閱讀:175 作者:chen 欄目:開發(fā)技術(shù)

這篇文章主要介紹“怎么開發(fā)基于Netty的HTTP/HTTPS應用程序”,在日常操作中,相信很多人在怎么開發(fā)基于Netty的HTTP/HTTPS應用程序問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么開發(fā)基于Netty的HTTP/HTTPS應用程序”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

目錄
  • 一、通過 SSL/TLS 保護應用程序

  • 二、HTTP 編解碼器

  • 三、聚合 HTTP 消息

  • 四、HTTP 壓縮

  • 五、HTTPS

  • 六、WebSocket

一、通過 SSL/TLS 保護應用程序

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)

怎么開發(fā)基于Netty的HTTP/HTTPS應用程序

下述代碼展示了如何使用 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 編解碼器

HTTP 是基于請求/響應模式的,客戶端向服務(wù)器發(fā)送一個 HTTP 請求,然后服務(wù)器將會返回一個 HTTP 響應,Netty 提供了多種多種編碼器和解碼器以簡化對這個協(xié)議的使用

下圖分別展示了生產(chǎn)和消費 HTTP 請求和 HTTP 響應的方法

怎么開發(fā)基于Netty的HTTP/HTTPS應用程序

怎么開發(fā)基于Netty的HTTP/HTTPS應用程序

如圖所示,一個 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());
        }
    }
}

三、聚合 HTTP 消息

在 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 壓縮

當使用 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

啟用 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

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>

向AI問一下細節(jié)

免責聲明:本站發(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)容。

AI