本章内容包括(分2次翻译):
1)利用SSL/TLS构建安全的Netty应用
2)构建HTTP/HTTPS的应用
3)处理闲置的连接和超时
4)空格符切分协议和长度切分的协议的解码
5)写入大数据
Netty为各式各样的协议提供了很多译码器和处理器的类,这些类你可以做到拿来即用,可以使你在有些比较麻烦的事件上不用花费不必要的时间和精力,在这个章节我们将会把这些工具介绍给你,且一一分析他们的作用,这些工具包括:支持SSL/TLS和Websocket的工具,也有比HTTP天然的数据压缩更高性能的数据挤压功能,等等
11.1 Securing Netty applications with SSL/TLS
数据隐私是当代一个比较重要的议题,所以作为开发者的我们应该做好十足的准备去处理这个问题,首先我们必须熟悉一些比较常见的加密协议,例如SSL或者TLS协议,这两种协议处于数据保密协议的最重要的位置,当我们去浏览一些安全性比较高的网站的时候,我们就会接触到这些协议,当然这些协议也是可以用于不是基于HTTP协议的应用中的,例如SMTPS邮件服务中,甚至是相关的数据库系统中
为了支持SSL和TLS,Java提供了javax.net.ssl工具包,这个包里面的SSLContext和SSLEngine可以简单直接地实现加密和解密,Netty利用这个Java实现的API去实现ChannelHandler接口名为SslHandler的类,这个类其实是内部使用SSLEngine来做一些真实的事
TIPS:Netty的OpenSSL/SSLEngine实现
Netty也提供了使用OpenSSL工具类实现的SSLEngine,这个类叫做OpenSslEngine,这个类比原始的JDK提供的SSLEngine有着更好的性能
当OpenSSL的jar包库在提供的情况下,Netty的客户端和服务端默认是使用的OpenSslEngine的,如果没有提供的情况下,将会在使用JDK提供的,更多关于OpenSSL的介绍可以查看Netty的文档
注意到不管你是用JDK实现的还是Netty的OpenSslEngine对于数据传输来说其实是一样的
代码清单展示了一个SslHandler是如何通过一个ChannelInitializer来增加到ChannelPipeline中的,回想一下,一旦Channel被注册ChannelInitializer将会被设置到ChannelPipeline中
在很多的案例中,SslHandler经常是作为ChannelPipeline中的第一个ChannelHandler的,这样可以保证加密的动作是发生在所有的业务逻辑操作之后
SslHandler有一些比较有用的方法,如表11.1所示,具体来说,在握手阶段,两个终端相互验证且达成一个加密方式,你可以配置SslHandler来编辑它的行为或者当SSL/TLS握手完成的时候提供通知,等做完这一切,所有的数据被加密,SSL/TLS的握手将会被自动执行
11.2 Building Netty HTTP/HTTPS applications
HTTP/HTTPS是我们最最常见的协议之一,随着移动手机端的日夜发展,对于任何一家公司来说,有一个支持手机端的网页已经变成了一个必不可少的需求,这些协议也被用在了其他的方面,WebService的API被很多组织暴露出来用来与他们的企业合作伙伴基于Http协议通信
接下来,我们将看见Netty提供的一些相关方面的ChanneHandler,有了这些工具你可以不需要编写自定义的译码器来使用HTTP和HTTPS协议
11.2.1 HTTP decoder, encoder, and codec
HTTP是基于请求响应模式的,客户端发送Http请求到服务器端,服务器端反馈一个HTTP响应,Netty提供了一系列的编码和解码的类来简化对该协议的使用,图11.2和11.3分别展示了完整的HTTP请求体和响应体
如图11.2和11.3展示的一样,一个HTTP请求或者响应包括多个数据组成部分,他们都是以一个LastHttpContent部分作为终止,FullHttpRequest和FullHttpResponse信息是具体的衍生类分别代表了一个完整的请求和响应,所有的实现HttpObject接口的Http信息类型的模型类Request或者LastHttpContent如下列表图11.2和表11.2展示:
表11.2给出了HTTP解码和编码一些常用的类一个总览
下面的代码清单中的HttpPipelineInitializer展示了如何将Netty对HTTP的支持运用到你的应用中去的示例,作为使用者的你几乎只需要将正确的ChannelHandler降入到ChannelPipeline中去就可以了
11.2.2 HTTP message aggregation
当所有的初始化的工作已经将处理器安装到ChannelPipeline中去后,你就可以操作不同类型的HttpObject对象了,但是我们之前讲过一个HTTP请求或者响应是由多个部分组成的,所以你需要将这些信息聚合一下组成一个完整的信息,为了消除这种令人头疼的工作,Netty提供了一个聚合工具可以使多个信息体合并成一个FullHttpRequest或者FullHttpResponse信息体,通过这种方法你就可以总是看见完整的信息内容了
这个聚合操作是有一定的性能损失的,因为到达的信息端需要在缓存区等待直到组装成完整的信息转向到下一个ChannelInboundHandler中,这种折中的好处就是你不需要担心信息破碎的问题
介绍这种自动的聚合只需要做一件事:增加一个ChannelHandler到管道中去就可以了,下面的代码清单展示了如何做:
11.2.3 HTTP compression
当使用HTTP协议的时候,我们推荐使用压缩技术来尽可能的减少传输消息体的大小,尽管压缩会花费CPU的部分性能,但是总而言之,压缩思想是一个不错的主意,特别是在文本传输的时候
Netty提供了ChannelHandler来支持压缩和解压缩,并且支持gzip和deflate两种编码格式
下面的代码清单展示了这个例子:
TIPS:压缩的jar包依赖
如果你使用JDK1.6或者更早的版本,你可能需要增加JZlib包的依赖到你的classpatch下来支持压缩功能,如果使用的是Maven,请使用如下的依赖
11.2.4 Using HTTPS
下面的代码清单展示了如果想要支持HTTPS,只需要增加一个SslHandler到混合的Handler中去就可以了
这个代码很好的展示了Netty的架构理念:可重复利用组件,诚如,你只需要增加一个ChannelHandler到ChannelPipeline中就可以提供一个高性能的且加密的应用了
11.2.5 WebSocket
Netty对基于HTTP协议的应用提供了大量的工具包,使用这些工具包可以支持很多现金的特性,在这个小节中,我们将初探WebSocket,一个由IETF在2011年提出的一个年轻的协议
WebSocket处理了“世纪”的难题,如果基于底层的基础协议HTTP去实时推送信息,HTTP做一个典型的请求响应交互协议原生是不支持这种场景的,虽然AJAX技术做了一些弥补,但是数据流的驱动依旧依赖于客户端,还有一些其他的其他的解决方案,但是到目前为止,这些解决方案都是在一种限制中为客户工作着
WebSocket规范和它具体的实现代表了这个领域更加先进的一次尝试,简而言之,WebSocket提供了一个单独的TCP连接用来进行双向的数据传输,有了WebSocket的API,现在对了数据双向传输有了新的方案,不需要像以前客户端轮询服务器端来获取双向数据传输这种功能了
WebSocket提供了服务器端和客户端一个真实的数据双向交互的技术,这个小节,我们并不会深入WebSocket技术内部去探究实现的更多的细节,但是我们必须意识到一些早期的双向通信是受限于文本数据传输的,现在这种限制将不复存在了,WebSocket可以为任何数据使用,看起来更像一种正常的协议了
图11.4展示了Websocket的设计理念图,在这种设计场景下,那么通信将以普通的HTTP协议为基础,且能升级功能使其有双向数据传输的功能
为了将WebSocket的功能增加到我们的应用中去,我们需要增加恰当的客户端或者服务器端的ChannelHandler到管道中去,这个类是专门用来处理WebSocket定义的一些特殊类型的信息,比我们我们熟知的frames,如表11.3所示,WebSocket可以被分类为数据或者控制帧
因为Netty原则上是服务器端的技术,所以我们偏向于创建一个WebSocket服务器,代码清单11.6向我们展示了使用WebSocketServerProtocolHandler构建一个简单的示例,这个类处理了协议,升级了握手阶段的处理,也使用了三个控制帧,Close,Ping,Pong,文本和字节的数据帧将通过此handler会随着管道传输到下一个handler中
TIPS:安全的WebSocket
为了增加WebSocket的安全性,我们只需将SslHandler当做第一个ChannelHandler增加到管道中就可以了
关于WebSocket的更多例子,详细请参考第十二章的内容,该章节的内容将会深入讲解一个实时webSocket应用的设计
11.3 Idle connections and timeouts
到目前为止,我们已经讲解了Netty通过给以几个具体的代码和处理类来提供对HTTP,HTTPS,Websocket的支持,这些技术可以使你的应用更加的高效,安全,利用率变得更高,并且可以更加高效地管理你的应用资源,所以,我们现在来聊聊一个比较核心的概念-------连接管理
检测空置的连接和超时对于及时的释放资源是至关重要的,对于这个重要而又普通的任务,Netty提供了几个具体的ChannelHandler实现来实现这方面的功能,表11.4给出了这个类的总览:
我们详细地说明一下IdleStateHandler,这是在实际项目里使用最频繁的类,代码清单11.7展示了当在60秒内没有数据接收或者发送的时候获取到一个通知的案例,使用一个比较大众的解决方案发送一个心跳包到远程端,如果没有响应,那么则把连接关闭
这个例子说明了如何雇佣一个IdleStateHandler来测试远程端是否还存活,如果远程端没有存活那么可以通过关闭资源来释放资源,IdleStateHandler以IdleStateEvent为入参如果连接在60秒内没有接受或者发送数据的时候调用userEventTriggered方法,HeartbeatHandler方法具体实现了userEventTriggered方法,这个方法会检测IdleStateEvent,这个事件会发送一个心跳信息,且增加了一个ChannelFutureListener当发送心跳包失败的情况下来关闭连接