V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  kuanat  ›  全部回复第 5 页 / 共 10 页
回复总数  181
1  2  3  4  5  6  7  8  9  10  
用 rust 重新实现一遍很不错,手动点赞。

只是 https://github.com/mitmproxy/mitmproxy 已经很成熟了,用的是 python 开发所以很方便直接用 python 脚本接管请求处理。不清楚 rust 要怎么支持一个脚本引擎来做类似的工作比较合适。
春节期间我把开源可以部署的本地 LLM 都试了一下,没有量化单纯体感,7B 这个参数尺度上,DeepSeek Coder 效果是最好的。
能做成网络请求的方式是最好的,即使是内网也可以临时用穿透方式来测试。

如果因为实时性或者与其他模块集成没办法,那就编译成 pyd/so ,如果是 so 的话记得移除符号表,pyd 应该是默认移除的。

做一点简单的限时逻辑,不要有明显的特征,尽量以 silent crash 的形式来处理。这样一般防老板是够用了。有应对措施的大概率就能自己做了。
既然有选择的空间,那就肯定不是类似于嵌入式环境定制这样的场景了。

这个问题不如反过来思考,什么时候会考虑换内核?

因为绝大多数情况下,选择内核版本是在选择发行版之后的事情,也就是说几乎不会主动去换内核。

- 某个版本内核爆出严重安全漏洞

- 需要调节某个编译期配置参数(开启功能支持或性能优化)

- 闭源驱动模块限制内核版本

一旦限制了内核版本可选范围,那么就尽量选择改范围内的 lts 版本,然后就基本保持不变了。版本本质上是内核功能集合的数值化表述,不需要新功能(或者性能优化)就不需要更新。

目前还在维护的 lts 版本有 4.19/5.4/5.10/... 等版本,基本上应该满足各种需求了。使用 lts 版本的原因是,出现严重漏洞的时候,相关补丁也会 backport 到所有 lts 版本上。

另外发行版一般不会直接使用某版本 mainline 内核,而是有自己的一套补丁,用以配合自家发行版的默认功能。但是发行版的版本基本上会匹配同版本号的 mainline 版本,所以还是建议尽量选择严肃的发行版,以便尽快获得安全补丁。


------手动分割线------

尽管 CentOS 已经停止维护很久了,但还是有人会因为各种原因选择它。对于这种情况,不仅仅要关注内核问题,还要关注 glib 等版本的问题。

相应的开发、测试或者 staging 环境几乎都要退化到比较低的版本。比较新的版本就不存在这些问题。
我提一个可能会被遗漏的细节,微服务环境里做日志,一定要有全链路的 trace 信息,不然出了问题也很难还原出错的场景。

前面有人讲了读写分离,写基本都是本地,然后通过后台 daemon 定期汇总到日志服务器上。

原始日志汇总存储之后,建议再定期根据 trace 重新组织一下(比如数据库视图,或者直接用文档类型存储)以方便查询。一般来说,日志信息往往会比生产数据的容量还大,如果用到了再去查可能会很慢。
57 天前
回复了 onichandame 创建的主题 程序员 local first 应用前景咋样
某种程度上,现在软件商业化形式是和 local first 理念相悖的,所以做这个的很少。

从另一个角度,我个人认为开发 local first 应用的技术难度也很大,笼统地说实现 local first 就面临分布式系统的同步问题。这个问题没有最优解,只有妥协,当你追求用户体验的时候,留给你的技术选择就非常少了。
paddleocr 内存分配管理有问题是个很长时间的问题了。我印象官方 github 有几个 issues 就是讨论这个的,而且跨了多个版本,你可以去查一下。

之前有说是因为框架缓存的原因,有人说不是。我之前遇到这个问题也是通过重启来解决,尝试读了一下 c++ 代码部分还是太复杂了,没有解决的精力和能力。
62 天前
回复了 sofukwird 创建的主题 程序员 浏览器爬虫再进化
如果我没理解错的话,这个实现的是挺厉害的,只是应用场景非常受限。

