妥协技术方案和风险累加

news/2025/11/25 23:19:56/文章来源:https://www.cnblogs.com/lenomirei/p/19270420

初始化方案的风险引入

为了业务独立,将各个业务独立成模块,由应用依赖各个模块,当模块升级时,只需要重新接入该模块(接口不变的情况下,应用本身都不需要改动)是一种也比较常见的依赖接入方式。

此时有一个公用模块E,不仅应用依赖,而且其他模块多多少少也有依赖,自然是各自接入,当然为了不出问题,模块E需要做好被多次初始化依然不出错的准备。(模块E通过创建一个主消息循环线程内部同步处理接口请求)
在这种结构下,多个模块均可以按照自己使用的时机初始化模块E,确保模块E可以正常使用。各个依赖模块E的模块和应用本身也不用在意其他模块,这些模块之间和应用本身都是各自独立的,不用理会对方的存在。

但是我遇到了使用另一套技术方案的场景。这个场景埋下了一些风险,给日后的维护和开发带来了不少问题。

需求背景是有一些模块希望在模块E中以应用的身份工作(以相同的id标识),本身模块E不在乎这些,只需要传入id,对比相同就可以完成该需求,但是问题就是这些模块需要记录这个应用id(或者以代码方式或者以配置方式),可能存在多配置同步问题。

于是依赖方式被改为如下图所示,并做出了改动,给模块E增加特殊接口,有应用方注入应用id,其他模块调用时,直接使用注入的应用id,看似确实能解决问题,但是这时就已经引入了第一个风险,其他模块并不知道应用何时注入应用id,如果调用过早,那可能此次调用就会失败

为了解决上述的第一个风险,接下来做出了如下补丁,还是上图所示的依赖结构,选择一个模块\U作为总模块,由该模块进行各个模块的初始化,包括模块E,而应用不再对每个模块初始化,但是却保留了调用这些模块的功能,即只将所有的初始化业务从应用中拿出在模块U中进行,功能调用却留在了应用中这时就引入了第二个风险,因为应用开发和模块开发是相对独立(甚至不同部门),双方合作并非紧密相连,应用可能在不知情的情况下在总模块U进行初始化之前就使用了其他模块的功能(因为应用只进行了“总”模块U的初始化)

后续风险偶尔爆发,会出现模块E功能不完全,或者某些模块未初始化就被使用。

针对这个场景,思考了一些解决方案

  • 针对风险一引发的需求,实际上是因为“模块独立所以配置独立”的想法,和对“能读取到配置”的不信任。所以没有采用一份公用只读配置。最早的方案甚至是每个模块持有自己的配置文件,看似相互独立,减少了配置文件修改相互影响的风险,实则在避开这个风险的同时引入了更加隐性的SDK风险。而且在补丁出现时,奇怪的网状依赖其实已经和“独立”说再见了。
  • 针对风险二,主要问题出在,应用对模块D模块M有依赖,但是却对模块D模块M的初始化业务失控了,应用本身体量较大,有开发惯性,保留着用啥初始化啥的思路(各个模块内部的依赖,对应用来说不透明),导致应用可能在使用模块U进行初始化之前,就已经调用了D或M,导致功能调用失败。针对这种,应用方作为功能开发的主导者,开发应该保持一定的透明,不应该“瞒着”应用,做一些可能会影响应用调用的逻辑。要么“总”模块U就真正的起到总模块的作用,所有的调用都经过U,对U透明,U对应用透明。要么所有模块的接口都对应用透明,继续保持独立。拆分的不三不四,导致依赖关系混乱。

后续补丁,可能会引入第三个风险,提议对功能调用进行缓存,未初始化的调用在各个模块内部缓存,初始化后一并把缓存的调用执行,这个补丁不通用,可能要各个SDK都根据自己的业务做缓存逻辑(有的业务可能不好做缓存),尤其是在模块E有自己的缓存逻辑的场景下,两个缓存如何同步是个难题,这个风险本可以通过上述方案避免,如果强行再引入这个风险,出问题后的新补丁可能也是可预见的。

有时解决问题,要从源头解决,方案如果拍脑门,可能就会一步步堆积起来,风险越开发越多,“屎山”代码就是这么来的。因“不信任”而打补丁带来的问题,可能远比不信任的问题本身风险来的更高。

配置文件的风险引入

配置文件的滥用。上个章节提到了,有些开发可能会有“模块独立”的执念。所以在业务模块越来越多之后,配置文件的数量也到达了较难维护的数量。

问题一:重复的配置项,重复的配置项出现的多个配置文件中,本可以抽象到公共配置文件中,却依然配置多份。并且每个模块都有自己的解析和默认逻辑(业务模块开发相对独立)。

