每日小收获

Posted by 孙继峰 on February 12, 2020
  • 重基础, 轻框架

  • 自定义 Validator 抛的异常是 BindException, 而 Spring 自带 Validator 抛的是 MethodParamNotValidException. 这就有点坑了, 一个是 is a 的关系 一个是 has a 的关系, 不方便聚合下沉.

  • ⚠️ UPDATE 语句千万不要忘记写 WHERE 条件

  • 微服务开发完成后, 出一个 jar 包, 包含着服务调用相关的领域模型, 由调用方引入

  • 使用云资源的时候不要轻易压测

  • 完美的 lambda 表达式只有一行

  • 流处理不要过于复杂, 否则很难维护

    class Example {
      public void test() {
          Stream<Tuple2<Integer, Integer>> tagIdChannel = tagIdChannelList.stream()
              .flatMap(GoodsInfoDO::getTagIdAndChannelStream);
          Map<Integer, Map<Integer, Long>> tagIdCount = tagIdChannel.collect(Collectors
              .groupingBy(Tuple2::getT1, Collectors.groupingBy(Tuple2::getT2, Collectors.counting())));
      }
    }
    



  • Redis 的不要求时效性的大任务要拆分成小任务执行, 已留给其他连接一些资源

  • 水平扩展: 加机器;垂直扩展: 换更好的CPU, 加内存, 加硬盘

  • 技术驱动业务: 技术给产品开会;业务驱动技术: 产品给技术开会

  • 多线程下 高内聚低耦合, 线程操作资源类, 判断干活唤醒通知, 防止虚假唤醒

  • 阶段性推送, 一阶段少量推送, 预热缓存, 二阶段剩余全部推送

  • Guava Lists.partition(gidList, 10); 对集合进行拆分
  • 没有测试,没有行为保持的手法,没有小步前进,要啥啥没有,你跟我扯啥重构
  • 刀砍斧劈地修改代码,不叫重构
  • 重构 = 质量保障 + 配置管理 + 项目管理 + 需求管理
  • 过于细致的分工是将工人螺丝钉化
  • 默认的传播行为: REQUIERP, 同一个类下没有 @Transactional 注解的方法调用有 @Transactional 注解的方法时无效; 不同类下有效

我还记得new grad的时候进公司不久问了老板一个问题:’我们每次deploy时,service就down了,我们岂不是会丢掉很多requests?’ 我当时的老板一口茶差点没喷出来。 后来我才知道原来changes可以分步deploy到hosts上。具体步骤分为3步:

  1. 在deployment前从VIP/Load balancer/DNS disable该host;
  2. Deployment。host运行app版本被更新为最新版;
  3. 在deployment完成后再把host加回VIP/Load balancer/DNS。
  • SOLID原则 尤其是单一职责原则, 把它做好了才能舒服的 TDD

  • IO密集型任务应该多分配一些线程;CPU 密集型任务应该少分配一些线程

如果你们手头已经有排期的工作受到影响,一定要提前沟通

HTTP 是人与机器交互的协议, RSocket 是机器与机器交互的协议

任何项目上线 都要在上线文档中 标明pull代码的具体时间

提交前准备工作:

  • 执行 IDEA 插件 FindBug 进行静态检查
  • 执行全部测试用例, 并检查语句顿号覆盖

  • 流程确认到流程图再开始写代码

  • 很多领域的边界就是流程的边界

  • 微服务拆分中如果没有CICD和自动化的运维能力,不建议拆的过细。

  • 数据库只是领域模型的持久化存储方式

  • 一个限界上下文理论上就可以设计为一个微服务

  • 与实体相关的所有业务逻辑都在实体类的方法中实现,跨多个实体的领域逻辑则在领域服务中实现。

  • 与其让用户阻塞等待成功, 不如让用户多请求几次
  • 只有在知道外部依赖可能返回什么的时候才能进行Mock
  • 面试过程中没有思路,可以向面试官寻求一些提示
  • 优秀地测试用例可以当做文档来阅读
  • @DisplayName(“测试用例展示名称”)
  • RedissonClient 与 RedisTemplate 不兼容

