测试经验-高级
基础编码
概要
1.并发控制,默认使用悲观锁,一锁二判三更新,乐观引入须谨慎。
2.幂等拦截,幂等新老要兼容,字段约束需一致,异常场景防击穿。
3.状态推进,流转设计要完整,状态推进凭指令,业务终态不可逆。
4.对象设置,成员变量慎赋值,引值引址需明晰,对象比较用 equals 。
5.数据库表, SQL 必须带字段, where 条件有索引,索引不含隐式转。
6.时间设置,关注时区和时令,避免设置当地值, string 传值带时区。
7.异常防御,请求校验防篡改,异常 catch 不能吞,线程对象清理好。
8.代码质量, CR 单测集成测,结果断言边界值,全量回归不能少。
详细解释
1.并发控制,默认使用悲观锁,一锁二判三更新,乐观引入须谨慎。
.为防止数据并行写入造成状态回退或乱序,原则上使用悲观锁,先对需要操作的数据进行查询检索并加锁,判断当前状态符合修改条件后再操作数据,最后提交事务释放锁。乐观锁,虽然性能上更好,但设计上和维护上都更复杂容易出错,如须使用,务必进行充分的方案评估,后续改动也要特别注意防范因乐观锁条件被破坏而造成的业务错误。
2.幂等拦截,幂等新老要兼容,字段约束需一致,异常场景防击穿。
·上下游对幂等的理解(包括请求中用于幂等控制的字段,幂等后返回的结果,幂等后的业务处置等),需要确保单一、明确,避免出现二义性理解。且幂等设计不仅包括公司内部各系统,与外部交互也需要确认幂等字段的有效性,例如文件中的外部单据号等。同时,幂等设计也要考虑异常场景,下游在无法明确业务处理结果状态时,不应返回上游明确成功、失败或幂等的结果,避免上游错误处理。
3.状态推进,流转设计要完整,状态推进凭指令,业务终态不可逆。
·状态机设计要考虑完整,避免业务状态流转设计缺失导致业务无法正常处理。状态推进一定要基于明确的业务指令或者集成契约,没有明确的业务指令系统不能擅自推进状态机,比如不能单纯因为技术的超时就把状态机推进为失败,实际上超时可能是成功也可能是失败。状态机的终态设计,一定要做好 review ,终态意味着不再更改,无论何时查询这笔业务都是同一结果,其他系统可以根据这个状态做进一步的决策动作。
4.对象设置,成员变量慎赋值,引值引址需明晰,对象比较用 equals 。
·对于成员变量的使用需要判断程序处理中对象是否是单例模式,单例模式下存在多线程访问会对成员变量造成并发修改情况,导致脏读脏写。
.日常设计中例如缓存对象值获取、 money 对象处理一定要分清楚引值/引址,是想在原有对象上进行修改,还是克隆一个新对象进行处理需要区分开,引值需要深拷贝,引址并发场景防脏写,避免使用语意不清晰导致原始对象被篡改造成数据异常甚至资金风险。
.对象比较必须使用 equals 方法,不接受任何理由的空指针。
5.数据库表, SQL 必须带字段, where 条件有索引,索引不含隐式转。
· SQL 查询必须指明要查询的字段,杜绝 select *。查询条件上要有索引,避免全表扫描影响数据库性能。且查询条件必须同 DB 字段类型一致,避免查询过程 DB 进行类型隐式转换造成索引不可用。
6.时间设置,关注时区和时令,避免设置当地值, string 传值带时区。
.时间设置要关注闰年、夏令时、时区。服务器时间都要设置成为 UTC (海外)或者 GMT +8(国内),一定不能设为当地时间(北
美、欧洲、澳洲一些国家都有夏令时)。
·时间不要用 datetime ,要用 timestamp 。用 string 传时间,一定要 ISO 格式带上时区信息。
7.异常防御,请求校验防篡改,异常 catch 不能吞,线程对象清理好。
.对于用户或机构通过各种端提交的数据,必须校验其合法性、完整性、正确性和真实性,避免各种非法篡改导致的安全或水平权限问题。代码编写中,对于 catch 住的异常,必须明确给出处理方式(至少要保证异常被日志记录),禁止不做任何处理就截断,严禁吞掉异常。为避免线程复用对上下文数据造成污染,上下文变量的设置与清除必须配对,在相应的 finally 中清理,避免内存泄漏。
8.代码质量, CR 单测集成测,结果断言边界值,全量回归不能少。
·代码上线前必须做好 CodeReview ,且必须由编码外的另一个同学完成 CR 检查后才可推进到下一步流程(包含代码/配置变更等多种场景)。单测用例要做好结果断言和边界值测试,代码变更后,一定要通过全量测试回归后才可以发布,不能只测试修改部分。
架构设计
概要
1.防御编程,强弱依赖要合理,单点异常可切换,线程隔离和限流。
2.网络调用,交互三态要牢记,错误定义要单一,未决处理遵协议。
3.接口规约,字段约定要明确,业务含义要清晰,接口升级要兼容。
详细解释
1.防御编程,强弱依赖要合理,单点异常可切换,线程隔离和限流。
核心系统禁止强依赖非核心系统,核心链路必须能主动降级非核心链路(关注弱依赖节点超时时间设置问题,避免弱依赖变"强依赖")。
如业务场景或部署架构无法避免单点,那么必须设计去中心化或者做好备份架构,具备切换功能。
做好线程隔离设计,避免不重要的服务异常影响到重要的服务。同时,容量保护是应用提供服务的必备防御条件之一,需要常态化配置限流。
2.网络调用,交互三态要牢记,错误定义要单一,未决处理遵协议。
交互三态,成功、失败、未知在一个接口设计中必须包含,成功和失败必须明确返回结果码,返回码不可有二义性,在未知返回处理上,必须遵照协议约定,决定是否重新发起业务操作或等待回查结果。
3.接口规约,字段约定要明确,业务含义要清晰,接口升级要兼容。
接口交互约定上,业务相关字段必须明确、单一,对于业务透传数据,也必须约定透传的 KEY 枚举和内容大小,确保交互双方业务理解一致。
接口升级,必须兼容原有接口请求和返回,不应在业务明确字段上叠加业务含义或破坏原有字段业务含义,令字段形成二义。·对于业务特殊,必须破坏或废弃原有接口的情况,建议重开接口并设计相关切换及核对逻辑。
中间件使用
概要
1.消息使用, groupld 要唯一,重复投递须关注,事务回查防悬挂。
2.缓存设计,缓存击穿要兜底,过期设计去热点,存储容量需考虑。
3.调度任务,调度重叠要避免,捞取数据可配置,熔断能力需具备。
详细解释
1.消息使用, topic 要唯一,重复投递须关注,事务回查防悬挂。
·rocketmq中,一个消费组内的消费者,其tag过滤行为必须一致。
.rocketmq中,一个消费组内的消费者,其订阅的topic必须是同一个
.rocketmq中,一个消费组内的所有消费者,其订阅关系必须保持一致。 https://www.jianshu.com/p/abbc80706ded
·消息投递不保证不重复,因此订阅端需要依据业务属性做好幂等处理;
.事务消息如果没有实现事务回查,会造成消息悬挂。
2.缓存设计,缓存击穿要兜底,过期设计去热点,存储容量需考虑。
.不管使用何种缓存,如业务属于不可降级核心链路,须确保缓存被击穿时,后端数据库容量可以承担对应的线上流量。
.在缓存失效设计上,需考虑打散,避免同一时间大量请求落到 DB 形成热点,缓存 KEY 规则也应避免形成热点 KEY 。
.缓存存储容量消耗要提前计算清楚。
3.调度任务,调度重疊要避免,捞取数据可配置,熔断能力需具备。
.定时任务调度,需要控制调度频率,避免出现上个任务尚未结束而下个任务就已开始的并发场景。
.分布式调度场景捞取数据必须可配置,以灵活处理数据。如果数据不可配置或者并发数控制不好,非常容易将下游系统打挂。
.当执行层数据量较大时,要有对应的熔断措施(可以通过上一步的配置来减轻压力),且执行调度任务的线程池要独立配置,避免与业务混布导致调度任务无法及时处理。
资损风险
概要
1.资金风险,证账实要对清楚,业务规则细分析,配置上下需一致。
2.金额计算,注意币种和元分,需要考虑丢精度,资金出入方向对。
3.核对设计,过程终态均核对,包含金额状态码,中间账户对余额。
详细解释
1.资金风险,证账实要对清楚,业务规则细分析,配置上下需一致。
.资金风险重点关注一致性,业务型、配置型风险
.一致性通过证账实理出关系
.业务型关注业务规则类(准入,金额计算等)的风险场景
.配置型的关注上下游配置一致性,相关风险点必须部署核对。
2.金额计算,注意币种和元分,需要考虑丢精度,资金出入方向对。
.金额计算主要涉及到上下游币种一致、计算精度这2个重点。上下游必须保持币种一致,是元还是分要明确,防止错误使用导致金额放大缩小,所有的地方,包括数据库存储,都要用三元组来表示金额:币种, amount ,单位(元还是分);
.金额计算考虑精度问题,比如到小数点后几位的四舍五入;
.金额操作双方保持方向一致
3.核对设计,过程终态均核对,包含金额状态码,中间账户对余额。
.核对设计的时候,一般考虑对过程和终态都做核对、核对要素包括金额、状态、和账户一致,涉及到中间过渡户的账号,必须部著流入流出自平衡+流入流出汇总核对,或者余额核对,保障账户资金正确。