V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  kuanat  ›  全部回复第 3 页 / 共 19 页
回复总数  380
1  2  3  4  5  6  7  8  9  10 ... 19  
2025 年 10 月 17 日
回复了 Hahaoh 创建的主题 Linux Linux 桌面显示效果始终调不好
如果你需要 Windows 次像素渲染的效果,可以通过 fontconfig 的配置手动开启,这个技术的专利已经过期很多年了,当前的发行版都有相关的实现,不需要额外补丁。

不过即便开启次像素渲染,你在 Linux 的感受和 Windows 也会有比较大差别。因为 Windows 的默认渲染策略是强 hinting ,在矢量图形光栅化的时候,尽可能对齐像素网格,牺牲字体还原性换取可读性。Linux 目前主流发行版的默认设定都是弱 hinting 策略。

在当前 Gnome/KDE 都转向了 wayland 合成器,次像素渲染是个会起到相反效果的技术,所以各大发行版都默认使用 grayscale 算法而不是次像素实现抗锯齿。这是因为次像素算法依赖显示器像素排布,一方面显示器的像素排布很多都不是常规 rgb/vrgb 了,另一方面这个技术最多只能在一个方向上提高分辨率。

最大的问题还是高 dpi 显示器上的缩放,次像素算法的前提假设是它输出的位图会 1:1 映射到物理像素上,然而这一点在有缩放的时候并不成立。强 hinting 策略在高 dpi 有缩放的情况下,也不是一个好策略。这里说起来比较复杂,可以简单理解为先放大再修正,比起先修正再放大,前者能够保留更多信息,最终渲染效果也会更好。

现在 Windows 新的 UI 框架编写的应用,在有缩放的情况下也会默认禁用次像素渲染转而使用灰度渲染。

以我个人的技术观点和常年 Linux 使用体验来说,我认为 Linux 的字体渲染是比 Win/macOS 都要更优秀的。
2025 年 10 月 16 日
回复了 facebook47 创建的主题 Linux 突发奇想, Linux 软件生态问题
Linux 开发者大概是软件开发领域中,最能用爱发电的一群人了。

我这里引用 GNU 宣言中的一段文字,感受一下其中蕴含的的精神:As a result, a user who needs changes in the system will always be free to make them himself, or hire any available programmer or company to make them for him. 中文译版是:其结果是,如果有用户需要更改系统,他总可以自由地自己修改或雇用其他程序员或公司来改。用户就用不再祈求拥有源代码的那一家公司或那一个程序员来帮他修改,没有人再处于独断的地位。

这份宣言写于 1985 年,所以单纯引用一句话可能会有断章取义之嫌,建议还是看完整原文比较好。脚注中特意提到了,自由和免费虽然都是同一个词,但在宣言中取的是前者。

Linux 的软件生态是由成千上万的用爱发电开发者无私奉献支撑起来的,绝大多数开发者的初衷是,我开发这个软件解决我遇到的问题,它有可能帮助到其他人,所以我把软件开源出来。因为大家都清楚使用这些软件的,极大概率都是和他们一样的 Linux 用户或者开发者,所以几乎没人想拿这些软件来收费。

抱怨 Linux 应用不够好的一般都是以下两种情况:

- Linux 上没有(其他平台上有的) XXXX 软件,生态很烂
- Linux 上 XXXX 软件不是按照我的需求和使用习惯来做的,很垃圾不好用

软件的开发者不会逼用户一定要用它不喜欢的软件,反过来作为用户也不应当指责特定软件的开发者。

设想一下如果换你作为开发者,你为了满足自己的需求开发了一款软件,这款软件有 A/B 两种实现方式,你觉得 A 对你更合适就实现了 A 方式的版本,然后你将它开源出来。这时有人提出来,就不能用 B 实现吗?你会是什么反应?按照 GNU 的精神来说,你完全不需要理会。有人想要 B 那就 fork 之后去做 B 实现好了,不管他是自己动手还是雇人开发。再换个情形,如果他提出的付费赞助请你开发 B 实现呢?你可以选择接受也可以不接受。