从功能上说,相当于被注入的页面向外提供 http 代理,该代理会复用浏览器环境,也就继承了相关 cookies 。

使用这个方案,相比基于 webdriver/cdp 的方式,可以省去提取 cookies 、模拟登录的过程,通过人工在图形界面上操作一次即可。

比较容易想到的局限在于:

- 网站有多处或者基于访问频率的人机检测,就需要大量人工介入(甚至难以介入,因为通过代理触发的人机检测并不会显示在浏览器界面上)

- 目标接口需要额外访问参数,一般反爬措施都会利用 vmp 混淆 js 代码来生成可以被服务器后端验证的动态参数

如果我说错了还请 OP 指正一下,因为我确实想不到更好的应用场景了。
字体渲染这个话题一两句话说不清楚,我自己的笔记大概有五六千字,如果有空闲我会考虑专门发个帖子。

这里回复主要是想表达一个观点:苹果的渲染效果是可以在 Linux 环境近似复制的。

我这里举两个例子,需要配合 #23 @agagega 的截图对比来看。

由于我这里没有苹方字体,不过看过效果之后就知道不影响了。我这里的环境是 Gnome+Firefox 200% 缩放。

第一张是我日常的效果

https://i.imgur.com/E7QDq7p.png

第二张是贴近苹果的效果(通过调整参数还可以更像)

https://i.imgur.com/3FNIPYO.png



----------手动分割线----------

Windows 那个就不做对比了,我说下 #23 macOS/KDE 和我这里的区别。

首先忽略标题里粗体,那个是字重的关系,其次忽略英文和数字,因为它们 fallback 到了不同的字体上。

如果你把图片保存下来放大看,那么你会发现,KDE 的字体渲染是有彩色边缘的,说明它使用了 subpixel 抗锯齿。而 macOS 实际上是用的 grayscale 抗锯齿。

接下来关注标题下面“XX 小时 XX 分钟”的那个“时”字,苹果系统那个“时”和日常里写法是不太一致的,右边“寸”的横非常靠下,而 KDE 版本就比较正常。这是因为 macOS 的渲染引擎会无视 hinting ,而 KDE 依赖的底层 FreeType 在 KDE 的默认值是 Slight Hinting ,当苹方字体本身没有 hinting 信息时,会按照算法在垂直方向上做自动 hinting 修正。

所以 Linux 环境里,如果通过配置 fontconfig ,设置 hinting=false hintstyle=hintnone ,基本上可以获得接近 90% 的苹果效果。也就是我这里图一的例子。

如果仔细看还是会发现,苹果的渲染标题下面“XX 小时 XX 分钟前”里面的汉字,明显要颜色偏深一些。实际上,如果设置了暗色模式,这个差别会更明显。

这里涉及到一个叫 stem darkening 的概念,在“正确”渲染的前提下,额外对灰度进行加深,以匹配人类视觉的非线性感知。

所谓“正确”是指,通过 alpha composing 将字符前景与背景合成,这个计算是在线性空间里完成,然后通过 gamma 矫正转换到输出空间。这个过程在 Win/macOS 上都是默认支持的,然而 Linux 缺少 gamma 矫正这一步。

可以通过 FreeType 的参数来强行启用 stem darkening ,就如我第二张图的效果。实际效果是轻微的加粗,对于灰度显示的部分影响比较明显。只是从“反映字体设计意图”的角度上说,这个渲染逻辑是不正确的。但考虑到追求渲染效果往往是因为设备 DPI 不够,所以正确与否反倒不那么重要了。
`Run or raise`
https://github.com/CZ-NIC/run-or-raise
https://extensions.gnome.org/extension/1336/run-or-raise/

PS
不依赖扩展的方式就是楼上说的 Super+数字,上面的插件里也提到了。

