求助,支付系统的异步通知实现 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
klgd
V2EX    程序员

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

  klgd 2016-09-28 13:37:13 +08:00 11737 次点击
这是一个创建于 3302 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

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

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

put 也是随机找一个 server 写入


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

rabbitmq 比较强大,能做消息系统
beanstalk 只能做队列
julyclyde
    23
julyclyde  
   2016-09-29 11:50:14 +08:00
用什么队列其实是无所谓的

轻负载的情况下,队列长度基本保持在 0 ,也就是收到之后立刻就能处理
当队列积压的情况下,虽然没及时确认,导致支付网关假确认,但你也没更好的办法了
所以其实没啥需要担心的
jerray
    24
jerray  
   2016-09-29 12:08:33 +08:00
延迟发送用 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
   2016-09-30 09:00:11 +08:00
@jerray 感谢回复,写的很详细,我让我们运维看看, RabbitMQ 没用过,不懂这块的东西
inputnames
    26
inputnames  
   2017-12-13 09:21:55 +08:00
楼主,请问你支付解决了吗,我也遇到同样的问题。求帮助呀
Evilk
    27
Evilk  
   2019-08-29 16:06:16 +08:00
@jerray 赞! 目前正打算用 RabbitMQ 来做此功能,跟你描述的完全一样,如果延迟时间相同,则可只创建一个死信队列,如果延迟时间不同,则需要为每种延迟时间创建对应的死信队列
关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2975 人在线   最高记录 6679       Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 26ms UTC 13:49 PVG 21:49 LAX 06:49 JFK 09:49
Do have faith in what you're doing.
ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86