举这个例子是想说,Linux 生态看上去的分裂,实际上正是自由精神的体现。无论开发者和使用者都是平等的,不满意就另起炉灶重做一个,没有重做的能力还可以雇人来做。任何人都拥有选择的权利。即便你只是商业软件的付费用户,很可能也是因为你拥有的潜在选择权帮你节省了大量金钱,而不用被商业软件垄断收割。



PS

再扯远一点,关于 Wayland/X11 的话题。

不少人对于 Wayland 的看法就如同对 Linux 软件一样,它让我的 XXXX 软件不能工作了,所以很垃圾。首先没人逼你一定要用 Wayland 。发行版决定不再打包对 X11 的支持了,请去喷发行版。实在不行可以自己动手。

Wayland 是个协议,甚至这些协议还有很多无法获得多数认同的废案,而协议的实现又有非常多,每个实现也可以独立决定自己要实现哪些部分,不要实现哪些部分。那你可能会问,为什么不搞个标准出来?

这又回到之前的自由软件精神的问题上了。开源软件领域不同于现实,现实里面商业结盟、资本推动都可以先立标准然后躺着收费,开源软件全看自由意志,谁的实现更好,用户就用谁的。砸钱也不是不行,比如红帽给 systemd 不知道投了多少钱了,选择 SysVinit/OpenRc/... 的依然很多。没有人非要听命于某个标准或者某个组织来行事,当然完全脱离标准别人不用也很正常,比如主流几个 Mutter/KWin/wlroots 对于五六种不同的输入法协议的实现支持也不一样,反正用户有得选。

说到底,标准是看谁实现得更好,更受欢迎决定的,不服还可以继续分裂( fork )。最重要的一点是,有的选才是大前提,自由软件精神就是用分裂保证了选择权。没得选才是最可怕的事情。
那个 chatgpt 看起来就是基于一点事实,车轱辘话一直重复,说不到重点上。不清楚是不是提问不够清晰的原因。我这里做个简单总结。

1.
关于 GNU/Linux 的冠名,算是个历史遗留问题。实际上也有 Linux 发行版完全在用户空间也完全不使用 glibc/gcc/make/bash/coreutils 等 GNU 工具的,比如常见的 Alpine 和 Android ,但它们都能被称作 Linux 发行版,Linux 对此没有异议,同时 GNU 也是不能一刀切要求所有 Linux 发行版都增加 GNU 冠名的。所以 GNU 的要求是 Linux 内核增加 GNU ,变成“GNU/Linux Kernel”,但大家都习惯把使用 Linux 内核的发行版称作 Linux 系统了,让大家把 Linux 系统称作 GNU/Linux 系统是个不现实的事情。

到了 2020 年前后 Linux 5.4 LTS 发布之后,就没有争论的必要了。因为从这个节点开始,Linux 已经可以由非 GNU 的 Clang/LLVM 工具链完成构建,而且是生产级别可用的。这个过渡大概用了近十年时间,最终还是靠 Google 大量投入才完成的。当然 Google 本身也不是为了给 Linux 正名,而是因为 Android 开发有相应的需求。

在此之前,GNU 确实有理由要求 Linux 内核增加 GNU 的冠名,因为理论上 Linux 内核本身也是要依赖 GNU 工具链才能完成编译从而实现其功能。之后 GNU 最多要求各大发行版在使用 Linux 的描述时增加 GNU 的冠名。但由于 GNU 开发的工具使用的 GPLv2 和 GPLv3 都没有在授权中明确要求这一点,所以这个冠名要求只能停留在道德层面而非法律层面。

2.
我个人是支持 GNU 精神的,所以我会优先使用 GPLv3+ 授权。但从务实的角度上说,我更支持 Linux 的做法,也就是商业上更宽松的 GPLv2 授权。所谓的 GNU 和 Linux 哲学之争,更多是商业化的意识形态之争,体现在纸面上就是 GPLv2 和 GPLv3+ 的区别。

