第 4 章 Channel、EventLoop 与异步结果
4.1 Channel 是连接的操作入口
在本书 TCP 示例中,客户端 Channel 可以理解为连接的操作入口,包含远端地址、是否活动、读写操作和 Pipeline。服务器监听 Channel 的职责不同:它接受新连接。
你可以调用 channel.writeAndFlush(message) 请求发送消息,但该方法返回时,不代表对方的业务已经处理完成。它返回一个 ChannelFuture,用来观察该次异步操作何时成功或失败。
4.2 EventLoop 是谁
EventLoop 负责处理注册到它上面的 Channel 事件和任务。通常一个 Channel 的 I/O 事件由同一个 EventLoop 线程处理,一个 EventLoop 可以管理多条连接。
因此,“有一万条连接就必须有一万个线程”并不成立。Netty 可以让有限的线程处理许多连接;但如果你在这个线程里执行耗时工作,同线程管理的其他连接也会被拖慢。
把 EventLoop 想成负责一排窗口的工作人员:每个窗口有事就处理一小步。若这个人突然花十秒钟搬货,其他窗口都得等。异步模型的价值来自快速处理事件,并让等待交给合适的机制。
4.3 sync 与监听器
启动线程中等待 bind().sync() 是为了确认监听结果。事件回调中则应避免对尚未完成的 Future 调用阻塞等待;这样可能阻塞 EventLoop,甚至形成死锁。
观察发送结果可以写成:
ctx.writeAndFlush("hello\n").addListener(future -> {
if (!future.isSuccess()) {
future.cause().printStackTrace();
ctx.close();
}
});Future 成功通常说明这次出站操作在本地传输层完成,不能证明对方程序读到、保存到数据库或提交了事务。业务上需要“确认处理成功”,必须设计应答消息、请求编号和超时。
4.4 不要在回调里做的事
直接 Thread.sleep、同步访问数据库、调用慢 HTTP 接口、读取大文件,都会占用当前处理器所在线程。如果它是 EventLoop,就会影响其他连接。第 12 章给出把业务处理移到独立线程池的方式。
小练习: 一个写操作 Future 成功,能否证明订单已经入库?需要什么额外约定?