002-加餐内容学习
[TOC]
设计模式、重构、编程规范相关的书籍
- 《设计模式:可复用面向对象软件的基础》GoF 的23种设计模式是设计模式的开山之作,经典之中的经典,很薄,但理解上会比较晦涩。可能作者是大佬,写的东西很有深度、很经典,但不是很容易被平凡人理解
- 《Head First 设计模式》口语化、场景化,但是例子比较脱离实践,可以通过该书对 “什么是设计模式?有哪些设计模式?” 有很好的解答,小白首选!
- 《图解设计模式》日本程序员-结城浩 写的
- 《深入设计模式》 一名乌克兰程序员 2014 年写的电子书,图文并茂,还有很多语言的例子
- 《设计模式之禅》、《研磨设计模式》
- 《程序员修炼之道:从小工到专家》
- 代码规范:《代码整洁之道》-Bob大叔、《代码大全》《编写可读代码的艺术》
- 重构三部曲:《重构:改善既有代码的设计》、《重构与模式》、《修改代码的艺术》
- 近两年好的新书:《架构整洁之道》Bob大叔、《代码的艺术》-章淼
- 提交代码前问自己七个问题
- 代码是正确的吗?
- 代码是完整的吗?
- 代码是安全的吗?
- 代码是可读、可维护的吗?
- 代码是可扩展的吗?
- 有没有一些让你可以自豪的地方?
- Google 开源项目风格指南
-
[《PHP PSR 标准规范》 PHP 技术论坛 (learnku.com)](https://learnku.com/docs/psr)
评论区中大家提到过的书籍
- 《操作系统导论》据说是最容易入门操作系统的一本书
- 《软技能-代码之外的生存指南》适合程序员的职场技能书籍
曹大:编程军规
一、基础编码 并发控制,一锁二判三更新,并行查询控超时,乐观悲观要合适。 幂等拦截,幂等字段要对齐,上下游要约定好,异常场景防击穿。 事务控制,悬挂监控要及时,必须防止空回滚,定要最终一致性。 SQL语句,使用要求最简化,业务逻辑归代码,索引不含隐式转换。
二、中间件使用 消息使用,一考虑重复投递,二考虑丢失延迟,三考虑消息乱序。 缓存使用,数据过期要控制,缓存击穿要兜底,存储容量要考虑。 调度任务,不重不漏不堵塞,捞取数据可配置,熔断处理必须有。
三、架构设计 防御编程,宁愿不做不错做,强弱依赖要合理,故障隔离能自救。 网络调用,交互三态要牢记,换号重发高风险,错误码映射要谨慎。 接口契约,字段约定要明确,业务含义要清晰,解耦研发独立发布。
四、质量与风险 开发自测,CR 单测接口测,断言回归边界值,系统质量不降分。 资损防控,时效治理全链路,应急止血要可控,纳入架构设计中。 监控核对,发现问题为目的,阈值设置要合理,及时降噪禁麻木。 线上变更,严格执行三板斧,应急手段要完备,恢复业务为要务。
五、国际特色 多 币 种,注意币种有无分,金额计算要克隆,外汇买卖分方向。 多 租 户,入口设值不可变,数据都要租户值,隔离拆分是本质。 多 时 区,切点时区和日历,三者不能缺其一,时间戳存 UTC。
军规解读
一、基础编码
-
并发控制,一锁二判三更新,并行查询控超时,乐观悲观要合适。 在并发场景下,避免读取的数据已经被另外的线程篡改。导致状态机推进等场景出现错误,在做数据库更新时,先锁定需要修改的数据库记录,通常采用 select for update 的方式,判断逻辑需要在select出的记录的基础上,经过业务逻辑等判断。确定无误后再更新数据。
-
幂等拦截,幂等字段要对齐,上下游要约定好,异常场景防击穿。 约定好上下游依赖的幂等字段是否是同一个字段。不仅包括公司内部各系统,与外部交互也需要确认幂等字段的有效性。例如文件中的外部单据号等。
在异常场景下,没有做妥善处理的情况下,幂等控制往往容易被击穿。如定时任务重试,消息重发等。在极短情况下,FO 主备切换过程中,数据库的唯一性约束也将失效。
- 事务控制,悬挂监控要及时,必须防止空回滚,定要最终一致性。 XTS 二阶段往往会出现悬挂,一阶段成功,二阶段提交不了,也无法回滚的情况。往往需要人工介入处理。出现悬挂的情况有很多,比如系统 bug、数据异常、超时空回滚等。需要对每一次异常报错都做错误监控,发现及时处理。
对于分布式事务,仅仅只能做到最终一致性。概念上经常会遇到部分同学与本地事物强一致性混淆。此处需要重点注意。比如A系统调用B系统更新数据,一阶段已经成功。在B系统内部二阶段还没处理完的时候,C系统立马去读取B系统的数据,可能无法读取到A系统的调用结果。
- SQL语句,使用要求最简化,业务逻辑归代码,索引不含隐式转换。 SQL 语句逻辑简洁清晰,涉及到复杂逻辑都交给程序来处理,如果用 SQL 来解决复杂逻辑,经常出现慢 SQL 导致超时,且不利于维护。每一条 SQL 都经历过索引合理性分析,通过 SQL 监控定期回归慢 SQL,分析索引是否走到最优。索引设计时不要用大字段。
二、中间件使用
-
消息使用,一考虑重复投递,二考虑丢失延迟,三考虑消息乱序。 消息处理需要注意是 pull 的方式还是 push 的方式,两种方式各有适用场景。push 的方式特别需要注意重复投递,当消费端处理响应较慢,可能会立马又接收到一个同样的消息。另外消息不能保证 100% 全部投递成功,必须要考虑消息丢失或者消息卡住,延迟的情况。可以通过 T + 1 核对等方式验证是否丢消息等。
-
缓存使用,数据过期要控制,缓存击穿要兜底,存储容量要考虑。 缓存的适用要特别小心。通常只建议存一些少量的,静态数据。如果是经常会更新的动态数据,为避免真实数据已经改变,而缓存数据仍然是老的版本。需要严格控制缓存的有效性。并且对短暂的不一致性做影响评估。极短情况下缓存仍然有可能被击穿,比如缓存容量满,或者缓存未命中等。再调用量极大,或者服务器刚重启完成的阶段,缓存命中率往往很低。此时可以考虑做部分预热。
-
调度任务,不重不漏不堵塞,捞取数据可配置,熔断处理必须有。 三层分发任务调度,需要控制调度频率。往往可能出现上一个任务还没结束,下一个任务又已经开始。两个任务重复的捞了同一部分数据。此时就要考验幂等控制的如何了。即便幂等会控制也白白浪费了系统资源,要避免这样的情况发生。
在定时任务过程中,一次捞取多少数量,处理频率等,都需要可配置,以适应业务的变化及对性能的灵活控制。也要有及时熔断的开关,可以做到立即停止。而不是明知道有问题,缺停不下来的尴尬局面。
三、架构设计
-
防御编程,宁愿不做不错做,强弱依赖要合理,故障隔离能自救。 对于代码可能发生的异常场景要有明确清晰的处理手段,不能仅以正常场景来写逻辑编码。对于不再设计预期内的异常,及时中断逻辑告警,不要走到其他逻辑。系统依赖要考虑清楚,不能 A 级系统强依赖 B 级系统的接口,级别不同的系统可用性要求不同。保护好自己的系统,当上游发生不可预期问题时,要能有手段进行及时降级。维护好自己系统稳定,不影响系统上其他业务运行。
-
网络调用,交互三态要牢记,换号重发高风险,错误码映射要谨慎。 网络调用通畅有 4 种结果:1. 调用成功;2. 找不到服务;3. 请求超时;4. 返回超时。
调用成功情况下,仍然要对返回数据做校验。针对非成功情况下,异常处理很关键。例如超时,调用方是无法感知到底是请求超时还是返回超时的。完全有可能 A 调用 B,A 看起来是超时了。但是 B 这边业务成功了。此时如果是写数据、重试等场景下,要考虑幂等。查询情况下也要注意合适的异常处理。没有异常等控制,信息流就是不受控制的,很危险。
- 接口契约,字段约定要明确,业务含义要清晰,解耦研发独立发布。 目前项目都需要增加集成文档评审,就是为了保证接口契约,契约建好,大家都要遵守,再进行修改就属于变更。字段要约定清楚,不能有二义性的业务含义,比如 要用 null 代表接口没有所查询到值就不可取,因为当系统异常时也可能会返回 null。开发时就要想清楚尽量做到依赖解耦,能独立发布。比如发布时上游系统依赖下游发布的逻辑,那么就要考虑在上游系统中做开关来解耦发布依赖。
四、质量与风险
-
开发自测,CR 单测接口测,断言回归边界值,系统质量不降分。 Code review 覆盖率要 100%,单测用例要对结果断言。并对方法的边界也做测试,比如数据的临界值等。可以极大的降低重构代码的风险。每次系统发布,对质量分的要求都不能下降。
-
资损防控,时效治理全链路,应急止血要可控,纳入架构设计中。 在系分时,考虑是否有资损、是否对有业务有影响,如何从架构设计上防止此问题,同时也要有发现问题的手段。应急止血要明确处理时效要求,告知时区如北京时间,精确到分钟级别。
-
监控核对,发现问题为目的,阈值设置要合理,及时降噪禁麻木。 监控要发现真正的问题, 报警发现的要介入排查。写代码时,错误码的日志级别要写正确,避免不需要关注的错误打印到了 common-error,引起监控大盘报错增多,干扰错误判断。阈值设置要正确,推进降噪。链路增加或变更要增加核对
-
线上变更,严格执行三板斧,应急手段要完备,恢复业务为要务。 线上变更是故障之源,敬畏线上变更,可灰度、可监控、可应急。回滚非万能手段,风险评估回滚场景提前分析。应急时,先确定影响面,然后评估确定是否回滚。
五、国际特色
-
多 币 种,注意币种有无分,金额计算要克隆,外汇买卖分方向。 需要正确意识到:有些币种的最小单位不是分,例如日元。金额加减乘除,区分 MultiCurrencyMoney 的 add/addTo、subtract/subtractFrom、multiply/multiplyBy、divide/divideBy,一个是返回新的对象,一个是返回当前对象。要使用 MultiCurrencyMoneyUtil 来进行金额处理。dalgen 正确使用 MultiCurrencyMoney。需考虑上下游系统币种处理能力是否一致。
-
多 租 户,入口设值不可变,数据都要租户值,隔离拆分是本质。 数据模型带上租户。统一从上下文里取租户。
-
多 时 区,切点时区和日历,三者不能缺其一,时间戳存 UTC。 数据库表相关的时间用 timestamp(6),0 时区。涉及到记账日期、账单对账日期等 8 位数字相关的日期,要使用主体所在的日期,使用 ialicore 的 DateUtil 进行转换。