以目前 Linux 的代码贡献来源来看,即便去除占比一半以上的厂家驱动部分,剩余代码中来自公司雇员的部分大概在 80% 到 85%,其余部分来自个人贡献者。如果没有商业化的参与,Linux 很难发展成今天的样子。而商业化的企业愿意参与到 Linux 开发中并贡献代码,能够接受的底线就是 GPLv2 了。

3.
GPLv2 和 GPLv3 的区别主要是两点:

一是 GPLv3+ 增加了 Anti-Tivoization 条款,这个条款要求使用了 GPLv3 代码的硬件厂商,不仅要将衍生代码开源,还要提供安装运行衍生代码所需要的“安装信息”。而 Linux 内核使用的 GPLv2 并不强制要求这一点。假如 Linux 内核也使用 GPLv3 的话,那么目前市面上所有手机都要强制提供 bootloader 解锁了。这里更准确的说法是,GPLv2 认可开源 loader 加载闭源 firmware 的形式,其中 firmware 部分不被视作“衍生作品”,而 GPLv3 认为闭源 firmware 属于“安装信息”的一部分。

二是 GPLv3+ 增加了反 DRM 条款,这个条款的意思是代码中的 DRM 限制不被视作法律意义上的保护措施。衍生代码可以去除相关功能再次分发,相当于剥夺了厂家使用法律武器限制那些想要绕过限制的用户的权利。

基本上商业公司是不会有为 GPLv3+ 贡献代码的意愿的,反倒是相对宽松的 GPLv2 达到了平衡,一方面厂家确实需要像 Linux 内核这样的基础设施代码,另一方面贡献代码并不会对商业行为造成事实上的影响。

4.
GNU 和 Linux 这二三十年的争论,实际上就说明了一个事情,开源并不能保证软件的生命力,软件维护需要长期大量的人力投入,用爱发电是比不过商业投入的。

现在的大方向主要是探索第三条路,通过 Linux 基金会、Apache 软件基金会这种组织,向开源软件提供非商业资金支持的形式来实现长期维护。
2025 年 10 月 14 日
回复了 karashoukpan 创建的主题 程序员 Java & Go 设计模式实现
设计模式是一种经验总结,属于应对特定问题的一般策略。实际上就算你一点设计模式不懂,一样能写出能用的代码,只是可能比较复杂容易出错,或者看起来没那么简洁易懂。当你反复遇到相同的问题时,很有可能自己重新发现并总结出某种设计模式。或者说你可能早就在用各种设计模式了,只是没有总结成特定的名词而已。

但所谓的设计模式毕竟是经验总结啊,也就是说它是有适用范围的。不能因为手里拿着设计模式的锤子,就看什么都是钉子。不同的编程语言差别很大,解决相同问题的经验是完全不同的,甚至在一种语言中的常见问题在另一种语言中甚至不存在。

举个例子,就拿参数传递来说,Java 因为不支持命名参数但支持重载,所以总结出了 builder 模式,而 Go 因为没有重载和构造函数,所以形成了函数式 Options 的习惯写法。非要 Go 用 builder 模式或者 Java 写函数式 Options 不是不行,只是没必要。

再比如楼上提到的装饰器模式,Java/C++ 这种常用是因为它们的 OOP 都是基于继承的,在不修改原始类的情况下动态增加新功能是比较复杂的,装饰器这个写法可以避免继承爆炸。换到 Go 里面正经人谁用装饰器啊? Go 的 OOP 是基于组合的,简简单单嵌入 struct 不就可以了。

当然面试八股确实喜欢考这些东西,但属实没必要把设计模式当作编程学习的目标。
2025 年 10 月 14 日
回复了 shinonome 创建的主题 Go 编程语言 json 当数据库有什么问题吗
既然是 go 板块,大概率是想避开 sqlite CGO 依赖吧。

目前无需 CGO 的实现就两个思路,要么 modernc.org/sqlite 这种自动化 c 代码转 go 代码的方案,要么就是 modernc.org/sqlite 这种外挂一个中间层通过 ipc 或者什么方式来使用。因为这俩方案在我看来都不理想,所以要么不用,用就老老实实 CGO ,无非就是写个多平台的构建脚本。