再 PS
tiling wm 环境里这个功能非常好实现,可以借鉴一下思路。绑定快捷键就用 Gnome 自带的 Keyboard shortcuts ,执行一个 bash -c "command" 命令。这个命令脚本用来判定运行程序和判定焦点,然后通过某个接口去操作窗口管理器。X11 的话,wmctrl 就可以。Wayland 需要通过 D-Bus 调用。
74 天前
回复了 flx413 创建的主题 程序员 QUIC 实践疑问
基于你这个描述,我提供一个方向,可能和我之前遇到的丢包问题是一样的。

并发加载资源的时候,NWListener 用单个 NWConection 连接会丢包,用多个 NWConnection 来处理,有可能就正常了。

一般化的测试方法是用 quic-interop-runner 来跑一下,看看是不是协议不同实现的问题。估计你用的服务端实现肯定在列表里,就是需要额外写个 ios 的客户端。

如果要定量测的话,可以配合 wireshark/mitmproxy ,这俩新版本都可以简单处理 quic 协议了。
Go 的 context 是 1.7 版本引入给 net/http 服务的,用来解决信号和取消问题,传 value 只是顺带的,同时特别强调了线程安全的问题。名字用了 context 但是语义上确实只有上文。所以当你真正需要上下文的时候 context 包是不够的。

一般中间件解决这个问题的思路是自定义 context ,其实我不太喜欢 gin 的方式,我个人的偏好是类似

```
type MyContext struct {
ctx context.Context
// custom field
key string
}
```
这样的方式。然后实现 Context 的接口方法,写几个 wrapper 就可以完成对 context.Context 的兼容,不影响原本 net/http 的信号取消机制。

剩下的就是语法层面的封装了,需要实现一组方法,比如从 context.Context 衍生出子 MyContext:
```
func DeriveMyContext(ctx context.Context) *MyContext {
myCtx, _ := ctx.Value(MyCtxKey).(*MyContext)
return myCtx
}
```
此处用接口断言是根据 context 的设计,value 通过自定义类型模拟命名空间,防止 key 冲突。

结合起来就是 `context.WithValue(context.Background(), key, value)` 中的 kv 对,实际上就是通过 context.Value 传递了一个特定的 key ,这个 key 等价于指向 MyContext 的指针,和你的思路是一致的。

这样中间件所有涉及的 context 都通过一个 MyContext 的结构共享上下文,如果涉及到多线程可以加 Mutex 锁。

反正 Go 在传递 context 这件事上已经一条道走到黑了,比如 1.21 标准化的 slog 日志库也可以接受 context ,稍微封装下也可以直接用。
140 天前
回复了 bocchi1amos 创建的主题 Python 为什么 Python 会有.venv 虚拟环境的概念?
@cnt2ex #63

我的描述是基于自己的记忆和理解,不一定就是正确的。

我原本是想要通过 __all__ 这个例子来体现,基于文件系统的 import path 对今天包管理机制造成的困扰。

在当时 windows/dos/Macintosh 的文件系统上,import * 遍历出来的文件名是大小写不固定的,也就导致了引入的符号表大小写不固定。于是设计了 __all__ 机制,因为 __all__ 定义的大小写是可控的。开发者可以选择 opt in 。

__all__ 机制是需要手动维护的,也不能阻止用户显示地手动引入。如果今天重新设计一门语言,__all__ 是完全不必要的,可以通过 public/private 关键词,也可以通过某种 name convention 来实现。

并不是说基于文件系统的 Package 组织形式不好,而是说这是一个选择。Python 选择了简洁,但是要在别的地方(包管理)付出代价。__all__ 机制就是这个代价之一。


回到虚拟环境的问题上,现在假想一下 Python 依旧使用基于文件系统的 Package 组织形式,但是在路径名中包含版本号。

现在 Python 就面临几种选择,要么以 import xyz==1.1.1 的形式使用,要么 Python 自己实现一个版本选择机制然后维持 import xyz 的向后兼容。

第一种方案几乎没有人选的。因为一旦要升级依赖,需要显示地在源代码里面做修改。但是这个方案的改版非常多见,比如 npm 的 package.json 就是集中把 xyz==1.1.1 放到了一起。

