文章目录
前言
Netty为许多通用协议提供了编解码器和处理器,几乎可以开箱即用,这减少了你在那些相当繁琐的事务上本来会花费的时间与精力,本文会结合一些案例进行分析和应用内置的编解码器
基于Netty构建HTTP/HTTPS应用程序
Netty内置的一系列编解码器和ChannelHandler,其中就有可以支持用来处理HTTP和HTTPS协议的
HTTP协议相关编解码器
HTTP是基于请求/响应模式的:客户端向服务器发送一个 HTTP 请求,然后服务器将会返回一个 HTTP 响应。Netty提供了多种编码器和解码器以简化对这个协议的使用
HTTP请求的组成部分:
HTTP响应的组成部分:
从上面的图可以看出,一个HTTP 请求/响应可能由多个数据部分组成,并且总是以一个LastHttpContent部分作为结束。其中FullHttpRequest和FullHttpResponse 消息是特殊的子类型,分别代表了完整的请求和响应。所有类型的 HTTP 消息(FullHttpRequest、 LastHttpContent 等等)都实现了 HttpObject 接口
HTTP解码器和编码器:
名称 | 描述 |
---|---|
HttpRequestEncoder | 将 HttpRequest、HttpContent 和 LastHttpContent 消息编码为字节 |
HttpResponseEncoder | 将 HttpResponse、HttpContent 和 LastHttpContent 消息编码为字节 |
HttpRequestDecoder | 将字节解码为 HttpRequest、HttpContent 和 LastHttpContent 消息 |
HttpResponseDecoder | 将字节解码为 HttpResponse、HttpContent 和 LastHttpContent 消息 |
HttpClientCodec | 将客户端请求和响应做一个组合 |
HttpServerCodec | 将服务端请求和响应做一个组合 |
HTTP聚合消息
由于HTTP的请求和响应可能由许多部分组成,因此你需要聚合它们以形成完整的消息。为了消除这项繁琐的任务,Netty 提供了一个聚合器HttpObjectAggregator,它可以将多个消息部分合并为 FullHttpRequest 或者 FullHttpResponse 消息。通过这样的方式,你将总是看到完整的消息内容
由于消息分段需要被缓存,直到可以转发一个完整的消息给下一个ChannelInboundHandler,所以这个操作有轻微的开销,其所带来的好处便是你不必关心消息碎片了
HTTP压缩
当使用 HTTP 时,都会建议开启压缩功能以尽可能多地减小传输数据的大小。虽然压缩会带来一些 CPU 时钟周期上的开销,但是通常来说它都是一个好主意,特别是对于文本数据来说
Netty 为压缩提供了一个HttpContentCompressor处理器,为解压缩提供了一个HttpContentDecompressor处理器,它们同时支持gzip和deflate编码
注意:如果你使用的JDK为1.6或者更早的版本,那么需要将JZlib (www.jcraft.com/jzlib/ ) 添加到CLASSPATH中以支持压缩功能。在Maven中,需要加入依赖:
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jzlib</artifactId>
<version>1.1.3</version>
</dependency>
配置SSL,启用HTTPS
Netty通过一个名为SslHandler的ChannelHandler来支持SSL/TLS,其内部使用SSLEngine来完成实际的工作。而启用HTTPS只需要将 SslHandler 添加到 ChannelPipeline 的 ChannelHandler 组合中即可
实战
自定义HTTP Server
/**
* 通过Netty自定义Http Server
*/
public class MyHTTPServer {
private static EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
private static ServerBootstrap bootstrap = new ServerBootstrap();
private static final int PORT = 8761;
private static final boolean IS_SSL = false; //是否开启SSL认证
public static void startServer() throws SSLException, CertificateException{
SslContext sslContext = null;
if(IS_SSL){
//签名认证
SelfSignedCertificate selfSignedCertificate= new SelfSignedCertificate();
sslContext = SslContextBuilder.forServer(selfSignedCertificate.certificate(),selfSignedCertificate.privateKey()).build();
}
try {
bootstrap.group(eventLoopGroup).
channel(NioServerSocketChannel.class).
localAddress(PORT).childHandler(new HttpInitHandler(sslContext));
ChannelFuture future=bootstrap.bind().sync();
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
}finally{
eventLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
//启动服务
try {
startServer();
} catch (SSLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (CertificateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
服务端添加相关的ChannelHandler
public class HttpInitHandler extends ChannelInitializer<SocketChannel>{
private SslContext sslContext;
public HttpInitHandler(SslContext sslContext) {
super();
this.sslContext = sslContext;
}
@Override
protected void initChannel(SocketChannel ch) throws Exception {
if(null!=sslContext){
SSLEngine engine=sslContext.newEngine(ch.alloc());
//将SslHandler添加到ChannelPipeline中以使用HTTPS
ch.pipeline().addFirst(new SslHandler(engine));
}
//添加HTTP请求的解码器
ch.pipeline