阿里巴巴JAVA开发手册提倡的理念是宁可抛错,绝不错写数据。

  • 代码优化要和开发用不同的 branch

  • 报告bug时应该准备好受影响范围、影响时间、如何修复
  • 上线前准备bug信息收集的脚本、修复数据的脚本

  • Iterables.getOnlyElement(anyList());获取集合中第一个元素, 如果还有第二个元素则抛异常

为一个延误的IT项目增加人员, 将导致更严重的延误.

一切技术不足管理补的所谓「规模化敏捷」都是扯蛋。 任何「规模化敏捷」、乃至任何「敏捷」的办法,必须基于极限编程的工程技术实践: 测试驱动开发,持续集成,重构。脱离这些工程技术实践的「敏捷」,统统是伪敏捷。

  • MyBatis-Plus 的 updateBatch 入参要校验是否为空集合, 在这上面栽好几次跟头了
  • ⚠️ MyBatis-Plus 的 in 条件一定要判空
  • 不了解正在使用的框架是很致命的

SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是 consts 最好。

EXPLAIN TYPE:

  • ALL: 全表扫描
  • INDEX: 遍历索引
  • RANGE: 区间查询
  • REF: 常量或等值查询
  • EQ_REF: 常量或等值查询
  • CONST: 主键查询
  • SYSTEM: 表中只有一条数据

  • 管道符: 把上一个命令执行结果当做参数传入下一个命令
  • InetAddress.getLocalHost() 每次返回的Host不一定是相同的

  • 数据大部分为查多改少, 与其在修改时更新多处缓存, 也不要查询时现场查多次数据库

  • Redis hMget 返回的顺序与 filed 集合一致, 也就是有序的

Redis 存数据的代码要与业务取数据的代码隔离, 可以同时演进, 但最好不要有依赖

站会

  • 等待所有人 all ready 的一个信号, 然后开始站会
  • 通过经典三句话 ”已经做过的事情,将要做的事情,提出困难和风险“ 来共享自己的状态
  • 提出自己的协作需求,例如:我下午需要找 BA QA 一起完成 Story 的Desk Check并约定时间
  • 响应团队成员的协作需求或者提出自己的建议
  • 发布团队级别的消息

小问题自己解决, 什么才是小问题呢?

  • 给组长合代码使, 大体没有什么问题, 只是有一点坏味道, 不影响运行, 在指出之后自行决定是否合并.
  • code review 时被指出方法命名不太好, 自行决定改什么, 或者列出几个备选项给组长

  • Iterables.getFirst(Collection, defaultValue) 超级实用

  • Future::get 结果会缓存, 多次 get 不会多次执行

  • 使用 Assert 代替先判空后抛异常

  • TDD:一个需求对应多个任务, 一个任务对应多个测试, 每个测试在15分钟内完成

  • 写测试是好的推至极限就是测试驱动开发
  • CodeReview是好的推至极限就是结对编程
  • 开发和产品沟通是好的推至极限就是现场客户
  • 尽早提交代码是好的推至极限就是持续集成

  • new StringJoiner(",", "[", "]") 自定义拼接字符串规则

  • 每次合完代码都部署一下, 我猜着是一个畸形的问题

旧的不变, 新的创建, 一键替换, 旧的再见.
– 重构16字箴言

  • 使用测试库调试 Quartz 定时任务时要通知组内成员关闭本地应用
  • Quartz Job Status 为 ERROR 的时候下次到执行时间的时候不会被执行

  • 开发时使用Redis缓存应该提前预估一下使用的容量, 避免Redis内存不足

  • 如果代码中需要写注释说明下面的代码干了什么, 那么下面的代码就很可能需要提到一个新的方法里

  • 前期不要设计的大而全, 即是预想到了以后这里可能会变化, 不妨先这样设计着, 原因是希望通过这个例子让设计者理解靠经验吃饭(By Experience), 踩坑是最好的经验来源

  • 事件风暴之后的聚合不能直接拼成限界上下文

  • bugFix分支合并: LocalBugFix -> MyRemoteBugFix -> OFashionRemoteBugFix -> OFashionRemoteDevelopment -> OFashionRemoteMaster

