Netty 的开箱即用
Netty 为许多通用协议提供了编解码器和处理器,几乎可以开箱即用,这减少了你在那些相当繁琐的事务上本来会花费的时间与精力。
在本章中,我们将探讨这些工具以及它们所带来的好处,其中包括Netty 对于SSL/TLS 和WebSocket 的支持,以及如何简单地通过数据压缩来压榨HTTP,以获取更好的性能。
通过SSL/TLS 保护Netty 应用程序
如今,数据隐私是一个非常值得关注的问题,作为开发人员,我们需要准备好应对它。
至少,我们应该熟悉像SSL和TLS这样的安全协议,它们层叠在其他协议之上,用以实现数据安全。
我们在访问安全网站时遇到过这些协议,但是它们也可用于其他不是基于HTTP的应用程序,如安全SMTP(SMTPS)邮件服务器甚至是关系型数据库系统。
为了支持SSL/TLS,Java 提供了javax.net.ssl 包,它的SSLContext 和SSLEngine类使得实现解密和加密相当简单直接。
Netty 通过一个名为 SslHandler 的 ChannelHandler 实现利用了这个API,其中 SslHandler 在内部使用 SSLEngine 来完成实际的工作。
数据流
图11-1 展示了使用 SslHandler 的数据流。
Netty 的OpenSSL/SSLEngine 实现
Netty 还提供了使用OpenSSL 工具包(www.openssl.org)的SSLEngine 实现。
这个OpenSsl-Engine 类提供了比JDK 提供的SSLEngine 实现更好的性能。
如果OpenSSL库可用,可以将Netty 应用程序(客户端和服务器)配置为默认使用OpenSslEngine。
如果不可用,Netty 将会回退到JDK 实现。
有关配置OpenSSL 支持的详细说明,参见Netty 文档:http://netty.io/wiki/forked-tomcat-native.html#wikih2-1。
注意,无论你使用JDK 的SSLEngine 还是使用Netty 的OpenSslEngine,SSL API 和数据流都 是一致的。
SslHandler 类
在大多数情况下,SslHandler 将是ChannelPipeline 中的第一个ChannelHandler。
这确保了只有在所有其他的 ChannelHandler 将它们的逻辑应用到数据之后,才会进行加密。
接口
API
SslHandler 具有一些有用的方法,如表11-1 所示。
例如,在握手阶段,两个节点将相互验证并且商定一种加密方式。
你可以通过配置SslHandler 来修改它的行为,或者在SSL/TLS握手一旦完成之后提供通知,握手阶段完成之后,所有的数据都将会被加密。
SSL/TLS 握手将会被自动执行。
方法名称 描 述
setHandshakeTimeout (long,TimeUnit) 设置和获取超时时间,超时之后,握手ChannelFuture 将会被通知失败
setHandshakeTimeoutMillis (long)
getHandshakeTimeoutMillis()
setCloseNotifyTimeout (long,TimeUnit) 设置和获取超时时间,超时之后,将会触发一个关闭通知并关闭连接。这也将会导致通知该ChannelFuture 失败
setCloseNotifyTimeoutMillis (long)
getCloseNotifyTimeoutMillis()
handshakeFuture() 返回一个在握手完成后将会得到通知的ChannelFuture。如果握手先前已经执行过了,则返回一个包含了先前的握手结果的ChannelFuture
close() 发送close_notify 以请求关闭并销毁底层的SslEngine
close(ChannelPromise)
close(ChannelHandlerContext,ChannelPromise)
代码示例
代码清单11-1 展示了如何使用ChannelInitializer 来将 SslHandler 添加到 ChannelPipeline 中。
回想一下,ChannelInitializer 用于在 Channel 注册好时设置 ChannelPipeline。
代码
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import javax.net.ssl.SSLEngine;
/**
* @author binbin.hou
* @since 0.0.1
*/
public class SslChannelInitializer extends ChannelInitializer<Channel> {
/**
* ssl 上下文
*/
private final SslContext sslContext;
/**
* 是否开启 tls
* 1. 如果设置为 true,第一条消息将不会被加密。
*/
private final boolean startTls;
public SslChannelInitializer(SslContext sslContext, boolean startTls) {
this.sslContext = sslContext;
this.startTls = startTls;
}
@Override
protected void initChannel(Channel ch) throws Exception {
// 为每一个 channel 新建一个 ssl engine
SSLEngine sslEngine = sslContext.newEngine(ch.alloc());
// 创建对应的 handler
SslHandler sslHandler = new SslHandler(sslEngine, startTls);
// 将 sslHandler 添加到 pipeline 的最开始
ch.pipeline().addFirst(sslHandler);
}
}
回顾
-
将 SslHandler 设置为第一个 handler
-
要懂得 SSL/TLS 协议,才能懂得这些工具的重要性。
拓展阅读
个人收获
-
一些安全协议本身比实现要重要的多。约定优于实现。
-
过滤器模式是通用的,特别是在加密解密,类似于 mvc 也有这种 filter。
参考资料
《Netty in Action》 P161