V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
SANJI59
V2EX  ›  问与答

关于系统并发问题,请各位 V 友帮忙分析下。

  •  
  •   SANJI59 · 322 天前 · 1707 次点击
    这是一个创建于 322 天前的主题,其中的信息可能已经有所发展或是发生改变。

    公司业务是物联网方向的,开发产品是共享充电桩,现在系统到了瓶颈期目前已经部署的在线设备量 1500 台左右系统预计部署 1w+。最近发现随着设备量的递增系统方面出现了 BUG ,不确定是否是并发问题还是程序代码问题,五一前以为是 SQL 问题然针对慢 SQL 一顿优化可以系统卡顿问题已解决,但是设备消息处理问题还是没解决,1000 台设备在线 30 秒最少有 1000 条消息。也没确定出现问题的原因。 业务流程是这样的,用户小程序扫码设备进入选择充电界面这时候会连接一个 WebSocket 并订阅一个设备的消息主题。当用户选择端口开启充电时,服务器会下发指令给设备,设备开启充电时会返回开启端口成功的指令到服务器,在服务再将消息处理之后使用 ws 推送给前端小程序表示订单已开启。 目前出现的问题是用户扫码开启端口充电时后台没有推送 WS 消息,导致订单处于为正常开启的状态(故障)其实设备已经回复开启成功了但是服务器还没有处理过来。出现问题的时间都是高峰期(下午六七点)。系统到这个点进行中订单有 800 多个。但是系统有时候会出现此问题有时候有没有。 目前问题不好确定导致不好下手解决,根据观察到的问题可能是出在高并发,WebSocket 使用的 Java 开发,处理设备业务消息使用线程池,WebSocket 、单服务、服务器 8 核 16G 。麻烦各位 V 友帮忙分析下。有好的设计思路和框架也可以向我提供。我可以跟公司申请有偿

    33 条回复    2023-05-12 16:10:22 +08:00
    opengps
        1
    opengps  
       322 天前 via Android
    主要就是找到瓶颈点,逐个击破即可
    具体问题点出在哪,单看这段问题是没办法准确定位的
    opengps
        2
    opengps  
       322 天前 via Android
    我现在能盲猜到一个因素是,你现在的 websocket 建立连接太慢
    brader
        3
    brader  
       322 天前
    ws 断线重连机制做了吗?心跳机制做了吗?及时剔除下线客户端,减轻 ws 连接列表维护的压力,然后记录一下 ws 连接数或者开个服务可以查询到连接数,还要记录或者能看到一次请求过来到响应的具体时长,观察下高峰期的数据,以及服务器的 cpu ,内存,带宽状况
    shore507
        4
    shore507  
       322 天前
    你这种情况建议长连接的服务就不要自己做了,用云服务比较好。另外从业务上来说,你小程序上的 ws 其实不是必要的,用户支付后用 api 获取订单详情即可,从业务流程上想办法省掉 ws 应该是可行的
    bootvue
        5
    bootvue  
       322 天前
    这种场景不适合 ws 吗 改造成 mqtt
    bootvue
        6
    bootvue  
       322 天前
    @bootvue
    pubby
        7
    pubby  
       322 天前 via iPhone
    java 增加日志记录订单对应的 ws 消息是否发送成功

    小程序不在前台 5s 后会断开 ws ,不知道扫码唤起摄像头过程会不会出现这种情况

    小程序再加个 http api 轮询机制,主动查看设备是否已成功打开,不要只依赖 ws 消息
    SANJI59
        8
    SANJI59  
    OP
       322 天前
    @pubby 是的,主要这个问题还不好复现,现在系统又没问题由于在运营中也不好随便更新😂😂
    SANJI59
        9
    SANJI59  
    OP
       322 天前
    @bootvue 嗯嗯有这个想法。现在猜测 ws 可能有问题,但是系统出现的问题没法复现,不好下手。
    hhjswf
        10
    hhjswf  
       322 天前 via Android
    盲猜不是性能问题,代码的问题
    SANJI59
        11
    SANJI59  
    OP
       322 天前
    @brader 这些都做了,服务器够大跑单体是没问题的。估计代码哪里有问题吧
    liqinliqin
        12
    liqinliqin  
       322 天前
    这个用 swoole 最好,
    git00ll
        13
    git00ll  
       322 天前
    http 轮询好了,这点并发量肯定没啥问题的
    corningsun
        14
    corningsun  
       322 天前
    服务器会下发指令给设备,设备开启充电时会返回开启端口成功的指令到服务器,在服务再将消息处理之后使用 ws 推送给前端小程序表示订单已开启

    改成

    服务器下发指令给设备的同时推送给前端小程序表示订单已开启。

    设备开启充电成功 /失败时,返回开启结果到服务器。如果失败了,再推失败的消息到小程序。
    sujin190
        15
    sujin190  
       322 天前
    1500 个设备都很少了,话说都没写日志的么?你这描述了半天看起来也没弄清楚是设备给服务器回开启成功有问题还是服务器给小程序回开启成功有问题,你这设备接入用的 mqtt ?而且你小程序是用 ws 发送给的开启指令么?其实阿里云的 IOT 云的 RR 消息就很容易处理这种,小程序这边短时请求用 ws 确实费劲,几千台设备么单体服务也足够也不复杂
    SANJI59
        16
    SANJI59  
    OP
       322 天前
    @sujin190 是的用的 MQTT ,生成订单的时候服务器就会下发指令给设备开启端口。服务器收到设备开启端口的返回指令之后将该消息处理之后再使用 ws 推给小程序。这个小程序收不到消息的问题时而有时而没有。
    sujin190
        17
    sujin190  
       322 天前
    @sujin190 #15 那就是说小程序其实是通过单独的 http 接口创建订单然后给设备发送指定的,那话说你怎么保证这时候 ws 是已经建立好的了啊?那会不会高峰期无论是带宽紧张还是应用负载原因导致 ws 建立成功时间变长了那是不是要出错了?设备执行命令成功给服务器通知结果其实应该是更新数据订单状态了吧,小程序通过数据库订单信息补偿就好了啊
    sujin190
        18
    sujin190  
       322 天前
    @SANJI59 #16 其实如果订单通过 http 请求发送的话,结果通过一个 long poll http 接口来获取应该是更简单可靠的

    https://segmentfault.com/a/1190000041190907

    我们直接也搞过不少这种,还是 http long poll 更方便,搞过 openrest 的,用 spring boot 的话担心连接占用过多线程,其实可以用 DeferredResult ,工作线程会被释放,连接放到底层 nio 了,也可以支持大量并发请求
    yishengyongyi
        19
    yishengyongyi  
       322 天前
    1000 台设备太少了,现在充电桩市场都饱和了,随便几万就能买一套源码,能带上万台设备
    cheng6563
        20
    cheng6563  
       322 天前
    推送本来就是要和轮训配合同时使用的,只单独靠推送就是这样非常不可靠。
    cheng6563
        21
    cheng6563  
       322 天前
    但靠推送的话,那就要设计一个回执逻辑,推送后未收到回执就要进行重试推送。
    sujin190
        22
    sujin190  
       322 天前
    @SANJI59 #16 顺便说,小程序要使用 ws 的原因一般也是因为 Http 接口服务和 mqtt 服务不是同一个,服务返回结果,分享一下我们再用的 Spring Boot 可以跨服务的 Event 吧

    https://gist.github.com/snower/f8ef25e57c72f9b41fb31ee8b164193b

    逻辑也毕竟简单,创建完成订单给设备发送命令前用订单号为 key 创建一个 Event ,然后接着给设备发送命令,设备执行成功后服务的收到结果通知更新万订单状态后,用相同的订单号为 key 也创建一个 Event ,接着调用 set 设置 Event ,http 接口这边 wait 就可以收到反馈了,具体订单信息可以再读数据库就好了

    不过依赖一个外部服务来完成的

    https://hub.docker.com/r/sujin190/slock
    LLaMA2
        23
    LLaMA2  
       322 天前
    共享充电没有太大的并发,同一时刻不会有很多人同时支付充电,而且他们极少会发生对同一个设备支付。
    你的并发应该是你的设备周期上报状态。这里是可以出现问题,只要不再支付和交付使用两个环节出问题,客户绝对不会找你麻烦。
    大概率你的 ws 的重连有问题,估计也没法放弃 ws ,因为充电桩的单片机程序不方便更新
    SANJI59
        24
    SANJI59  
    OP
       322 天前
    @sujin190 没有做保证连接成功的机制😂,但是如果是高峰期导致的 ws 连接时间边长或者 ws 其他原因,设备上报成功开启端口的指令是没有影响的。然后再服务器中观察订单状态是没有更新成功的还是处于订单未开启的状态。按道理我更新订单状态和更新设备端口使用状态这个过程不受影响的啊。所以这个问题很迷,打算换其他方式。谢谢指导
    sujin190
        25
    sujin190  
       322 天前
    @SANJI59 #24 就是服务器收到设备返回的开启成功的通知但是数据库看订单状态没改?这订单状态应该是服务器改的吧,这还会有问题?莫非是主从延迟导致的?或者是事务提交延迟?不过正常应该是订单创建完成强制事务提交了之后才给设备发的指令的吧,用 spring 这种框架的话要注意框架的自动事务管理逻辑啊,一般来说如果你的设备操作只是一个简单的电磁锁开关的话,估计延时能在几十毫秒到 200ms 以内吧,数据库负载上来事务提交延时超过应该还是很容易的吧
    SANJI59
        26
    SANJI59  
    OP
       322 天前
    @sujin190 是的,只有高峰期会这样,主从搭了还没用。用 mqtt 工具监听设备开启端口的指令已经回复了。我猜测是不是消息太多导致线程池处理不过来堵塞了?不在高峰期就算后台几千个订单进行中开启充电也是毫秒级的
    netnr
        27
    netnr  
       322 天前 via Android
    扫码服务器处理完成后一并返回,这个过程要很久吗,弄一个等待界面 30 秒内搞定 体验效果也不差,超时就重新扫,或想办法改进扫码处理的逻辑
    sujin190
        28
    sujin190  
       322 天前 via Android
    @SANJI59 处理跟不上慢的话看日志应该很好区分吧
    totoro52
        29
    totoro52  
       321 天前
    "在服务再将消息处理之后使用 ws 推送给前端小程序表示订单已开启"
    感觉这个 WS 是可以省掉的,没必要建立一个 WS , 用于开启充电后服务器会下发执行给设备,设备返回成功执行给服务器,服务器在通过小程序推送的方式推送给用户即可
    首先是一个订单状态问题, 用户开启后应该会生成一笔订单,并开始计时,用户只需关心订单状态,建议直接改成一个定时轮询的方式去查订单的状态
    其次是通知这块,你可以对接微信小程序的消息模板, 这也是一个优化手段。

    我理解的业务流程是
    用户扫码-》 用户支付-》服务器下发执行-》 设备执行-》 设备执行返回服务器-》 服务器更改订单状态-》 用户前台轮询查看订单状态。
    totoro52
        30
    totoro52  
       321 天前
    你可以关注一下共享充电宝和共享单车的解决方案
    共享单车有些是利用了蓝牙的
    共享充电宝有些就是采用轮询, 只是动画上做了个进度条,让用户感知上看起来舒服点。
    zbinlin
        31
    zbinlin  
       321 天前
    数据库中的订单状态是由设备返回开启端口成功的指令消息来更新的吧?你说订单状态还没更新成功,说明服务器没收到设备返回开启端口成功的指令或者处理这个指令消息还在排队或者处理时出错了?
    SANJI59
        32
    SANJI59  
    OP
       321 天前
    @zbinlin 是的是这样,高峰期就会。
    zbinlin
        33
    zbinlin  
       321 天前
    @SANJI59 那你就先排查服务器有没有成功收到指令。如果有收到,再排查处理这个指令的服务是否有问题不就可以了吗?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3323 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 13:30 · PVG 21:30 · LAX 06:30 · JFK 09:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.