问题二:流水线工业化程度不高,多个配置文件并没有通过流水线配置实现(因为配置文件也独立,没有统一规则),导致要花费人力去处理配置文件的更新和维护,偶尔就会出现配置项打错,或者错用了另一个环境的配置文件的问题。

问题三:配置项混合。一些配置项是有默认值的,就算配置没有,模块也可以照常工作,但是有一些配置项是没有不行的,没有模块工作可能就会出问题。两种配置项被混合在一起放在配置文件中,结合问题二,当风险爆发出问题时,可能显性,可能隐性。

问题四:多个配置文件为了防止篡改,有加密和验签的逻辑,但是多个配置文件之间用的加密密钥如果不一致,维护成本就高,如果一致,又没有独立分开的意义,处于尴尬地位。

  • 抽象出共通只读配置文件,降低维护成本。
  • 采用共通解析逻辑,这点其实可以通过一个总模块或者专门独立开发的配置模块来实现。
  • 考虑到不同业务模块可能有自己的专属配置。可以采用配置文件合并的脚本来实现。提高工业化流水线在业务中的占比,减少人力成本,降低人力替换更新带来的风险。

模块违反接口设计原则

拿模块E来举例,作为一个可以动态加载的模块,看成动态库,接口设计时,要求对接口进行复杂的参数合规检查,接口的json str参数可能存在上千字节的调用时,这个复杂的参数合规校验会进行每个key-value的字符串合规校验。这无疑是耗时的,同时接口要求同步返回错误,对调用方来说,如果在调用方的主线程(假如是UI线程的话),是不能接受的。

但是当接到性能问题的投诉后,做出了如下补丁。

依旧保留复杂的校验,但是转移到模块E的主线程去做(模块E为了异步执行逻辑,持有一个主线程),这时风险就引入了,模块E的接口设计并没有提供任何回调,所以当参数检查失败时无法通知调用方,意味着这次调用就被吞掉了,但是调用方根本不知情,因为无法从接口的返回值感知。这种补丁对动态库的设计来说非常不规范。所以这次改动只能称得上是“补丁”,而不是性能优化。

  • 减少参数数量,其实上千字节的调用需要跟调用方协商进行裁剪
  • 校验规则简单化,同步的参数校验应该尽量简单不耗时,或许可以考虑拆分json,分散到各个参数中
  • 增加回调,通知调用方,对调用方透明。

关于Qt UI的不规范设计和使用

QWidgets一般可以用form的UI文件进行拖拽控件开发,也可以使用代码进行实时布局开发。

UI文件既支持拖拽,也支持使用代码进行布局调整,虽然可以使用代码,但是会引入问题,那就是所见并非所得,不利于多人协同开发,和维护,UI既会被form文件影响,也会被代码影响。出现UI问题或者是新的UI需求要进行修改时,容易出现混乱。

界面固定的布局可以使用UI文件进行开发,可以提高开发速度

但是要注意的是,尽量给每个控件改名,需要进行控件调整时可以精准定位到每个控件,我之前遇到的开发UI界面里面在使用默认的控件名称widget2 widget3,而且还混用UI文件布局和代码布局(因为UI变化多,需要代码实时调整),代码里充斥着widget2widget3,不把整个UI文件看一遍,甚至代码在操作哪个控件都不清楚。

如果UI设计上控件不多,但是变化大,可以考虑使用纯代码布局

尽量保持一个项目内使用一种布局方式,否则就需要详细的文档说明,否则将不利于维护和多人协作。

关于多进程业务分层设计

首先先来聊聊Chromium的多进程模型设计。在Chromium中,基本的业务都集中在主进程Browser进程,包括UI,网络请求等等,而子进程则各司其职,renderer进程处理负责网页渲染业务,GPU进程负责渲染和加速。这样做的好处是,避免了多进程中,每个进程都持有重复的模块,从而专注于自己的业务处理,方便维护,业务的集中方便迭代修改,有统一的模块入口。真正的业务模块化了。(但是Chromium这些业务都在自己的代码控制下,并非第三方库,一开始的结构就设计好了)

