这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
《互联网产品的服务端架构·内容、社交与实时互动》3.2 推模式:写时扩散
从一条粉丝 500 万的明星动态出发,拆解推模式为什么把成本放在写时:一条内容要扩散多少行、写队列如何被大 V 堵死、普通用户为什么跟着延迟,并用可测试的 Go 策略选择与成本函数固定粉丝门槛、写读成本与负数校验的边界,最后给出队列隔离与幂等重试的取舍。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
从一条粉丝 500 万的明星动态出发,拆解推模式为什么把成本放在写时:一条内容要扩散多少行、写队列如何被大 V 堵死、普通用户为什么跟着延迟,并用可测试的 Go 策略选择与成本函数固定粉丝门槛、写读成本与负数校验的边界,最后给出队列隔离与幂等重试的取舍。
整理 macOS 上排查 linkd-v2 的可用命令:lsof、netstat、tcpdump、jcmd、jstack、JFR 与 Netty 系统属性,每条给出用途、可复制命令、输出读法与风险提示,并给出从连接积压到堆外内存上涨的组合排查路径。
按 Bootstrap、Channel、EventLoop、Pipeline、ByteBuf、编解码、HTTP、WebSocket、HTTP/2 与连接池十组,列出 Netty 4.2 常用类、正确包名、方法签名与对应章节,并标出容易记错的常量与重载差异,供写 linkd-v2 时直接查表。
本节只写 Linux 部署侧:EpollEventLoopGroup 与 EpollServerSocketChannel 的配置、SO_REUSEPORT 与 TCP_FASTOPEN 的内核前提、classifier 与 glibc 兼容性,以及 Epoll.isAvailable() 为 false 时的回退。全部为部署说明,不是本机实测。
isAvailable() 为 true 不等于 server 真的跑在 kqueue 上。本节用本机探针打印 serverChannel 与 eventLoop 的实际类名,给出 KQueueEventLoopGroup 启动代码、Apple Silicon classifier,并处理 JDK 25 对 System.loadLibrary 的受限方法警告。
NIO、Epoll、KQueue 三套 transport 不是三个开关,而是同一组接口的三份实现。本节讲清 Epoll.isAvailable() 与 KQueue.isAvailable() 的探测、classifier 构件选择、NativeEventLoops 的回退顺序,以及原生传输为什么不保证更快。
gRPC 不是又一个网络框架,而是 HTTP/2、protobuf 与生成代码的组合。本节用 linkd-v2 的 9092 端口拆开这三层,给出 pom 依赖、linkd.proto、LinkdServiceImpl 与 NettyServerBuilder 服务端、NettyChannelBuilder 客户端和 deadline,并说明 shaded Netty 的版本隔离边界。
把 11.1 的 pipeline 变成 9091 上可运行的 h2c 服务端。本节给出 ServerBootstrap、Http2FrameCodec、Http2MultiplexHandler 与子 channel 的完整装配,讲 HEADERS/DATA 如何创建和关闭 stream、GOAWAY 与流量控制窗口怎么处理,并用 curl --http2-prior-knowledge 验证。
HTTP/2 帧分连接级与 stream 级两层。本节用 linkd-v2 的 9091 端口拆开 Http2FrameCodec 与 Http2MultiplexHandler 的分工,说明 preface、SETTINGS、HEADERS、DATA、WINDOW_UPDATE、GOAWAY 归谁处理,对比 h2c prior-knowledge 与 ALPN,并给出多路复用与连接池的取舍。
连接多了以后,回显要变成广播。本节用 ChannelGroup 管理会话,用 AttributeKey 绑定 userId,用 IdleStateHandler 加 ping/pong 清理死连接,给出 /ws/broadcast 的完整 WebSocketHub,并说明慢客户端过滤、广播风暴与按房间分组的扩展边界。
升级之后,入站的都是 WebSocketFrame。本节分清文本、二进制、ping、pong、close 五类帧在 WebSocketServerProtocolHandler 里的去向,讲 continuation 分片与 WebSocketFrameAggregator 的位置和上限,给出 /ws/echo 的文本与二进制回显代码,以及大帧带来的内存与背压问题。
从关注 500 人与 5000 人的首页刷新出发,拆解拉模式为什么把成本放在读时:一次刷新究竟要合并多少来源、并发查收件箱怎样拖垮 p99、关注 3000 人如何把首页延迟推到 3 秒,并用可测试的 Go 合并函数固定去重、排序与截断的边界,最后给出容量估算与降级顺序。
「回声」的缓存命中率升到 92%,端到端延迟却变差了。本节把命中率拆成请求、token 与成本三个口径,说明为什么必须按能力与租户分层、精确命中与语义命中要分开、缓存污染与语义误命中要单独计数,并给出可运行的 TypeScript 分层指标实现。
WebSocket 仍以 HTTP/1.1 Upgrade 开始。本节按 HttpServerCodec、HttpObjectAggregator、WebSocketServerProtocolHandler 的顺序装配 9090 端口,讲路径匹配、握手完成事件与 400 失败分支,并给出可运行的 WebSocketServer 与 curl 验证命令。
AUTO_READ 默认开启时,Netty 读完一轮就自动继续读,业务队列满也不会停。本节用本机输出验证手动 read() 的一轮边界,讲清 channelReadComplete 的恢复时机、慢消费者队列上限与 RCVBUF_ALLOCATOR 的边界,并给出 linkd-v2 的 ReadBackpressureHandler 与流量整形取舍。
write() 与 writeAndFlush() 的差别不在名字,而在 ChannelFuture 什么时候完成。本节用本机输出验证入队与 flush 的分界,解释批量写后一次 flush 的收益、待写队列在不可写状态下继续增长的风险,并给出 linkd-v2 广播发布器的批量写与监听器重试、丢弃策略。
WriteBufferWaterMark 的两个参数到底在统计什么?本节用 linkd-v2 的 WritabilityPump 拆开 Channel.isWritable 的判定、channelWritabilityChanged 的触发与 4.2 默认的 32/64 KiB 水位,给出生产者暂停与恢复的完整 handler,并用本机输出核对 config 打印值与状态迁移。
关闭一条连接有 close、disconnect、shutdownOutput 三种语义,半关闭处理错会留下永远不释放的连接。本节说明 ALLOW_HALF_CLOSURE 与 ChannelInputShutdownEvent 的配合,用 ChannelGroup 批量关闭,给出 linkd-v2 GracefulShutdown 的步骤与期限,以及 SO_LINGER 触发 RST 的边界。
连接池的关键不是池化本身,而是何时借、何时还、何时判定连接已坏。本节拆开 FixedChannelPool 的三个生命周期回调,配合 ChannelHealthChecker 与获取超时,用 AbstractChannelPoolMap 按 host:port 建池,并用 Little 定律说明池大小以及 HTTP/1.1 与 HTTP/2 的取舍。
客户端重连的难点不在 connect 调用,而在失败路径、触发时机与退避节奏。本节拆开 Bootstrap.connect 的拒绝、超时与握手失败三种结果,用 closeFuture 触发重连,给出 linkd-v2 ReconnectHandler 的状态机、指数退避、0.8 到 1.2 抖动、排队上限与惊群规避。