(看起来很完美对吧,但是当时没有人意识到这个 NP 问题会成为日后的麻烦。这种组织方式从机制上就不允许引入同一个包的不同版本,所以 NP 问题退化为 P 问题的途径就少了一个。)

第二种方案,考虑到时间节点,不是一个好的选择。但这个路线是近十年来以软件工程为导向的语言的首选。

这里需要明确区分 Package 的组织形式和 import path 的实现,前者是一种 specification 规范,后者是一个实现方式。Python 完全可以从规范层面要求所有的包名都带版本号,然后 import hook 的实现无视它,固定使用版本号最大或者最小,甚至文件最后修改信息最新或者最旧的版本。


虚拟环境解决的是:在 Package 组织形式是基于文件系统、且 Package 规范不包含版本号的前提下,提供一种在同一个系统中,安装同一个包多个不同版本的可能性。

如果 Package 规范要求包名带版本号,那么“安装”这个行为是不受限制的,也就不需要虚拟环境了。至于安装好了之后,使用时“选择“哪个版本,这是另一个事情。

技术层面上,我猜 Python 转向 npm 类似的包管理方式是没有太大难度的,PEP 621 规范了 pyproject.toml ,也就是前面说的方案一,楼上 frostming 开发的 PDM 就是这么做的。现在的问题是 Python 的 import hook 本身并不支持路径名带版本号的,所以还是需要再套一层虚拟环境,让 Python 只能访问到特定的包。

说得再具体一点,包管理器在文件系统里,以包名+版本号的形式,集中管理所有的包。虚拟环境指定了 $PATH ,在当前环境中的包软链接到实际的带版本号的包,然后软连接的名字不包含版本号。包管理器额外维护一个 pyproject.toml 记录当前所用的包,同时负责计算依赖。在此环境内的 Python 解释器和源码都不需要做任何改变,完全向后兼容。
141 天前
回复了 bocchi1amos 创建的主题 Python 为什么 Python 会有.venv 虚拟环境的概念?
@flyingghost 60

我没有听说过类似的方案,如果让我评论的话,我认为两个方向都不合适,一个是结合了版本号之后命中率很

另一个方面,一个包 A 如果同时依赖 B 和 C ,我猜测不存在一个多项式算法,可以从 B 和 C 依赖计算出 A 的依赖。(需要 backtrack 到所有依赖重新计算)导致这样的缓存中根本上不可用。
我的映射是 capslock 短按是 esc 长按是 ctrl 。
142 天前
回复了 bocchi1amos 创建的主题 Python 为什么 Python 会有.venv 虚拟环境的概念?
@cnt2ex #57

这里我说得太简略了。

在设计这个 import 机制的 1.x 版本,要实现 import * 需要去文件系统里遍历有哪些模块。底层文件系统可能会对某个名为 xyz 的模块,返回 XYZ/Xyz/xyz 几个不同的结果。当时对于这个问题的解决方案是设计 __all__ 让维护者自己声明是哪一个。

之后的 PEP 才明确了模块名应该( should )全小写,import 的所有符号都是小写。再之后 __all__ 才成为一种工程上的控制机制。
142 天前
回复了 Goooooos 创建的主题 Java 吐槽下 Google 开源的组件
@Knife42 #8

自我定位是 long time lurker 所以没有什么社交账号。

v2ex 发帖的初衷是想在 AI 生成内容的时代留下一点有价值的东西。
143 天前
回复了 bocchi1amos 创建的主题 Python 为什么 Python 会有.venv 虚拟环境的概念?
@frostming #54

我又看了一遍自己写的东西,其实不太准确,我这重新描述一下。

版本选择之所以是 NP 问题,源于好几个假设。其中有几个假设是没法改变的,能够改变的还有两个:

- 一个包可以声明自己依赖零个或多个特定版本的包。
- 不允许同时选择同一个包的两个不同版本。