接下来说说我遇到的情况。在其他模块带来的风险引入章节中,我提到了模块U多进程相关的风险,原因是多进程业务混乱。背景是对U的不信任,希望能剥离U异常对应用进程的影响。但是最终的结构如下所示,修改侵入到了适配层(对某些模块进行逻辑特化),当子进程使用的模块需要依赖其他业务模块时,需要回到主进程调用,但是无法继续兜底,当模块依赖的业务模块也有依赖的模块时,无法一直侵入修改所有的模块都通过Special process dll这个来调用,不仅会让Special process dll臃肿不堪难以维护,而且依赖关系混乱,也不好区分模块是在哪个进程运行。

  • 分离所有业务,模块U只保留进程间通信,所有业务和依赖全部在进程U2里面执行,彻底剥离整个业务,保持依赖都在U2,也能完成剥离U异常对应用进程的影响的需求,该做法最简单,无需考虑过多场景
  • 业务拆分更加细致,只把有风险引发异常的业务剥离到U2进程,然后其他业务都通过进程间通信告知模块U来处理。可能无法解决多个依赖双方都持有的问题,但是可以让业务更加干净,减少对其他模块的影响(比如模块E),但是这要求被分离的业务尽量不要依赖需要工作在主进程的模块,独立模块并不清楚自己工作在什么进程,也不具备切换进程的能力,属于不受控业务。用webview举个例子的话,需要设计一套像cef那样的接口,虽然复杂度高,但是业务剥离的干净,只把webview部分的创建和显示拿到其他进程,业务都在主进程处理。但是也许webview也有自己的业务逻辑,没有办法像cef工具那样纯粹提供能力。在考虑他的业务的依赖的场景下,多进程的方案可能一开始就困难重重。

到这个时候,已经没有能快速解决改动小的方案,最终都引入了较为复杂的逻辑,需要额外的工作量,在时间紧任务重的场景下,可能都无法选择,但是如果这一切都是一开始可以用别的办法避免的呢?

假如背景是,U的异常是因为依赖的UI库版本过低有较为严重的兼容性问题,本可以通过升级版本解决呢(只需解决一些兼容性问题)?或者剥离U的异常模块,换用可以单独更新的第三方库来避开异常呢?这两个方案的开发量和维护的复杂程度都比上面的方案简单得多,也可能不会引发日后的问题导致需要修改其他模块进行配合。那为什么会走到这么困难的境地呢?

统一接口调用入口

本意是统一接口调用入口,这样新增接口的时候可以不用改适配层,让应用无感更新(虽然这种做法对应用来说有点不透明了),是希望减少维护成本,降低新接口开发时候的代码量,但是

  1. 测试应该提出异议,因为这样做,相当于新业务的影响范围扩大到了接口映射层,回归覆盖范围扩大
  2. C++本身不支持映射,依然要靠人力手动修改和维护映射层,较难仅通过配置文件就完成这项工作
  3. 映射是耗时的,直接的函数调用,和解析调用数据后再调用一定是耗时不同,如果将映射层再独立成dll,更是增加了耗时,原本一次接口调用经过一个dll,现在变成调用经过两个dll。

总结

也许解决方案因为具体业务场景的问题并不能真正的投入使用,但是这些经历让我知道了,风险一点点堆积起来后带来的问题。有时思考问题的本质,从源头解决问题比直接打补丁的方案能为以后带来更少的困难和烦恼。

不是不能妥协,而是妥协时选择风险更小,可测试的方案,和当发现不应该再继续妥协时,尽快调转车头,采用合理的方案,是对产品开发还有测试都更加负责的做法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/976576.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

目标跟踪(BOT-SORT)yolo默认的跟踪算法 - MKT

目标跟踪(BOT-SORT)yolo默认的跟踪算法1. 算法背景多目标跟踪问题的核心是对视频帧中的目标进行准确检测,并在连续帧中维持目标身份的一致性。传统方法可能在快速移动的目标、遮挡以及检测精度不高时表现较差。BOT-…

Day2 Scrum冲刺博客

Day2 Scrum冲刺博客 1. 团队会议 todo补充会议照片 1)昨天已完成的工作前端前端项目初始化完成,实现了路由、菜单配置和登录页面 "关于我们"弹窗初步完成后端完成了爬虫程序的初步实践 产生了示例数据,用…

第36天(中等题 数据结构)

打卡第三十六天 2道中等题题目:思路:先相加所有数,然后依次减去一个数,看剩余的数是不是偶数,是就/2,看/2得到的数有没有在剩余的数里,在就代表减去的数就是异常值,然后用ans存储这个异常值,找出所有异常值并…

Save Time Money: KEYDIY KD NB22-4 Universal 4-Button Remote (5pcs/lot) for Euro/American Cars

The Hidden Costs of Key Fob Failures: Why Mechanics and Car Owners Need a Reliable Universal Solution In the world of automotive repair, few issues frustrate both mechanics and car owners more than bro…

翻转课堂 1st

> 开场相信很多同学都在行指针和列指针这堂课上听懵了吧,希望这篇随笔会对你有所帮助,德尔菲神庙是古希腊世界中最nb的一所神庙,古希腊人认为它是”世界的脐带“,它是供奉太阳神阿波罗的主要圣地(阿波罗是光明、…

拍卖监控与奖励机制优化策略

本文探讨了通过延迟检查和奖励机制优化拍卖设计的方法,该机制能在监控买家真实价值后给予诚实报价奖励,从而提高拍卖收益并减少信息租金,特别适用于数字商品等可监控场景。监控和奖励诚实出价以提高拍卖收益 在论文…

实验三类和对象

实验任务一: 问题1:答:是组合关系,Button对象是WIndow对象的组成部分。 问题2:答:(1)优点:用户可以调用函数判断窗口中是否有某按钮,帮助用户了解窗口。缺点:接口变大了,用户可能会使用has_button接口自己…

java---gradle的使用总结

最近做java项目,使用Gradle进行项目构建,从最开始的懵,到现在还是会用了,简单总结下: 1、Gradle是国外的,下载安装比较慢,可以先下载Gradle进行本地安装打开腾讯云Gradle镜像页面:https://mirrors.cloud.tence…

二叉树 节点的个数关系

度数为2的节点a个度数为1的节点b个度数为0的节点c个2a+b=a+b+c-1a=c-1也就是说度数为2的节点的个数=度数为0的节点个数减去1 n2=n0-1 n0=n2+1

整合 MyBatis 代码生成器插件

1. mybatis-generator 是什么?mybatis-generator-maven-plugin 是一个 Maven 插件,用于生成 MyBatis 的代码(如 Mapper 接口、Mapper XML 文件等),官方文档地址: https://mybatis.org/generator/ 。它可以根据数据…

java---Idea

开发java项目使用idea: 下载地址:https://www.jetbrains.com/idea/蓝色的是收费版本,功能很全,有30天试用: 黑色的功能少,免费使用。 IDEA项目结构介绍:然后打开软件:第一步:创建项目 可以选择创建一个空项目:…

java---基础

最近学了学java,做一些整理和梳理:菜鸟笔记https://www.runoob.com/java/java-tutorial.html1、写一个基本的方法: 右键点击【src】选择【新建】【类】勾选【创建一个主方法】然后点击创建即可。 创建一个方法:pub…

内存马研判

内存马如何进行研判? 内存马本身无文件、在内存中运行,无文件的web后门,成功标志是攻击者能够通过特制的HTTP请求与它进行交互,并成功执行命令,获取回显或建立更高级的C2信道。 内存马大致分为:明文/弱加密内存马…

实用指南:__工艺数据管理的范式转变:金仓数据库替代MongoDB实操实践__

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

KEYDIY KD NB27-3 3-Button Universal Flip Remote - 5pcs for Audi Universal Fit

Struggling to Find Reliable Audi Remotes? Meet the KEYDIY KD NB27-3 Universal Flip Remote The Problem: Affordable, Universal Audi Remotes Are Hard to Come By For European and American automotive repai…

NOIP 集训 day3 图论1

MST 相关 知识点:Kruskal Prim Kruskal 重构树洛谷 P4768 [NOI2018] 归程 经典题。先对海拔建出 kruskal 重构树,然后从起点开始通过海拔 \(>p\) 的点可达的所有点就是一个 \(v\) 的一个祖先(深度最小的满足海拔…

计算机网络—TCP和UDP

TCP 和 UDP 有什么区别? TCP:提供了可靠、面向连接的传输,适用于需要数据完整性和顺序的场景 UDP:提供了更轻量、面向报文的传输,适用于实时性要求高的场景 区别总结:对比项 TCP UDP连接方式 面向连接(三次握手…

Universal 3-Button Flip Remote Key for Hyundai: KEYDIY KD NB25-3 (5pcs/lot)

The Hyundai Key Replacement Solution: KEYDIY KD NB25-3 PCF Universal Flip Remote Key Problem: Frustration with Hyundai Key Replacements For European and American Hyundai owners and repair shops, tradit…

接口自动化平台用例执行引擎 — ApiTestEngine

前言 ApiTestEngine 主要是为了快速进行接口自动化平台开发,基于 unittest 设计的接口用例执行引擎,其实之前开发的接口自动化框架 apin 也可以作为测试平台的用例执行引擎。但是 apin 最初设计的初衷是基于 JSON 或…

NOIp 知识点复习

1. Floyd //exam: B3647 【模板】Floyd #include <iostream> #include <cstring> #include <cstdio> #define int long long using namespace std; const int MAXN = 105; int f[MAXN][MAXN]; int n…