V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
klgd
V2EX  ›  程序员

求助,支付系统的异步通知实现

  •  
  •   klgd · Sep 28, 2016 · 12154 views
    This topic created in 3507 days ago, the information mentioned may be changed or developed.

    目前的实现方式:
    参考支付宝的异步通知,每个订单的异步通知实行分频率发送:15s 3m 10m 30m 30m 1h 2h 6h 15h
    脚本每秒请求数据库获取到时间需要发送的通知,返回成功则结束,失败则按下一个时间写入数据库,等待发送
    目前的方式虽然实现了但是 1 、每秒请求有点儿浪费资源; 2 、通知方式不稳定; 3 、无法承受大数据量等等

    现在想改为使用队列的方式实现,但是不知道如何实现通知返回失败,往队列里写下一次通知?
    方案 1 :到下一次发送通知时间时,写入队列,队列实时处理,但不知道该如何实现到指定时间写队列
    方案 2 :队列里延迟,也找到了可以实现这个延迟的队列 beanstalk,但是无法实现分布式,单台的方式公司运维不给弄,他建议用 kafka,但是 kafka 好像不可以延迟

    想请教大家,异步通知是实现方案,或者上面的问题怎么解决?

    27 replies    2019-08-29 16:06:16 +08:00
    pubby
        1
    pubby  
       Sep 28, 2016
    benstalk 支持分布式的 类似 memcache ,客户端可以同时连几个 beanstalk 服务器
    beanstalkd 的队列数据也可以写入磁盘 log 的

    但是,如果一个挂掉,磁盘又出故障无法恢复的话,会丢失一部分队列数据
    klgd
        2
    klgd  
    OP
       Sep 28, 2016
    @pubby 看介绍说支持分布式,我安装了 php 扩展 https://github.com/nil-zhang/php-beanstalk/, phpinfo 里也看到有 beanstalk 扩展了,但是按照示例执行,始终返回 false ,不知道哪里的问题,而且扩展都是好几年前的了
    moro
        3
    moro  
       Sep 28, 2016
    可以用 golang 写服务来处理,自身包含队列,异步发送通知。
    klgd
        4
    klgd  
    OP
       Sep 28, 2016
    @moro golang 不会啊
    pubby
        5
    pubby  
       Sep 28, 2016
    @klgd 不要 so 扩展,不稳定。

    我用的是 composer require mrpoundsign/pheanstalk-5.3
    xss
        6
    xss  
       Sep 28, 2016
    rabbitmq 不可以?
    pubby
        7
    pubby  
       Sep 28, 2016
    @klgd 不过我用的不支持连多个 server ,你可以其他找找看
    moro
        8
    moro  
       Sep 28, 2016
    用 redis 的有序集合,按时间排序,每次把需要处理的事务插入进去。
    处理部分每秒读取一次有序集合,只取出当前时间的进行处理就可以了。
    moro
        9
    moro  
       Sep 28, 2016
    分布式的话可以在 redis 使用 lua 脚本来保证一致性。
    klgd
        11
    klgd  
    OP
       Sep 28, 2016
    @xss 不知道是否可以 没用过 rabbitmq
    @moro 谢谢,我去尝试一下,每秒请求有没有好的方案?
    @pubby 谢谢你的提供,这个包应该是可以用, so 扩展确实不好用,读队列就用死循环?有没有其他好的方案?
    TangMonk
        12
    TangMonk  
       Sep 28, 2016
    rabbitmq 有个插件可以延迟发送
    moro
        13
    moro  
       Sep 28, 2016
    redis 每秒请求无压力,都是内存的。
    fansgentle
        14
    fansgentle  
       Sep 28, 2016
    Python 平台的话 Celery 就很赞 ...
    pubby
        15
    pubby  
       Sep 28, 2016
    @klgd 看了一下,这个库在 pool 上 reserve() job 的时候也有问题
    它是随机找一个 server 获取的,如果队列很空的话,可能会长时间饿死状态。

    put 也是随机找一个 server 写入


    可以改变一下应用方案:
    1. 生产者 put 的时候可以向所有 pool 里面随机挑选一个写入(内部已经实现)
    2. 每个消费者脚本只连一台 beanstalkd 进行任务处理。任务忙,可以多启动几个消费者连接同一台 beanstalkd 。
    accacc
        16
    accacc  
       Sep 28, 2016
    使用 redis 的 zset 结构做队列 score 写入时间戳 按照小于当前时间的出队
    hankwh
        17
    hankwh  
       Sep 28, 2016
    异步通知不好实现的话,你提供查询订单结果的接口就可以了,让接入方主动查询
    klgd
        18
    klgd  
    OP
       Sep 28, 2016
    @pubby 嗯,今天在试用时发现这个问题了,不过还没来得及思考如何和业务需求结合
    @hankwh 查询接口有提供
    @accacc redis 的排序功能?这个思路也不错,回头我想想怎么具体实现
    @fansgentle python 不会啊
    youxiaer
        19
    youxiaer  
       Sep 28, 2016
    beanstalk 做这个很合适
    sherlocktheplant
        20
    sherlocktheplant  
       Sep 28, 2016
    rabbitmq 好像可以实现你的功能 也支持分布式部署
    cszchen
        21
    cszchen  
       Sep 28, 2016
    可以用 php-resque ,本身是一个队列,支持定时执行。
    pubby
        22
    pubby  
       Sep 28, 2016
    印象中 rabbitmq 貌似没有 message 的优先级,几年前用过,至少 php 的 amqp 扩展没有支持优先级设置。

    rabbitmq 比较强大,能做消息系统
    beanstalk 只能做队列
    julyclyde
        23
    julyclyde  
       Sep 29, 2016
    用什么队列其实是无所谓的

    轻负载的情况下,队列长度基本保持在 0 ,也就是收到之后立刻就能处理
    当队列积压的情况下,虽然没及时确认,导致支付网关假确认,但你也没更好的办法了
    所以其实没啥需要担心的
    jerray
        24
    jerray  
       Sep 29, 2016
    延迟发送用 RabbitMQ 可以实现,并不需要任何 RabbitMQ 扩展。

    方案:

    比如说你有一个 Exchange EA ,一个队列 QA ,通过 EA 进来的消息会被分发到 QA 上, Consumer 监听着队列 QA ,一旦有消息就会被消费。
    然后创建一个 15s 消息超时的延时队列 QA_deferred_15s ,设置参数 x-message-ttl 为 15000 , x-dead-letter-exchange 为 EA 。

    QA 的 Consumer 消费队列消息时,如果认为需要延时重试,则把这条消息发送到 QA_deferred_15s 中。由于设置了 x-message-ttl 参数, 15 秒后, QA_deferred_15s 中的这条消息会超时。由于 x-dead-letter-exchange 设置为了 EA ,超时的消息会被发送到 EA ,再由 EA 分发给 QA 。

    依次类推,创建其他延时队列。大致流程就是这样:

    Publisher -> EA -> QA -> Consumer
    Consumer -> QA_deferred_15s -> message timeout -> EA
    Consumer -> QA_deferred_3m -> message timeout -> EA
    Consumer -> QA_deferred_10m -> message timeout -> EA
    ...

    需要在 Consumer 中改写消息,以便下次需要重试时能知道把消息丢进哪个延时队列。
    klgd
        25
    klgd  
    OP
       Sep 30, 2016
    @jerray 感谢回复,写的很详细,我让我们运维看看, RabbitMQ 没用过,不懂这块的东西
    inputnames
        26
    inputnames  
       Dec 13, 2017
    楼主,请问你支付解决了吗,我也遇到同样的问题。求帮助呀
    Evilk
        27
    Evilk  
       Aug 29, 2019
    @jerray 赞! 目前正打算用 RabbitMQ 来做此功能,跟你描述的完全一样,如果延迟时间相同,则可只创建一个死信队列,如果延迟时间不同,则需要为每种延迟时间创建对应的死信队列
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   3328 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 82ms · UTC 11:32 · PVG 19:32 · LAX 04:32 · JFK 07:32
    ♥ Do have faith in what you're doing.