几年前,我在纽约买第一所房子的时候,一个房地产经纪人说得很有道理, “这房子得大修,而且是彻底大修,这所有 65 年历史的房子有绝对的升值潜力。 现在你得把所有需要修的东西列个清单,而且在六个月内把一切弄好。一定要在六个月之内解决问题。”
“你疯了么?我付过定金、税金、律师费以后,已经没钱了。 而且我是个很能自律的人,可以用五年的时间慢慢把一切修理好。” 她说,“不,你不会的,因为六个月以后,你就会习惯现在的状况。 你会觉得一切都挺好。就算客厅里有具尸体,你也会若无其事地跨过去。” 我至今仍记得这些话。让我惊讶的是,一切都被她说中了。 错的是我,六个月内没有清理的那些东西,在五年之后我卖房子的时候仍然没修。 —- 《变革之心》

  • 看到了问题长期没有去解决, 那慢慢的就不是问题了

  • by experience (靠经验吃饭) 需要有 experience, 没有 experience 的 by experience 就是瞎猜.

  • 服务端非增量变动时需要通知上游确认
  • 服务端改动无论大小, 通知测试一声

  • 提交 bugFix 时确定影响范围

大凶

  • 加班
  • 上级以解决方案的方式下需求
  • 找不到业务与测试人员
  • 大需求
  • 一次提交代量代码
  • 流水线是摆设/流水线红着没人管
  • code review 次数少

ThoughtWorks 技术雷达峰会

适合中国的技术创业模式: 在一线城市成功发展业务后, replication 到其他一线城市, 再到二线城市 遗留系统快速上云方法: 打包到容器中, 用容器上云 对待自己的系统要想对待牛肉一样, 而不是像对待宠物一样

服务提供了一个单个操作的接口, 极有可能会继续提供批量的接口, 所以以后还是尽肯能的做成批量的接口

云计算的花样玩法,区别于流水线,代码从测试环境流倒预发布环境再流到线上环境。 promotion 方式是从测试环境升级成为预发布环境再升级为线上环境。

开发Feature 前确定排期, 跟哪个版本一起上线, 如果有就合并到dev一起上线, 如果没有就先不要合并到dev, 影响其他版本上线, 虽然有cherryPick 可以解决问题, 但是不产生问题才是最好的

1.bugFix -> dev 2.bugFix -> master

提升服务可逆性

  • 接口要向本版本兼容,便于回滚. 其次才是向前版本兼容, 外部改动会少一些.

Spring 入参校验, 在 Validator 集合中看看哪一个 Validator 能 support 绑定当前的参数(抽象为一个对象), 并缓存起来

  • @Transaction 可以强制走主库
  • RDS-MySQL: SQL 前缀加 /*FORCE_MASTER*/ 可以强制走主库 留意主从同步数据的时间, 别再采坑了

向非DDD项目中强行制造有行为的”聚合根”是不太好的, 因为项目是以数据为中心, 不是以领域为中心的, 不存在聚合根, 所以相同的行为很核能会存在于多个领域模型中

以终为始: 新需求来时, 首先考虑上线流程

  • 写在文档上的构建流程, 不如实例化出来的流水线
  • 写在文档上的验收用例, 不如实例化出来的单元测试

新需求来时要与上游确认版本生命周期(提测时间、预发布时间、上线时间), 防止上游服务上线后, 下游服务还未上线

SkyWalking 在HTTP Header 中加入调用链路

开发环境一键启停, 晚上关闭开发环境, 白天开启

使用阿里的JDK, 可以加速应用启动 基于进程快照的启动, 拉镜像, 启动容器, 不用再等待Java启动

阿里云 arms, 方法级别, 行级别的问题监控

阿里云 SAE 0改动上 Serverless, 秒级扩容 阿里云FC 毫秒级扩容

业务组装层非常适合Faas, 调用多个API 组合成用户端希望的数据 回归测试: 对线上流量进行复制一份, 打到Faas里, 自动化对比返回结果(阿里凤凰平台), 前提应用是不需要DML操作 问题: 自己是快了, 但是下游处理速度并没有改变, 有可能把下游打挂了, 弹性是利也是弊

Tech Leader 该做的事:

  • 100件重要的事情能使业务变好, 找出最重要的3件
  • 自上而下同步战略方向
  • 自下而上的痛点收集
  • 梳理流程给别人, 别人去迭代

