架构重构实践心得
一、前言
大多数的技术研发都对重构有所了解,而每个研发又都有自己的理解。从代码重构到架构重构,我参与了携程大型全链路重构项目,积累了一点经验心得,在此抛砖引玉和大家分享。
二、重构的定义
重构是指在不改变外部行为的情况下,改进其内部结构的软件系统更改过程。
三、重构的原因
3.1 组织架构调整
目前携程大部分业务场景都使用了微服务架构,要求服务应该封装单一的责任或单一的能力,以形成松散耦合的服务架构。
根据著名的康威定律,保证一个团队可以独立工作、快速交付变更、尽可能消除团队之间协作和协调的费力度。
所以当组织架构因为业务发展需要做相应调整时,决定了服务的架构也会需要相应的重构。
3.2 提升研发效能
重构遗留代码的目的,通常是使系统其更易于维护,减少维护系统所需的工作量,释放出研发资源处理更高价值的任务。
即使是在项目成功落地之后,仍然需要持续的系统迭代,支持新的业务需求。迭代过程引入新的缺陷也是难以避免的。
即使是最好的团队也可能会提交不成熟的代码,这往往会导致代码复杂性让系统变得难以维护,长期以往系统会越来越难以维护,影响研发效能。越来越长的开发周期,导致业务需求发布被推迟。
重构可以减少构建周期时间,改善需求交付时间。
3.3 偿还技术债
技术债是很多研发团队为了追求交互的速度,倾向于选择更简单但不太可靠的方案所付出的代价。短期内为了更快地交付做出的妥协都会在未来带来更多的工作量。
重构有助于消除开发人员积累的技术债务的数量。
3.4 应用现代化
遗留代码可能无法利用现代 DevOps 集成来加速 CI/CD。
遗留代码需要改造成无状态服务,这样可以适配云上的弹性伸缩,优化成本。
四、重构的影响面
从重构的影响面可以分为最高层次的架构重构和最低层次的代码重构。
代码重构是在修改代码后不会改变对外的行为,主要目的是提高整个软件的质量。一个重构通常是一个小任务,但是多个重构应用于代码可以显著提高其质量。
架构重构主要是将现有代码重新组织成新的层级,改变代码在逻辑层次中的组织方式。
4.1 架构重构和代码重构的比较
代码重构通常在一个开发团队内部达成一致,影响的范围容易控制和验证,风险较低。
架构重构必须要多个团队甚至是技术委员会达成一致,影响范围大,风险和成本较高。通常在做架构重构前尝试用可量化的指标来确定投入产出比,决策是否值得重构。
此外架构重构相比代码重构难度更高,对研发的专业知识要求也会更高,项目组里至少需要有一个熟悉链路上所有服务的资深研发,负责重构决策(特别是服务和服务边界处)。
五、重构计划
在开始重构编码之前,对现有系统深入分析,评估哪些部分应该被重构以及重构的优先级,有助于列出重构计划。
全链路上的大规模重构往往都是长期项目,初期制定的计划也肯定会有变化调整,特别是重构过程中会有新的业务需求进入,需要调整优先级,所以计划要分成不同的阶段性里程碑目标。
六、业务研发团队沟通
重构期间需要和负责业务需求的研发团队同步协调计划,优先对新需求部分重构,避免新功能完成后立即又成为重构任务。
例如,重构后的版本在下周就会发布运行,有些业务需求计划可以直接在新版本上实现,这样既避免了业务研发被技术债务困扰,也减少了重构项目组的工作量。
七、分层设计
分层设计即定义服务之间、模块之间的边界,是完成重构的关键,让代码之间的依赖关系变得更清晰,减少耦合性。
根据关注点分离原则,把现有的功能分类,将对应的代码移动到合适的层级和该层级对应的模块。
前提是需要理解每个类的功能是什么,当面对实现多个功能的类,必须要拆分成多个只服务于一个目的的类。
单一职责的类更具备可测试性,依赖更少的外部功能,减少测试用例里 Mock 的工作量。单一职责的类也更容易被复用,更容易维护。
大型重构项目会有许多工作要做,分层后的设计就像地图,避免研发迷失方向。
八、灰度
在分阶段重构的过程中,引入抽象层(例如面向接口编程),同一个接口有新老两套实现。抽象层的引入允许显示系统里新老版本多个实现的共存。
这种看似额外的临时兼容工作,可以带来以下好处:
-
我们在编写测试用例代码的时候,也尽量面向接口验证,这样在旧代码验证通过的案例能够运行在重构后的新代码上。
-
使用特性开关控制新老版本实现的切换,可以在不破坏原有的功能且对调用方透明的前提下逐步灰度在线上版本进行重大更改,最后新版本实现稳定后,删除老代码的实现。这种方式也能减少研发在重构时候的心智负担。
-
特性开关的引入也能够实现快速回退,控制因为新实现带来的破坏范围。但验证不能完全依赖线上业务流量(对业务有损),下文会提到重构版本在发布前的改善质量的方法。
九、改善质量
9.1 静态检查
静态检查工具 Sonar
-
解决循环依赖
-
解决 Cyclomatic Complexity
-
控制每个类的方法数
-
控制每个方法的行数
-
解决 warning(删除不使用的代码,也意味着减少重构的工作量)
9.2 自动化测试
自动化测试是重构最重要的基础,它能确保系统的行为不会因为重构而改变。
在重构开始优先保证测试的覆盖率,所以我们会在重构前优先使用集成测试来验证重构结果:
-
集成测试相比单元测试覆盖率会更大,随着覆盖率越高,覆盖剩余的测试成本也会越大,例如一些异常边界场景,用单元测试覆盖的成本会更低。
-
有些单元测试往往和老系统代码耦合,没有面向接口测试,或者是一些静态类的测试。重构代码意味着单元测试需要重写,在早期重构阶段优先用集成测试覆盖验证。
设计集成测试需要减少对其他系统(尤其是和外部第三方交互)的依赖,通过一些 Mock 工具提供稳定的测试数据,让测试结果更稳定可靠,比如使用缓存保证相同的请求能拿到相同的结果,这也能帮助后续新老版本的比对回归验证。
借助 CI 持续迭代,每一次变更提交会触发自动化测试回归验证,在早期开发阶段快速暴露问题。同时也鼓励研发多次提交变更,避免一次提交包含太多的功能点,减少测试不通过排查问题的复杂度。
9.3 链路比对
在整个链路上的服务完成一次里程碑后,会做链路上的端到端的自动化测试,比对新老系统结果,验证整条链路。
a. 线上版本链路入口在收到业务真实请求后,会把请求和响应推送到消息队列里。
b. 比对工具 SmartDiff 订阅消息队列,复制请求重构后的版本链路入口,拿到结果后和线上版本结果比对
c. SmartDiff 把比对结果写入 ES 日志,研发分析日志排查比对差异原因,修复问题。
十、可观察性
很多时候,软件架构容易被作为一个学术概念。即使是在一些成熟的研发团队,团队成员能够描述系统架构应该实现哪些重要的非业务功能需求(吞吐量、耗时、稳定性等等),但很难证明系统确实做到了这些。
尤其是系统经过长期多次迭代后,当前的架构实现是否还遵循原先的架构设计呢?如果无法验证,研发对架构也会失去信心。
所以我们需要对系统定义多个非功能性指标并将其可视化,线上持续地观察和确认是否和我们当初设计期望的指标一致。
比如读写缓存接口响应 P90 保持在 100ms 以内,假设后续为了提升缓存命中率,架构重构引入了多个缓存,P90 线 100ms 就可能会不达标。
指标可视化帮助我们快速发现新的设计带来的问题并做相应调整。这些非功能性指标就如同测试用例一样,快速发现问题、增强研发对架构重构的信心。
相关文章:

架构重构实践心得
一、前言 大多数的技术研发都对重构有所了解,而每个研发又都有自己的理解。从代码重构到架构重构,我参与了携程大型全链路重构项目,积累了一点经验心得,在此抛砖引玉和大家分享。 二、重构的定义 重构是指在不改变外部行为的情…...

【配置环境】Windows下 VS Code 远程连接虚拟机Ubuntu
一,环境 Windows 11 家庭中文版VMware Workstation 16 Pro (版本:16.1.2 build-17966106)ubuntu-22.04.2-desktop-amd64 二,关键步骤 Windows下安装OpenSSHVS Code安装Remote - SSH插件 三,详细步骤 在Ubun…...

【设计模式——学习笔记】23种设计模式——组合模式Composite(原理讲解+应用场景介绍+案例介绍+Java代码实现)
案例引入 学校院系展示 编写程序展示一个学校院系结构: 需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系 【传统方式】 将学院看做是学校的子类,系是学院的子类,小的组织继承大…...

vue3+Luckysheet实现表格的在线预览编辑(electron可用)
前言: 整理中 官方资料: 1、github 项目地址https://github.com/oy-paddy/luckysheet-vue-importAndExport/tree/master/https://github.com/oy-paddy/luckysheet-vue-importAndExport/tree/master/ 2、xlsx vue3 json数据导出excel_vue3导出excel_羊…...

前端html中让两个或者多个div在一行显示,用style给div加上css样式
文章目录 前言一、怎么让多个div在一行显示 前言 DIV是层叠样式表中的定位技术,全称DIVision,即为划分。有时可以称其为图层。DIV在编程中又叫做整除,即只得商的整数。 DIV元素是用来为HTML(标准通用标记语言下的一个应用&#x…...

【linux基础(二)】Linux基本指令(中)
💓博主CSDN主页:杭电码农-NEO💓 ⏩专栏分类:Linux从入门到开通⏪ 🚚代码仓库:NEO的学习日记🚚 🌹关注我🫵带你学更多操作系统知识 🔝🔝 Linux基本指令 1. 前言2. 删除…...

ceph集群---使用RBD块存储
文章目录 创建和删除池RBD设备的配置及使用RBD 块设备数据的导出和导入 块存储接口是一种主流的存储访问接口,也是常见的存储形态,比如服务器下的/dev/sdx都是块存储设备。你可以像使用磁盘一样来使用Ceph提供的块存储设备。 在创建块存储设备之前&#…...

2022.09.17【读书笔记】丨生物信息学与功能基因组学(第十三章 蛋白质结构预测 下)
目录 蛋白质结构预测三种方法同源建模(比较建模)穿线法从头预测(ab initio)基于假设推荐策略 精度与方法选择Alphafold2相关信息 蛋白质结构预测 三种方法 同源建模(比较建模) 建模4步骤 1.模板选择和确定折叠构象 通过blast或delta-blast搜索同源蛋白…...
ardupilot获取飞行员目标倾斜角度
目录 文章目录 目录摘要1. 4.0.7获取目标倾斜角度2. 4.3.7获取目标倾斜角度3.仿真摘要 本节主要记录ardupilot获取目标倾斜角度的两种实现方法,主要针对4.0.7和4.3.7进行对比。 1. 4.0.7获取目标倾斜角度 1.姿态模式下获取函数 //获取飞行员期望的倾斜角度get_pilot_desire…...

机器人制作开源方案 | 智能垃圾桶
1. 功能说明 智能垃圾桶是一种利用物联网技术和智能感知能力的智能设备,旨在提高垃圾分类和处理的效率。通常具备以下特点和功能: ① 智能感知:智能垃圾桶配备各种传感器,如压力传感器、红外线传感器等,可以实时感知…...
【手撕】list
系列文章目录 文章目录 系列文章目录前言list_node<T>(节点)_list_iterator<T, Ref, Ptr>(迭代器)成员变量构造函数运算符重载 List<T>(链表)成员变量构造函数析构函数区间构造函数拷贝构…...
QMQTT快速入门
文章目录 QMQTT快速入门环境搭建mosquitto 服务器和客户端配置服务器配置客户端配置模拟MQTT的发布订阅 QMQTT - Windows下的客户端项目代码展示遇到的问题 QMQTT快速入门 环境搭建 准备一台linux设备和一台windows设备虚拟机也是可以的;安装mosquitto ࿱…...

Dooring-Saas低代码技术详解
hello, 大家好, 我是徐小夕, 今天和大家分享一下基于 H5-Dooring零代码 开发的全新零代码搭建平台 Dooring-Saas 的技术架构和设计实现思路. 背景介绍 3年前我上线了第一版自研零代码引擎 H5-Dooring, 至今已迭代了 300 多个版本, 主要目的是快速且批量化的生产业务/营销过程中…...
Linux chmod
chmod 首先chmod 用于修改文件权限,使用命令 ll 查看文件列表,或者使用stat文件名 可以查看其相应的权限 显示的形式为例如 rwx r- - r-- ,即所有者拥有读写执行的权限 ,同组人员和其他人都只拥有读的权限 一般修改权限为三部分…...

java商城系统和php商城系统有什么差异?如何选择?
java商城系统和php商城系统是两种常见的电子商务平台,它们都具有一定的优势和劣势。那么,java商城系统和php商城系统又有哪些差异呢? 一、开发难度 Java商城系统和PHP商城系统在开发难度方面存在一定的差异。Java商城系统需要使用Java语言进…...
【HTML】常用实体字符(如 nbsp; 空格)
文章目录 显示结果描述实体名称实体编号空格 <小于号<<>大于号>>&和号&"引号" ’撇号' (IE不支持)¢分(cent)¢¢£镑(pound)£ £元&…...

华为eNSP通过VMnet8虚拟网卡,NAT转换访问互联网
防火墙上配置: 配置G1/0/1接口IP地址,其实G1/0/1的IP就是终端PC1的网关地址。 配置G1/0/0接口自动获取IP地址,从VMnet8中自动获取地址。 配置安全区域zone,把对应的接口加入到对应的zone中 配置安全策略,放通trust安全区域到u…...

手撕顺序表
> 作者简介:დ旧言~,目前大一,现在学习Java,c,c,Python等 > 座右铭:松树千年终是朽,槿花一日自为荣。 > 望小伙伴们点赞👍收藏✨加关注哟💕…...

Python实战项目——旅游数据分析(四)
由于有之前的项目,所以今天我们直接开始,不做需求分析,还不会需求分析的可以看我之前的文章。Python实战项目——用户消费行为数据分析(三) 导入库 import numpy as np import pandas as pd import matplotlib.pyplo…...
前端CryptoJS-AES加解密 对应php的AES-128-CBC加解密踩坑(java也相同加解密)
前端部分注意看填充是pkcs7 有个前提,要看前端有没有转成hex格式,如果没转,php那边就不需要调用特定函数转hex格式的 const keyStr 5hOwdHxpW0GOciqZ;const iv 0102030405060708;//加密function Encrypt(word) {let key CryptoJS.enc.Ut…...

C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...

springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文通过代码驱动的方式,系统讲解PyTorch核心概念和实战技巧,涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...