支付资损防控
2021-07-07 17:46:37 15 举报AI智能生成
支付资损防控是指通过一系列措施,对支付过程中可能出现的资金损失进行预防和控制。这些措施包括加强支付系统的安全性、提高用户对支付风险的认识、加强对支付机构和商户的监管等。通过实施有效的支付资损防控措施,可以有效地减少支付过程中的资金损失,保障用户的权益,维护金融市场的稳定。
电子商务
软件开发
模版推荐
作者其他创作
大纲/内容
业务产品分析资损防控规范<br>
2.1业务产品分析<br>
支付公司的大部分业务都涉及到资金的流转,对于每一笔的资金<br>业务,往往既涉及信息流的变化,也会涉及资金流的变化。<br>在我们做业务的需求分析设计的时候,如果和业务方未能理解一<br>致或者分析不全,将会从业务源头上产生资损风险。<br>
业务产品功能<br>
常见业务产品分析资损风险<br>
资金流、信息流、业务流等和理解和业务产品方不一致。<br>
只分析了信息流的处理,遗漏了资金的处理,或遗漏了某<br>些阶段的资金处理。<br>
比如基金类商户的出款类交易迁移的时候,没有考虑商户不<br>需要真正的出款。<br>
资金需求中,对资金结算模式等理解不一致。<br>比如退款成功的理解不一致;是退到商户户还是退到个人户<br>的理解不一致。<br>
业务产品设计的时候,部分支付产品充退接口设计不完整,<br>尤其资金流设计不完整。<br>
执行充退逻辑时异常考虑不完备会有资损。<br>
系统业务设计未能遵守先借记后贷记的原则。<br>
业务缺少资金平衡检查。
正反交易资金流不一致容易资损。
内部户的使用不当。
比如挂销机制不合理。<br>
内部作业人员操作界面未设置合理的限制条件。<br>
如营销平台的卡券发放等。<br>
常见业务产品分析资损防控点
需求分析中对信息流、资金流的分析要完整全面
业务需求中要包括完整的信息流和资金流(业务系统还需要包<br>括相关的物流等业务流),不但要包括正向的完整描述,也要包括反<br>向的描述;不但包括正常情况的描述,也要包括异常情况下的处理描<br>述。<br>
业务迁移过程中,要梳理出新旧业务全面的信息、资金流流向<br>
验证过程中,要核对业务信息、支付信息、渠道信息、银行<br>账户流水信息等。<br>
新支付产品需求分析的时候,要对支付产品的信息流和资<br>金流做完整分析。<br>
有其是反向资金流。<br>尤其是内部业务系统包装的渠道,尤其注意冲退逻辑的实现<br>是否完备。<br>
资金结算模式的理解。<br>
需求分析过程中,要与业务方梳理出清晰完整的资金结算模<br>式。<br>形成双方认可的资金结算的流向图,流转到具体的资金账户。<br>
业务设计的时候遵从先借记后贷记原则。<br>
要保证正反交易的资金流向一致。
对于正方交易资金流向不一致的场景(如冲退转代发、部分<br>支付产品冲退退余额),设计与系分的时候要着重评审,并重<br>点测试验证。<br>
收单、渠道、支付等系统都需要设计相关的资金平衡检查。<br>
如渠道冲退的金额不能大于原交易流水的金额、收单商户退<br>款的金额不能大于可退金额等。<br>
内部账户的使用。<br>
必须由业务方和运营财务人员沟通后申请,技术人员不可自<br>行配制;要有挂销机制;要有相关的业务和技术核对机制。<br>
内部作业系统设置合理的业务条件限制。
不允许有无限制条件的输入输出。<br>如 OMS 查询可以限制为一个月;营销平台的卡券发放一个人<br>可以设置上限 1000个等。<br>
业务参数调整<br>
常见业务参数调整资损风险<br>
商户参数调整。<br>
业务自行调整合同业务参数影响其他收单商户。<br>
渠道参数调整。<br>
结算规则调整。<br>
<p style="line-height: 14.03pt; margin: 0pt 0in; direction: ltr; unicode-bidi: embed; word-break: normal;"><span style="font-size:<br>14.0pt;font-family:Arial;mso-ascii-font-family:PJQBNN+ËÎÌå;mso-fareast-font-family:<br>Arial;mso-bidi-font-family:PJQBNN+ËÎÌå;mso-fareast-theme-font:minor-fareast;<br>color:black;mso-font-kerning:12.0pt;language:zh-CN;mso-style-textfill-type:<br>solid;mso-style-textfill-fill-color:black;mso-style-textfill-fill-alpha:100.0%">通过数据修订等方式进行业务参数调整</span><span style="font-size:14.0pt;font-family:Arial;mso-ascii-font-family:PJQBNN+ËÎÌå;<br>mso-fareast-font-family:Arial;mso-bidi-font-family:PJQBNN+ËÎÌå;mso-fareast-theme-font:<br>minor-fareast;color:black;mso-font-kerning:12.0pt;language:zh-CN;mso-style-textfill-type:<br>solid;mso-style-textfill-fill-color:black;mso-style-textfill-fill-alpha:100.0%">。</span></p>
常见业务参数调整资损防控<br>
可以验证的业务参数,需要在测试环境等验证过进行调整。<br>
风险较大的业务参数调整的时候,可以通过限流等措施在<br>产线上逐步开放。<br>
系统在设计的时候,要避免可能引起业务作业人员误解的<br>配置,避免商户间的串用、滥用、误用。<br>
业务类参数的调整要平台化,不要通过数据修改等方式进<br>行参数调整。<br>
资损与资损防控
什么是资损防控
广义的资损定义:任何由于系统故障、缺陷、人为操作、安全<br>漏洞导致支付公司或支付公司的客户蒙受直接或间接损失的<br>事件,都属于资损事件。<br>
狭义的资损定义:由于支付公司的原因,导致支付公司产生直<br>接资金损失,或产生赔付。<br>
资损产生的原因<br>
由于支付行业的特殊性与复杂性(主要处理资金相关业务),支付<br>公司处于资损的风口浪尖,最容易发生资损,可以说资损风险无处不<br>在。常规来说,资损原因主要可以分为以下三类:<br>1)系统方面的原因。主要由系统分析、设计与实现处理错误等原<br>因导致的资损。<br>2)产品设计运营以及使用方面的原因导致的资损<br>3)信息安全方面的原因导致的资损
如何做好资损防控
简单来说,资损防控就是从资损发生的<b>前、中、后<br></b>三个阶段做好相关的<b>防、控、管</b>等各项工作<br>
防主要指的是防范资损发生。在资损发生之前,从产品与系统<br>设计、实现、运营等来源,对资损进行分类预防进而避免资损<br>的发生。<br>
从流程上讲,对于涉及到资金相关的项目需求:<b>启动阶段</b>要求<br>需求方提供资金清结算与核对方案;<b>需求阶段</b>要求明确资金正<br>常流程与异常流程;<b>系分阶段</b>要产出资损点分析、制订完善的<br>防控措施;<b>测试阶段</b>除进行信息流的分析验证,还要进行资金<br>流的分析与验证;<b>发布评审时</b>要再次进行资损风险评估,各防<br>控措施是否落实到位;<b>验证时</b>,产品与业务方需要对资金流进<br>行完整的验证才算验证结束。<br>
控主要指的是控制资损。一旦资损,我们需要知道哪里资损了?<br>资损了多少?及时进行止损,将资损规模降到最低
监控与核对是“控”常用的两大技术手段:<br><ul><li>系统与产品需要制定相关的系统、业务监控指标,及时进行</li></ul>资损风险的提前预警;资金上通过证证、证账、账账、账实等核<br>对手段进行资金流操作结果的核对。<br><ul><li>资损发生后,我们需要及时止损。这一阶段时间与速度最为重</li></ul>要,根据经验,在资损发生的几个小时内,止损的成功率较高,<br>损失几乎可以全部追回。如果是已经识别的资损故障,可以根<br>据已经制定的应急预案进行按部就班执行即可;未识别的资损<br>故障,需要及时汇报给应急小组,以最高效的沟通手段制定应<br>急方案,快速处理。<br>
管指的是资损的管控
在资损处理结束之后,对资损故障进行详细的复盘与分析。<br>排除类似或相关联的缺陷与隐患,制定并实施改进计划;<br>对相关经验进行分析与沉淀,避免重复犯错。<br>
系统设计资损防控规范
从系统架构层面整体来看,支付公司的系统可以抽象为如下结构:<br>1)对外部商户提供收单服务类的系统<br>2)连通支付公司与各金融渠道的网关类系统<br>3)支付公司的内部业务处理系统<br>4)消息、调度等中间件系统<br>5)数据库、缓存等存储<br>
从系统架构与业务架构上来讲,各个结构连接的地方最容易出现资损。<br>因此我们将从接口服务层面与系统设计层面对资损进行分析并总结<br>相关规范
1.1服务类接口设计
按照上述系统抽象架构所示,支付公司接口服务可以分为三类服务:<br>1)渠道网关类服务<br>2)收单类服务<br>3)内部系统接口服务
渠道网关类服务<br>
常见资损风险
常见资损风险:渠道网关是连通支付公司与各金融渠道的网关类应用,<br>处于资金处理的最后一个环节。渠道网关类服务是最容易发生资损的服务。<br>
返回码的处理
返回码的处理是渠道端最容易出现资损的部分。<br>我方系统会对渠道的返回码做映射,当映射错误,或渠道返回系<br>统异常等未知错误我方处理不合适,或我方系统流水勾兑异常等情况<br>发生时,容易造成资损。如我们在和渠道方无特殊约定的情况下,将<br>无此交易处理成失败就会导致资损。<br>
调用渠道使用的流水号产生重复。<br>
如果流水号重复时候,渠道报一个流水重复,我们置为处理中;<br>当我方调单查询的时候,查到了被重复的上一笔交易,状态勾兑为成功,就会发生资损。<br>
流水号设计不合理发生重复(如商户号+8位 sequence,一<br>年后开始重复;日期+6 位流水号,交易量超百万,一天开<br>始重复);<br>
流水号使用不合理发生重复(我方不同业务,渠道同一接<br>口,使用不同 sequence 出现重复; 同一业务的不同交易<br>之间使用不同 sequence,导致重复)<br>
升级迁移导致重复(生成规则升级,结果和历史数据重复;<br>业务迁移拆分时,新建了 sequence,和原有的重复)<br>
调用渠道方流水号的产生与使用机制不合理。
如我方在报文发送时生成流水号或对流水号做二次处理均可<br>能导致渠道方自身的幂等失效。<br>
渠道服务接口中金额单位的处理
内部金额单位和外部系统金额单位不一致,转换出问题。内部<br>单位为忽(*1000000),渠道接口单位为分、为份额,漏转换或转换<br>错误;币种参数传递错误,导致对方按错误币种处理;交互接口金额<br>格式特殊,转换出错(如银行部分接口前补 0)。<br>
渠道日切的处理<br>
由于银行有日切问题,金融渠道也有交易日日切等问题。当涉及跨日问题时,<br>处理不当(如查询用自然日期而非约定的渠道交易日期),容易导致资损。<br>此外,日切处理不当还会导致清结算差错处理的问题<br>
不同金融机构对接细节尤其关键的幂等控制细节不清楚
不同的渠道存在着不同的幂等性控制,想当然的去进行渠道对接<br>容易出现幂等性问题进而资损。<br>
和渠道对接,未严格遵从渠道端的信息安全规范。<br>
如果报文未签名、加密或安全级别不够,或使用了不合适的第三<br>方 jar包,可能导致信息被篡改,或导致其他争议
渠道网关接口连错环境<br>
由于渠道端测试联通测试不方便,经常需要修改代码进行测试、<br>或者我方测试环境连对方生产环境;如果代码提交生产或者测试队生<br>产进行了错误的交易,极易导致资损。<br>
调用渠道接口先发后至、后发先至和响应乱序
由于我方和渠道费连接的网络环境或网络连通方式比较复杂,极<br>易导致先发后至、后发先至和响应乱序等情况。<br>比如渠道方是反交易比正交易先到、查询请求比交易先到,处理<br>不到就会造成资损;极端情况下,还可能出现响应乱序情况,即我方<br>发出的 A请求,收到的 B请求的结果。<br>
渠道网关类服务使用规范<br>
返回码的处理是渠道处理的重中之重,必须慎重。<br>需要遵从如下规范:<br>
返回码必须要按接口维度进行成功、失败、未知分类。返<br>回码变更必须慎重,原则上必须禁止通过线上 SQL变更。<br>
返回码在业务上线时候或变更上线的时候一定要验证支付<br>交易、查询、交易、冲正类交易的成功、失败、处理中和<br>相关异常的情况。<br>
明确返回结果“成功”字段背后的含义,是操作成功,还<br>是业务成功,还是两者双成功。返回结果真正的“成功”<br>只有一个(可以对“成功”做硬编码处理)。<br>
渠道返回系统内部异常、超时、无此交易、流水重复、我<br>方系统流水勾兑异常等不能代表交易失败,不能明确确认<br>成功失败,需设置结果为未知。<br>
流出类交易慎设置为失败,流入类交易慎设置为成功。<br>
规范渠道流水号的生成和使用。
各渠道网关系统需要采用规范的流水号生成规则,避免灵<br>活的生成方案。<br>
新渠道、新业务或有迁移发生的时候,需要对 sequence使<br>用方式专项评估,设置合理的初始值
流水号的产生必须在受理或预处理阶段提前生成,之后不<br>允许再重新生成或更新任何流水号(包括对格式进行转换);<br>绝对禁止在报文发送环节进行渠道流水号的生成。<br>
在发送报文给渠道的时候时,需要考虑第三方 jar 包会有<br>重发的默认设置(如 httpclinet 默认是重发 3 次),我方<br>需要慎重选择重发策略;对方无明确的幂等说明的时候,<br>严格禁止设置重发。<br>
时间规范
统一使用数据库时间,禁止自定义日期处理函数。<br>业务处理发起尽可能避开对方可能的日切,跨日,跨月,跨年<br>时间段;如果渠道方跨日业务需要特殊处理,涉及时需要进行<br>特殊的分析设计。<br>部分金融交易类渠道在接入的时候,尽可能使对方接受以我方<br>时间为准。<br>
金额单位规范
目前出口网关金额以分为单位为主,分、元、份额都有,要将金<br>额转换放在出口系统,内部需要保持一致的金额单位;金额的计算过<br>程,对原始金额数据不能更新,计算得出的结果作为新的金额字段存储。<br>新业务新渠道上线一定要验证支付交易内部记账结果和实际变动<br>相符(可以去渠道查询):如银行渠道入金,要保证内部记账金额和<br>银行卡扣款金额一致。<br>
设计上要考虑因先发后至或后发先至等情况
查询交易和反向交易处理的时候,要控制和原交易间隔一<br>定的安全时间;要先关联原交易,关联不到原交易需要保<br>证返回处理结果后,原交易不会被执行(可以插入幂等表<br>以避免原交易被执行)。<br>
要控制渠道等外部调用及数据库访问等的超时时间,避免<br>资源的长时间堵塞,造成后到的交易先处理。<br>
对于消息、调度等,要从意识上认为其没有任何次序,从<br>设计上考虑保证防重以及和先后次序无关
必须严格遵守渠道方的加密签名等安全规范
测试和生产环境必须严格隔离。
源头上必须禁止为了测试需要修改代码(必须修改时候,要将测<br>试相关的信息外化配置,和产线隔离),war 包中不能有临时性的测<br>试解决方案。<br>测试环境如果要连接到渠道方的生产环境的话,需要特殊申请并<br>且一定要指明防火墙的失效日期。<br>
收单类服务<br>
常见资损风险<br>
服务描述不清楚、不规范。<br>
对于商户接口类服务,服务接口、数据交互描述不清楚或不规<br>范,导致商户错用接口,进而导致商户资损。<br>
金额参数单位乱用<br>
在收单服务接口的所有与出入参数里,金额单位的约定是极易<br>出错的地方,分、元、忽的使用一定要明确,否则会导致金额扩大或<br>缩小 100、1000000倍。<br>
幂等参数不明确、不合理<br>
收单服务的幂等性控制参数一定要明确指出,否则容易造成商<br>户。如接口调用时,对同一笔业务进行多次服务调用,导致业务重复<br>处理,造成资损;支付公司结果通知(同步或异步)时,商户没有幂<br>等处理,对同一笔也处理多次导致资损。<br>
接口参数长度或类型不明确<br>
收单服务对外出入参数中,各参数的类型长度约定不明确,将引<br>发数据库超长、数据转换与截断等问题,严重时将导致资损。<br>
请求参数合法性校验不严密或未校验<br>
对外提供的接口,没有对参数进行合法性校验或缺乏明确的校<br>验规则。如收到外部请求,传入未签约的支付方式,没有签订手续费<br>费率,可能导致漏收手续费。<br>
接口的错误码或返回码定义不明确<br>
错误码、返回码对于异常、超时、幂等等结果不明确,会导致<br>商户重复针对同一笔业务进行多次调用或将可以判定为成功的业务<br>而判断为失败。<br>另外,外部响应结果如果包含多层状态(通讯状态、业务状态<br>等),需要明确业务状态是由哪个状态或者哪几个状态组合决定,判<br>断错误会导致严重资损。<br>
收单服务接口设计规范<br>
接口描述规范
接口服务能力描述简洁明了,对于服务能力、功能、数据交互<br>有一个清晰的描述。关键的资金处理业务需要由流程图表达。<br>
金额单位规范
金额参数必须以“元”为单位,同时精度要求小数点后两位。禁<br>止以分或忽为单位(线下收单行业规范可以以分为单位)。<br>
幂等控制
需要约定好幂等控制的参数是哪个参数(如外部交易单号)或<br>哪些参数(如交易日期+交易流水号)组成,对于幂等控制的方式双<br>方需要达成一致。<br>
收单服务的参数类型与长度必须明确且设计合理。
任何一个参数都要明确其类型与数据长度范围,该范围需要确<br>保在整个体系内有效。<br>另外,对于关键的控制类参数(如幂等控制的参数)需要设计<br>为字符串,减少不必要的转换。<br>
对于商户的接口权限与接口参数做好权限控制与合法性校验。
对于错误码或返回码需要清晰定义。<br>
返回的结果中需要明确成功失败状态的含义:业务成功,操作成<br>功,双成功。明确错误码的含义,准确识别出成功、失败、处理中、<br>超时处理、重复处理、不做任何等重要处理结果;对于各种异常处理<br>(如超时重发等),不能缺少。<br>
对外服务接口规范需要遵从最新的“文档规范说明.docx”。<br>
内部接口类服务<br>
平安付内部系统间默认使用 DUBBO 作为分布式服务框架,进行系<br>统间的 RPC调用。<br>
常见资损风险<br>
服务接口中,各参数类型、长度等相关约定不清楚,会导致数据<br>库超长、被截断等隐藏问题,严重时候会导致资损。<br>
服务接口提供方在应用层面,对服务的被使用情况不清楚,导致<br>服务调用方使用错误的接口或组装错误的逻辑。<br>
服务接口中,幂等性参数未说明或未正确设置,导致同一笔业务<br>重复调用被服务方重复执行成功,可能导致资损。<br>
服务接口中,金额参数单位未按照规范进行使用,造成金额被扩<br>大或缩小 100倍或 1000000倍,造成资损。<br>
返回码对于重复、超时、异常等结果说明不清晰,请求方错误判<br>定业务状态导致错误的业务逻辑处理,或者更换幂等参数后错误<br>的对同一笔业务进行重复调用。<br>
关键性且有时效性的业务处理,只依赖于关联系统的消息通知且<br>无其他的补救措施。由于消息的异步性,到达时间是不可靠的,<br>会影响业务的处理
服务接口设计的过程中,没有考虑向前、向后的兼容,以及版本<br>发布过程中的兼容性处理问题。如服务使用方使用老服务落单,<br>发布后使用了新服务支付,新老服务不兼容,有可能使应有的校<br>验失效,导致资损。<br>
服务接口需要序列化的对象子类和父类具有共同属性的时候<br>(Dubbo使用 Hessian做序列化与反序列化),会导致属性值丢<br>失。<br>
对于涉及文件处理与批量处理的服务接口,缺乏相关明细数据的<br>说明及相关的校验机制。由于内部系统均使用 NAS等进行文件共<br>享,接口中传递的只是文件路径;没有进行过相关的加密,没有<br>文件正确性校验功能,可能会导致数据丢失。<br>
内部服务接口设计规范<br>
接口定义需要完整、清晰、规范。<br>
接口里面需要详细的定义好出入参的类型、长度、选必<br>填以及相关取值等约定;服务使用方在使用前需要对输入<br>做好相关校验。<br>
服务提供方接口里需要详细描述接口的功能以及对业<br>务和其他接口的影响,涉及资金流向的关键的接口还需要<br>提供资金流向说明。<br>
服务使用方使用接口前需要判断接口是否符合需求(功<br>能、信息流、资金流等)、以及自身如何利用服务方接口完<br>成自身的业务处理。<br>
由于序列化问题,façade 接口中的对象规避子类和父<br>类具有共同属性问题。<br>
接口中的金额参数使用忽(*1000000)为单位。<br>
内部系统的金额参数不要被更新,如果有更新等需求,使用新的<br>字段。计算的时候使用 Money类。<br>对于金融产品,有时候有收益的计算(对收益的计算处理方式不<br>尽相同,如“四舍五入”):那么该产品收益计算的需求首先要明<br>确,并根据需求进行代码开发,并且重点关注临界值的计算。<br>
幂等参数
接口描述必须对幂等参数做着重说明,服务提供方需要使用服务使用<br>方的幂等参数来做幂等控制。<br>
对关键性且有时效性的业务来做处理时候,不要只使用消息来<br>进行通讯。<br>
原消息的生产者可以使用 RPC 对消费者进行调用以获取明确的响<br>应,消费者异步进行任务处理;必须用消息解耦的时候,需要增加监<br>控以及手工触发相关运维功能以保证业务的处理。<br>
内部接口涉及交易处理的,服务端不管有无幂等控制,不设置<br>默认重发。<br>
接口的兼容性处理。<br>
1)接口在设计过程中,要避免无兼容性设计。<br>2) 在非破坏性变更的时候,既需要保证向前向后的兼容性,<br>也需要保证发布过程中的兼容性(包括接口字段和各种<br>业务处理)。<br>
返回码(错误码)的处理。<br>
要明确每种返回的结果码的含义和场景,包括成功、失<br>败、系统超时、系统异常、重复请求等重要场景;<br>
需要明确返回结果成功的的含义,是操作成功,还是业<br>务成功,还是双成功;<br>
返回的错误编码要包含三位的内部系统编码,且要约定<br>好每种错误场景的业务处理。<br>
接口中涉及文件及批量场景的处理。
内部系统间接口中如果有文件内容的传递,接口说明中<br>明确相关文件内容,并需要对关键信息进行校验,防止文<br>件内容不完整或者错误(可以在接口中约定传递文件的 MD5<br>值)。<br>
文件和批量的明细内容需要对没条数据做清楚的说明:<br>包括是否每笔有自己的唯一流水、是否用其做幂等控制;<br>对文件和批量的整体需要说明是否存在接受部分成功、部<br>分失败的情况。<br>
1.2系统功能类设计<br>
在分布式的环境中,一个系统的设计可以抽象为图:<br>除掉可用性问题外,导致资损的问题可以归结为如下三类:<br>1)幂等控制问题<br>2)兼容性问题<br>3)并发互斥问题<br>
幂等性控制设计<br>
幂等性问题是分布式系统设计中最常见的问题,是分布式系统服<br>务的一种承诺, 承诺只要调用接口成功, 外部无论何种原因的多次<br>调用对系统的影响是一致的。<br>在支付公司,我们常说的幂等性控制往往不是重复访问的只读结<br>果一样,更多的是指业务活动、资源变更等重复请求,同一个服务操<br>作实例最多只允许执行一次。幂等性控制包括两个层级:<br>1)最基础的幂等性控制需要完成唯一性控制。主要根据幂等<br>性(唯一性)控制字段即可完成。<br>即:根据约定的幂等控制字段(唯一性字段),对业务重复<br>提交、请求方重复请求、定时任务、网络原因等导致的重<br>发的数据来判断是否重复提交。如收单系统根据商户号+日<br>期+外部订单号,拒绝同一个商户在同一天内重复发送同一<br>个外部订单号的交易请求。<br>2)在唯一性控制的基础上,如果要做更复杂的幂等性控制;<br>这时候,需要和其他业务要素一起来进行判断。<br>如果全部关键业务数据请求和现有的数据一致,可以使得<br>请求一次和重复请求多次都不会对业务处理产生影响。如<br>在收单系统中,如果外部商户请求的“商户号+日期+外部<br>订单号”与现有交易中的数据一致,并且对其他关键字段<br>金额、收付款方等做比对;如果比对一致可以返回前面处<br>理的结果,不一致可以提示商户“请求参数与原先参数不<br>一致,请检查”。<br>幂等性未做控制或控制不合理是系统设计中最容易产生资损风险<br>的环节。在系统设计中,各资金处理业务至少需要保证幂等性的第一<br>层级“唯一性控制”。<br>
常见幂等控制问题资损风险<br>
子主题上下游调用中,幂等字段使用自身产生的流水号做幂等控制容<br>易出现幂等被击穿。<br>
幂等字段的选用不合理。<br>
选错幂等字段,在业务上导致应该做幂等控制的未做或不该控制<br>的被控制。<br>比如本该选用交易流水号做幂等控制的选用了业务流水号。<br>
幂等字段设计的全局性考虑不全。<br>
空间全局性考虑不全。<br>
如外部商户请求的幂等控制,是使用“商户号+请求流水”还<br>是使用“商户号+日期+请求流水”还是使用“商户号+日期+交<br>易类型+请求流水”来设计幂等字段做幂等约束。<br>
时间全局性考虑不全。
幂等字段的设计是幂等一天,还是三个月,还是永远。在我们<br>使用数据库来做幂等控制,由于分区、归档等原因,会导致幂<br>等失效。比如分区后建立的本地索引(非全局)的唯一索引,<br>只会在当前区有效;数据归档之后,幂等控制无法判断归档走<br>后的数据。<br>
幂等底层控制设计不合理导致幂等失效。
如使用 select 而非 insert进行唯一性控制,高并发的时候就会<br>使幂等失效。<br>
消息、定时任务等非同步场景的幂等性未考虑。<br>
上下游系统交互的幂等约定不清楚。<br>
如很多银行机构接口无幂等性保证,如果我方不清楚将框架设置<br>为异常重发就会造成资损。如我们只约定了唯一控制的幂等约定,对<br>哪些字段进行幂等第二层级的判断没有约定。<br>
幂等性控制设计规范<br>
幂等性字段规范。<br>
使用上游流水做幂等控制:上下游系统调用中,下游系统在<br>可以使用上游系统的请求流水进行幂等控制时,不要自己生<br>成流水进行幂等控制。<br>
关键的资金业务处理,需要选用全局的唯一处理流水号做幂<br>等控制。如账务系统、各渠道网关系统均需要选用 UTPP 的<br>支付流水号来贯穿始终进行幂等控制,以保证幂等在整个支<br>付过程中、在整个资金体系中全局有效一致。<br>
选用幂等字段时候,除了理解其字段控制含义,还需要理解<br>其业务含义;以达到选用合理幂等字段的目的。如上游有业<br>务流水号、交易流水号,下游系统到底选用哪一个做控制,<br>需要考虑其在特定场景下的业务含义。<br>
幂等字段的选用要考虑其空间和时间的全局性、扩展性。要<br>考虑选用哪些字段进行幂等控制、幂等字段流水号的长度够<br>不够用。幂等控制时间是多少,由于数据库的归档、分区、<br>分库等操作,会不会导致幂等失效;部分的数据库结构操作、<br>数据操作甚至需要我们变更幂等的控制机制。<br>
除了约定哪些字段进行幂等唯一性控制;对于实现了幂等控<br>制第二层级的服务,还需要约定哪些字段用于幂等控制。如<br>收单系统和商户约定了“商户号+请求流水”做幂等唯一性<br>控制;还约定了“收付款方账号+金额”做第二层级的幂等<br>控制字段,重复请求下“收付款方账号+金额”一致会返回<br>交易状态,不一致会返回“请求参数与原先参数不一致”<br>
消息处理、定时任务等非同步场景需要和同步场景一样考虑幂等性<br>
对于消息的处理,不能依赖消息次序来进行业务处理;要<br>考虑乱序、消息同步处理等问题。可以通过消息创建时间、<br>现在的业务状态顺序判断消息是否过期;消费掉消息后是<br>否需要再次进行业务处理。<br>
定时任务执行的时间、次数都是不可控的。更需要通过业<br>务状态、业务规则对定时任务进行幂等控制。<br>
需要明确:消息处理、定时任务只是业务逻辑处理的一个<br>触发,并且其触发是不保证次序和次数的,实际生产中消<br>息中心消息重发、调度中心多次调度都是存在的;一定要<br>保证在系统的业务逻辑处理中考虑业务的幂等控制。<br>
幂等服务的约定。<br>
幂等性的处理机制约定:仅幂等唯一性控制,还是需要<br>进一步根据哪些字段做幂等第二层处理。<br>
幂等性交互的约定:上下游交互,需要明确幂等性处理<br>的相关规约(如幂等异常处理机制、返回码、错误码等)。<br>
服务重发的约定:
交易类的服务,禁止网络自动重发。无论内部的 dubbo、<br>外部的 httpclient、xfire等禁止使用框架组件自动重<br>发。<br>
上游系统调用下游系统,请求未发送前,上游异常,可<br>以置失败、进行重发。<br>
上游系统调用下游系统,请求已发送再出现系统异常,<br>必须向服务方确认状态后,明确可以重发才能进行重发。
不能明确可以重发的,就不要重发;遵循重复就严原则。<br>
服务的幂等实现。<br>
常见的幂等实现方法,有如下几种:<br>
1)幂等控制表实现。<br>一般情况下,我们会在数据库上设计一张幂等控制表,并<br>根据幂等控制字段建立唯一索引。<br>在服务使用方请求过来的时候,先往幂等控制表 insert<br>一条数据,通过数据库本身唯一约束进行控制;当有重复请求,<br>将不能 insert 重复的数据,有相关异常抛出,提示幂等控制<br>表已经有同样数据存在。达到幂等控制目的。<br>由于是基于数据库的幂等控制表的方案,当有数据库变更,尤<br>其是幂等控制表有归档等数据迁移、分区、分表、分库等情况<br>时候,将会导致数据库唯一约束失效的场景,需要着重考虑。<br>
2)其他实现<br>也可以借助分布式缓存等进行幂等控制实现。<br>此种方式不是建议使用控制方式,如果需要使用,请和各种域<br>架构师进行评审。<br>
兼容性设计<br>
<br>由于业务在发展,系统一定处在变化之中,变化就一定会有兼容<br>性问题的存在。大到每次业务上线、系统发布,小到一次业务迁移、<br>配置项变更;每一次变化都会带来一定的系统风险。<br>兼容性考虑不充分除了影响可用率外,还会导致资损的发生。因<br>此兼容性设计是系统设计的重中之重,产线上已知的很多资损问题都<br>和兼容性考虑不周有关。<br>常见的兼容性设计需要考虑如下的兼容性场景:如图<br>
常见兼容性问题资损风险<br>
老系统和新数据结构等不兼容。
由于我们现在生产发布是 DB脚本发布现行,每次发布过程中均<br>存在着老 war包新 db(表结构、索引、数据等)的情况。考虑不周<br>会导致服务与数据出现兼容性问题。<br>
新系统不能处理老系统产生的数据。<br>
如老系统落单了一笔支付交易,新系统上线后不能对其进行支付。<br>
老系统不能处理新系统产生的数据。
如系统上线后发生了回退,老系统不能处理新系统产生的数据将<br>会导致问题。<br>
收单服务接口、渠道网关接口、内部服务接口兼容性考虑<br>
不全。详见接口规范章节。<br>
发布过程中,由于存在一定时间的新旧 war包并存,消息<br>和定时任务的兼容性考虑不全。<br>
虽然我们在产线进行了消息分组,一定程度上规避了老系<br>统生成的消息,新系统消费的消息兼容性问题;但当消息的源<br>头生产者没有进行上下线的时候,会出现新系统消费老格式消<br>息的问题。<br>
定时任务(尤其是非调度中心触发的调度任务)的调度频<br>率、执行系统、内部执行逻辑不兼容,容易出现资损。如某个<br>系统自身的定时任务选择系统执行者的逻辑发生变化,本来该<br>只有一个执行者的任务,导致两个执行者执行。<br>
发布过程中,新老系统的幂等控制、并发控制不当,导致资损。<br>
有可能单独来看,新老系统都能控制好幂等和并发,但是在发布<br>过程中,有可能双方的控制机制不一致等情况,使得业务重复处理,<br>引起资损。<br>如网关的幂等在发布过程中曾经被击穿导致的资损。也可能老系<br>统的业务没有并发场景,新系统上线后需要考虑并发,新老系统处理<br>同一笔请求的时候(尤其是任务调度处理的时候),将做不了并发控<br>制。<br>
新老业务流程、业务处理规则不兼容。<br>
比如一笔理财的抢购支付过程中,分为落单和支付两个环节:库<br>存控制的逻辑老业务流程是支付成功后再扣库存,新业务流程是落单<br>扣库存再支付;如果落单使用老系统、支付使用新系统;在兼容性考<br>虑不周全的情况下会导致不扣减库存,出现超卖。<br>再比如,某个业务系统业务发生变化,状态机状态迁移发生了变<br>化,原来的一个业务状态再支付成功的时候迁移为处理中,新状态机<br>设计为迁移为成功;那么对于这笔中间态的业务数据的处理就可能导<br>致资损。<br>
业务迁移对新旧业务的连续性等兼容性考虑不全导致资损。<br>主要包括参数字段(包括备注)、业务规则处理、反交易处理、开<br>关处理等方面。<br>
业务迁移不仅仅是新的业务规则替换老的业务规则,在整个替换<br>过程中,更需要保证业务的连续性;系统如果不能兼容新旧业务的处<br>理会有极大的资损风险。<br>如 UTPP迁移过程中,新增或变更了很多业务参数,业务平台切换<br>新接口的时候,如果对新旧参数做兼容处理,会导致资损;新账务迁<br>移过程中,商户出金中的基金划拨场景的商户出金交易新旧业务规则<br>不同导致实际划拨了资金;新账务迁移过程中,如果一笔交易正交易<br>走老体系,冲销走新体系将会出问题(反之亦然);如迁移的开关复<br>用了以前的开关,开关混用,导致问题。<br>
数据或数据存储迁移导致的兼容性问题。
我们后续会碰到很对的数据迁移:如缓存从 redis迁移到 hippo、<br>数据库从 oracle、mysql迁移到 pg等等。如果迁移过程中数据格式<br>不同、甚至丢失精度会引发资损。<br>业务数据迁移,如会员、ccdc等数据迁移,如果新老系统对业务<br>数据的处理不兼容会导致资损。<br>
系统兼容性设计<br>
随着业务的不断变更和发展、系统和架构也在不断的调整和优化;<br>这其中既有新业务、新系统的上线,也有老业务、老系统、老数据的<br>迁移。<br>在这些新老的变化过程中,我们既不能导致业务的间断影响可用<br>率,更不能产生资损造成公司损失。为了防范这些新旧间处理的风险,<br>我们需要从数据、系统、发布、业务等各个环节做好兼容性设计。<br>
数据库、接口服务新增的向前兼容性原则。<br>
向前兼容设计更多的体现在数据库、接口的初始化设计阶段(新增数据库结构、<br>新增接口)。数据库结构与服务接口在设计过程中,在数据库、接口复杂度与<br>字段(个数、长度等)允许的范围内,尽可能的考虑到未来可以预见的情况,<br>支持未来的业务拓展。新增设计时,需要考虑到:<br>
数据库字段、接口字段的合理冗余。比如在设计交易接口<br>的时候,除了考虑交易金额字段,考虑未来有收费的场景,可以增加<br>手续费金额字段。此时,也要注意避免过度设计。<br>
数据库字段、接口字段长度的适度放大与类型的泛化。比<br>如在允许的范围内,可以将交易金额字段设计的范围长度大一些;某<br>些类型字段定义为字符串类型而非整数类型;时间参数精度设计到毫<br>秒等。<br>
数据库字段、接口字段或接口处理逻辑的约束。考虑到未<br>来有可能扩展或放松现有约束(如非空判断等),在接口中考虑默认、<br>异常等多种处理情况。<br>
数据库、接口服务变更的向后兼容性原则。<br>
向后兼容设计是最常见的应用场景,更多的提现在数据库、接口<br>的变更阶段(变更数据库结构、接口)。<br>服务提供方在做接口非破坏性的变更时,需要确保服务以向后兼<br>容的模式演变;在无需变更现有消费者的实现或配置的条件下被更多<br>的消费者重用。<br>由于我们发布过程是 DB先发布,所以新老系统是会同时使用一个<br>数据库结构的。因此必须保证数据库结构发生变化的时候,新旧系统<br>可以同时使用变更后的数据库结构。<br>一般数据库、接口变更,分为如下场景:<br>
删除字段
<ul><li>禁止直接删除正在使用的字段。</li></ul>
<ul><li>对于接口字段,如果确认该字段所有的请求方和服务方都</li></ul>不再使用。分多个版本,按照请求方先去掉该字段,确认所有请<br>求方完成后,服务方方可以进行字段的删除。<br>
<ul><li>对于数据库字段,老系统要先解除和数据库结构间的字段</li></ul>依赖(包括语法依赖和业务依赖)再进行删除。对于已经产生的<br>数据,要考虑保存机制。<br>
新增字段。<br>
<ul><li>服务提供方需要考虑各服务请求方的兼容性。服务提供方</li></ul>对该字段的约束允许为空,接口处理可以以提供默认值等方法保<br>证逻辑处理兼容性。<br>
<ul><li>数据库结构新增字段的时候,需要保证系统与数据库结构</li></ul>的语法与业务兼容。数据库字段可空。<br>
<ul><li>服务请求方在升级新增字段的时候,根据服务提供方该新</li></ul>增字段是否已经上线,制定兼容性方案。<br>
变更字段约束。
<ul><li>禁止直接进行字段约束从严的变更。接口进行从严变更(如</li></ul>长度收缩)的时候,需要各服务请求方先检视请求方约束<br>是否符合条件;不符合条件的,先完成请求方的变更,后<br>续进行服务方的升级。数据库进行从严变更的时候,要保<br>证老数据符合规范、系统先完成约束控制后进行变更。<br>
<ul><li>字段约束放松的变更。接口按照服务提供方先进行约束放</li></ul>松,后续版本各请求方进行变更;请求方不能早于服务方<br>进行变更。<br>
变更字段名称。<br>
<ul><li>禁止直接变更正在使用的字段名称。尤其是对于为多个服</li></ul>务请求方提供服务的接口的字段。<br>
<ul><li>当需要变更接口字段名称的时候,按照新增字段、删除字</li></ul>段的流程来处理变更。<br>
变更字段<strike>类</strike>型。<br>
<ul><li>禁止直接变更正在使用的字段类型。</li></ul>
<ul><li>如果需要变更类型的时候,建议新增字段来解决。</li></ul>
数据库索引变更的时候要考虑对产线执行计划的影响。<br>
数据库、接口服务删除原则。<br>
禁止直接删除正在被使用的数据库结构和接口服务。
如果需要进行删除。接口按照服务请求方先变更,在确认已无<br>服务请求方请求接口的前提下,服务提供方才可以进行接口删<br>除。数据库结构需要保证系统不会再放我的情况下,并做好数<br>据的保存。<br>
对于文件字段、缓存对象、消息对象的使用和设计,也需<br>要遵从数据库与接口服务相关的兼容性设计原则,保证向前<br>向后的兼容设计。<br>
生产系统发布过程中兼容性考量。<br>
发布过程中,要从系统、数据、业务组合考虑兼容性。<br><br>
需要考虑系统和数据之间的兼容性
设计过程中要保证新老系统产生的过程数据可以相互处理,即老<br>系统产生的中间数据,系统发布后,新系统可以出来;新系统产生的<br>数据,系统一旦出问题回滚后,老系统也可以处理。<br>如果不能处理的时候,不能选用直接的 war 包回滚方案。可以通<br>过中间兼容版本保证回滚的兼容性、也可以通过数据订正,保证新系<br>统产生的数据部会被老系统处理(数据订正方案是紧急情况下的处理<br>方案,不建议采用);另外,也可以通过引流等方式解决这部分数据<br>问题,以保证其兼容性。<br>
需要考虑系统和业务处理之间的兼容性<br>
生产系统发布过程中,在老系统受理的业务,新系统可以继续<br>处理(如老系统落单、新系统继续支付,老系统正交易逻辑、新系统<br>反交易逻辑),并且要保证业务规则、业务流程的兼容处理。<br>
新老系统间的兼容性,避免幂等击穿、并发失效等问题<br>
对于新系统的功能变更,幂等机制、并发控制机制等只可以增<br>加,不可以减少,避免老系统未能控制的幂等、并发,新系统也可以<br>控制住;如果是相关机制变更的话要慎重,控制只可以从严,不可以<br>从松,控制字段、控制场景、控制业务要考虑新旧系统兼容的情况。<br>
兼容性分析过程中,不可以孤立看新旧业务、新旧系统、新旧数据的处理,需要组合考量。<br>
<ul><li>组合考量业务的处理。</li></ul>
<span style="font-size: inherit;">业务处理的各个场景和过程中都会有被新 war 包、老 war<br></span>包处理的两种情况。要通过分析业务各个场景和过程中在<br>新旧 war 包中的处理步骤和流程,判断他们之间在业务上<br>是否有依赖关系,并分析在组合的业务场景下是否可以被<br>正确处理。<br>比如,订单交易平台修改了支付的冲销逻辑,业务兼容性<br>需要做如下分析:对于一笔支付业务,有支付和冲销两种<br>场景,支付场景、冲销场景被老系统如何处理、被新系统<br>如何处理?支付和冲销有没有依存关系?老系统的支付是<br>不是必须被老系统冲销?老系统支付新系统冲销可以不可<br>以 被 支 持 ? 新 系 统 支 付 老 系 统 冲 销 可 不 可 以 被 支<br>持?<br>如果冲销服务有查询状态、冲销发起两个过程,还需要考<br>虑着两个过程和各个业务场景组合的情况。<br>
<ul><li>组合考量新旧系统兼容性的处理。</li></ul>
系统的多种处理和控制机制是分多个阶段的,每个阶段也<br>都会有被新 war 包、老 war 包处理的两种情况。通过分析<br>者每个阶段在新 war 包和老 war 包中不同处理以及控制的<br>目的、逻辑、方式直至字段代码,判断其相互之间有无依<br>赖,每个阶段在什么场景下可以成为下个阶段的前置;并<br>分析其在组合场景下可否互相处理完成功能。<br>比如,某个系统对幂等控制进行优化。需要分析该系统处<br>理的任务有任务抓取、处理等阶段,老 war 包怎么进行任<br>务抓取?怎么进行任务处理,幂等控制的?新 war 包是怎<br>么处理的?抓取的任务是做完幂等控制才能被处理?还是<br>抓取后流入到处理进行幂等控制?新 war 包抓取的任务老<br>war包能不能处理<br>如果除了处理阶段流程变化,还有处理逻辑变化,还需要<br>进一步考虑并发、幂等控制的字段及实现代码在各个组合<br>场景下的兼容性<br>
<ul><li>组合考量新旧系统与新旧数据的兼容性处理。</li></ul>
发布过程中存在新系统写数据、老系统写数据、新系统读<br>老系统写的数据、新系统读新系统写的数据、老系统读老<br>系统写的数据、老系统读新系统写的数据供六种场景。要<br>分析以上各个场景的读写是否正确,读写之后的业务处理<br>方式是否正确,是否会对下一步业务处理产生不兼容的影<br>响?<br>比如一个业务改造走挂内部帐逻辑的改造。要考虑新系统<br>的时候写数据会记录挂账单编号 ,老系统写数据没有挂账<br>单编号,新系统读老系统没有挂账单编号的业务数据怎么<br>处理?<br>
生产系统发布过程中,要考虑消息、调度的兼容性。
按照前述的处理原则,发布的时候还需要避免消息处理、调度<br>的不兼容处理<br>其他处理方案(不建议):当有消息不兼容的时候,需要告知<br>部署同学按分组下线消息源头生产者;当有调度不兼容的时候,部署<br>的时候可以暂停调度<br>
业务迁移过程中,涉及到新旧业务流程、新旧业务规则、<br>新旧业务数据等的兼容处理,需要从分析、设计、上线、验<br>证等各个环节把控。<br>
对于业务迁移,必须详细分析所迁移业务的新旧规则、流<br>程、参数,并和上下游系统确认。<br>
新老业务与新老系统之间必须能够业务处理流程和规则上<br>保证平滑处理、无缝切换。<br>
尽可能保证新老业务、系统、数据直接的交叉处理。不能<br>保证的时候,要通过相关表示避免交叉。<br>
业务迁移的风险较大,需要通过开关、引流等进行产线验<br>证。<br>
迁移过程中,要有完整的报警、核对等跟踪机制,保证及<br>时发现问题;有完整的开关等流控机制,保证有问题的时<br>候可以及时断流。<br>
数据和数据存储的迁移
涉及到系统的底层环节(数据),要分析不同数据结构、不同数据<br>存储之间的差异,保证这些差异可以被系统正常接受处理<br>
数据迁移(如 CCDC数据迁移、会员数据迁移)前后,要保<br>证业务系统能正常识别和处理。不能保证的话,要考虑停<br>机迁移。<br>
数据存储迁移,尤其是不同存储之间的迁移,要考虑其之<br>间的差异(如 oracle和 pg的差异、redis和 hippo的差异<br>等)。尤其是精度、sequence等相关的差异更应该注意,这<br>些都和资损密切相关。
迁移过程中,要有完整的报警、核对等跟踪机制,保证及<br>时发现问题<br>
数据是系统的底层环节,迁移的时候一定要有完备的回退<br>机制。<br>
风险较大的系统发布与业务迁移,采用开关的方式规避风<br>险,保证业务的兼容运行。<br>
并发互斥控制设计<br>
在整个分布式系统中,大到业务的并发处理、服务的并发请求,<br>小到数据库表的并发读写、java对象的多线程访问,并发无处不在。<br>因为并发,所以互斥。<br>我们所说的系统层面的并发互斥控制,主要包括:<br>
<ul><li>服务的并发请求控制(进程级)。</li></ul>服务的并发请求,既包括 dubbo、http等同步调用的场景也<br>包括消息、调单等异步调用场景,需要一起考虑。<br>此外,在业务处理中,还存在各种互斥的资金处理的场景与<br>流程,也是我们需要在设计的时候考虑的。<br>
<ul><li>服务内处理的并发请求控制(线程级)。<br></li></ul>1)内存中的并发控制。主要是内存中的对象、线程变量等资<br>源共享资源进行并发控制。<br>2)内存外的并发控制。主要是指内存外的存储资源,如数据<br>库表的访问等的并发控制。<br>
常见并发互斥类资损风险<br>
在互斥的业务流程与场景中,业务处理过程中,无状态迁<br>移变化的控制。<br>
复杂的场景中未使用状态机。或者状态机状态迁移合法性验证不<br>全面。或者状态机设计不合理,使得状态迁移变化不可靠。<br>常见的状态机设计问题有:<br>
状态被执行有多次。如支付成功多次可能导致业务被推进<br>多次。<br>
终态还可以继续变化。如代发成功又变更为待支付<br>
互斥状态没有做并发控制。如发货和退款同时发生,同时<br>成功。<br>
状态机没有统一控制。<br>
多个状态机耦合在一起,状态控制混乱。如交易核心支付<br>状态、交易状态耦合在一起,由于不同的状态控制,导致支付<br>状态成功、交易状态失败的问题
状态机的状态迁移被漏处理。<br>
并发请求处理中,没有考虑到消息与调度场景。<br>
比如收单服务请求支付核心进行支付处理,同步响应结果和消息<br>通知结果同时到达,未做并发处理,业务被推进了两次(可能申购了<br>两次基金、也可能通知商户发货了两次)。<br>
多线程环境下,对内存中资源未做并发控制或并发控制不<br>严格,尤其是对线程变量的使用。<br>
如对共享数据访问不加锁、单例对象进行写操作、线程变量进出<br>不清空等均会导致并发控制问题。<br>
数据脏读,并根据脏数据做判断。<br>
需要注意的是,不仅仅是对数据库中的数据访问存在脏读,<br>分布式缓存中的数据访问也存在脏读。<br>
常见的数据库锁操作问题。<br>
为了防范脏读脏写、不可重复读等常见的数据库并发问题,我<br>们常用锁机制进行处理;不合理的锁使用会导致风险。常见的问题有:<br>
先读后锁,而非先锁后读。读的数据不能保证是最新数据,<br>会导致仍然基于脏数据做判断的风险。<br>
批量 update 流水的时候, where 条件中没有原有流水的<br>状态,且没有判断更新条数。容易出现不可重复读的问题
SQL 语句中使用 update set select 完成数据的更新,有<br>可能导致 update锁机制不生效。<br>
锁没有顺序且未设置超时等退出机制(如 SQL 中未加 no<br>wait) ,导致死锁。<br>
系统并发互斥设计<br>
业务状态比较复杂的场景,使用状态机进行状态控制。<br>状态机是针对业务状态比较复杂的场景下,并发互斥控制比较常<br>见的设计模式。如续期宝业务、统一订单交易平台均使用状态机来进<br>行业务状态的管理。<br>
状态机设计过程中需遵循如下规范:
由于业务状态的迁移,可能由同步服务请求、异步消息、<br>后台调度、用户操作、运营操作等多种情形触发。在此情<br>况下,需要保证状态机统一进行控制,不能设计多个状态<br>迁移变更模块。更不能设计多个状态机进行对不同情形做<br>状态迁移。<br>
要设计终态,且保证终态不能被继续迁移。如一笔交易订<br>单的超时关闭就是终态,在设计上就要避免其不能再次迁<br>移为待支付。<br>
要保证状态迁移和业务处理的一致性。
设计中,针对状态迁移的处理,需要对所有可能的状态做<br>判断。极端情况下,可以判断所有的状态。<br>
由于业务状态比较复杂,为防止状态机状态设计腐化,与<br>状态机实现代码表现不一致。状态机不能只根据文字描述<br>进行开发,要先设计或更新状态迁移表或状态迁移图;可<br>以明确表达出入口状态(初始状态)、终态、状态跃迁路径<br>再。<br>
不能用同一个状态字段表达多种状态迁移。<br>
简单的业务处理,可能不使用状态机进行状态迁移的并发控制。<br>但仍然需要使用统一模块进行状态控制、并考虑多种情况下的并发请<br>求,做好业务的并发与互斥处理。<br>
并发请求的处理。<br>
针对分布式环境下的并发请求的处理,多是基于数据库或分布式<br>缓存等资源等进行并发访问控制。常见的并发访问控制模型主要有如<br>下几种:<br>
1、 基于资源的并发控制 (悲观)。<br>
这是比较经典的并发控制模型,使用较多,如账务系统中的记账<br>操作就使用该模型控制用户账户余额的并发更改。<br><br>
基于资源的并发控制 (乐观)。<br>
由于是在第 4点更新领域对象的时候检查并发;其实整个过程中<br>并没有锁定任何对象和记录。所以采用该机制的时候,系统应该要容<br>许不可重复读问题的出现。<br>
基于分布式锁服务的并发控制。<br>
分布式锁可以借助分布式缓存实现。<br>
数据操作的规范。一锁二判三改。<br>
一定要保证先锁后读,避免脏读。数据库可以使用 select<br>for update 这种模型进行加锁。<br>
提交的时候要判断更新的数据的状态是否符合要求,避免<br>不可重复读。要在 SQL 的 where 条件中增加原有流水的状<br>态,且检查了 update方法返回的记录条数是否满足预期。<br>如果是状态机的状态进行 update的时候,要基于状态迁移<br>来写 where 条件中的前置状态值,where status=xx 或者<br>where status in (xx,yy)。update的结果,影响行数,0、<br>1、N的时候处理逻辑均需要实现。<br>
不要使用 update set select 完成数据的更新,有可能导<br>致 update锁机制不生效。<br>
多线程操作规范。<br>
多线程操作规范需要遵循常规的 java 并发操作规范,保证操<br>作的原子性。<br>代码开发的指导原则应该是:尽可能的避免在多线程间共享数<br>据、共享操作;如果要共享数据,需要对共享数据加锁;尽可能<br>的使用 spring 提供的单例框架配置单例的 bean;不要对单例的<br>bean进行写操作;业务代码开发过程中,尽可能的避免使用线程<br>变量;使用线程变量的时候,一定要在出入口进行清空<br>
避免死锁。<br>
无论是数据库还是分布式锁服务或者 jvm内部的锁,在可能发生<br>死锁的场景,加锁的时候需要通过加锁排序和设置超时时间等方式避<br>免死锁。<br>
Collect
Get Started
Collect
Get Started
Collect
Get Started
Collect
Get Started
评论
0 条评论
下一页