|
| 1 | +写一个广播器 |
| 2 | +==== |
| 3 | + |
| 4 | +本节,我们将写一个广播器。下图展示了广播一个 DatagramPacket 在每个日志实体里面的方法 |
| 5 | + |
| 6 | + |
| 7 | + |
| 8 | +1. 日志文件 |
| 9 | +2. 日志文件中的日志实体 |
| 10 | +3. 一个 DatagramPacket 保持一个单独的日志实体 |
| 11 | + |
| 12 | +Figure 13.2 Log entries sent with DatagramPackets |
| 13 | + |
| 14 | +图13.3表示一个 LogEventBroadcaster 的 ChannelPipeline 的高级视图,说明了 LogEvent 是如何流转的。 |
| 15 | + |
| 16 | + |
| 17 | + |
| 18 | +Figure 13.3 LogEventBroadcaster: ChannelPipeline and LogEvent flow |
| 19 | + |
| 20 | +正如我们所看到的,所有的数据传输都封装在 LogEvent 消息里。LogEventBroadcaster 写这些通过在本地端的管道,发送它们通过ChannelPipeline 转换(编码)为一个定制的 ChannelHandler 的DatagramPacket 信息。最后,他们通过 UDP 广播并被远程接收。 |
| 21 | + |
| 22 | +*编码器和解码器* |
| 23 | + |
| 24 | +*编码器和解码器将消息从一种格式转换为另一种,深度探讨在第7章中进行。我们探索 Netty 提供的基础类来简化和实现自定义 ChannelHandler 如 LogEventEncoder 在这个应用程序中。* |
| 25 | + |
| 26 | +下面展示了 编码器的实现 |
| 27 | + |
| 28 | +Listing 13.2 LogEventEncoder |
| 29 | + |
| 30 | + public class LogEventEncoder extends MessageToMessageEncoder<LogEvent> { |
| 31 | + private final InetSocketAddress remoteAddress; |
| 32 | + |
| 33 | + public LogEventEncoder(InetSocketAddress remoteAddress) { //1 |
| 34 | + this.remoteAddress = remoteAddress; |
| 35 | + } |
| 36 | + |
| 37 | + @Override |
| 38 | + protected void encode(ChannelHandlerContext channelHandlerContext, LogEvent logEvent, List<Object> out) throws Exception { |
| 39 | + byte[] file = logEvent.getLogfile().getBytes(CharsetUtil.UTF_8); //2 |
| 40 | + byte[] msg = logEvent.getMsg().getBytes(CharsetUtil.UTF_8); |
| 41 | + ByteBuf buf = channelHandlerContext.alloc().buffer(file.length + msg.length + 1); |
| 42 | + buf.writeBytes(file); |
| 43 | + buf.writeByte(LogEvent.SEPARATOR); //3 |
| 44 | + buf.writeBytes(msg); //4 |
| 45 | + out.add(new DatagramPacket(buf, remoteAddress)); //5 |
| 46 | + } |
| 47 | + } |
| 48 | + |
| 49 | +1. LogEventEncoder 创建了 DatagramPacket 消息类发送到指定的 |
| 50 | +InetSocketAddress |
| 51 | +2. 写文件名到 ByteBuf |
| 52 | +3. 添加一个 SEPARATOR |
| 53 | +4. 写一个日志消息到 ByteBuf |
| 54 | +5. 添加新的 DatagramPacket 到出站消息 |
| 55 | + |
| 56 | +*为什么使用 MessageToMessageEncoder?* |
| 57 | + |
| 58 | +*当然我们可以编写自己的自定义 ChannelOutboundHandler 来转换 LogEvent 对象到 DatagramPackets。但是继承自MessageToMessageEncoder 为我们简化和做了大部分的工作。* |
| 59 | + |
| 60 | +为了实现 LogEventEncoder,我们只需要定义服务器的运行时配置,我们称之为"bootstrapping(引导)"。这包括设置各种 ChannelOption 并安装需要的 ChannelHandler 到 ChannelPipeline 中。完成的 |
| 61 | +LogEventBroadcaster 类,如清单13.3所示。 |
| 62 | + |
| 63 | +Listing 13.3 LogEventBroadcaster |
| 64 | + |
| 65 | + public class LogEventBroadcaster { |
| 66 | + private final Bootstrap bootstrap; |
| 67 | + private final File file; |
| 68 | + private final EventLoopGroup group; |
| 69 | + |
| 70 | + public LogEventBroadcaster(InetSocketAddress address, File file) { |
| 71 | + group = new NioEventLoopGroup(); |
| 72 | + bootstrap = new Bootstrap(); |
| 73 | + bootstrap.group(group) |
| 74 | + .channel(NioDatagramChannel.class) |
| 75 | + .option(ChannelOption.SO_BROADCAST, true) |
| 76 | + .handler(new LogEventEncoder(address)); //1 |
| 77 | + |
| 78 | + this.file = file; |
| 79 | + } |
| 80 | + |
| 81 | + public void run() throws IOException { |
| 82 | + Channel ch = bootstrap.bind(0).syncUninterruptibly().channel(); //2 |
| 83 | + System.out.println("LogEventBroadcaster running"); |
| 84 | + long pointer = 0; |
| 85 | + for (;;) { |
| 86 | + long len = file.length(); |
| 87 | + if (len < pointer) { |
| 88 | + // file was reset |
| 89 | + pointer = len; //3 |
| 90 | + } else if (len > pointer) { |
| 91 | + // Content was added |
| 92 | + RandomAccessFile raf = new RandomAccessFile(file, "r"); |
| 93 | + raf.seek(pointer); //4 |
| 94 | + String line; |
| 95 | + while ((line = raf.readLine()) != null) { |
| 96 | + ch.writeAndFlush(new LogEvent(null, -1, file.getAbsolutePath(), line)); //5 |
| 97 | + } |
| 98 | + pointer = raf.getFilePointer(); //6 |
| 99 | + raf.close(); |
| 100 | + } |
| 101 | + try { |
| 102 | + Thread.sleep(1000); //7 |
| 103 | + } catch (InterruptedException e) { |
| 104 | + Thread.interrupted(); |
| 105 | + break; |
| 106 | + } |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + public void stop() { |
| 111 | + group.shutdownGracefully(); |
| 112 | + } |
| 113 | + |
| 114 | + public static void main(String[] args) throws Exception { |
| 115 | + if (args.length != 2) { |
| 116 | + throw new IllegalArgumentException(); |
| 117 | + } |
| 118 | + |
| 119 | + LogEventBroadcaster broadcaster = new LogEventBroadcaster(new InetSocketAddress("255.255.255.255", |
| 120 | + Integer.parseInt(args[0])), new File(args[1])); //8 |
| 121 | + try { |
| 122 | + broadcaster.run(); |
| 123 | + } finally { |
| 124 | + broadcaster.stop(); |
| 125 | + } |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | +1. 引导 NioDatagramChannel 。为了使用广播,我们设置 SO_BROADCAST 的 socket 选项 |
| 130 | +2. 绑定管道。注意当使用 Datagram Channel 时,是没有连接的 |
| 131 | +3. 如果需要,可以设置文件的指针指向文件的最后字节 |
| 132 | +4. 设置当前文件的指针,这样不会把旧的发出去 |
| 133 | +5. 写一个 LogEvent 到管道用于保存文件名和文件实体。(我们期望每个日志实体是一行长度) |
| 134 | +6. 存储当前文件的位置,这样,我们可以稍后继续 |
| 135 | +7. 睡 1 秒。如果其他中断退出循环就重新启动它。 |
| 136 | +8. 构造一个新的实例 LogEventBroadcaster 并启动它 |
| 137 | + |
| 138 | +这就是程序的完整的第一部分。可以使用 "netcat" 程序查看程序的结果。在 UNIX/Linux 系统,可以使用 "nc", 在 Windows 环境下,可以在 <http://nmap.org/ncat>找到 |
| 139 | + |
| 140 | +Netcat 是完美的第一个测试我们的应用程序;它只是监听指定的端口上接收并打印所有数据到标准输出。将其设置为在端口 9999 上监听 UDP 数据如下: |
| 141 | + |
| 142 | + $ nc -l -u 9999 |
| 143 | + |
| 144 | +现在我们需要启动 LogEventBroadcaster。清单13.4显示了如何使用 mvn 编译和运行广播器。pom的配置。pom.xml 配置指向一个文件`/var/log/syslog`(假设是UNIX / Linux环境)和端口设置为 9999。文件中的条目将通过 UDP 广播到端口,在你开始 netcat 后打印到控制台。 |
| 145 | + |
| 146 | +Listing 13.4 Compile and start the LogEventBroadcaster |
| 147 | + |
| 148 | + $ mvn clean package exec:exec -Pchapter13-LogEventBroadcaster |
| 149 | + [INFO] Scanning for projects... |
| 150 | + [INFO] |
| 151 | + [INFO] -------------------------------------------------------------------- |
| 152 | + [INFO] Building netty-in-action 0.1-SNAPSHOT |
| 153 | + [INFO] -------------------------------------------------------------------- |
| 154 | + ... |
| 155 | + ... |
| 156 | + [INFO] |
| 157 | + [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ netty-in-action --- |
| 158 | + [INFO] Building jar: /Users/norman/Documents/workspace-intellij/netty-in-actionprivate/ |
| 159 | + target/netty-in-action-0.1-SNAPSHOT.jar |
| 160 | + [INFO] |
| 161 | + [INFO] --- exec-maven-plugin:1.2.1:exec (default-cli) @ netty-in-action - |
| 162 | + LogEventBroadcaster running |
| 163 | + |
| 164 | +当调用 mvn 时,在系统属性中改变文件和端口值,指定你想要的。清单13.5 设置日志文件 到 `/var/log/mail.log` 和端口 8888。 |
| 165 | + |
| 166 | +Listing 13.5 Compile and start the LogEventBroadcaster |
| 167 | + |
| 168 | + $ mvn clean package exec:exec -Pchapter13-LogEventBroadcaster / |
| 169 | + -Dlogfile=/var/log/mail.log -Dport=8888 -.... |
| 170 | + .... |
| 171 | + [INFO] |
| 172 | + [INFO] --- exec-maven-plugin:1.2.1:exec (default-cli) @ netty-in-action - |
| 173 | + LogEventBroadcaster running |
| 174 | + |
| 175 | +当看到 "LogEventBroadcaster running" 说明程序运行成功了。 |
| 176 | + |
| 177 | +netcat 只用于测试,但不适合生产环境中使用。 |
| 178 | + |
| 179 | + |
| 180 | + |
| 181 | + |
0 commit comments