最常見的一種協(xié)議是 HTTP/HTTPS ,尤其在智能手機上更是廣泛使用。雖然我們可以通過HTTP或HTTPS訪問一家公司的主頁,但是它其實還有別的用處。就像許多組織會通過HTTP(S)來公開 WebService API ,這樣做的目的是可以緩解獨立平臺帶來的弊端。
下面讓我們來看看 Netty 提供的 ChannelHandler是怎樣允許您使用 HTTP 和 HTTPS 而無需編寫自己的編解碼器。
HTTP 是請求-響應(yīng)模式,客戶端發(fā)送一個 HTTP 請求,服務(wù)就響應(yīng)此請求。Netty 提供了簡單的編碼、解碼器來簡化基于這個協(xié)議的開發(fā)工作。圖8.2和圖8.3顯示 HTTP 請求和響應(yīng)的方法是如何生產(chǎn)和消費的
Figure 8.2 HTTP request component parts
Figure 8.3 HTTP response component parts
如圖8.2和8.3所示的 HTTP 請求/響應(yīng)可能包含不止一個數(shù)據(jù)部分,它總是終止于 LastHttpContent 部分。FullHttpRequest 和FullHttpResponse 消息是特殊子類型,分別表示一個完整的請求和響應(yīng)。所有類型的 HTTP 消息(FullHttpRequest ,LastHttpContent 以及那些如清單8.2所示)實現(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)用程序是多么簡單。僅僅添加正確的 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 消息進行操作。但由于 HTTP 請求和響應(yīng)可以由許多部分組合而成,你需要聚合他們形成完整的消息。為了消除這種繁瑣任務(wù), Netty 提供了一個聚合器,合并消息部件到 FullHttpRequest 和 FullHttpResponse 消息。這樣您總是能夠看到完整的消息內(nèi)容。
這個操作有一個輕微的成本,消息段需要緩沖,直到完全可以將消息轉(zhuǎn)發(fā)到下一個 ChannelInboundHandler 管道。但好處是,你不必擔心消息碎片。
實現(xiàn)自動聚合只需添加另一個 ChannelHandler 到 ChannelPipeline。清單8.3顯示了這是如何實現(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ù)據(jù)以減少傳輸流量,壓縮數(shù)據(jù)會增加 CPU 負載,現(xiàn)在的硬件設(shè)施都很強大,大多數(shù)時候壓縮數(shù)據(jù)時一個好主意。Netty 支持“gzip”和“deflate”,為此提供了兩個 ChannelHandler 實現(xiàn)分別用于壓縮和解壓??聪旅娲a:
客戶端可以通過提供下面的頭顯示支持加密模式。然而服務(wù)器不是,所以不得不壓縮它發(fā)送的數(shù)據(jù)。
GET /encrypted-area HTTP/1.1
Host: www.example.com
Accept-Encoding: gzip, deflate
下面是一個例子
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
}
}
}
上面的代碼就是一個很好的例子,解釋了 Netty 的架構(gòu)是如何讓“重用”變成了“杠桿”。我們可以添加一個新的功能,甚至是一樣重要的加密支持,幾乎沒有工作量,只需添加一個ChannelHandler 到 ChannelPipeline。
HTTP 是不錯的協(xié)議,但是如果需要實時發(fā)布信息怎么做?有個做法就是客戶端一直輪詢請求服務(wù)器,這種方式雖然可以達到目的,但是其缺點很多,也不是優(yōu)秀的解決方案,為了解決這個問題,便出現(xiàn)了 WebSocket。
WebSocket 允許數(shù)據(jù)雙向傳輸,而不需要請求-響應(yīng)模式。早期的WebSocket 只能發(fā)送文本數(shù)據(jù),然后現(xiàn)在不僅可以發(fā)送文本數(shù)據(jù),也可以發(fā)送二進制數(shù)據(jù),這使得可以使用 WebSocket 構(gòu)建你想要的程序。下圖是WebSocket 的通信示例圖:
WebSocket 規(guī)范及其實現(xiàn)是為了一個更有效的解決方案。簡單的說, 一個WebSocket 提供一個 TCP 連接兩個方向的交通。結(jié)合 WebSocket API 它提供了一個替代 HTTP 輪詢雙向通信從頁面到遠程服務(wù)器。
也就是說,WebSocket 提供真正的雙向客戶機和服務(wù)器之間的數(shù)據(jù)交換。 我們不會對內(nèi)部太多的細節(jié),但我們應(yīng)該提到,雖然最早實現(xiàn)僅限于文本數(shù)據(jù),但現(xiàn)在不再是這樣,WebSocket可以用于任意數(shù)據(jù),就像一個正常的套接字。
圖8.4給出了一個通用的 WebSocket 協(xié)議。在這種情況下的通信開始于普通 HTTP ,并“升級”為雙向 WebSocket。
Figure 8.4 WebSocket protocol
添加應(yīng)用程序支持 WebSocket 只需要添加適當?shù)目蛻舳嘶蚍?wù)器端WebSocket ChannelHandler 到管道。這個類將處理特殊 WebSocket 定義的消息類型,稱為“幀?!叭绫?.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 的主要是一個服務(wù)器端技術(shù)重點在這里創(chuàng)建一個 WebSocket server 。清單8.6使用 WebSocketServerProtocolHandler 提出了一個簡單的例子。該類處理協(xié)議升級握手以及三個“控制”幀 Close, Ping 和 Pong。Text 和 Binary 數(shù)據(jù)幀將被傳遞到下一個處理程序(由你實現(xià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 第一個 ChannelHandler
SPDY(讀作“SPeeDY”)是Google 開發(fā)的基于 TCP 的應(yīng)用層協(xié)議,用以最小化網(wǎng)絡(luò)延遲,提升網(wǎng)絡(luò)速度,優(yōu)化用戶的網(wǎng)絡(luò)使用體驗。SPDY 并不是一種用于替代 HTTP 的協(xié)議,而是對 HTTP 協(xié)議的增強。SPDY 實現(xiàn)技術(shù):
SPDY 主要有5個版本:
SPDY 被很多瀏覽器支持,包括 Google Chrome, Firefox, 和 Opera
Netty 支持 版本 2 和 3 (包含3.1)的支持。這些版本被廣泛應(yīng)用,可以支持更多的用戶。更多內(nèi)容詳見 Chapter 12
更多建議: