重构代码的一些想法

· 浏览次数 : 40

小编点评

以下是根据您提供的内容整理出的重构服务的建议: ### 重构服务的关键点 1. **明确服务核心功能执行时机**: - 外部驱动:如任务队列、API 请求、内部定时器等。 - 内部逻辑:如定时任务触发、内部状态机切换等。 2. **模块化设计**: - 核心功能执行内容应独立,便于单元测试。 - 非核心功能应与核心功能解耦,减少依赖。 3. **构建优化**: - 使用依赖少的第三方库。 - 尽量静态链接,或手动实现简单依赖。 - CI 构建应与手动构建并存,以适应不同规模的项目。 4. **持续集成与持续部署(CI/CD)**: - 使用 Dockerfile 保存构建过程。 - 支持多种部署方式,如 systemd、docker 等。 5. **代码规范与质量控制**: - 公有函数中定义代码规范。 - 私有函数实现功能,不涉及资源同步。 - 接口定义应清晰,以便外部逻辑调用。 6. **单元测试与覆盖率**: - 确保核心功能 100% 覆盖。 - 非核心功能覆盖到关键路径。 7. **日志与监控**: - 日志分文件,按模块区分。 - 使用不同日志等级(如 INFO, WARNING, ERROR, FATAL)。 - 热点路径的日志使用更高等级(如 TRACE)。 8. **性能优化**: - 优先实现功能,功能收敛后进行性能分析。 - 使用常见优化技巧,如 C++ 中的 thread_local,Go 中的 sync.Pool。 9. **Go 语言特性**: - 函数参数尽量简洁,使用命名空间作为关键参数。 - 超时时间等可选参数可设计成 option。 10. **部署与运维**: - 支持 systemd 或 docker 等容器化部署。 - 确保服务在高可用环境下稳定运行。 ### 实践效果 - 提高代码质量:通过重构,提升了代码的可读性和可维护性。 - 增强单元测试覆盖率:有助于及早发现问题,减少 bug 发生。 - 加强稳定性:模块化设计和解耦减少了潜在的故障传播。 - 提升开发效率:自动化构建和测试流程简化了开发周期。 - 更好的扩展性:服务易于扩展新功能,同时保持现有功能的稳定性。 重构是一个持续的过程,需要定期评估和调整以适应项目的发展。

正文

重构服务的一些想法

最近对一个服务进行了大重构(不仅仅是代码的重构,还有构建、部署和单元测试等),之前很多实践的经验都应用上了,实践下来效果比较满意。

模块设计

需要明确服务的核心功能

  1. 执行时机(被谁驱动)
  2. 执行内容
  3. 和非核心功能的关系

从模块话的角度看,这三个部分其实都可以独立实现,这样更利于单元测试用例的编写,扎实的单元测试覆盖率大大提高对稳定性的信心。

执行时机一般都是外部驱动,如收到任务、请求甚至内部定时器驱动。

核心功能的执行内容一般不多,但是实现需要严谨,不要轻易放过错误。因为被非核心功能依赖,这部分的稳定可以减少非核心功能不必要的防御性代码,

和非核心功能的关系参考内核的各种 HOOK 实现,核心功能提供 HOOK 点,非核心功能在这些 HOOK 点上被执行,这样各模块就被解藕了。

构建

尽量选择依赖少的三方库,除非没得选(一般是性能要求)。

构建尽量静态链接,如果存在三方依赖但手动实现简单的话,那么可以考虑手动进行实现。

CI 构建的方式应该和手动构建方式并存(前者调用后者),在规模不是非常大的情况下 CI 速度是没有本地编译的速度快的,这样对开发环境的更新可以效率高一些。

三方工具的构建应该持久化构建过程,比如使用 Dockerfile 来保存构建过程,这样有信创这种需求过来一般稍加修改即可。

C++ 尽量使用 cmake,不用写 makefile及可以生成 compile_commands.json。

代码规范

锁只出现在公有函数中,私有函数只实现功能,不考虑资源同步的情况(如果必须控制小粒度另说)。

业务代码优先定义好接口,由外部逻辑调用接口进行驱动,业务直接实现接口即可,接口定义好坏的一个判断参考:后续增加业务无需修改驱动逻辑,新增接口实现业务逻辑就被集成进去。

代码尽量模块化,模块化清晰一个判断参考:能够直接通过单元测试启动这个模块。

函数实现显式化阻塞逻辑,然后交给外部调用决定是否需要启动线程/协程将其放至后台。

Go 的函数参数只放必要参数,比如创建一个命名空间,那么命名空间名称就是关键的参数,超时时间这种就可以设计成 option 实现。

性能

以 profile 为准,不重写标准库函数,一般性能热点不会出现在这些地方,况且标准库也是会进化的(如早期 nginx 优化的 memcpy 早已比不上现在 glibc 的实现了)

优先实现功能,功能收敛后进行 profile,根据热点进行优化,在实现的时候可以使用一些常见的优化,如 C++ 中的 thread_local,Go 中的 sync.Pool

单元测试

核心功能尽量 100% 覆盖

非核心功能覆盖到关键路径

日志

日志分文件,不同的模块可以通过不同的日志文件进行区分

日志分等级

  • 在服务内部没有状态改变的情况,默认等级为 INFO 且日志不会增加,定时器的逻辑不能使用 INFO
  • 热点路径的日志只使用 TRACE 等级,并且日志代码语句放在日志等级判断逻辑里面,避免不必要的 CPU/Memory 消耗。

日志可动态调整,应对出现问题又保持现场的场景(生产环境)

部署

支持 systemd/docker 的方式

与重构代码的一些想法相似的内容:

重构代码的一些想法

重构代码的一些想法 模块设计 需要明确服务的核心功能 执行时机(被谁驱动) 执行内容 和非核心功能的关系 从模块话的角度看,这三个部分其实都可以独立实现,这样更利于单元测试用例的编写,扎实的单元测试覆盖率大大提高对稳定性的信心。 执行时机一般都是外部驱动,如收到任务、请求甚至内部定时器驱动。 核心功

趣图|代码重构前vs重构后

很多程序员对自己写的代码平时很随心所欲,但当有一天让他维护他人的代码,他就会抓狂,很容易激发他体内重构的瘾。(大多数程序员审阅完别人代码后,先会忍不住吐槽一番,然后会忍不住想重构一把,😂)

要想后期修改少,代码重构要趁早

摘要:在敏捷中,让设计简单化,必须让设计从简单开始,然后变得成熟。要做到这一点,重构是唯一的出路。 本文分享自华为云社区《敏捷技术实践之重构》,作者:华为云PaaS服务小智 。 前言 极限编程(XP)的创始人之一Ron Jeffries说道:“在敏捷中,让设计简单化,必须让设计从简单开始,然后变得成

ASP.NET 6启动时自动创建MongoDB索引

最近,在使用MongoDB时,碰到这样的一个需求:针对某个Collection手动在开发环境创建了索引,但在测试环境和生产环境不想再手动操作了,于是就想着干脆通过代码的方式在ASP.NET 6应用启动时自动创建,如果是重复创建则直接跳过。

想要做好代码质量,如何破局?

作者:苗现方 想要做好代码质量,我们不得不提什么是代码质量?本文中讨论的代码质量一般是指代码的风格、重复率和复杂度等,代码是技术团队的价值产物,是宝贵的财富,同样代码质量的好坏可以直接体现出团队的重视程度和技术管理水平。 代码质量的下降是内在原因,通常会恶性循环,主要表现出以下两个特性: 感染性:坏

三星电视无法下载《条款和条件、隐私政策》的问题 (消息代码: 0-1)

因为想看一部影片引发的对三星电视 app 功能的探索,没有重刷固件、没有远程协助,解决问题的方案居然是再简单不过的手机热点,将解决过程记录下来希望能帮到更多的三星中国用户,从一个侧面见证了三星产品在中国大陆退潮的缩影…

C++多态与虚拟:运算符重载(Operator Overloading)

运算符重载:与function overloading异曲同工的是,C++提供所谓的Operator overloading。所谓operators是像 +(加)-(減)*(乘)/(除)>>(位右移)<<(位左移)之类的符号,代表一种动作。 面对operators,我们应该把他想像是一种函数,只不过

服务器重置实例后的部署工作

参考:https://www.cnblogs.com/warrenwt/p/18215341(docker安装redis) 因为服务器前段时间一直由木马,而且还被挖过矿,想直接重装下系统吧,顺便捋一下整个服务器需要各项配置,以下是我的整理清单 使用nginx做反向代理,nginx是直接yum安装的

[转帖]JAVA 应用提速之 Large pages「译」

https://zhuanlan.zhihu.com/p/533305428 一、前言 我最近花了很多时间在 JVM 的内存预留代码上。它开始是因为我们得到了外部贡献,以支持在 Linux 上使用多个大小的 large page。为了以一种好的方式做到这一点,必须首先重构一些其他的东西。在沿着 内存

【VS Code 与 Qt6】QAction 类的一些事

QAction 类表示用户命令的一种抽象,包括命令文本、图标、命令触发后要执行的代码。菜单、工具栏按钮往往存在相同的功能,将这些命令独立抽出来,放到 QAction 以象上,可避免编写重复的代码。比如“文件”菜单下有“保存”命令,工具栏上也会有“保存”按钮。因此,创建一个表示“保存”的 QActio