系统架构设计

一、引言

        最近拜读了携程一位大牛:Gavin的文章干货 | 携程商旅订单系统架构设计和优化实践,里面谈及许多架构分层的理念,读后感悟很多,结合博主自己在系统开发过程中的经验聊一聊架构设计。(这里已经征得Gavin的同意)

系统架构设计_第1张图片

架构设计

二、架构设计

         架构可以分为技术上的架构和业务上的架构,业务架构注重于边界分层、流程统一复用,着重于解耦以及敏捷开发。

        技术架构着重于系统交互、工具开发使用,目的是为了达到更快的处理效果,更自动化的处理异常、系统交互合理减负。

1、业务架构

        业务架构的划分首先区分于领域,将不同领域的系统功能分开,每个大的领域下又会有一些子域,领域划分之后需要根据功能特性进行划分,例如着重于数据计算、用户交互、中间流程等,需要区分业务面向的对象。

        当面向对象相同时,还需要做一些额外的分界,比如作者手中有个安全风控的系统,但是其中一部分功能涉及到大量流量不是很稳定,虽然作者通过自研的计算限流GitHub - SongTing0711/count-limit、redis回滚GitHub - SongTing0711/redis-transaction、分布式代理锁GitHub - SongTing0711/distributed-proxy-lock等工具控制了这种不稳定的流量和计算,但是做系统不能寄希望于没有更加异常的情况。

        另外一部分功能涉及到B端用户上千万的资金流转,不能出任何问题,因此将这两部分功能再次划分成为两个微服务。

        而在Gavin的文章中提到了B端系统C端化,这其实站在了一个比较高的维度看系统分层,逻辑和服务与产品、产线解耦,博主认为是领域、功能划分的理念,功能可配置、可扩展、可插拔的方式可以用于敏捷开发、快速迭代。

2、技术架构

2.1、数据

         数据架构的区分主要在于实时性要求,对于强一致的实时性数据无论是直接查询DB还是业务侧处理到redis再对外都会额外增加一份风险,比如数据量过大—超时、查询较多—CPU、内存飙升、redis与DB数据一致性问题,会导致实时数据的风险随着系统体量增大成几何系数上升。

        这里Gavin对于数据问题的感受和博主是一致的,Gavin的解决方案参考了数仓建模,形成一种准实时的业务数据处理流程,暴露给上下游的各个领域。

        这里基本是可以满足大部分业务场景,准实时的数据一般博主是使用es进行存储的,但是受限于话语权和业务很难要求一部分场景也使用准实时数据。博主比较推荐的是京东的HotKey--热点探测工具,通过简单的api调用将热点数据缓存在本地,但是内存占用会变高,需要权衡。

系统架构设计_第2张图片

(引用1)

2.2、api交互

        微服务的交互反复、链路过长、分布式事务一直是服务拆分被诟病的点,但是大量的系统功能又不得不拆分,作者遇到过得很多情况不单单是开发、维护的复杂,还有一些情况是有风险的。

        举个例子,门店库存变化之后推出mq消息,但是有的下游系统一收到mq就来查数据,这时候事务都还没提交,查到就是不对的。有的同学说我提交之后再发mq就好了,但是一般暴露出去的api都是查从库的,从库跟主库的同步也是需要时间的,这种风险依然存在。一般会尽量要求下游不要这样查,实在不行的会在mq把他需要的信息查出来直接带出去,但是这种做法也不是最佳的。

        Gavin提到需要避免各个服务之间相互直接调用,相互订阅广播通知导致系统的耦合和边界不清晰,其实也是在反映交互的问题。

2.3、流程

         做软件就是想要把统一复用的工作由人转移到机器,如果说架构上单一特殊的点太多,完全没法抽象出统一的技术流程,再牛的软件也会成为码山,前面的人设计的时候不考虑,后面的人不敢改。

         抽象出流程引擎把每一个流程独立化,再由配置、事件进行排列组合是Gavin极力推荐的。

系统架构设计_第3张图片

(引用2)

三、示例

         博主在之前的分析中讲述了一些例子,这里再用一些实际示例介绍架构设计的复杂性。

1、异步结果

        有时候调用下游,下游短时间是给不出一个结果的,这时候一般会采用异步通知,mq或者把结果通过接口暴露出去,上游定时去查,这两种各有优缺点,等mq会有处理失败的情况,异常被吞掉或者mq没有支持ack确认,会导致结果处理问题。

        定时任务去查就是会有延迟,为了安全、实时博主一般会两个一起用。

系统架构设计_第4张图片

2、幂等性

2.1、并发

        Api或者mq的幂等性是个老生常谈的问题,一般是要结合分布式锁防止并发的,博主遇到过一种情况是加锁的问题,开发人员没有判断是否本线程加锁就进行了解锁,导致第三个线程抢到锁,产生了并发问题,所以博主基于收拢规范和敏捷开发做了分布式代理锁工具GitHub - SongTing0711/distributed-proxy-lock。

系统架构设计_第5张图片

2.2、顺序消费 

        但是只防止并发还不行,会存在重试或者乱序消费之类的问题,这时候就需要按照某一个key进行hash顺序消费,比如门店id、订单编号。

2.3、时间戳

        还有时候需要看时间戳,比如机器心跳,因为即使是顺序消息,但是不同时间戳进入mq,如果当前消息的时间戳比上一条早,就应该被丢弃。

2.4、唯一键

        还有一些处理情况需要在业务数据存储上游调用或者发布消息是的唯一键,用来防止重复处理,比如博主调用财务系统会给他一个唯一单号,根据这个唯一单号财务只处理一次,防止调用过程中超时熔断之类的情况导致调用成功但是上游却不知道。

        

3、自动矫正

        由于分布式环境的数据隔离,数据存在不一致的情况,博主一般会用定时任务或者在流量不高的场景下进行数据比对,即比较DB和redis,也比较和其他服务的,发生不一致就清理掉生成一条正确的。

4、自动补偿

        这种在上面很多示例中都有使用,定时任务补偿处理、下游异步结果补偿通知等等。

四、总结

         博主这里只是基于自己的经验结合对Gavin文章的一些理解做了分享,Gavin对于架构设计的研究远不止这些内容,博主没有亲身经历所以没有写的那么全局丰富,有兴趣的同学欢迎讨论。

         这里特别感谢Gavin编写的干货 | 携程商旅订单系统架构设计和优化实践,受益良多!

你可能感兴趣的:(java,架构,java,系统架构)