V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
kai92zeng
V2EX  ›  程序员

[独立开发] 单机 Rust 如何抗住 10w+ 在线行情推送?告别 Redis/Kafka,聊聊我的“极简主义”架构

  •  
  •   kai92zeng · 9 小时 3 分钟前 · 1084 次点击

    大家好,我是《交易学徒》的独立开发者。

    最近在重构后端行情网关,目标是支撑 10w+ 同时在线用户。在技术选型时,很多“标准答案”是微服务、Redis 集群、Kafka 消息队列。但作为独立开发者,维护这一套重型架构的运维成本和心智负担太高了。

    于是我反其道而行之,选择了一种“极致单机”的方案:没有任何外挂组件( No Redis, No Kafka ),所有状态在进程内解决,纯 Rust 函数调用。

    经过实战验证,这套架构不仅部署简单(就一个 Binary ),而且足够稳定。今天分享一下我是如何用 Rust 的特性“白嫖”性能的,欢迎 V 友们拍砖。

    一、 核心理念:按需订阅,算一笔带宽的账 早期的 demo 喜欢大包大揽,客户端连上来就推 Top 20 币种。这在用户量大时是灾难。我的做法是“用户看哪里,就只推哪里”

    客户端停留在 BTC/USDT 的 15 分钟 K 线界面,服务端就只建立这一个订阅关系。一旦切换,立马移除旧订阅。

    算一笔账( Resource Cost ): 假设一个行情包 Payload 是 200 Bytes ,推送频率 5 次/秒。

    全量推送(推 Top 20 ):10 万用户 x 20 个币种 x 200B x 5 = 2 GB/s (带宽直接破产)。

    按需订阅(推 1 个):10 万用户 x 1 个币种 x 200B x 5 = 100 MB/s 。

    结论: 简单的逻辑改变,带宽节省 95%,单机千兆网卡轻松抗住。

    二、 列表行情:Polling + 边缘计算(白嫖 CF ) 对于“行情列表”这种一屏显示几十个数据的页面,建立长连接维护成本太高。 我采用了 1 秒轮询 + Cloudflare 边缘缓存 的策略。

    策略: 设置 HTTP 响应头,让 CF Edge Cache TTL = 1 秒。

    效果:10 万人同时刷新列表,99% 的流量被 CF 的全球节点挡住了,真正打到我源服务器 Rust 进程的 QPS 只有个位数。

    收益: 既利用了 CDN 的带宽,又保护了单机后端。

    三、 架构做减法:进程内通信替代中间件 这是我这次重构最大的感悟:单机并发足够高时,不需要 Redis 和 Kafka 。

    替代 Redis: 使用 Rust 的 DashMap (Concurrent HashMap)。数据就在内存里,读写是纳秒级,没有网络 IO 开销,没有序列化/反序列化成本。

    替代 Kafka: 使用 tokio::sync::broadcast 和 mpsc::channel 。

    优势: 传统的“发布-订阅”为了解耦上了 MQ ,但在单机 Rust 里,一个 Arc<Channel> 就能解决问题。部署时不需要操心 MQ 挂没挂,只要我的进程活着,消息系统就活着。

    四、 信使模式 (Messenger Pattern) 与背压 在 Tokio 异步编程中,最忌讳 await 阻塞。 如果客户端网络卡顿(比如进电梯),socket.send().await 可能会阻塞,导致同个 Loop 下的心跳包处理被卡死,造成“假死”。

    我的解法:

    读写分离: 为每个连接 spawn 一个独立的 send_task ,通过 mpsc::channel(128) 通信。

    严格背压 (Backpressure): 使用 try_send 。如果 Channel 满了(说明客户端来不及收),直接丢弃新行情。

    理由: 实时行情旧数据无价值。这行代码是系统的“保命符”,防止慢客户端拖垮服务端内存( OOM )。

    五、 极致性能:Zero-Copy (零拷贝) 在广播行情时,不要为 10 万个用户 copy 10 万份数据。

    Rust // 内存中只有一份二进制 Payload let payload = Arc::new(Bytes::from(vec![...]));

    // 10 万次分发只是增加了 10 万次引用计数( Reference Counting ) // 几乎没有任何内存分配开销 for client in clients { client.tx.try_send(payload.clone()); } 利用 Rust 的 Arc 和 Bytes ,让 CPU 缓存极其友好。 六、 扫地僧:资源清理 长运行的单机服务最怕内存泄漏。 当 socket 断开时,必须像外科手术一样精准清理:

    从 DashMap 移除 Client 。

    清理反向索引 subscriptions 。

    如果某个 Topic 无人订阅,立即 drop 掉对应的 channel 发送端,停止上游数据生产。

    总结 作为独立开发者,资源有限。这套“Rust 单机 + 无外挂组件”的架构,让我用最低的成本(一台服务器)抗住了业务压力,而且睡得很安稳。

    Simplicity is the ultimate sophistication.

    🎁 V 友专属福利 软件名字叫 《交易学徒》,是一个辅助交易员复盘、模拟、练盘感的工具。前端也是我用 Flutter 写的( Rust + Flutter 真是全栈开发的绝配)。

    官网下载: https://www.zgjiazu.top

    Google Play: https://play.google.com/store/apps/details?id=com.zengkai.jyxtclient

    回帖福利: 在本帖回复并附上 App 内的 ID (在‘我的页面’可以看到),我后台人肉送一个月 VIP 会员。

    同时也欢迎大家在评论区提 Bug ,或者交流关于 Rust 后端与 Flutter 前端开发的坑!

    21 条回复    2026-01-23 00:44:48 +08:00
    ljkgpxs
        1
    ljkgpxs  
       7 小时 57 分钟前
    消息会持久化存储吗,程序挂了消息咋处理
    twocold0451
        2
    twocold0451  
       7 小时 4 分钟前
    学习一下👍
    kai92zeng
        3
    kai92zeng  
    OP
       7 小时 1 分钟前
    @ljkgpxs 私聊会存储,大厅聊天不会存储,只会在内存里保留最新的 N 条,新用户进入聊天服务器可以选择发不发最近的聊天数据,再远的数据没有意义。行情报价也是,行情价格时存储在数据库的,可以同步缺失的数据,服务挂了也不会影响行情报价数据的缺失,启动起来数据会完整的。
    Gilfoyle26
        4
    Gilfoyle26  
       6 小时 35 分钟前
    你这样做从性能来说确实可以,但是使用 Redis 和 Kafka 其实更多是为了高可用,更多不是为了性能
    scholes1989
        5
    scholes1989  
       6 小时 23 分钟前
    这个项目没有苹果的吗
    shine1996
        6
    shine1996  
       6 小时 22 分钟前
    极致的优化 👍
    Yangpengxu
        7
    Yangpengxu  
       6 小时 21 分钟前
    新颖的想法
    scholes1989
        8
    scholes1989  
       6 小时 12 分钟前
    大佬问下这个数据源是 从哪里接哈
    grayish
        9
    grayish  
       5 小时 39 分钟前
    10+下载量就开始幻想 10W 在线了么
    lusi1990
        10
    lusi1990  
       4 小时 59 分钟前 via iPhone
    是个 C100k 的问题。
    kai92zeng
        11
    kai92zeng  
    OP
       4 小时 18 分钟前
    @Gilfoyle26 哥们说得很对,在通用的互联网架构中,Redis 和 Kafka 确实是高可用的基石,不过在高频行情推送这个业务场景下,架构的权衡逻辑有点不一样。我需要的时极致的性能和稳定,越简单的架构,故障率和延迟就会越低。行情的特点是“过期即废”,用户不需要堆积的数据,我个人认为,我这个单机会比 redis 和 kafka 更加高可用,对于我的项目来说。😄
    kai92zeng
        12
    kai92zeng  
    OP
       4 小时 17 分钟前
    @scholes1989 我没有苹果开发机,这几天准备斥巨资买一个 mini 盒子准备上架工作,敬请期待!
    kai92zeng
        13
    kai92zeng  
    OP
       4 小时 16 分钟前
    @shine1996 目标奔着单机十万在线去的,就是现在只有几十个注册用户,哈哈
    kai92zeng
        14
    kai92zeng  
    OP
       4 小时 15 分钟前
    @Yangpengxu 独立开发,各方面都要省着花,挖空心思把性能提高一点。
    kai92zeng
        15
    kai92zeng  
    OP
       4 小时 15 分钟前
    @scholes1989 花点小钱,网上太多公司卖数据了。
    kai92zeng
        16
    kai92zeng  
    OP
       4 小时 14 分钟前
    @grayish 目标是十万在线,我会持续努力的,请大佬见证
    kai92zeng
        17
    kai92zeng  
    OP
       4 小时 11 分钟前
    @lusi1990 开发过程就奔着十万在线目标去的,现在也达到了这个目标(模拟十万连接),整个项目,开发反而是最简单的,难一点的是 UI 设计和整体风格界定,更难得是怎么获取用户和留住用户,任重而道远……
    Charlie17Li
        18
    Charlie17Li  
       1 小时 51 分钟前
    想问下行情数据从哪里弄的呢? akshare ?
    KyleRicardo
        19
    KyleRicardo  
       1 小时 44 分钟前
    这思路不错啊就,值得学习。
    另外请教下大佬,官网是 AI 写的吗?或者用的什么模板?
    gongxuanzhang
        20
    gongxuanzhang  
       1 小时 21 分钟前
    思路非常对,支持,我觉得程序员就是应该去考虑如何榨干一台机器,而不是"加机器的问题都不是问题"
    drymonfidelia
        21
    drymonfidelia  
       5 分钟前
    不写 rust ,不过 C#的 MemoryCache 和 R3 用起来也超爽
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   1469 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 16:50 · PVG 00:50 · LAX 08:50 · JFK 11:50
    ♥ Do have faith in what you're doing.