一般来说我不推荐用任何文件作为 naive 数据库,因为你需要实现最起码的并发控制(线程安全)、原子写入与(内存和持久化副本)一致性。当然这个东西没那么难,vibe 一下 100 行可能就够用了。不推荐的原因主要还是维护起来麻烦,因为用着用着就发现,这种方案不是很方便存储结构体,需要注意序列化反序列化的问题,然后增删改查也要重新造轮子。日后换方案也是麻烦事。

我比较常用的方案是 etcd-io/bbolt 这个 kv 数据库,该有的功能都有,有个自带的命令行程序,管理导出都方便,不用担心换技术栈。如果不是很依赖 sql 范式对数据做校验,推荐直接把逻辑交给应用代码,数据库就单纯做存储了。这样还有个好处,就是也用不到 orm ,大多数情况下 orm 的表达能力比代码差远了。用 orm 就很容易对调整数据库 schema 的事情产生抵触,毕竟改一次几乎所有语句都会要跟着改,用 kv 数据库一般就没这顾虑。
@oblax #31

x390 不知道你说的哪一代,无论是整机功耗还有电池容量,和现在的机器都没法比了。

只有比较新的平台( 7nm 之后)才能做到我说的效果,最起码进入待机之后要在 0.5w 以内,这样才能做到 60Wh 常规容量还能待机比较长。之前 14/22 工艺的平台,cpu 可能待机不会差太多,但是主板等外围设备进入低功耗状态之后耗电太高了。这里主要是强调平台要新,比如 intel 12/13 代移动版 cpu 能效比很差,但不妨碍这些平台的笔记本盒盖待机依然能有很长时间。
2025 年 9 月 30 日
回复了 wojiugaiming 创建的主题 程序员 分享一下新发现的 go 语言 GUI 框架 cogentcore
是不是该用跨平台框架来写 ui 是个哲学问题,即便你的立场为是,还是要继续抉择,要每个平台都用相同的(非原生风格)自定义 ui ,还是要每个平台上用原生的 ui 。基本上后者都是走浏览器了,因为没人或者团队能承担得起这样的维护成本。

在跨平台、自绘 ui 这个场景中,本质上要看在各个平台上底层用的什么 api ,楼上有人提到 go 的 goroutine 与系统线程 pinning 相关的硬伤,这个确实无解,因为现在的操作系统都是同样的 ui 线程逻辑。

这种项目最大的困难是长期稳定地维护下去。而且通常来说好看、小巧和兼容性强是不可兼得的。以 win 为例,想要小就必然对接 win32 api ,用这个写现代界面,光一个 dpi 自适应就头大。到了 linux 又会面临是 gtk/qt 还是协议层造轮子,qt 因为授权大部分时间会被排除在外,如果是直接 wayland 协议开始写,且不说什么消息循环都要手动,就天天跟着上游更新都是很大的工程量。

当然如果写的项目不需要长期维护,这类 ui 框架还是很有市场的。
2025 年 9 月 30 日
回复了 woodslee 创建的主题 计算机 需要买一个 16 寸国产笔记本,听听各位的建议
64g 内存是个不太好达成的条件,多数都要上顶配或者定制款,加上 16 寸也是个小众品类,能选的不多。

我是 16 寸 linux 多年的用户了,按照条件来匹配的话,thinkbook 16+ 是符合预算的,插槽内存,而且我确认主流新版本 linux 是开箱即用的,包括指纹这种设备都正常。发售的时候 libinput 没更新 quirks 会用不了触摸板,现在没问题了。

另外我说几个不太容易注意的点,现在大多数设计上看上去比较好的笔记本,都为了减重或者造型选择了 oled 屏幕。作为开发者来说,我更推荐 lcd 的型号,主要是 oled 的防烧屏功能 linux 基本上不可用。

另外你是 mac 用户过来的,肯定习惯了高 dpi 屏幕。我是更建议分辨率在 3200x2000 即整数缩放 1600x1000 以上的,因为 linux 生态的问题,分数缩放并没有那么理想。2880x1800 也可以接受,但 1440 水平有效宽度很多时候对于开发来说不够用,如果是前端这个问题会更明显。

