服务隔离后续

116 阅读4分钟
先上前后的架构图:

before image.png

after image.png

正如前文列举的原因:

  1. 不同的链路有不同的资源消耗 -- 例如管理端以 TPS 为主,资源消耗往往大批成量;用户端以 RT 为主,资源消耗基本短少精简;
  2. 不同的链路有不同的开发模式 -- 例如管理端重逻辑、开发以各类 DSL 使用和磁盘访问为主;而用户端重性能、开发以各类 KV 访问和内存访问为主;
  3. 不同的链路有不同的发布频率 -- 明显地,核心逻辑核心链路相对稳定;
  4. 避免不当复用带来的接口腐化 -- 例如直接在某一接口上新增字段新增逻辑,慢慢地这个接口就臃肿复杂,而每一次变更影响所有复用链路!

不同的资源消耗意味着不同的监控指标、而不同的开发模式则有不同的编码手则、至于发布频率则影响着服务稳定性。

我们得到了哪些好处:
  1. 变更是万恶之源,较低的发布频率可以保证更好地稳定,对于核心服务来说,逻辑较少改变或者近乎不变的场景很实用;
  2. 在固定精力和资源下有更好的期望值,无论是在优化上还是审查上,想想只有十几个接口、一两千行代码的全量审查量 ( 可怕的不是有坏代码,而是不知道哪儿有坏代码 );
  3. 对任何需求的问题都可以转化为:是否要动核心服务 -> 进一步可以有意识审查放核心链路是否合适,会带来哪些风险;
  4. 完全资源隔离,无须担心有意或无意的资源混用和代码复用造成的相互影响,如批量导出带来的阻塞和停顿、如代码复用带来的审查量、潜在稳定性以及复用链路全回归。

通过服务隔离,能访问什么,不能访问什么,可以很简单便捷地通过包引用地判别,而这直接可以借助 ArchUnit 来完成,可以进一步结合流程加强对核心服务的监控和管理,在有限的资源下实现价值最大!

接下来需要做哪些:
  1. 删除无用代码,因为我们是 copy 整个项目创建新 ( 核心 ) 服务,在移除其中非核心接口后,需要把无用的方法和类也一并删除,这个进行多次迭代;
  2. 移除不可能代码路径,由于接口复用的原因,在核心链路中会有不可能出现的路径,例如提交下单不可能有搜索推荐;
  3. 规整代码,例如重命名、提取方法,这些都是直接用 IDEA 提供的重构快捷键,无需担心会破坏功能;
  4. 隔离线程池,例如下单一个线程池、列表一个线程池,每个场景一个独立的线程池,一方面是因为隔离,另外方面则不同场景都有不同的线程数配置;
  5. 修改配置,从配置上保障资源隔离;

因为接口数量和代码行数相比之前极其少,有非常清晰的修改路径,上述的工作量并不算大,在完成了隔离之后,大概花了两天时间把核心链路上的 bad smell 清理了一遍。在简化代码的基础上,也很容易预估每个接口的性能表现,那么可以得出这个接口理应的性能表现,再对比实际表现 ( 性能优化的一个关键点是:得先知道能做到什么程度,才能有的放矢,不会脱离实际也不会无从下手 )。

这其中有什么注意事项:
  1. 每一步都不应该涉及业务代码逻辑变更,风险最小成本最低,在迭代紧张人手短缺的情况下非常实用;
  2. 小步快跑,周期要短,因为是代码复制,在这期间开发的逻辑需要双写代码,为了避免双写的带来的工作、情绪和可能遗漏,需要速战速决;
  3. 需要依赖方修改的话需要尽可能简单,哪怕方案不那么完美,这能有效得推动改造落地;
  4. 安排演练,发版前降级非核心链路,谁知道你的接口会被怎么用?处理漏网之鱼,筑起最后一道防线!

防杠说明:软件领域没有银弹,隔离同样不是银弹,只能减少问题,但不能杜绝问题,如就开头的架构示意图:核心接口同处一个实例仍然会相互影响;又如因为主库共享,用户端和管理端事务存在相互影响的潜在问题。