写在前头
大家曾经有没有遇过日常技术交流的时候,会讨论某某技术之间的关系是什么,某些技术是否应该用到微服务。我相信热爱技术交流的您,就算不是在微服务这里领域,或多或少都会跟其他同行会做一些争议话题的探讨,而且我敢肯定这些讨论绝对热火朝天。
今天我想从微服务的4个比较火热的话题进行出发,与大家分享我对微服务的一些个人见解,这4个话题分别是:微服务来带的新问题、微服务与SOA、微服务与DDD、是否有必要引入聚合层。这里部分话题,在业界会存在一定的争议性,例如DDD的引入、微服务与SOA的关系。
一千个人眼中有一千个哈姆雷特,不同的人以不同的视角去看待这些问题,都会拥有不同观点与答案。观点上,咱们求同存异,不盲目遵从别人的想法,也不自以为是的把自己当成标准。
当然了,我并不会为了个人见解而故意制造观点与话题,更不会把我所有的观点当成一个“所谓的”标准。因此在该篇文章,我会把多处理收集的资料梳理好放到文内,有理有据地结合自己的见解与大家分享一二。
这些内容可能是大家日常容易混淆的,也可能是大家多次纠结不知道如何做出选择,因此我希望通过我该篇文章的分享,能给大家带来新的观点上的碰撞,或是见解上的共鸣。
微服务带来的新问题
做技术选型就如网上购物一样,即使知道了它的优点,还得看看它的差评。我们得多方面评估,事先知道团队与业务是否能抵御与承受“坑”的风险。
既然任何一样技术都无法成为软件工程的银弹,那么必然解决了某种问题的同时,也会带来一些新问题,微服务也不例外。我回顾了下当时我实施时的难点确实有不少。不过,我个人认为微服务给我们带来最核心的问题主要有三点,两文化与一思维:
- 自动化文化
- 可观测性文化
- 分布式系统思维
上面这三个问题,每个的内容单独拿来出讲的篇幅,都足以出一篇完整的文章甚至是书,基于此我会挑一些重点和大家分享。当然,几乎每部分的文末,我会放入相关内容的文章外链,如果大家有兴趣可以自行扩展阅读。
自动化:避免重复的人力劳动
任何的架构模式,也是需要同等的开发模式与之配合的,随着应用的拆分后服务的数量由量变引起质变,因而需要接受自动化来代替从前的“人工处理”,包括服务的部署、服务注册发现等等。自动化,是软件工程的其中一种处理手段,允许团队采用主流的工具、流程形成一套自动化机制,从而减少重复性工作、减少人力干预的不确定性因素。
这里说说我对软件工程的理解:通过多人协作、有目标、有步骤、有计划的,并使用科学方法论指导开发与维护程序的这个过程,也可以换成一条公式表达:软件工程 = 工具 + 流程 + 模式。
无论是我们讨论的这些“事”、技术工具,还是流程制度都是需要人(团队组织)的参与,人的延申就是团队与文化,就如上述所说的软件工程是多人协作的工作,只有当然团队目标一致,共同负责承担团队的项目,共同接受同一种文化才能很好的实施自动化。
我简单举个例子:如同多匹马拉车一样,只有它们都有共同的目标的时候,才能快速拉车到目的,如果它们一匹向东一匹西,只会让马车无法前行甚至四分五裂。
说到这里有些人觉得疑惑,自动化这种能给团队省时省力、减少重复工作量、增加幸福度的技术难道还有人或者团队会抗拒的?是的,还真的有。
我曾经就接触过几个团队的Leader,他们的团队都是推行自动化失败了,失败的原因就在于成员不配合,成员拒绝配合的理由是:没有自己亲手去做总觉得机器不靠谱。我们先忽略他们的想法是对是错,回到团队与文化,从上面可知,统一团队的目标一致性是有必要的,作为技术领导者推行优良的方案,是我们的职责之一。如果团队成员无法配合,导致推行受阻,我们一共有三种应对策略:激励、考核和逐步试行。
如果有条件的公司可以设置奖金激励,如果有绩效考核的,可以将自动化的实施纳入考核目标,如果这俩都没有,那就选取团队里愿意改变的同事牵头试行,假如使用过后都说好,那么会更有说服力。
这部分就说到这里吧,基于此话题的内容比较多,广而泛,篇幅有限。
可观测性:提高团队对系统运作的信息量
如果说自动化是给团队带来稳定性,减轻工作量的,那么可观测性就是提高团队对系统运作的信息量。建立可观测性的这项工作,虽然无法直接给系统带来健壮性,但能够使我们通过这些信息充分地了解到系统正在运作的情况,以至于最大程度地做出最合适的定位、判断与决策。
在单体应用的场景下,我们也是需要可观测性的,但是单体的架构相对简单,项目调试也更加便捷,无论是从复杂度和规模的角度来看,单体跟微服务相比都要低不少,也因此单体对可观测性的需要,相比于微服务显得没那么重要。
而我们只要进行了微服务实施后,因为应用被拆分成了细粒度,从而导致了架构从量变引起质变,这个时候可观测性的作用在微服务场景下被“无限放大”,也因此我们利用"可观测性",给与我们提供应用与服务器的监控、快速跟踪与问题定位的功能。
可观测性——可以由系统的外部输出推断其内部状态的程度,在软件系统中,可观察性是指能够收集有关程序执行、模块内部状态以及组件之间通信的数据
而可观测性三个维度组成:日志(logging)、跟踪( tracing)、指标(Metrics)。
日志(logging)的定义特征是它记录离散事件,目的是通过这些记录后分析出程序的行为。
跟踪( tracing)的定义特征是它处理请求范围内的信息,目的是排查故障。
指标(Metrics)的定义特征是它们是可聚合的,目的是监控和预警。
可观测性 | 类型 | 名称 |
跟踪( tracing) | 分布式链路跟踪 | SkyWalking |
日志(logging) | 日志系统 | ES+Filebeat+kibana |
指标(Metrics) | 系统监控 | Prometheus |
因此基于上述总结,有日志记录才能清楚知道当前系统的运行状况和具体问题;指标是给与后续做优化和定位偶发性问题的一些参考,没指标参考就没标准;我们平常做得多的调试、查看调用栈也是跟踪的一种,但是在分布式时代,更多考量的是跨进程通信的调用链路。
日常有小伙伴曾问过我,如果出了那些很奇怪的问题,应该怎么定位、排查,本质上其实还是得提高我们对这个问题或系统的信息量。
例如:是哪个模块、接口出问题?具体服务器表现的情况是怎样?CPU还是IOPS高?具体报错是什么?
你看,说白了还是可观测性的三个要素日志、跟踪、指标。我们在工作中只有灵活结合这三者,才能提高我们对系统运行情况的信息量,信息量越高思考的越是更加全面,才能尽可能地减少“不知道问题出在哪”的状况,所以当我们不清楚具体发生问题的原因时,建议你侧重做一件事:就是尽可能想办法,提高我们对问题与系统的信息量。
新思维:不可避免的分布式系统问题
上文的自动化和可观测性,主要偏向于运维层面,而作为后端开发人员更加关注的是数据与应用服务的层面,特别是服务的拆分后,数据的一致性该如何处理。我相信这问题困扰着不少的后端开发,接下来我将从幂等性、数据的一致性的读与写三个方向,跟大家分享一二。
幂等性
幂等性的定义——相同的参数在同一个方法里,无论执行一次还是多次都会响应相同的结果
对于查询和删除的场景都有天然的幂等性,那么我们考虑幂等性的处理,更多是关注于新增数据与更新数据。
新增数据缺乏幂等性,则会因为网络抖动导致请求重试或者是客户端重复点击,而引发的数据写入重复,其解决方案也相对简单,只要从客户端生成主键传给后端API 就可以解决,在这里得注意一点,只有请求成功或者主动刷新才会重新生成主键。
更新数据缺乏幂等性,主要会造成两种情况,数值错误自增和ABA问题。首先,数值错误自增,可以结合事务凭据与新增幂等性的方式解决。
而ABA问题,解决方案相对简单,可以在更新操作时带上版本号判断进行解决。
ABA问题
对某条记录先更新了A数据,紧接着又更新了B数据,理应是B是最新的,但是因为其他客观原因使接口Retry或者别的问题,导致A数据再次请求覆盖了B。
幂等性处理方案 | ||
场景 | 问题 | 方案 |
新建数据 | 重复创建 | 由调用端预生成订单号,唯一键约束 |
更新数据 | ABA覆盖问题 | 添加版本号判断 |
金额自增 | 使用流水凭据 |
数据一致性(读)
数据一致性读,其实说白了就是做数据关联,从我过往用的解决方案来看共有三种,应用层的接口聚合数据、把更新频率低的字段冗余存储、把数据库同步到一台服务器进行SQL联表处理,每种方式各有优缺点,我结合切身体会和过往经验,以表格方式整理呈现出来,你可以根据业务场景自行选择解决方案。
数据关联方案 | |||
方案名称 | 方案描述 | 优点 | 缺点 |
应用层数据聚合 | 分别调用查询API,在业务逻辑层组装,适用于简单的关联。 | 实现简单 | 该方案只能适合简单的查询过滤,以主表为驱动的关联 |
冗余设计(反范式) | 在目标表添加冗余字段,适用于记录递增的,不适用于冗余字段更新频繁,实现起来简单,有扩展性问题 | 实现简单,以应用层数据聚合方案有更多的过滤条件 | 冗余的字段如果更新存在同步问题,该方案适用于更新频繁少的递增日志类数据 |
数据库从库集成 | 通过主从同步把相关表同步到一台服务器做跨库查询,适用于复杂查询、报表类的,有技术复杂度,从长远收益来看能应对多种场景 | 通过强大的SQL解决复杂的报表类查询 | 拥有技术复杂度,需要数据库主从处理 |
数据一致性(写)
写的数据一致性,其实就是分布式事务,主流的方案有TCC、本地消息表(基于消息可靠的最终一致性)、异步请求/回调。其他的多数是基于以上几种方法的变种,例如RocketMQ的消息事务,就是TCC+本地消息表的变种。
分布式事务方案,用文字描述起来相对比较吃力,因此我通过流程图代替文字描述展示给大家。下方表格是我对这三种方案的优缺点与使用场景的总结。
分布式事务方案 | |||
名称 | 场景 | 优点 | 缺点 |
异步请求/回调 | 跨网络环境、同网络环境 | 实现简单 | 强业务 |
TCC | 跨网络环境、同网络环境 | 有现成的框架、实现简单 | 强业务 |
基于消息可靠的最终一致性 | 同网络环境 | 有现成的框架、通用性强 | 中间件依赖 |
小结
从"拆"的层面来看,微服务的思想与优势给与开发人员带来了更多的便捷性,如技术细节的隐藏与认知负担的降低,使得开发人员更加关注自己负责业务代码的迭代。
但是,拆了后还得重新整合,如果拆了无法整合,那么这样的设计是没有意义的。从"整合"的层面,微服务大大提高了对架构师技能的要求,从通信交互到分布式问题,从自动化工程到可观测性,每一项对于架构师都是一种新的挑战。