之前我在别的帖子里回复过如何用 live usb 安装盘配合 来测试 linux 兼容性 https://v2ex.com/t/1128164
这个话题站里出现过挺多次了,我之前大概说过结论,越公版设计越稳定,intel 集显型号比较容易达成。

Windows 是不是有什么负优化我不知道,至少我个人用 linux 来说,从 2011 年之后我手上的笔记本就是随用随开,modern standby 或者叫 s0ix 待机,满电可以稳定一周以上,从来没有过放包里异常发热的情况。

这个主要是看硬件平台,前些年 intel evo 认证的几乎都可以,现在这个东西也没了。
2025 年 9 月 26 日
回复了 leokun 创建的主题 浏览器 如何知道网络请求是从浏览器发出的
这个话题属于懂行的不愿意讲(毕竟多数都会涉及黑灰产),而不懂的基本说不到重点的那种。我就简单总结下算是抛砖引玉了。

浏览器从来都不是可信环境,理论上没有任何办法可以稳定 100% 准确区分真人和机器。

对抗爬虫或者 bot 的基本思路就是提高攻击成本。比如登录之后才能看的,就有帐号成本,限制访问频率的,就有 ip 成本,甚至 cf 五秒盾也可以理解为采集时间成本。

想要提高攻击者的成本,那防御方也要付出代价,比如设想个极端场景,防御方要求所有请求都过一遍 recaptcha ,那防御方确实提高了攻击方的打码成本,但自己也付出了带宽成本,以及造成不便损失正常客户的成本。所以防御方更希望的是,有纯软件的方案,只付出开发成本和少量的运营成本,就能大幅提高攻击者成本的方法。于是就有了各种检测技术。

我这里随便列举一些常见的技术,以及攻击方的应对策略:

1. tls 指纹检测

因为浏览器和常见 python/go 等编程语言的底层 tls 库是不一样的,通过在流量入口做 ssl offloading 的时候,顺便检测一下请求中的 kex (密钥交互)配置,就能起到很好的筛选作用。

应对方式也比较简单,替换 tls 库或者伪装成特定的指纹配置即可。

2. 额外校验字段

同样是针对看请求直接构造接口数据的。在常规业务字段之外增加校验字段,一般由 js 代码执行后产生。

这种可以通过 cdp 控制浏览器或者跑无头等方式绕过。

3. 浏览器环境检测

基本上是前一种方法的增强版。既然攻击者能用真浏览器来伪装,那就检测那些不合理的参数,比如窗口 viewport 大小,一些特定的全局对象等等。到了这一步,基本上标配都是 js 混淆了。

对于水平不高的检测,有经验的攻击者大部分能根据调用栈定位到关键函数方法,绕开检测逻辑直接生成校验字段。

4. js vmp 混淆

基本上这就是最后的防线。把前面各种检测技术打包起来放到 js 中,然后用 js 代码写个虚拟机,再把原始的代码编译成虚拟机指令。这个对抗手段是针对人的,就是拉高对攻击者的技术门槛要求,逆向 vmp 类混淆是要比前面都难的。

从攻击者的角度来看,硬怼 vmp 还原 ast 指令也不是不行,就是累,而且没办法保证这次逆向出来了能用多久。毕竟防守方的策略是,换个混淆参数就是新虚拟机了。

所以多数情况下都是把 js 代码完整扒出来,把它当黑盒来调用。因为外部 js 环境和浏览器不一样,缺少浏览器的很多对象,所以有个专门的说法叫“补环境”,让 js 代码能正常运行。想要知道 js 代码都检测了哪些环境信息,又有一些插桩、自吐的应对策略。

就算实在搞不定,专门搞一个浏览器,就真实地跑校验字段生成,然后把结果给其他自动化的部分用也可以。


大致上就是这样了。对抗的路线最终都会转换成为“对抗成本”的问题。而且从技术原理上说,攻击方是永远可以看到代码的(尽管可能是混淆版本),所以根本藏不住。
2025 年 9 月 25 日
回复了 ohohohh 创建的主题 Go 编程语言 go 给数据脱敏返回前端展示有推荐的库吗
https://pkg.go.dev/log/slog@master#example-LogValuer-Secret

