这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
《Netty 高性能网络编程》7.3 握手、认证与连接绑定
9093 上任何能连上的客户端都能发 REQUEST。本节用 HELLO/token 三态握手把未认证连接挡在业务 handler 之前:AttributeKey 与 Channel 绑定、MessageDigest.isEqual 比较 token、10 秒认证超时、失败即 ERROR 加 close。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
9093 上任何能连上的客户端都能发 REQUEST。本节用 HELLO/token 三态握手把未认证连接挡在业务 handler 之前:AttributeKey 与 Channel 绑定、MessageDigest.isEqual 比较 token、10 秒认证超时、失败即 ERROR 加 close。
连接空闲时双方都不知道对端是否还活着。本节拆开 IdleStateHandler 的三种空闲与 IdleStateEvent.state(),给出服务端 30 秒发 PING、客户端回 PONG、连续两轮无响应关闭的完整 handler,并说明心跳为什么替代不了业务超时。
16 字节头里的每个字段都有代价。本节讲清 magic 为什么不是安全措施、version 怎么兼容、type 与 flags 留了多少扩展位、requestId 的无符号语义和 1 MiB 上限,并给出 LinkdFrame 与两个编解码器的完整实现。
从一次 3 秒网关超时在重试与连接池排队中被放大成 30 秒的事故出发,说明绝对超时预算如何向子调用传递、取消后的工作为何仍占连接,以及级联故障的离散事件模拟;给出 Go 实现并实测健康、池饥饿与三次重试三组配置在成功数、尝试次数和连接占用上的差异,并说明模型不能替代真实压测。
审核失败时只回一句「内容违规」,用户会反复提交同一内容,客服也无法解释。文章把失败提示拆成规则、可修改性和时限三个问题,给出申诉状态机与超时边界,并用 Go 实现 pending 到 accepted、rejected、expired 的合法迁移,复盘一次重复送审放大审核成本的故障。
解码失败不是一句 catch 就能收尾。本节讲 DecoderException、TooLongFrameException、CorruptedFrameException 在 pipeline 里的传播路径,给出 linkd-v2 的 ERROR 帧与关闭策略、HTTP decoderResult 的 400 处理,并说明 exceptionCaught 吞掉异常后连接为什么会半死。
LengthFieldBasedFrameDecoder 的五个参数最容易配错。本节用三组真实字节序列算清 lengthFieldOffset、lengthFieldLength、lengthAdjustment 与 initialBytesToStrip,给出 linkd-v2 十六字节头 strip 16 的配置,并对比 LengthFieldPrepender、分隔符与定长解码器。
粘包拆包都从 ByteToMessageDecoder 开始。本节拆开它的 cumulation 缓冲、callDecode 循环、decodeLast 与 isSingleDecode,给出可运行的行解码器和 linkd-v2 帧解码器,并说明 decode 里 release 入参、读不够却推进 readerIndex、以及用累积缓冲承载超大帧会付出什么代价。
拆开 PooledByteBufAllocator 的 arena、thread cache、chunk、page 结构,用 metric() 读出 arena 分配数与线程缓存命中,验证 4.2 里 ByteBufAllocator.DEFAULT 的真实类型,并给出 linkd-v2 的分配器配置与指标打印代码。
从 readRetainedSlice 的释放责任讲起,给出 retain/release 的所有权规则,说明 SimpleChannelInboundHandler 与 ByteToMessageDecoder 的释放边界,用一个静态 Map 反例复现 LEAK 日志,并整理 paranoid 级别的排查步骤与 ReferenceCountUtil 的使用范围。
用 Unpooled.buffer 把 readerIndex、writerIndex、capacity、maxCapacity 的每次变化跑成表,对照 JDK ByteBuffer 的 position 与 limit,讲清 readableBytes 为什么不能当消息长度,以及 slice、duplicate、retainedSlice 谁共享内容、谁增加引用计数。
本节用 linkd-v2 的 HandshakeHandler 说明哪些字段让 handler 变成有状态,@Sharable 在添加时检查什么,共享实例会带来哪些并发与串状态风险,并给出 AttributeKey、ChannelGroup 与不可变 LinkdFrame 的正确用法,以及 DefaultEventExecutorGroup 的能力边界。
本节按时间顺序拆开 handlerAdded、channelRegistered、channelActive、channelRead、channelReadComplete、channelInactive 与 handlerRemoved,说明 ChannelInitializer 如何移除自己,并用 HandshakeHandler 演示每连接状态迁移。
本节拆开 linkd-v2 的两条 pipeline:head/tail 哨兵、入站与出站方向、addLast 顺序为何决定 HttpServerCodec、HttpObjectAggregator 与业务 handler 的先后,以及 ctx.writeAndFlush() 与 channel().writeAndFlush() 的起点差异。
本节解释为什么 JDBC、文件 IO 和同步 HTTP 不能放在 EventLoop 上,并用本机输出对比三种隔离:DefaultEventExecutorGroup、独立线程池、JDK 25 虚拟线程。给出 GatewayHandler 把转发任务提交到虚拟线程再回写 Channel 的完整代码,以及虚拟线程在 CPU 密集和下游容量上的边界。
用一条被误判的宠物视频串起机审、人审与复审:机审输出置信度,人审处理灰区,复审靠抽样与申诉触发而不是把内容再跑一遍。文章给出 pass、reject、review 三档的阈值对照表和 Go 实现,并复盘一次阈值过严导致人审队列积压六小时的故障。
本节把 EventLoop 当作单线程调度器来用:execute、schedule、scheduleAtFixedRate 的提交与执行顺序,ChannelFuture 回调里如何安排重连退避,GlobalEventExecutor 与 DefaultEventExecutorGroup 各自适合什么,以及心跳任务写成定时器后为什么会静默停止。给出阻塞任务推迟定时器的本机输出。
本节回答 linkd-v2 上线前的三个问题:boss 与 worker 各开多少线程、一个 Channel 为什么终身只属于一个 EventLoop、group.next() 如何分配连接。用探针打印 channel.eventLoop() 与线程名,给出 10 核机器的真实分配、io.netty.eventLoopThreads 的效果,以及多连接共享 EventLoop 的延迟代价。
ChannelOption 是强类型常量,作用在监听或连接 channel 上。本节拆开 SO_BACKLOG、TCP_NODELAY、SO_KEEPALIVE、CONNECT_TIMEOUT_MILLIS、ALLOCATOR、RCVBUF_ALLOCATOR、WRITE_BUFFER_WATER_MARK 与 AUTO_READ,给出 linkd-v2 参数表和打印 config 的方法。
Channel 从注册到注销经历 registered、active、read、readComplete、inactive、unregistered 六个入站事件,ChannelFuture 串起异步结果与监听器。用 linkd-v2 LifecycleLoggingHandler 与 RegistryHandler 演示事件顺序、closeFuture 关闭、CLOSE 监听器和连接失败处理,说明 sync() 死锁边界。