领域模型反应的是现实世界的业务概念和关系, 不是数据库模型

订单超时未接单报警大致逻辑: 用缓存去维护订单, 缓存设置过期时间和过期回调, 比如五分钟过期, 超时回调去触发报警. 如果订单流转了, 重新设置一下过期时间, 直到订单流转到终态, 把缓存删了.

Nacos Client 使用长轮询去 Nacos Server 拉配置, Nacos Server 接到 Client 的请求会 hold 一段时间 这期间有新的配置发布时 Server 会将最新的配置响应给这个请求, 如果没有新配置 hold 到阈值后会响应 304, 标识没有新的配置被发布 Nacos Client 接到响应后会立即发起下一次长轮询

随着时间的增长, 项目应该累计可重用的领域资产, 而不是技术债

Guava ImmutableMap.as() 可以优雅的初始化 Map

每年新增500万条数据的表需要分库/分表

  • 流程图适用于单模块流程, 不能体现主体
  • 泳道图适用于多模块流水作业, 不能体现请求方向
  • 时序图适用于多模块交互
  • 状态迁移图适用于审批流、订单流

在流程中加入编码分数排名可以侧面刺激代码质量(工具待调研)

如果需求量足够大, 可以开发一个流量回放服务, 去验证上线前与上线后流量的响应是否一致, 工具: OTDD

限界上下文划分取舍

  • 自研&开源&采购
  • 弹性边界
  • 独立发布&绑定发布
  • 是否会引入分布式事务
  • 模型之间依赖关系


战略

  • 识别场景(不遗漏需求)
  • 识别事件(xx已xx)
  • 识别命令(触发者)
  • 识别模型

战术

  • 识别实体与值对象
  • 划分限界上下文


使用日志收集后日志不落盘, 避免磁盘不足 & 清理日志时服务停顿

使用日志收集可以收集统一格式的日志, 以提供 Trace & Metric & Alert 功能

使用日志收集进行数据分析 & 运营, 比如不同业务部门看到不同的账单, 关注每个业务的收入与支持

分布式文件存储模型

  • 元数据模型: 无须迁移数据, 保证元数据高可用即可
  • 算法模型: 一致性Hash, 需要迁移数据

使用预计算对查询进行优化, 针对不同算子(max、min、avg、sum、count)进行优化

failover: 失效转移, 主节点挂了, 流量切到备节点 failfast: 快速失败, 发现问题马上返回, 不进行后续处理(增加check point)

CNCF cloud event 协议, 设计事件协议时可以参考

MQ Client 概率性拉取 retry topic, 保证投递失败的消息会被重新投递 Broker 通知 MQ Client 应该 rebalanced 了

ao、vo、dto、do,不管叫什么o,能说明意图的o就是好o

使用二分法排查问题,如何算二分,需要从排查工作量上做二分,比如远端环境上的java方法输入输出本地不能复现,就需要下载一个arthas,连上java进程,再敲一系列命令定位,这个的工作量比较高

getter setter 方法要单纯,不要夹杂其他逻辑,getter就要单纯的把属性return,setter就要单纯的给熟悉赋值,因为大众普遍的认知getter、setter中就是没有逻辑的,如果要往里面藏逻辑,会徒增debug难度

spring-cloud gateway流量入口,通过predicate找到route,使用route下的filter操作请求内容

lombok 优缺点 优点:省事!低安装&学习成本!这两个理由就能让大部分“为什么我不推荐lombok”文章观点站不住脚(标题党真该死!),只要是手写过getter setter,或者对增量属性生成equals、hashcode的人都能很快的学习,时间成本大约10分钟 缺点:黑盒

约定优于编码,不怕你手写getter、setter、equals、hashcode,就怕你往这些方法里藏逻辑,不利于debug

鲁棒性的边界在哪?哪些错误要兼容,哪些错误要fail-fast? 批处理时错误要兼容,保证批处理完成,不卡在某一条数据上。 非批处理时要fail-fast,及时抛出问题,排查脏数据来源, 防止进一步数据污染 外部依赖错误要兼容 用户输入错误要fail-fast