这是 golang slog 官方示例写法,和一楼给的思路是一样的。需要通用性做成接口就好了。
2025 年 9 月 24 日
回复了 Librola 创建的主题 Windows 奇怪的 Windows 睡眠和休眠,从睡眠转换为休眠失败
@liyafe1997 #5

感谢指正,我确实说错了。
2025 年 9 月 24 日
回复了 Librola 创建的主题 Windows 奇怪的 Windows 睡眠和休眠,从睡眠转换为休眠失败
这里用技术语言来描述:S0 正常状态,S3 睡眠(持久化到内存),S4 休眠(持久化到硬盘)。

原始问题是,S3->S4 ,中间要经过 S0 吗?技术层面答案是要,但一般语境中认为不需要。操作系统在进入 S3 的时候,会注册一个目标时长的 wake event 回调,即特定时长之后转睡眠。当硬件到达特定时间点,就会 S3->S0 ,此时操作系统会检查唤醒原因,如果是用户的操作,那就继续完全恢复至 S0 ,如果是自己注册的睡眠回调,那就在只唤醒硬盘等必要设备,然后完成休眠准备进入 S4 。

排查故障先看是否能手动进入休眠,很多支持 Modern Standby 的新设备都不支持休眠了。

如果能手动休眠,大概率是 bios 问题,可以反编译 acpi 看哪里出了问题。
2025 年 9 月 23 日
回复了 kuanat 创建的主题 Zed Zed Linux vim 模式输入法切换
unbind key 的语法类似于

```
[
{
"context": "Editor",
"bindings": {
"ctrl-space": null,
}
}
]
```

回复里可能排版不正常,你可以看一下默认 key bindings 的写法,context 是上下文状态,将对应按键设置成 null 就可以。


插件我没有做,执行 `fcitx5-remote -s keyboard-us` 切换是很容易的,官方扩展 api 里面有个 process 模块。但是没有一个好的切入点让这个插件版的命令在 vim 退出插入模式的时候自动执行,最后还要回到 task 上面来。跟我这个方案没区别。

另外 zed 在某个版本之后修改了底层 key handling 的逻辑,比较接近 DirectX 这样从底层取按键状态,而不是取窗口管理器传递的按键消息,所以对输入法造成了很大影响,特别是在 pre-edit 状态下,输入法和 zed 本身在按键处理上可能产生混乱。开发者明显是不懂 ime 的,所以 cjk 用户提的方案很难被接受,尽管开发者很努力地尝试理解,但在这个底层处理机制之上做修改还是太难了。现在什么情况我不太清楚。
2025 年 9 月 22 日
回复了 Suinn 创建的主题 程序员 这样是否可以保证 OCR 识别率接近百分之 100
实践中所有这种需求场景几乎都采用人工复核的方式,倒不是因为人一定对,而是因为人可以担责。如果你的方案里要求去掉人,那这个问题就无解,除非你能为出错的数据兜底。你能兜底的程度越低,相应的置信度阈值就要拉得越高,实践中能够自动化识别的样本比例就越低。

另外单据 ocr 识别是个多少年的需求了,做这个的外包公司或者团队怕不是遍地走。这事根本没必要上大模型,传统 ocr 算法完全够用。

各种 ocr 算法方案在归一化之后的性能表现差距很小。差别大的方面是,在没有前置信息的情况下,先识别出哪里有文字,字符间如何分隔,以及判断文字可能的语言的阶段,以及整体的识别速度。

对 2000 年前后基于传统算法的方案来说,ocr 识别能力属于有多少人工就有多少智能的水平。只要是标准化印刷单据加手写的识别场景,几乎都可以暴力解决。算法判定不准文字位置、字符集,但是人知道啊,提前对单据照片或者扫描图进行畸形校正、裁切和二值化,再把手写的部分抠出来切分,最后只把识别的过程交给 ocr 。这个流程差不多是过去 20 年最主要的方案,基本上只看你归一化做得是不是细致。据我了解有些团队做久了,积攒下几千种不同的单据模板。

