最常見(jiàn)的一種協(xié)議是 HTTP/HTTPS ,尤其在智能手機(jī)上更是廣泛使用。雖然我們可以通過(guò)HTTP或HTTPS訪問(wèn)一家公司的主頁(yè),但是它其實(shí)還有別的用處。就像許多組織會(huì)通過(guò)HTTP(S)來(lái)公開(kāi) WebService API ,這樣做的目的是可以緩解獨(dú)立平臺(tái)帶來(lái)的弊端。
下面讓我們來(lái)看看 Netty 提供的 ChannelHandler是怎樣允許您使用 HTTP 和 HTTPS 而無(wú)需編寫自己的編解碼器。
HTTP 是請(qǐng)求-響應(yīng)模式,客戶端發(fā)送一個(gè) HTTP 請(qǐng)求,服務(wù)就響應(yīng)此請(qǐng)求。Netty 提供了簡(jiǎn)單的編碼、解碼器來(lái)簡(jiǎn)化基于這個(gè)協(xié)議的開(kāi)發(fā)工作。圖8.2和圖8.3顯示 HTTP 請(qǐng)求和響應(yīng)的方法是如何生產(chǎn)和消費(fèi)的
Figure 8.2 HTTP request component parts
Figure 8.3 HTTP response component parts
如圖8.2和8.3所示的 HTTP 請(qǐng)求/響應(yīng)可能包含不止一個(gè)數(shù)據(jù)部分,它總是終止于 LastHttpContent 部分。FullHttpRequest 和FullHttpResponse 消息是特殊子類型,分別表示一個(gè)完整的請(qǐng)求和響應(yīng)。所有類型的 HTTP 消息(FullHttpRequest ,LastHttpContent 以及那些如清單8.2所示)實(shí)現(xiàn) HttpObject 接口。
表8.2概述 HTTP 解碼器和編碼器的處理和生產(chǎn)這些消息。
Table 8.2 HTTP decoder and encoder
名稱 | 描述 |
---|---|
HttpRequestEncoder | Encodes HttpRequest , HttpContent and LastHttpContent messages to bytes. |
HttpResponseEncoder | Encodes HttpResponse, HttpContent and LastHttpContent messages to bytes. |
HttpRequestDecoder | Decodes bytes into HttpRequest, HttpContent and LastHttpContent messages. |
HttpResponseDecoder | Decodes bytes into HttpResponse, HttpContent and LastHttpContent messages. |
清單8.2所示的是將支持 HTTP 添加到您的應(yīng)用程序是多么簡(jiǎn)單。僅僅添加正確的 ChannelHandler 到 ChannelPipeline 中
Listing 8.2 Add support for HTTP
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) {
pipeline.addLast("decoder", new HttpResponseDecoder()); //1
pipeline.addLast("encoder", new HttpRequestEncoder()); //2
} else {
pipeline.addLast("decoder", new HttpRequestDecoder()); //3
pipeline.addLast("encoder", new HttpResponseEncoder()); //4
}
}
}
安裝 ChannelPipeline 中的初始化之后,你能夠?qū)Σ煌?HttpObject 消息進(jìn)行操作。但由于 HTTP 請(qǐng)求和響應(yīng)可以由許多部分組合而成,你需要聚合他們形成完整的消息。為了消除這種繁瑣任務(wù), Netty 提供了一個(gè)聚合器,合并消息部件到 FullHttpRequest 和 FullHttpResponse 消息。這樣您總是能夠看到完整的消息內(nèi)容。
這個(gè)操作有一個(gè)輕微的成本,消息段需要緩沖,直到完全可以將消息轉(zhuǎn)發(fā)到下一個(gè) ChannelInboundHandler 管道。但好處是,你不必?fù)?dān)心消息碎片。
實(shí)現(xiàn)自動(dòng)聚合只需添加另一個(gè) ChannelHandler 到 ChannelPipeline。清單8.3顯示了這是如何實(shí)現(xiàn)的。
Listing 8.3 Automatically aggregate HTTP message fragments
public class HttpAggregatorInitializer extends ChannelInitializer<Channel> {
private final boolean client;
public HttpAggregatorInitializer(boolean client) {
this.client = client;
}
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
if (client) {
pipeline.addLast("codec", new HttpClientCodec()); //1
} else {
pipeline.addLast("codec", new HttpServerCodec()); //2
}
pipeline.addLast("aggegator", new HttpObjectAggregator(512 * 1024)); //3
}
}
使用 HTTP 時(shí)建議壓縮數(shù)據(jù)以減少傳輸流量,壓縮數(shù)據(jù)會(huì)增加 CPU 負(fù)載,現(xiàn)在的硬件設(shè)施都很強(qiáng)大,大多數(shù)時(shí)候壓縮數(shù)據(jù)時(shí)一個(gè)好主意。Netty 支持“gzip”和“deflate”,為此提供了兩個(gè) ChannelHandler 實(shí)現(xiàn)分別用于壓縮和解壓??聪旅娲a:
客戶端可以通過(guò)提供下面的頭顯示支持加密模式。然而服務(wù)器不是,所以不得不壓縮它發(fā)送的數(shù)據(jù)。
GET /encrypted-area HTTP/1.1
Host: www.example.com
Accept-Encoding: gzip, deflate
下面是一個(gè)例子
Listing 8.4 Automatically compress HTTP messages
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) {
pipeline.addLast("codec", new HttpClientCodec()); //1
pipeline.addLast("decompressor",new HttpContentDecompressor()); //2
} else {
pipeline.addLast("codec", new HttpServerCodec()); //3
pipeline.addLast("compressor",new HttpContentCompressor()); //4
}
}
}
壓縮與依賴
注意,Java 6或者更早版本,如果要壓縮數(shù)據(jù),需要添加 jzlib 到 classpath
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jzlib</artifactId>
<version>1.1.3</version>
</dependency>
啟用 HTTPS,只需添加 SslHandler
Listing 8.5 Using HTTPS
public class HttpsCodecInitializer extends ChannelInitializer<Channel> {
private final SslContext context;
private final boolean client;
public HttpsCodecInitializer(SslContext context, boolean client) {
this.context = context;
this.client = client;
}
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
SSLEngine engine = context.newEngine(ch.alloc());
pipeline.addFirst("ssl", new SslHandler(engine)); //1
if (client) {
pipeline.addLast("codec", new HttpClientCodec()); //2
} else {
pipeline.addLast("codec", new HttpServerCodec()); //3
}
}
}
上面的代碼就是一個(gè)很好的例子,解釋了 Netty 的架構(gòu)是如何讓“重用”變成了“杠桿”。我們可以添加一個(gè)新的功能,甚至是一樣重要的加密支持,幾乎沒(méi)有工作量,只需添加一個(gè)ChannelHandler 到 ChannelPipeline。
HTTP 是不錯(cuò)的協(xié)議,但是如果需要實(shí)時(shí)發(fā)布信息怎么做?有個(gè)做法就是客戶端一直輪詢請(qǐng)求服務(wù)器,這種方式雖然可以達(dá)到目的,但是其缺點(diǎn)很多,也不是優(yōu)秀的解決方案,為了解決這個(gè)問(wèn)題,便出現(xiàn)了 WebSocket。
WebSocket 允許數(shù)據(jù)雙向傳輸,而不需要請(qǐng)求-響應(yīng)模式。早期的WebSocket 只能發(fā)送文本數(shù)據(jù),然后現(xiàn)在不僅可以發(fā)送文本數(shù)據(jù),也可以發(fā)送二進(jìn)制數(shù)據(jù),這使得可以使用 WebSocket 構(gòu)建你想要的程序。下圖是WebSocket 的通信示例圖:
WebSocket 規(guī)范及其實(shí)現(xiàn)是為了一個(gè)更有效的解決方案。簡(jiǎn)單的說(shuō), 一個(gè)WebSocket 提供一個(gè) TCP 連接兩個(gè)方向的交通。結(jié)合 WebSocket API 它提供了一個(gè)替代 HTTP 輪詢雙向通信從頁(yè)面到遠(yuǎn)程服務(wù)器。
也就是說(shuō),WebSocket 提供真正的雙向客戶機(jī)和服務(wù)器之間的數(shù)據(jù)交換。 我們不會(huì)對(duì)內(nèi)部太多的細(xì)節(jié),但我們應(yīng)該提到,雖然最早實(shí)現(xiàn)僅限于文本數(shù)據(jù),但現(xiàn)在不再是這樣,WebSocket可以用于任意數(shù)據(jù),就像一個(gè)正常的套接字。
圖8.4給出了一個(gè)通用的 WebSocket 協(xié)議。在這種情況下的通信開(kāi)始于普通 HTTP ,并“升級(jí)”為雙向 WebSocket。
Figure 8.4 WebSocket protocol
添加應(yīng)用程序支持 WebSocket 只需要添加適當(dāng)?shù)目蛻舳嘶蚍?wù)器端WebSocket ChannelHandler 到管道。這個(gè)類將處理特殊 WebSocket 定義的消息類型,稱為“幀。“如表8.3所示,這些可以歸類為“數(shù)據(jù)”和“控制”幀。
Table 8.3 WebSocketFrame types
名稱 | 描述 |
---|---|
BinaryWebSocketFrame | Data frame: binary data |
TextWebSocketFrame | Data frame: text data |
ContinuationWebSocketFrame | Data frame: text or binary data that belongs to a previous BinaryWebSocketFrame or TextWebSocketFrame |
CloseWebSocketFrame | Control frame: a CLOSE request, close status code and a phrase |
PingWebSocketFrame | Control frame: requests the send of a PongWebSocketFrame |
PongWebSocketFrame | Control frame: sent as response to a PingWebSocketFrame |
由于 Netty 的主要是一個(gè)服務(wù)器端技術(shù)重點(diǎn)在這里創(chuàng)建一個(gè) WebSocket server 。清單8.6使用 WebSocketServerProtocolHandler 提出了一個(gè)簡(jiǎn)單的例子。該類處理協(xié)議升級(jí)握手以及三個(gè)“控制”幀 Close, Ping 和 Pong。Text 和 Binary 數(shù)據(jù)幀將被傳遞到下一個(gè)處理程序(由你實(shí)現(xiàn))進(jìn)行處理。
Listing 8.6 Support WebSocket on the server
public class WebSocketServerInitializer extends ChannelInitializer<Channel> {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(
new HttpServerCodec(),
new HttpObjectAggregator(65536), //1
new WebSocketServerProtocolHandler("/websocket"), //2
new TextFrameHandler(), //3
new BinaryFrameHandler(), //4
new ContinuationFrameHandler()); //5
}
public static final class TextFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
@Override
public void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
// Handle text frame
}
}
public static final class BinaryFrameHandler extends SimpleChannelInboundHandler<BinaryWebSocketFrame> {
@Override
public void channelRead0(ChannelHandlerContext ctx, BinaryWebSocketFrame msg) throws Exception {
// Handle binary frame
}
}
public static final class ContinuationFrameHandler extends SimpleChannelInboundHandler<ContinuationWebSocketFrame> {
@Override
public void channelRead0(ChannelHandlerContext ctx, ContinuationWebSocketFrame msg) throws Exception {
// Handle continuation frame
}
}
}
加密 WebSocket 只需插入 SslHandler 到作為 pipline 第一個(gè) ChannelHandler
詳見(jiàn) Chapter 11 WebSocket
SPDY(讀作“SPeeDY”)是Google 開(kāi)發(fā)的基于 TCP 的應(yīng)用層協(xié)議,用以最小化網(wǎng)絡(luò)延遲,提升網(wǎng)絡(luò)速度,優(yōu)化用戶的網(wǎng)絡(luò)使用體驗(yàn)。SPDY 并不是一種用于替代 HTTP 的協(xié)議,而是對(duì) HTTP 協(xié)議的增強(qiáng)。SPDY 實(shí)現(xiàn)技術(shù):
SPDY 主要有5個(gè)版本:
SPDY 被很多瀏覽器支持,包括 Google Chrome, Firefox, 和 Opera
Netty 支持 版本 2 和 3 (包含3.1)的支持。這些版本被廣泛應(yīng)用,可以支持更多的用戶。更多內(nèi)容詳見(jiàn) Chapter 12
更多建議: