支付子系统架构及常见问题

支付流程

    对于支付系统来说,它最重要的其实是安全,所以整个支付流程采用秘钥加签的方式进行操作,一共四对秘钥,以支付宝在线支付为例子,首先通过RSA2算法生成商户公钥以及商户私钥,同时支付宝平台会提供支付宝公钥和支付宝私钥,将支付宝公钥下载到平台项目中,同时将商户公钥上传到支付宝后台,当用户在前端表单中填写好充值金额后,生成对应商户订单,如果用户选择支付,将会把支付价格、订单号以及订单描述三个参数排列组合后利用商户私钥进行加签操作,同时将密文url进行重定向到支付宝统一支付接口,支付宝平台会利用商户公钥进行验签操作,验签通过后将对支付宝钱包余额进行减量操作,同时利用支付宝私钥对回调参数进行加密,包括商户订单号,支付宝订单号以及支付状态,支付成功后,支付宝平台会将带参进行回跳操作,回调到平台后,利用支付宝公钥对参数进行解密,随后利用回调的商户订单号或者支付宝订单号对平台的钱包余额以及订单状态进行修改操作。

支付一致性问题

    由于我们平台的充值业务会面临一些高并发情况,也就是单用户可能同一时间点同时支付充值操作,如果一秒内同时有三笔50元的支付请求成功,后台可能会出现支付一致性问题,也就是余额可能只增加50的情况,这里为了保持数据一致性,我们采用了redis的setnx分布式锁进行操作,当单用户进行余额修改流程之前,先利用商户uid作为key获取分布式锁,余额修改完成后,释放分布式锁,一般情况下,考虑到程序的健壮性,防止服务宕机意外报错等情况发生,会将释放锁放到异常捕获机制的finally中,因为理论上finally肯定会执行,不会出现死锁问题,您觉得finally会百分之百执行吗?其实不一定,因为机房可能会发生物理断电的问题,即使进入try代码块,finally也不一定会执行,这样就造成了死锁问题,所以需要给分布式锁设置一个10秒的生命周期,如果10秒内没有修改成功,我们会认为该操作发生了异常自动释放锁。

分布式锁问题

  设置了过期时间,如果业务还没有执行完成,但是redis锁过期了,怎么办?

  加锁的时间是30秒.如果加锁的业务没有执行完,那么到 30-10 = 20秒的时候,就会进行一次续期,把锁重置成30秒.那业务的机器万一宕机了呢?宕机了定时任务跑不了,就续不了期,那自然30秒之后锁就解开了.

具体使用redisson模块

友商QPS问题

    在做三方支付平台对接的时候,实际上支付宝的统一支付接口是有qps限制的,qps限制是100,也就是一秒内只支持100单的支付请求,超出的会直接返回403状态码,其实这种设计也是合理的,因为友商没有必要帮我们承担高并发请求,所以我在订单支付和请求支付宝接口之间做了一个缓冲区,生产者不会直接和消费者产生关系,而是通过缓冲区解耦,这个缓冲区就是异步任务队列,队列容器我采用redis数据库,因为redis性能优势比较明显,同时内置的list数据类型比较契合队列这种数据结构,工具类内置了,初始化方法,入队方法,出队方法,队列长度,以及查重唯一方法。

    每当商户提交支付请求,将订单id进行入队操作,遵循fifo原则,在消费者端使用多线程的方式进行消费,也就是出队操作,这里的线程数我们可以通过变量进行控制,峰值线程数大概维持在80左右,不会突破100,起到一个削峰填谷的作用。

支付回跳问题

   这是QA提出一个问题,就是在支付过程中,会有因为网络因素或者其他原因导致支付宝没有回跳成功,此时客户端就会停留在支付页面动不了,造成问题,其实没有回跳成功,不外乎两种结果,就是支付成功,或者支付失败,解决这个问题可以采用定时任务,每隔十秒检测订单状态为支付中的订单,通过订单id做为参数,请求支付宝的订单查询接口,用来判断是否支付成功,随后定时任务会自动将接口返回的订单状态同步到数据库的订单状态中,这里定时任务我采用的是redis中的有序集合,利用zadd方法,将支付中状态订单id作为key,delay参数设置为当前时间戳加10秒后时间,入库。将时间作为score标识物,出队调用zrangebyscore方法,min_score永远为0,max_score就是当前时间戳,这样遍历会形成一个实践窗口,只要定时任务进入时间窗口,就会自动执行,非常方便。

延时队列实现

class DelayRedisQueue:def __init__(self,key):self.key = keyself.r = redis.Redis(decode_responses=True)# 入队def add(self,uid,delay=0):print("延时队列入队,%s秒后执行删除uid%s的任务" %(delay,uid))self.r.zadd(self.key,{uid:time.time()+delay})# 删除延时任务def remove(self,uid):return self.r.zrem(self.key,uid)# 出队逻辑def pop(self):# 起始位置min_score = 0# 区间结束为止max_score = time.time()# 获取队列res = self.r.zrangebyscore(self.key,min_score,max_score,start=0,num=1,withscores=False)if res == None:print("暂无延时任务")return Falseif len(res) == 1:print("延时任务到期,返回执行任务的uid%s" % res[0])return res[0]else:print("延时任务没有到时间")return False

订单缓存问题 mysql-redis数据一致性问题

        我的订单模块由于读取的是订单表,为了分担数据库压力,我们使用redis进行缓存操作,但是如果订单状态修改了,redis中的数据需要做同步,这就带来了mysql-redis的数据同步问题。

        最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。

所以,我们追求的是尽可能保证缓存和数据库的最终一致性。

      在开始之前,我们先来科普一下缓存+数据库读写,最经典的Cache Aside Pattern。

           读取:先读取缓存,缓存里没有,读取数据库,然后返回响应,顺斌保存缓存

           更新:先更新数据库,然后删除缓存

为什么是删除缓存,而不是更新缓存?

        并发情况下更新缓存可能会带来种种问题,直接删除缓存更加稳妥。 缓存更新在很多时候需要耗费资源,直接删除,用时再从数据库读取,写进缓存,更省性能。

一致性问题

那么我们采用这种先更新数据库,再删除缓存,可能会出现什么问题呢?

 假如,我们更新数据库成功,接下来还没来删除缓存,或者删除缓存失败怎么办?

那么很明显,这时候其它线程进来读的就是脏数据。

先删除缓存,再更新数据库一致性问题

我们看一下,如果先删除缓存,再更新数据库可能会带来什么问题。在并发情况下,先删除缓存,再更新数据库,此时数据库还未更新成功,这时候有其它线程进来了,读取缓存,缓存不存在,读取数据库,读取的是旧值,这时候,缓存不一致就发生了。

延时双删

就是在删除缓存,更新数据库之后,休眠一段时间后,再次删除缓存。利用的也是延时队列操作

这就是支付系统的介绍。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/96689.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/96689.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

内存传输速率MT/s

1 0 0 0 0 0 0 0 0 010 9 8 7 6 5 4 3 2 1十 亿 千 百 十 万 千 百 十 个亿 万 万 万传输速率 …

.env文件的作用和使用方法

目录 什么是 .env 文件? 为什么要使用 .env 文件?(好处) 如何使用 .env 文件? 通用步骤: 具体技术栈中的实现: 最佳实践和注意事项 总结 什么是 .env 文件? .env 文件&#x…

深度拆解 Python 装饰器参数传递:从装饰器生效到参数转交的每一步

在 Python 装饰器的学习中,“被装饰函数的参数如何传递到装饰器内层函数”是一个高频疑问点。很多开发者能写出装饰器的基本结构,却对参数传递的底层逻辑一知半解。本文将以一段具体代码为例,把参数传递过程拆成“装饰器生效→调用触发→参数…

【Vue2 ✨】Vue2 入门之旅 · 进阶篇(七):Vue Router 原理解析

在前几篇文章中,我们介绍了 Vue 的性能优化机制、组件缓存等内容。本篇将深入解析 Vue Router 的原理,了解 Vue 如何管理路由并进行导航。 目录 Vue Router 的基本概念路由模式:hash 和 history路由匹配原理导航守卫Vue Router 的路由过渡动…

Linux磁盘级文件/文件系统理解

Linux磁盘级文件/文件系统理解 1. 磁盘的物理结构 磁盘的核心是一个利用磁性介质和机械运动进行数据读写的、非易失性的存储设备。 1.1 盘片 盘片是传统机械硬盘中最核心的部件,它是数据存储的物理载体。盘片是一个坚硬的、表面极度光滑的圆形碟片,被安装…

【星海出品】rabbitMQ - 叁 应用篇

rabbitMQ 的基础知识这里就不阐述了,可以参看我早年写的文章 -> rabbitMQ 入门 https://blog.csdn.net/weixin_41997073/article/details/118724779 Celery 官网:http://www.celeryproject.org/ Celery 官方文档英文版:http://docs.celeryproject.org/en/latest/index.h…

C# 每个chartArea显示最小值、平均值、最大值