2010 年之后 ocr 算法过渡到了 cnn 为主,但相对于之前的暴力解法来说,没什么差别。原来甲方用了 ocr 还是要有个人负责复核,现在一样需要这个人,就算用上了什么大模型,即便出错概率极低,还是需要一个人来兜底。
2025 年 9 月 22 日
回复了 justdoit123 创建的主题 程序员 微服务之间,如何处理常量、DTO 冗余问题?
如果是同一个语言的微服务,最起码还可以直接引入代码。如果是不同语言的项目混用,那几乎就只有 dsl 代码生成这一条路了。

抽象出来 common 包存放数据结构之类的定义,这个思路是对的。我大概几年前也经历过类似改造,尽管项目本身不大,但这个过程挺痛苦的,因为这个改造看的其实是自动化的水平。

我印象当时选定的方案是 git submodules ,因为这个功能在之前的工作流中几乎用不到,还专门开会做团队培训……然后是几个人花了几天抽象了一个公共数据结构定义,写了个 common 包,准备一个一个微服务重构。

这个过程中发现的各种不一致的 bug 就不提了,最大的障碍是没人敢直接用新实现替代旧的版本,尽管初期改的几个包都是挑的代码少的软柿子。而且 submodules 方案也增加了开发人员的心智负担,因为一般的特性分支工作流,需要经常在维护旧代码和开发新功能上来回切换,用 submodules 就要注意每个分支和 common 依赖版本的对应。最后是自动化运维的部门出了大力,在生产环境做了个流量镜像,复制了一部分请求到新版本部署的测试服务器上,然后看执行结果和原版是否一致。

在这之后又增加了一个人为规定,就是所有项目 repo 都必须包含构建脚本,其实就是几个 hooks 检查 submodules 是否更新或者版本对应正确。因为我们用的是特性分支工作流,只要核心的 master/dev/staging 几个分支提前做好与 common 分支的对应(技术上是对应到 commit ),common 的更新就可以通过特性分支简单合并到每个项目中,实现由上至下的传播,开发人员只要知道在当前项目的哪个分支上工作即可,不需要关心依赖的手动更新。

这样改完了效果还是挺明显的,没有各种重复、不一致的定义,ide 补全也可用。只是增加管理成本,特别是负责 common 维护的。因为基础数据结构的改变,可能影响 API ,还可能影响 ABI ,即便是结构体增加一个字段,都有可能不兼容。同时 common 的改动要伴随所有微服务模块的改动,测试一定要跟得上才行。

至于常量的部分,我认为需要根据常量本身来区分。如果是那种界面上的文字肯定是放到直接使用的包比较好,剩下大部分常量,比如服务器地址这些,我认为还是在环境变量中设置比较好。体现到项目中就是 dotenv 这种,环境不同设置也不同。这个方案需要额外处理两个细节,一个是如何集中管理或者持久化,二是一些类似密钥的不适合用明文的要如何处理。

对于单语言项目,简化处理可以用公共 constant 包,对于多语言项目来说,一般是集中定义,然后通过 sed 之类的工具在源码级别上做替换。
2025 年 8 月 19 日
回复了 cnt2ex 创建的主题 程序员 你们在备份数据时会暂停服务以保持数据的一致性吗?
我的话一般会停止服务,不过这个行为取决于备份粒度。一般来说我不会把很多服务的数据放在一起备份,而是单独处理。

从应用或者服务的层面看,大多数写入行为都是非同步的,像数据库一般会设计成 WAL 同步写,主库带缓冲的形式,暂停服务可以降低不一致的“来源”。

如果备份行为是复制,还要多考虑同一批文件由于备份这个行为的造成的时间差异,这也是不一致(匹配)的来源。如果是在 CoW 系统上,先创建快照,再将快照持久化备份会好一些。

当然这个事情还是看需求场景,只是一般来说,维护一个高可用系统的备份和恢复机制更麻烦一些,停止备份再恢复相对来说更容易自动化。
2025 年 8 月 11 日
回复了 huangxiao123 创建的主题 信息安全 把 Clash RCE 武器化的典型案例
@codehz #8 感谢解释。

