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

基于 TCP 实现极简的业务流控

  •  
  •   feng32 · 2020-06-14 11:07:11 +08:00 · 3319 次点击
    这是一个创建于 1621 天前的主题,其中的信息可能已经有所发展或是发生改变。
    假设有一系列实时数据 (Message 结构) 要通过 TCP 发送

    我们要实现: TCP 传输通畅的时候,按照预定速率发送; TCP 阻塞 (但是连接没有断开) 时,可以丢掉一些 Message

    我想到的一个非常简单的方法是:把 TCP 发送缓冲区调大点,然后调用 send 时,如果能把 Message 一次性填进去,就填进去,否则就把 Message 整个丢掉

    问题来了:这个方案可实施吗?如何让 send api 工作在原子的、非阻塞模式?
    24 条回复    2020-06-15 07:34:24 +08:00
    ho121
        1
    ho121  
       2020-06-14 11:09:39 +08:00 via Android
    udp 不好么
    catror
        2
    catror  
       2020-06-14 11:21:33 +08:00 via Android
    可以,不过当前消息有部分填进去的话,你只能丢后面的消息。
    gamexg
        3
    gamexg  
       2020-06-14 11:35:06 +08:00 via Android   ❤️ 2
    增加一个队列
    开一个线程,从队列取消息写入 tcp 连接
    其他线程发消息都是发送到队列,队列满了就丢弃。
    rrfeng
        4
    rrfeng  
       2020-06-14 11:51:28 +08:00 via Android
    还是从业务层实现比较好。
    xuanbg
        5
    xuanbg  
       2020-06-14 12:01:09 +08:00
    不好,传输层不应该干涉 /影响业务层的逻辑
    feng32
        6
    feng32  
    OP
       2020-06-14 12:02:39 +08:00
    @ho121 UDP 长度不够

    如果消息比较短,UDP 就完全没问题了
    feng32
        7
    feng32  
    OP
       2020-06-14 12:04:01 +08:00
    @catror 要避免的,就是 “只填消息的一部分进去”
    catror
        8
    catror  
       2020-06-14 12:08:14 +08:00 via Android   ❤️ 1
    @feng32 所以我说只能丢后面的消息,填了一部分的消息剩余部分你要发完。对于你来说,丢当前这条和丢下一条并没有区别。
    Vegetable
        9
    Vegetable  
       2020-06-14 12:19:23 +08:00
    说的好像很复杂,其实逻辑抽象一下很简单,就是发送消息的时候判断是否要丢弃下一条消息。
    如果所谓阻塞是通过前 N 条消息判断的:当前时间距离之前 N 条消息开始发送的时间已经超过了 t,则认为当前网络阻塞,判断下一条消息是否可以丢弃,可以丢弃则丢弃。

    这么抽象一下之后,就和使用什么协议没有关系了。
    optional
        10
    optional  
       2020-06-14 12:20:09 +08:00 via iPhone
    debug 的时候要死人。
    feng32
        11
    feng32  
    OP
       2020-06-14 12:25:07 +08:00
    @catror 假设生产的速率是一定的,在 3F 的解法中:

    每次生产出一个 Messsage (onNewMessage),就调用一次 queue.push() 方法实现原子操作;另一个线程从 queue 中不断读取数据并发送

    在 8F 的解法中:

    每次生产一个 Message (onNewMessage),就调用 send() 方法把这个 Message 整个发出去,如果需要等待,就一直阻塞在那里;这时因为发生了阻塞,实际上也就不会有新的 Message 产生

    本质上,前者是 “生产不阻塞” 模式,后者是 “阻塞生产” 模式

    我想了一下,对我的实际场景,两者都是可以的。但是 8F 的解法对生产的逻辑有一个要求:生产者要能感知到阻塞的发生,在阻塞结束后,测量一下阻塞了多少时间,然后下次生产新的实时数据,而不因为阻塞导致后续生产的数据实时性发生整体偏移
    reus
        12
    reus  
       2020-06-14 12:43:46 +08:00
    TCP 是可靠传输,除非你断开连接,否则不可能实现“丢 message”

    再说了,协议实现怎么知道什么是“message”? TCP 是流式传输,传输层也不可能引入这个概念

    没有哪个 socket 实现提供缓冲区的控制,所以你这纯粹是空想

    允许丢弃的当然用 UDP,自己切帧再组合就是了,不存在什么“长度不够”

    想那么多,难道不会写个 demo 验证自己的想法?如果没有验证的能力,那还是多学习,少思考比较好。
    jedihy
        13
    jedihy  
       2020-06-14 12:44:00 +08:00 via iPhone
    缓冲区大小和网络拥塞不拥塞有什么关系
    feng32
        14
    feng32  
    OP
       2020-06-14 12:50:11 +08:00
    @jedihy 在原方案下,即能够知道缓冲区能不能再放下一个 Message,而此时原本最多只有 64K 缓冲区,一个 Message 都放不下,那就肯定不行了

    但是如果采用 3F / 8F 的方案,就没有这个问题了
    feng32
        15
    feng32  
    OP
       2020-06-14 12:54:28 +08:00
    @reus 标题里已经提到了,这里要做的是业务层的流控,传输层用的是 TCP

    3F / 8F 已经给出比较好的解法了,3F 是一个解法,8F 是一个绕过的思路
    feng32
        16
    feng32  
    OP
       2020-06-14 12:59:17 +08:00
    @reus 我刚才查了一下,还真有这个方法,我原本以为这种功能应该不存在的

    https://stackoverflow.com/questions/38359272/linux-sockets-how-to-get-number-of-bytes-packets-in-sending-buffer

    所以这就是第三个解法了 :)
    GeruzoniAnsasu
        17
    GeruzoniAnsasu  
       2020-06-14 15:00:41 +08:00
    “业务流控” 跟 tcp 压根就没什么关系
    sujin190
        18
    sujin190  
       2020-06-14 15:01:03 +08:00
    如果你的意思是很多大 message 会占用很多传输时间,网络不好的时候会阻塞整体性能的话
    一般来说这种情况下用 tcp 的话,更常用的做法应该是大 message 使用单独连接发送,小消息使用一个连接,网络不好大消息超过一定时间无法传输完成直接关闭连接就是了,考虑到连接建立开销啥的,用连接池就行了吧
    不能开连接的话,那么应该是分小帧传输,加入优先级就行,大消息优先级比小消息优先级更低,这样也可以保证良好性能
    raynor2011
        19
    raynor2011  
       2020-06-14 15:10:13 +08:00 via Android
    接收方判断下消息发送接收时间就行了吧
    feng32
        20
    feng32  
    OP
       2020-06-14 15:11:55 +08:00 via Android
    @sujin190 实际上是类似视频直播的场景,已经卡了几帧了那就算了,下次继续发最新画面
    feng32
        21
    feng32  
    OP
       2020-06-14 15:13:07 +08:00 via Android
    与其重传过时的内容,选择跳帧
    momocraft
        22
    momocraft  
       2020-06-14 15:29:45 +08:00
    隐约读到 "粘包" 两个字
    araaaa
        23
    araaaa  
       2020-06-15 01:00:40 +08:00
    traffic shaping
    love
        24
    love  
       2020-06-15 07:34:24 +08:00 via Android
    通过实际发送消息速率动态调整发送间隔呢?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5837 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 02:00 · PVG 10:00 · LAX 18:00 · JFK 21:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.