4.2 Transport API
transport的核心API是接口Channel,它被用在所有的I/O操作上,图4.1展示了Channel的继承关系
图中展示了一个Channel被分配了一个ChannelPipeline和一个ChannelConfig,ChannelConfig包含了所有的channel的配置且支持动态改变,因为具体的传输服务需要一些自己独有的配置,这就需要实现ChannelConfig
由于每一个Channel都是独一无二的,所以Channel也是继承了java.lang.Comparable,作为它的子接口,用来保证它的顺序,如果两个不同的channel实例返回同样的hashcode的时候,在AbstractChannel中的compareTo()方法会抛出一个错误信息
ChannelPipeline中包含了所有的channelHandler实例,这些实例应用于数据输入输出的事件处理,这些ChannelHandler包含所有的应用程序逻辑对数据状态的处理
ChannelHandler的一些经典的运用是:
1)将数据从一个格式转化成另一个格式
2)提供异常通知
3)提供Channel的active和inactive的事件通知
4)提供当一个channel去注册或者取消注册到EventLoop的通知
5)提供用户自定义事件通知
INTERCEPTING FILTER channelPipeline运用了一个我们常用的设计模式,拦截过滤器,UNIX就是这个设计模式的另一个很好的例子:命令链接在一起,输出的结果被链上的一个个的命令过滤
如果有需要,你也可以通过增加或者移除ChannelHandler修改正在运行的中的ChannelPipeline,Netty的这种特性一般被运用在构建高灵活性的应用中,举例来说,当你任何时间有需求,需要将你的应用增加STARTTLS协议,你只需要简单的写一个ChannelChannel(一般是SslHandler)到你的ChannelPipeline就可以了
当然,除了我们刚刚介绍的ChannelPipeline和ChannelConfig的一些特性,我们也应该充分利用Channel本身一些常用的方法,表4.1中列出了Channel中最最常用的方法
方法名 | 描述 |
eventLoop | 返回被安排在该Channel上的EventLoop |
pipeline | 返回被安排在该Channel上的ChannelPipeline |
isActive | 返回该channel是否是active的,active的定义取决你的底层传输服务 举例来说,如果是socket的传输服务,只要链接建立那么就是active,然而对于dataGram传输服务而言,只要它打开就可以算是active了 |
localAddress | 返回本地的SocketAddress |
remoteAddress | 返回远程的SocketAddress |
write | 写数据到远程端,数据被输送到ChannelPipeline中,只有数据被flush才会在进入队列等待被处理 |
flush | 将刚才写入的数据刷到底层的传输服务中,例如socket中 |
writeAndFlush | 一个很方便的方法,先调用write方法,随后调用flush方法 |
随后的时间里,我们会仔细地介绍这些特性,现在,你只需要记住一点,Netty使用如此少的方法完成了大量的工作,这就意味着你只需要修改一些重要部分的修改,而不是大范围地重构你的代码
思考一个简单的任务:写入数据且将数据刷到远程端,下面的代码清单向你展示了使用Channel的writeAndFlush的方法完成这个任务
Netty的Channel是线程安全的,所以你可以存储一个对象的引用到Channel中,然后你可以在任意的时间里将这个对象传输到远程端,即使在这段时间有很多线程在使用,下面的一个例子中向你展示了如何使用多线程写入,需要注意的是,我们需要保证信息按照顺序被写入(为了保证顺序接收)