我这边检查了一下,是我之前拦截了对 localhost 的访问,我还以为之前的 PNA 通过了呢。

我又回头确认了一遍,RCE 是两部分共同实现的,通过 api 控制是 POST/json ,本身是需要跨域的,但 clash 默认允许……第二个漏洞是配置项在 unmarshalling 的时候,解析的路径没有检查就直接使用,所以可以下载任意文件并执行。
2025 年 8 月 11 日
回复了 huangxiao123 创建的主题 信息安全 把 Clash RCE 武器化的典型案例
现在 chrome/firefox 应该都会默认阻止对 localhost 端口的非直接访问,这个第一步应该就执行不下去。
我之前在关于 go 的一些帖子里提到过一个观点,因为 go 的依赖注入太普遍,也太简单了,所以 go 的依赖注入“框架”是非常不流行的。而 google/wire 也不是其他语言一般意义上的依赖注入框架,更准确地说它是自动生成初始化代码的框架,解决的是自动化的需求问题,而非解决注入依赖的问题。还有一个原因是,go 语言提倡显式控制流,所以类似 try...catch 的跳转机制,包括基于中心注册的注入依赖框架(通过运行时反射匹配),都不容易被 go 社区接受。

造成这种现象的更深层的原因,我认为在于 go 对于面向对象的抽象有两个重要的思想转变,一个是接口由需求方定义,另一个是用组合代替继承。前者就是所谓的 duck typing ,实现接口并不需要显式声明,后者是 struct embedding/receiver method ,两者结合,go 可以用极其简单的方式实现 mock 功能以及对第三方代码的复用。

作为对比,Java/C# 在设计思想上还停留在供给方定义接口的层面上,而需求方通常又不会主动 fork 修改供给方的代码,所以要“实现”一个接口,往往是通过所谓的适配器 Adapter 模式。相比于 struct embedding ,这种适配器模式尽管达到了在不修改第三方代码的前提下“实现”接口的目的,但它要求适配器要持有原始类的实例对象。

如果是 1:1 的接口转换,这种模式还勉强够用,如果是比较复杂的接口转换场景,receiver method 的优势就体现出来了,既可以对自定义的结构增加实例方法,也可以通过别名对第三方或者内置类型添加方法。C# 为此增加了 Extension Methods ,使得它能做到和 go 一样的效果。但是 Java 是没有办法在不修改代码的前提下,为已有类型添加方法的。

所以你会看到 Java 生态中,大部分的供应者在编写代码时,会首先编写接口,毕竟测试与 Mock 场景极其依赖接口。同时适配器模式不是复用数据结构,而是间接持有实例,所以会导致需要管理(相对于 go )更多的对象实例。于是,很多大型项目会直接使用 mocking 框架来简化对于大量实例的管理。由此可以看出,go 多数时间是只需要管理自己编写的(或者封装的)实例的,这极大简化了开发者的心智负担,也减少了对使用工具管理实例的需求。

这种对于大量对象的管理需求,才是依赖注入框架的意义所在。对于实例的管理,除了初始化构造,还包括生命周期。现在无论让谁来设计一套通用的依赖注入框架,它对于对象实例本身的管理一定是全局的,也就有了所谓的 DI 容器,另一方面它对于对象实例声明周期的管理一定是隐式的,开发者可以声明 transient/scoped/singleton 之类的意图,但框架并不允许开发者自己手动管理。

回到 go 的生态来看,一方面既没有管理那么多对象实例的需求,另一方面也不提倡隐式控制流,加上 go 生态中显式传递 context 是事实上的标准,所以 DI 的存在空间就非常小了。如果非要在 go 中实现依赖注入,就好比手持锤子看什么都是钉子,解决一个不存在的问题。
1  2  3  4  5  6  7  8  9  10 ... 19  
关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   3262 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 51ms · UTC 12:21 · PVG 20:21 · LAX 05:21 · JFK 08:21
♥ Do have faith in what you're doing.