前言
在业务发展过程中,考虑到要快速进行业务开发并上线,许多的服务都是耦合到一起的,即同步远程调用各个系统的接口。
这种方式在发展初期可以快速的迭代开发上线,但随着业务的发展,同步调用的劣势逐渐暴露出来。由于业务变得越来越复杂,系统越来越多,导致过多的依赖外部系统,从而使接口性能降低,其可用性也会对其它系统造成影响。如果依赖的系统有新版本的上线,其它依赖它的系统都要进行联调测试,这样会使项目的迭代速度变得缓慢。
站在架构师的角度,如何去解决业务发展过程中遇到的一系列问题?
我们该通过什么样的方式来设计系统?
有没有一个通用的思考模型去思考系统面临的问题?
对于架构师来说,要站从全局技术视角看待出现的问题,并且分析解决问题的底层思维逻辑很重要。我们可以从如下四个方面来思考:
- 问题的背景与复杂度
- 解决方案
- 评估标准
- 技术实现
问题的背景与复杂度
如果当下系统不满足业务发展需求,我们就要先进行系统复杂度的分析,然后给出架构的设计方案,这样项目的大方向才不会出错。
要深挖问题的产生,不要盲目的设计。
我们不能因为设计而设计,如果当前系统因为业务逻辑复杂导致了功能耦合严重,却设计了一个高性能的架构方案,这就失去了设计的意义,我们要以解决主要问题为目标,不能偏离了方向。
系统复杂度的评估:功能与非功能的对比。
功能性复杂度
在业务快速发展的过程中,系统会越来越多,团队间协作的效率反而越来越低。这是因为系统间的耦合度越来越大,某一系统的迭代升级都需要其他系统的联调,影响开发效率。针对系统间的强耦合,可以引入消息队列来解耦各系统,释放各个子系统的能力,提高系统的迭代速度。
非功能性复杂度
系统架构的设计还要考虑业务的后续发展,满足业务以后的增长需求。首先要考虑的就是系统的高可用。在引入的消息队列之后,要防止消息队列宕机,这会导致后续服务的不可用以及消息的丢失。这种情况下,在前台页面可以提示用户重新操作,开发人员要及时修复问题,要做好服务的监控,防止有单点故障的产生。
对于业务发展后期,用户量的增加,客户访问的频繁这种情况,要考虑到系统的高性能。如何去设计系统的高性能?要看哪个系统的压力大,统计系统的QPS与TPS,计算分摊到每台服务上的数据是多少,以峰值来计算的话可以乘以一个系数得到最大值,然后根据业务发展情况,推算出大概的QPS与TPS,看下是否需要设计高性能架构。
而对于初创公司,开发团队人手不足,实力也不够,那么公司要综合考虑下成本的问题,当前遇到的问题是否需要做高可用、高性能的架构,用最小的成本解决当下的问题也是一种方式。
解决方案
在明确了系统当前面临的问题后,就有了设计目标,可以开始进行架构方案设计了。我们假设当前遇到的问题是系统间的强耦合来设计几种结构方案。
- 使用MQ消息队列进行系统解耦
开源的消息队列有Kafka、RocketMQ、RabbitMQ、ActiveMQ等。在我们进行技术选型的时候,需要根据当前的业务场景来选择合适的方案。MQ性能对比如下:
| 特性 | ActiveMQ | RabbitMQ | RocketMQ | kafka |
|---|---|---|---|---|
| 开发语言 | java | erlang | java | scala |
| 单机吞吐量 | 万级 | 万级 | 10万级 | 10万级 |
| 时效性 | ms级 | us级 | ms级 | ms级以内 |
| 可用性 | 高(主从架构) | 高(主从架构) | 非常高(分布式架构) | 非常高(分布式架构) |
| 功能特性 | 成熟的产品,在很多公司得到应用;有较多的文档;各种协议支持较好 | 基于erlang开发,所以并发能力很强,性能极其好,延时很低;管理界面较丰富 | MQ功能比较完备,扩展性佳 | 只支持主要的MQ功能,像一些消息查询,消息回溯等功能没有提供,毕竟是为大数据准备的,在大数据领域应用广。 |
- 使用Redis实现消息队列
方案一引入了MQ来实现系统解耦,这会增加系统的维护成本,也会引入新的问题(如消息的确认、数据的一致性、服务的可用性等)。 使用Redis来实现轻量级的消息队列,可以降低系统的维护成本和实现复杂度。
- 基于List的 LPUSH+RPOP 的实现
- PUB/SUB,订阅/发布模式
- 基于Sorted-Set的实现
- 基于Stream类型的实现
- 使用内存队列+MySQL来实现解耦
方案二虽然使用了轻量级的Redis实现了的消息队列,还是引入了缓存系统,同样也会带来运维成本。
当前方案是使用内存队列,异步持久化到MySQL数据库,再通过定时任务从MySQL中读取数据处理后续业务。
评估标准
在系统设计之初,一般都会设计系统的监控等以达到项目技术风险的可控。比如,系统无单点,限制了系统设计时不能出现单点服务的设计;如系统可降级,限制了系统在高并发场景下具备可降级的能力,这就需要设计一个数据兜底的技术方案。
从系统的复杂度进行评估。
从功能性复杂度来说,解决的是业务发展过程中带来的系统开发的效率问题。要解决这个问题,要站在更高的视角来考虑,既要考虑现有团队的整体能力,又要考虑成本以及投入的周期等要素。
从非功能性复杂度来说,首先要做到系统的高可用,不能有单点故障。
其次要保证系统可水平扩展,消息队列MQ和Redis有先天的优势,而内存+MySQL这种方式需要做分库分表的设计,还要考虑未来因业务量的增加而对容量的预估。
为了提高系统的可用性,当系统出现故障的时候进行降级处理,提供有损的兜底服务。常用的手段有三种,如下:
- 限流:超出系统设置流量阈值的用户,丢弃访问或排队等待;
- 熔断:消费者调用生产者时,如果超时就发生短路,请求直接断掉,直接返回兜底数据,防止整个链条因阻塞造成服务雪崩;
- 降级:目的是为了确保系统核心功能不受影响,提供有损的数据兜底服务;
技术实现
确定了解决当前问题的架构解决方案后,要进一步地说明技术上落地的实现方式和原理。比如,对比几种消息队列解决方案,都有哪些优缺点?会给系统带来哪些问题?如何规避或解决这些问题?这些消息队列的实现原理是什么?等等。这样既能从广度上给出了多种解决方案,从深度上深入了解每种解决方案的优劣势,又能说出他们的实现原理。最重要的是要契合当下的业务场景,选择合适的解决方案。
总结
做架构设计不能因为技术而技术,是要解决当下的业务痛点,选择合适的解决方案。可以从四方面来作为设计的标准。
- 明确业务痛点,结合当下业务以及业务的后续发展,找到问题所在;
- 针对当下具体的业务问题,设计出可供参考的几套解决方案;
- 从功能性和非功能性两个角度来评估架构方案的合理性,对于难决策的方案,可以从更高层的视角进行考量;
- 而在具体的落地技术细节上,要充分理解各种技术的实现原理,不能只停留在框架的组合上。