private void AddStatisticsAnnotations(ChartArea chartArea, int channelIndex) {RemoveExistingAnnotations(channelIndex);// 获取ChartArea的相对坐标(百分比)float chartAreaX chartArea.Position.X; // X坐标(百分比)floa…

打破“不可能三角”:WALL-OSS开源,具身智能迎来“安卓时刻”?

目录 引言:当“大脑”学会思考,机器人才能走出实验室 一、具身智能的“不可能三角”:机器人“大脑”的核心困境 二、WALL-OSS的四把重锤:如何系统性地破解难题? 2.1 第一锤:更聪明的“大脑”架构 —— …

SigNoz分布式追踪新体验:cpolar实现远程微服务监控

前言 SigNoz是一款开源的应用性能监控工具,专为微服务架构设计,集成了指标、追踪和日志分析功能。它能够全面监控分布式系统的性能,帮助开发团队快速定位问题根源。SigNoz支持OpenTelemetry协议,可以无缝集成各种编程语言和框架&…

python编程原子化多智能体综合编程应用(下)

上述代码实现了基于Mesa框架的诊断智能体类,包含以下核心功能: 模块化设计:通过类属性分离数据与行为,支持不同专科智能体的扩展 状态管理:实现idle/processing/error等状态转换,支持任务调度 诊断推理:集成机器学习模型,支持症状提取与多分类诊断 错误处理:包含模型加…

QT M/V架构开发实战:QSqlQueryModel/ QSqlTableModel/ QSqlRelationalTableModel介绍

目录[TOC](目录)前言一、初步介绍二、QSqlQueryModel1.基础定位2.特点3.核心接口4.典型用法5.优缺点三、QSqlTableModel1.基础定位2.特点3.核心接口4.典型用法5.优缺点四、QSqlRelationalTableModel1.基础定位2.特点3.核心接口4.典型用法 (示例:employees表有 dept_…

Terraform 从入门到实战:历史、原理、功能与阿里云/Azure 上手指南

前言:在云时代,企业的IT基础设施早已从“几台服务器”演变为“横跨多云的复杂网络、计算、存储集群”。但随之而来的,是管理复杂度的爆炸式增长:开发环境和生产环境不一致、手动配置容易出错、多云平台操作方式各异、资源变更难以…

【计算机网络 | 第10篇】信道复用技术

文章目录信道复用技术:高效利用通信资源的智慧方案一、频分复用(FDM):按频率划分的并行通道二、时分复用(TDM):按时间分割的轮流占用三、统计时分复用(STDM):…

安卓13_ROM修改定制化-----禁用 Android 导航按键的几种操作

Android 设备的导航按键通常包括后退键(Back)、主页键(Home)和最近键(Recents),这些按键位于屏幕底部或设备实体区域。禁用导航按键可以帮助在特定应用场景(如信息亭模式或儿童锁模式)中限制用户操作。安卓设备上禁用底部虚拟导航键(返回、主页、多任务键)有多种方法…

通过S参数测量评估电感阻抗:第2部分

S21双端口分流和双端口串联方法 T这是两篇文章中的第二篇,专门讨论使用网络分析仪测量 S 参数进行电感阻抗评估主题。上一篇文章 [1] 描述了阻抗测量和计算S11使用单端口分流器、双端口分流器和双端口串联方法的参数。本文专门介绍阻抗测量和计算S21使用双端口分流…

[deepseek] C语言头文件与汇编实现讨论

我想询问一种代码实现方式,使用C语言,例如main.c包含了自己编写的库文件abc.h,我想问的是:一、abc.h中是否可以有实现函数的代码;二、abc.h中的函数是否可以在另一个后缀为asm的汇编文件中实现?非常好&…

`.cursorrules` 与 `.cursorcontext`:Cursor AI 编程助手时代下的“双轨配置”指南

.cursorrules 与 .cursorcontext:AI 编程助手时代下的“双轨配置”指南关键词:Cursor、AI 编程、上下文管理、开发规范、技术治理 适合读者:前端 / 全栈工程师、技术负责人、AI 辅助编程实践者1. 为什么又多了两个“点”文件? 随着…

XR 和 AI 在 Siggraph 2025 上主导图形的未来,获取gltf/glb格式

Meta 的 Boba 和 Tiramisu XR 耳机(来源:Meta) Siggraph 2025 今年重返不列颠哥伦比亚省温哥华,庆祝《玩具总动员》诞生 30 周年和视频游戏实时渲染 20 周年。虽然 Siggraph 需要时间来欣赏过去,但它更多的是展望未来…

在 Ubuntu 22.04 系统(CUDA 12.9)中,通过本地DEB 包安装 cuDNN 9.13.0 的方法步骤

以下是在 Ubuntu 22.04 系统(CUDA 12.9)中,通过本地单个 DEB 包安装 cuDNN 9.13.0 的完整步骤,核心包含 GPG 密钥配置与包安装验证,确保每一步可执行。 一、安装前核心检查(必做) 确保系统已满足基础条件,避免安装失败: 验证 CUDA 版本:打开终端执行命令,确认当前…

Element 中 upload 编辑回显文件上传信息技巧

文章目录需求分析需求 upload 编辑状态下回显已上传的文件信息 分析 添加fileList <el-uploadstyle"width: 100%"ref"uploadRef"class"upload-demo"action"/prod-api/jc/files/upload"multiple:limit"1":on-success&q…