本章内容包括:
1)UDP的总览
2)广播应用的一个简单示例
到目前为止,我们使用的所有例子都是基于连接形式的协议,例如TCP,在这个章节中,我们将会聚焦于无连接x形式的协议(User Datagram Protocol UDP),这个协议常常使用于对性能要求极其高但又可以允许少量的丢包的情况存在
我们先讲解一下UDP的概念,讲解一下它的特性和限制,接下来我们会描述一下这个章节示例应用的业务背景,这个示例将会很好的说明如何使用UDP协议的广播特性,我们也会利用解码器和编码器来处理一个POJO,在这个章节的最后部分,你应该可以使用UDP协议应用到你的项目中
13.1 UDP basics
基于连接的传输服务例如TCP这类类型的协议管理着两个终端之间建立的连接,在连接的整个生命周期里,信息可以有序的可信赖的传输,在连接的中断的时候可以有序地关闭终端,相反,像UDP这类无连接的协议,是没有稳固耐用的连接,每个信息都是一个独立的传输体
还有就是UDP没有TCP协议存有的那个纠错的机制,这种机制是指每个终端当它收到一个消息包的时候,都会给出一个反馈,没给返回的包,发送者将会重新发送
对比一下,一个TCP连接就像一个电话通信,在这个通信中信息可以在两个终端有序的传播,相反,UDP就像邮箱里的一堆明信片,你无法知道它们到达邮箱的先后顺序,甚至都不能知道是否所有的明信片是否已经全部收到
因为这些UDP协议的这些局限性可能会限制你,但是这也很好的解释了为什么UDP协议快于TCP,握手阶段的性能损耗和消息管理被消除了,很明显,UDP对于那些可以接受消息部分损失的系统将是一个非常好的选择,当然这种协议是不适用金融信息传输的
13.2 UDP broadcast
到目前为止我们使用的传输模式被称为“单点传播”,单点传播的定义是这样的:消息的发送只会发送到由唯一地址确认的网络目的地,这种模式可以被有链接和无连接的两种协议所支持,UDP提供了其他额外的传输模式用来发送信息给多个收件人
1)组播(Multicast)----------传输到定义好的一组终端里
2)广播(Broadcast)--------------传输到整个网络中的所有host中
在我们这个章节的示例应用中,来说明UDP的广播机制,通过将信息发送到同一个网络的所有host,出于这个目的,我们将使用特殊的受限制的广播零点网络地址255.255.255.255,信息将会传送到这个地址,这将是本地网络所有host的地址,并且不会被路由器转送到其他的网络中
接下来我们将会讨论这个应用的设计
13.3 The UDP sample application
我们的示例应用将会打开一个文件然后将文件的每一行数据都当做一种信息通过UDP指定的具体端口进行广播,如果你对类Linux的操作系统很熟悉的话,你可以将这种业务场景想象成syslog功能的简化版,那么UDP对于这样类似的场景是很适用的,因为日记文件的偶尔的行损失是完全可以被接受的,给以的文件存储在文件系统中,还有就是这种方案提供了一个非常有价值的高效的解决大数据量传输的方案
那么关于接收者的设定呢?如果使用UDP的广播机制,那么你可以创建一个事件管理器来接收日记信息,我们可以简单地启动一个监听程序,这个程序在某个具体的端口监听,注意这种太过容易的访问会引起潜在的安全问题,这也是为什么UDP协议不会用在不安全的环境中的原因之一,因为这样同样的原因,路由器经常会阻塞信息的广播,限制他们只能在他们起源的网络段中
发布/订阅:像syslig这样的应用是典型的“发布订阅”类型的系统,一个生产者或者一个服务发布事件,多个客户端订阅这个事件然后接收他们
图13.1展示了这个应用系统的俯瞰图,它包含了对一个或者多个事件管理器的广播,这个广播监听是否有新的内容出现,如果它出现了,将它用UDP协议将其当做一种广播信息传播出去
所有的时间管理者监听着UDP的端口来接收广播的信息
为了让我们的示例尽可能的保持简单,我们不会将授权,验证,加密这些组件加入到我们这个简单的应用中去,但这并不意味着将这些特性混合使用很难,将这些组件组合在一起对于Netty来说很简单,且这些组件可以增加应用的健壮性,实用性
在下一个章节中,我们将开始探索广播组件的设计和实现细节
13.4 The message POJO: LogEvent
在消息应用中,数据经常是由一个POJO来表示的,这个POJO中包含一些配置信息或者一些处理的信息甚至还有一些真实的信息内容,在这个应用中,我们将处理信息当做一种事件,这是因为数据是来自于日记文件的,所以我们将其命名为“LogEvent”,代码清单展示了这个POJO的细节
我们已经定义了消息组件,我们可以实现应用系统的广播的逻辑了,在下一个章节,我们将学习Netty框架提供的一些类,这些类可以用来对LogEvent消息对象的编码和传输
13.5 Writing the broadcaster
Netty提供了一定数量的类用来支持UDP应用代码编写,我们主要使用的几个类就是消息容器和Channel的类型,我已经在13.1中一一列出了:
Netty的DatagramPacket是一个简单的信息容器,再使用DatagramChannel的具体实现可以很方便地进行终端的通讯,在我们之前提及的一样,我们举得明信片的例子,这个明信片中包含了收件人的地址也可能会有发件人的地址,还有信息本身承载的一些信息
为了将EventLog信息转化成DatagramPackets,我们需要一个编码器,当然我们不需要自己从零开始写,我们可以使用第九章和第十章介绍的MessageToMessageEncoder来帮助我们完成这样的工作
图13.2展示了三个log对象的广播,每一个Entity都有它们对应的专有的DatagramPacket
图13.3展示了LogEventBroadcaster的ChannelPipeline的高级层次图,展示了LogEvent如何在里面流动的
你可以看见所有的数据都被封装成LogEvent消息格式来传输,LogEventBroadcaster将LogEvent写入到channel中,将其通过管道发送,在此之前,这些消息可能会被编码成DatagramPacket消息格式来传播,最后他们将会被UDP广播,最后被终端获取到
下面的代码清单展示了我们定制的MessageToMessageEncoder,它将会完成我们之前讨论的那些功能
伴随着LogEventEncoder的实现,我们现在开始准备启动服务端,这个服务端需要设置一些启动参数,并且需要安装一些需要的ChannelHandler到管道中,这都将由LogEventBroadcaster的方法完成,如下面代码清单所示:
这就完成了你应用的广播组件了,测试的开始阶段,你可以使用netcat,在UNIX/Linux系统上你可以找到nc软件安装,Windows的版本下载可以在http://nmap.org/ncat中下载
netcat很适用于一个软件最基本的测试,它做的事情很简单:在某个端口上监听,打印出这个端口接收到的所有数据,在我们这个项目中我们我们可以通过如下的命令设置在9999端口监听UDP数据
现在我们需要启动我们的LogEventBroadcaster,下面的启动日记展示了如何使用mvn去编译然后运行广播器,在pom.xml中指定的那个文件是经常被变更的,加入在linux环境下设置文件的路径为“/var/log/messages/”,并且设置端口号是9999,那么这个文件将通过UDP的广播接收到广播的信息,然后当我们启动netcat的时候,就能记录所有控制台输出的所有信息
如果想要改变文件的输出路径和端口号,我们可以在运行mvn命令的时候以系统变量的形式去重新指定值,下面的日记输出展示了如何重新设置log的路径和端口号的
当你在日记信息中看见LogEventBroadcaster能够正常运行,你就可以确认知道它已经成功启动了,如果有错误,那么错误信息将会被打印出,一个错误的信息将会被打印,一旦进程在运行的时候,它将会广播每一条log信息将其增加到log文件中
使用netcat很适合我们应用的测试,但是对于应用于生产环境的系统来说,这是不太适用的,因为这个原因,我们需要开发我们应用的第二个模块,广播监控者,下一个小节,我们将实现这个功能
13.6 Writing the monitor
我们的目的就是将netcat替换成一个更加完整的事件消费者,这个新的功能我们称之为EventLogMonitor,这段程序的功能是这样的:
1)接收LogEventBroadcaster广播的UDP的DatagramPakets
2)将接收的消息解码成LogEvent消息
3)将解码的LogEvent用System.out打印出
与我们之前一样,监控应用的业务逻辑块是由我们自定义的四个解码器完成的,我们继承了MessageToMessageDecoder,图13.4描述了LogEventMonitor的ChannelPipeline,并且展示了LogEvent是如何通过这个管道的
在这个管道中的第一个解码器是LogEventDecoder,它负责将其输入的DatagramPackets转换成LogEvent消息,这是所有的Netty应用一个典型的设置,一开始进行将输入的信息进行转化,下面的代码清单给出了实现
第二个ChannelHandler的作用就是将第一个Channel创建的LogEvent消息进行一些处理,在我们这个简单的案例里,我们只是简单的将其打印出来,可能在真实的生产环境中,你可能会把不同数据源的日记聚合起来然后将聚合的结果存储到数据库中,下面的代码清单展示了LogEventHandler如何完成这项任务的
LogEventHandler将以一种比较易读的方式打印出LogEvent信息,打印出的信息内容包括
1)接收到的时间戳
2)发送者的InetSocketAddres,这个里面包括IP和端口号
3)LogEvent文件产生的绝对路径名
4)真实的日记信息,包含了一行完整的日记输出
现在我们将我们的处理器安装到ChannelPipeline中,就如我们图13.4所示,这个工作将由我们的LogEventMonitor的主函数完成
13.7 Running the LogEventBroadcaster and LogEventMonitor
与我们之前一样,我们使用Maven来运行我们的程序,不过这次我们需要打开两个运行窗口,两个都用来运行程序,每一个都会保持运行的状态,知道你使用Ctrl+C建停止程序
首先,你需要启动LogEventBroadcaste,因为我们之前已经构建过这个应用,所以下面的运行命令就足够了:
从现在开始,它将会通过UDP来开始广播日记信息
现在再打开一个新的窗口,构建和启动LogEventMonitor来接收和展示广播的消息:
当你看见LogEventMonitor运行的时候,你将意识到应用已经成功运行了,如果有错误存在,那么异常信息将会在控制台打印出。
控制台将会展示所有的事件,如下面日记所示,所有的信息格式都是由LogEventHandler规范创建的
如果你不能获取到UNIX的日记,你可以创建一个自定义的文件手动将其应用到你的应用中,下面的命令展示了如何使用这个文件,首先先创建一个空的文件
我们现在开始重启LogEventBroadcaster,通过系统变量指向我们刚才创建的文件
一旦LogEventBroadcaster运行起来,你可以手动地像刚才创建的文件中添加信息,然后你就可以在LogEventMonitor的控制台看到广播出来的信息,使用echo重定向输出到文件中:
你可以任意创建多个监控实例,每一个都会接收打印出同样的信息
13.8 Summary
在这个章节中,我们讲解了UDP这样的无连接的协议,我们构建了一个简单的项目示例,我们将日记信息通过UDP的datagrams形式以广播的方式传播给每一个订阅者,这个实例告诉我们Netty的UDP的使用方式,且UDP可以在一些比较特殊的场景有一些比较独特的运用
在接下来的两个章节中,我们将有幸看见来自世界级公司的开发人员如何运用Netty去构建工业级的应用的