在快速迭代的互联网背景下,系统为了实现快速上线,常常会选择最快的开发模式,例如我们常见的mvp版本迭代。大部分的业务系统对于未来业务的发展是不确定的,因此随着时间的推移,往往会遇到各种各样的瓶颈,例如系统性能、无法适配业务逻辑等问题,这时可能就涉及到系统架构的升级。系统升级往往包含最基础的两个部分:接口迁移重构和数据迁移重构,在系统架构升级的过程中,最重要的是需要保证系统稳定性,即用户不感知。因此文本的目的是提供一种可灰度、回滚的设计思路,实现稳定的架构升级。,在我们系统迭代过程中,往往涉及到重构、数据源切换、接口迁移等场景,为了保障系统平稳上线,因此在接口迁移过程中应该保证可回滚、可灰度。接口迁移可能也涉及到数据迁移,两者的先后顺序应该不影响到系统的稳定性。总结一下,接口迁移的目标:,本文主要为接口迁移和数据迁移提供了一种思路,在第3节里会有实践的核心代码实现。(代码只是提供思路,并不是能够直接运行的代码),下图表示了接口迁移的思路,参考了cglib的jdk的代理方式。假设你有一个待迁移接口类(目标类),那么你需要重新写一个代理类作为迁移后的接口。目标类和代理类的选择通过开关去控制,开关涉及到两个层面:,,针对不同的接口逻辑,代理接口实现逻辑会有差异,具体场景如下文所述。,针对单条数据,可以通过数据源来判断来源。基于可灰度和回滚的原则,目标类和代理类的路由规则如下:,不同于单条数据的查询,我们需要查询中新表、老表中所有符合条件的数据,多条数据查询涉及到数据重复的问题(即数据会同时存在于老表和新表中),因此需要对数据进行去重,然后再合并返回结果。,因为在数据迁移后到系统灰度的过程中存在中间时间,所以在数据更新时我们应该通过双写来保持新、老表数据的一致性。同时为了对接口和数据进行收口,我们也要先判断总控开关是否开启,如果总开关已经打开,则数据更新只需要更新新表即可。,对数据和接口收口,我们需要对增量数据进行切换,因此直接使用代理类并将数据插入到新表中,控制老表的数据增量,在数据迁移的时候只需要考虑存量数据即可。,例如在零售场景中,每个门店都有唯一的身份标识门店id,那么我们的灰度列表就可以存放门店id列表,按门店维度进行灰度,来粒度化影响范围。,分发逻辑是核心逻辑,数据的去重规则、接口/仓储层代理转发都是基于这套逻辑来控制:,接口代理主要通过切面来拦截,通过注解方法的方式来实现。代理注解如下,切面的实现核心逻辑就是拦截注解,根据代理分发的逻辑去判断是否走代理类,如果走代理类需要解析代理类型、方法名、参数,然后进行转发。,如果走了代理类,那么逻辑都会被转发到ProxyManager,由代理类管理器来负责数据的分发、去重、合并、更新、插入等操作。,代理查询流程图如下图所示,目标接口的目标方法会通过代理被切面拦截掉,切面判断是否需要走代理接口,,例如单个门店的信息查询,那么我们核心控制器ProxyManager方法逻辑就可以这么实现:,多条数据的去重逻辑是一样,去重规则如下:,基于以上去重逻辑,所有的查询接口都可以抽象成统一的方法,核心的流程如下图所示,目标接口的目标方法都会被切面拦截,转发到代理接口。代理接口在调用数据源的地方可以进一步转发给ProxyManager进行查询&合并。如果总开关未开启,说明全量数据还没有迁移验证完毕,那么还是需要查老的数据源(防止数据遗漏)。如果开关开启了,则说明迁移完成,此时不会再调用原来的仓储层服务,达到了对老的数据源收口的目的。,,例如批量查询门店列表,可以这么合并,核心实现如下:,合并工具类实现如下:,代码省略,直接执行代理仓储层的插入方法即可,更新数据需要双写,如果总开关打开(即迁移完毕),则可以停止老数据的写入,因为不会再读了。,本文只是提出一种迁移的方案思路,可能并不能适用于所有场景,但是在系统升级的过程中,工程师面对的最终的目标应该是一致的,即为了让系统稳定的上线,并且在出现问题时能够安全回滚。本文的实现逻辑是通过注解和切面实现对目标接口的方法进行转发,转发到代理类接口,从而切换到新逻辑和新数据源,并由ProxyManager来适配数据源的代理分发逻辑,完成数据的查询、更新、新增逻辑。
,天猫校园技术团队,一个致力于服务校园人群,提升校园人群生活品质,提供校园saas全套解决方案的技术团队。
© 版权声明
文章版权归作者所有,未经允许请勿转载。