V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
duanhui8
V2EX  ›  程序员

求小伙伴们,推荐一款 c/c++成熟高性能的队列,谢谢

  •  
  •   duanhui8 · 2018-04-29 11:00:01 +08:00 · 8827 次点击
    这是一个创建于 2405 天前的主题,其中的信息可能已经有所发展或是发生改变。
    场景:200-400 线程同时向队列中写入消息,每秒写入次数大概 7000+,每次写入 100 byte 左右
    消费端,由多线程消费,将消息取出并通过 socket 发送出去。

    小伙伴有没有高性能的队列推荐啊?要支持多线程读写,并且要安全,性能尽量好点,c/c++代码
    另外这种场景非阻塞队列更适合吧?
    不想去百度,怕被坑,求小伙伴们推荐,谢谢
    45 条回复    2018-05-02 09:02:21 +08:00
    pynix
        1
    pynix  
       2018-04-29 11:05:29 +08:00
    zmq?
    lsmgeb89
        2
    lsmgeb89  
       2018-04-29 11:14:23 +08:00
    boost 里好像有个 lock-free 的,但没研究过。
    duanhui8
        3
    duanhui8  
    OP
       2018-04-29 11:17:56 +08:00
    @pynix 已经集成了 nanomsg,但是发布消息时间太长,导致用户线程 wait,所以考虑再接一个队列,用户线程只向队列写消息,有其他线程负责取数据发送。
    duanhui8
        4
    duanhui8  
    OP
       2018-04-29 11:18:50 +08:00   ❤️ 1
    @pynix 另外 zmq 的多线程不安全,会导致 double free 内存,昨天刚踩的坑
    hncqp
        5
    hncqp  
       2018-04-29 11:23:32 +08:00 via iPhone
    同机器的话,手写
    duanhui8
        6
    duanhui8  
    OP
       2018-04-29 11:24:28 +08:00
    @lsmgeb89 嗯嗯,正在测试 lock-free,第一次使用 boost 库,正在研究,所以对性能,原理机制都不太清楚
    orafy
        7
    orafy  
       2018-04-29 11:26:13 +08:00
    每秒 7000+的写入,还是多线程不需要考虑高性能。
    优先考虑易用性,可以考虑 folly 库里的 MPMCQueue
    duanhui8
        8
    duanhui8  
    OP
       2018-04-29 11:27:02 +08:00
    @hncqp 大神,小弟技术有限,自己写 是给自己挖坑啊,这程序要求比较高,出错的后果很严重
    有什么推荐的吗?有现成的代码也行^_^
    htfy96
        9
    htfy96  
       2018-04-29 11:27:59 +08:00
    如果 N 个消费者等价的话不如开 N 个队列,生产的时候 round-robin 到 N 个队列中的 1 个。用自旋锁+sched_yield 保护队列,感觉对 7k qps 的情况应该还能撑过去
    duanhui8
        10
    duanhui8  
    OP
       2018-04-29 11:29:30 +08:00
    @orafy 嗯嗯,好的,我先研究一下,谢谢
    htfy96
        11
    htfy96  
       2018-04-29 11:30:41 +08:00
    不等价的话楼上的 folly::MPMCQueue 是个比较成熟的方案,不过性能可能没有 round-robin + MPSCQueue 高
    duanhui8
        12
    duanhui8  
    OP
       2018-04-29 11:38:08 +08:00
    @htfy96 嗯嗯,多个队列,应该可以降低多线程对单队列并发争用,是个思路,谢谢
    duanhui8
        13
    duanhui8  
    OP
       2018-04-29 11:41:25 +08:00
    @htfy96 MPMCQueue 相比 boost lockfree 如何呢?没经验,谢谢指点^-^
    htfy96
        14
    htfy96  
       2018-04-29 11:50:13 +08:00
    @duanhui8 boost::lockfree Boost 1.53 加入的,个人觉得还不太成熟。不过对你这个情景应该够用了
    duanhui8
        15
    duanhui8  
    OP
       2018-04-29 11:53:48 +08:00
    @htfy96 tank you
    feverzsj
        16
    feverzsj  
       2018-04-29 11:57:59 +08:00
    要数据安全,只能用基于数据库的消息队列,其他消息队列都无法保证数据不丢失
    duanhui8
        17
    duanhui8  
    OP
       2018-04-29 12:03:51 +08:00
    @feverzsj 审计信息,丢个几条也没事。加队列 主要是不能让用户线程 wait 太长,提高响应。队列性能最好高点,然后保障多线程安全,就可以了
    hncqp
        18
    hncqp  
       2018-04-29 13:19:16 +08:00 via iPhone
    @duanhui8 7000qps 不算高
    开源的不太了解
    我们这边都是自己造轮子,3w 多 qps
    简单点的话读写锁加 queue 然后加个 eventfd 通知
    或者环形队列实现
    要不然上个 kafka
    forestyuan
        19
    forestyuan  
       2018-04-29 13:19:20 +08:00
    不知道你的程序运行在什么操作系统上,如果是 Windows 的话,Microsoft Message Queue 应该能满足你的要求
    gabon
        20
    gabon  
       2018-04-29 13:45:18 +08:00 via Android
    ringbuffer 了解一下,可以参考 java 的 disruptor 实现。底层是个环形数组,用硬件 cas 竞争写入,无锁。开发者测试每秒 600w 订单。
    cholerae
        21
    cholerae  
       2018-04-29 13:57:27 +08:00   ❤️ 2
    https://github.com/cameron314/concurrentqueue 线上有服务用了一阵子了,还是挺靠谱的
    duanhui8
        22
    duanhui8  
    OP
       2018-04-29 14:11:24 +08:00 via Android
    @gabon 好的,我了解一下,谢谢
    duanhui8
        23
    duanhui8  
    OP
       2018-04-29 14:13:16 +08:00 via Android
    @cholerae ok,回去后看看,谢了
    duanhui8
        24
    duanhui8  
    OP
       2018-04-29 14:13:32 +08:00 via Android
    @gabon 嗯嗯,好的
    shilyx
        25
    shilyx  
       2018-04-29 14:14:37 +08:00
    正在开发,基于 websocket++,发布 /订阅,服务端 C++跨平台,客户端支持 C++、node、js ( web )、python、golang
    nroskill
        26
    nroskill  
       2018-04-29 18:31:00 +08:00
    hxndg
        27
    hxndg  
       2018-04-29 19:51:12 +08:00 via Android
    dpdk 那个也是 ringbuf,用的原子操作
    fakevam
        28
    fakevam  
       2018-04-29 21:07:19 +08:00   ❤️ 1
    ...7000 要啥这么复杂的东西, 起个 unix domain socket, N 个线程打一个线程, 那边 recv 完事
    hardwork
        29
    hardwork  
       2018-04-30 00:06:29 +08:00 via Android   ❤️ 1
    什么场景要跑 200 到 400 线程,这本身就是低性能吧?请教一下各位
    noli
        30
    noli  
       2018-04-30 03:33:18 +08:00
    @cholerae

    #21 扫了一遍终于有人推荐这个了。同推荐给楼主。
    duanhui8
        31
    duanhui8  
    OP
       2018-04-30 17:06:33 +08:00 via Android
    @noli 谢谢
    fakevam
        32
    fakevam  
       2018-04-30 23:35:05 +08:00
    @noli 大哥们, 这个不适合线程不确定的场景, 会死锁...你们有认真看过他代码么?
    noli
        33
    noli  
       2018-04-30 23:38:07 +08:00
    @fakevam #32

    有看过,并且仿写过。做不到比他的更好。

    会死锁百分之九十的情况不是 Queue 的问题吧?
    什么叫做 “线程不确定的场景”?
    fakevam
        34
    fakevam  
       2018-05-01 00:45:22 +08:00
    @noli 线程数量无法确定的情况下, 这货的代码不能用...他只能用于线程数固定的场景, 否则会触发死锁, 因为他的 bucket 分配的问题
    fakevam
        35
    fakevam  
       2018-05-01 00:46:11 +08:00
    @noli 另外, 7000 的并发, 要这么复杂的方案做啥, 谁维护? 单线程代码写得稍微好点, libevent 随便路路, 不是随便 10W 的 qps 么...为什么要搞这么复杂的设计...
    noli
        36
    noli  
       2018-05-01 03:58:01 +08:00
    @fakevam #34 #35

    他不是在 issue 里面提供了 work around 吗? 我觉得这基本不是问题。

    一开始我并没有太多关注题主的应用情景。
    我甚至不确定他说的 7000 是指整个 queue 的负荷还是单个 producer   的负荷。

    如果是 7000 是指整个 Queue 的负荷,那么直接用系统锁就足够了,
    但如果是指单个 producer 7000 ops,那确实只能用 这种基于 spinlock 的。

    如果题主不太介意局部写入延迟比较大的话,我本人倒是试过不用靠预先分配的解法。
    fakevam
        37
    fakevam  
       2018-05-01 16:01:08 +08:00
    @noli 200 * 7000 是 140W 的 qps, 通过 socket 打网络出去, 你算过带宽么, 单核 socket 小包 15Wqps 很好了, 千兆网卡的话, 100W 就已经是瓶颈了, cpu 再多也不怎么上的去了..万兆网卡的话, 另说...
    noli
        38
    noli  
       2018-05-01 16:15:59 +08:00
    @fakevam #37

    说不定楼主是土豪金融公司,连光纤延迟都要考虑在内的那种,万兆是标配。

    不过按理,如果真的这么大的工作负荷,应该会考虑多个转发点的设计吧?
    又或者这种负荷就是要求单个转发点的性能标准?
    duanhui8
        39
    duanhui8  
    OP
       2018-05-01 18:45:32 +08:00
    @fakevam 不使用队列,由用户线程发送审计信息,会增加用户访问延时,并且降低数据库性能
    duanhui8
        40
    duanhui8  
    OP
       2018-05-01 18:47:38 +08:00
    @noli 确实是金融公司,但是不需要记录查询操作,所以,记录量少很多。高峰估计几 W 的 TPS
    mattx
        41
    mattx  
       2018-05-01 19:56:15 +08:00
    @cholerae 问下 这个是不能保证顺序的对吧?
    fakevam
        42
    fakevam  
       2018-05-01 21:14:39 +08:00
    @duanhui8 你没明白我啥意思, 通过 socket 去做队列, 不写队列那么复杂的东西了, 当然, 几万的 tps, 你就是 stl 加锁, 性能也毫无问题, 不要小看现在的 cpu...

    直接 libevent, consumer 监听一个本地的 unix domain sock, 所有的 producer 连接 unix domain sock, 然后往里面写, consumer 负责从里面读, 读出来以后, 写到后端 db, 怎么会增加数据库的性能压力...

    当然,你就是 stl + lock 也毫无问题, 不要过早优化, 不要过早优化, 不要过早优化, 重要的事说三次, 在你没有压测之前, 把各种乱七八糟的东西都丢掉! 简单的 lock 可以解决 99%的问题, 特别是啥 spinlock 和 lockless lockfree, 把他们当彻底忘记掉, 他们没有意义, 除非你要 ns 级别的延迟.

    我特别特别讨厌各种东西, 上来就是复杂设计, 简单的设计, 测过了才知道, 起码拿着 perf 之类的看过了, 热点到底在哪里, 而不是想当然的各种轮子往上套, 套出一堆复杂得没办法维护的东西出来
    superzmy
        43
    superzmy  
       2018-05-01 21:19:27 +08:00
    我这边有一个多线程写单线程读(或者读加锁把数据复制到栈上)的缓冲池,就专门适应这种网络收发。
    测试性能 25Mqps ( 2500 万次 /s )实际工作性能 8M qps,或者说 800 万次 /s。数据不定长,跟上面的 concurrencyqueue 定长类型是不一样的。
    不过代码不会免费分享
    haibocui
        44
    haibocui  
       2018-05-01 21:33:09 +08:00 via Android
    一个线程一个队列
    duanhui8
        45
    duanhui8  
    OP
       2018-05-02 09:02:21 +08:00
    @fakevam 嗯嗯,谢谢分享你的经验,知道了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1401 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 23:45 · PVG 07:45 · LAX 15:45 · JFK 18:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.