第一条过于严格了。如果把特定版本换成一个范围,这个问题就简单多了。第二条也可以适当放宽,和第一条一样,都是建立在 semantic versioning 主动向后兼容的假设之上。虽然不能引入同一个包的 1.X.X 和 1.Y.Y ,但是可以同时引入 1.X.X 和 2.Z.Z 。

这两个条件放宽之后,NP 问题就不存在了。但是对 APT/RPM/Node/Python 这些起步较早的社区来说,这两个要求依然太难了。

至于主包覆盖依赖的限制,这是用来解决菱形依赖的,不是规避 NP 问题的核心措施。还是之前的例子,只要保证 C 符合 semantic versioning 向后兼容就好了。这个措施实际体现的是解决问题的思路转变。

具体的来源比较分散记不清了,我凭印象做个总结。第一个做 NP 规避的应该是 Rust/Cargo 。之后 Go 做了一个叫 Dep 的类 Cargo 实现,实践了一段时间,吸取经验教训,重做之后形成了正式的 Go mod 方案。Rust/Go 用相同的方法解决菱形依赖,但是基础思路是不一样的。这一点从 Rust 总是选择最高版本,而 Go 总是选择最低版本能看出来。

这里想象一个场景:A==1.1.0 依赖 B==1.5.0 ,之后 B 发布 B==1.6.0 ,由于 B 引入了非兼容改变,导致 A==1.1.0 无法依赖 B==1.6.0 构建。这个时间节点,按照 Rust 的设计,所有依赖 A 的用户(包括依赖 A 的老用户)都会受到影响,而 Go 这边只有同时依赖 A==1.1.0 和 B==1.6.0 的新用户(老用户不受影响)才会受影响。

之后 A/B 至少有一方要打补丁发新版。Rust 认为要么 A 在 B 没有提供补丁的情况下,主动发新版声明不兼容 B==1.6.0 ,要么 B 发新版修复对 A 的支持。实际上在开源社区这两个事情都是很难的。Go 的设计是除非用户主动升级,都会保持作者发布时的最低依赖版本,(毕竟发布那个时刻的版本依赖几乎是都可以构建的)这样就给 A/B 争取了非常多的修复时间。

Rust 的设计者更希望为开发者提供最好的体验,希望一己之力解决所有问题。Go 的设计思路是依靠社区合作,依赖所有人主动去帮助解决对自己来说比较容易,而别人不好解决的问题。所以你能看到,Go 官方一直不遗余力地推动向后兼容,因为 Go 的整套实现逻辑都强依赖整个社区对于兼容性规范的共识。



最后赞美 pnpm ,感谢!
143 天前
回复了 hankli 创建的主题 程序员 试一下用 VersionFox 替代 asdf-vm?
@hankli #7

我一开始没注意到 VersionFox 是跨全平台的。为了兼容 Windows/PS 的话,确实单可执行文件比脚本靠谱。(我觉得理智一点的开发者,在 Windows 环境不用 WSL ,也应该用 MSYS2 吧哈哈)
143 天前
回复了 hankli 创建的主题 程序员 试一下用 VersionFox 替代 asdf-vm?
支持一下~

asdf shell 一直都有,global/local/shell 都支持。我记得 shell 的实现比较粗暴,就是类似 ENV=xxx cmd 的方式。

shim 机制的问题在于需要 reshim ,比如 python pip 安装了某个可执行文件,需要 reshim 才能在当前 shell $PATH 索引到。极小概率的情况,比如某些程序运行后释放执行脚本,又硬编码 #!env python 这样会导致出错。

asdf 有个 direnv 插件,可以管理环境变量,解决每次执行都要 shim 查找的问题。(话说慢真是个问题吗?)

asdf 的优点是插件可以比较方便复用已有的构建脚本,本地构建安装而不是下载二进制文件。

另外我个人比较喜欢这种脚本都用 git 做管理的模式。
1  2  3  4  5  6  7  8  9  10  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3247 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 30ms · UTC 10:56 · PVG 18:56 · LAX 03:56 · JFK 06:56
Developed with CodeLauncher
♥ Do have faith in what you're doing.