阅读视图

发现新文章,点击刷新页面。
🔲 ☆

老板花重金买了台 128 核服务器,我的 Go 程序反而变慢了?

本文永久链接 – https://tonybai.com/2026/03/12/go-concurrency-scalability-issues-on-128-core-cpu

大家好,我是Tony Bai。

设想一个极其真实的职场场景:

你负责的 Go 核心微服务最近流量暴涨,CPU 频频告警。为了解决这个问题,老板大笔一挥,批了几十万预算,采购了最新一代的 128 核 256 线程的怪兽级服务器(比如 AMD EPYC 或 Intel 至强)。

你满心欢喜地把程序部署上去,期待着 QPS 翻倍、延迟减半的奇迹。

结果盯着监控面板,你傻眼了:核心数翻了 4 倍,但程序的吞吐量根本没有线性增长,甚至 P99 延迟还比以前在 32 核机器上时变高了!

老板拍着你的肩膀问:“这服务器是不是买亏了?”你满头大汗,不知道问题出在哪。

别慌,这可能真不是你代码写得烂。在 2026 年的今天,随着芯片制程逐渐逼近物理极限(2nm),单核性能基本停滞,硬件厂商只能疯狂“堆核心”。这就导致了一个在过去只有超算中心才会关心的底层概念,如同幽灵般降临到了每一个普通开发者头上——NUMA(非一致性内存访问)架构

今天,我们就来拆解一下:为什么 Go 语言引以为傲的并发模型,在超多核时代开始“水土不服”?而 Go 核心团队,又打算在今年如何打赢这场史诗级的性能翻身仗?

Go 调度器的“间歇性失忆症”

在小几十核(比如 32 核及以内)的普通机器上,Go 的 GMP 调度模型(Goroutine – Processor – Machine)堪称完美。调度器会尽量让一个 Goroutine (G) 在同一个 Processor (P) 和同一个系统线程 (M) 上运行,以保证 CPU 缓存(L1/L2 Cache)的高命中率。

但在 128 核/256线程(Go眼中 NumCPU()返回 256)的庞然大物上,这种亲和性(Affinity)被极其残酷地撕裂了。

一个值得怀疑的原因是 GC(垃圾回收)带来的 STW(Stop The World)。

每次 GC 开始和结束时,世界都会短暂停止,所有的 P 都会被冻结。当几毫秒后世界重新启动时,Go 的调度器会得一种“失忆症”:它会把“复活”的 P 分配给任意空闲的 M。

这就好比你原本在工位 A 办公,桌上摆满了你需要的资料(CPU Cache 中的热数据)。突然老板喊停,重新洗牌,把你随机分配到了工位 B。你需要重新跨过大半个办公室去搬资料(导致极其严重的 Cache Miss)。

此外,GC 标记工作在 STW 期间启动,并以高优先级调度,这使得它们很可能在之前运行 G 的 P 上运行,即使有空闲的 P。这会迫使 G 迁移到另一个 P 上。

如果你打开 Go 的 Execution Trace,你会看到一幅灾难般的景象:短短 10 毫秒内,你的 Goroutine 就像弹珠一样,在 128 个 CPU 核心之间来回横跳(下面是一个开发者在真实环境采集到的数据, G11到G19在多个P上切换)。微秒级的跳跃积累起来,就成了吞噬性能的黑洞。

NUMA 架构下的双倍“跨省流量”惩罚

如果说缓存失效是“切肤之痛”,那么NUMA 架构带来的内存惩罚,就是真正的“断骨之痛”。

在 128 核这种级别的 CPU 里,物理内存是被划分成多个“大区(NUMA Node,简称Node,每个Node通常有16到64个CPU核)”的。

  • CPU 访问自己大区的内存,极快。
  • CPU 跨大区去访问别人的内存(Remote Node),延迟会瞬间飙升 2 倍甚至更多

但问题是,目前的 Go 语言是“非 NUMA 感知”的!

当你的代码执行 new(struct) 申请内存时,Go 的全局自由列表(Global Free List)完全可能把一块物理位置位于 Node 1 的内存,分配给正在 Node 0 上运行的 CPU。结果就是,你之后的每一次内存读写,都在交高昂的“跨省长途费”。

更要命的是 Go 引以为傲的“工作窃取(Work-Stealing)”算法

当某个 CPU 核心闲下来时,它会去偷别的核心队列里的 Goroutine 来执行。这在以前是神来之笔,但在 NUMA 时代却成了毒药:

它把任务偷了过来,但任务对应的数据还留在原来的 NUMA 节点上!这就好比你抢了别人的砖头搬,但你每次都得跨越一整个城市去拿砖。

面对 2 倍以上的内存访问物理延迟,你写再多牛逼的设计模式,也无济于事。

针对上述问题,Go 1.25 和 1.26 已带来部分改进(容器感知的 GOMAXPROCSGreen Tea GC),NUMA 感知的内存分配等更深层优化仍在 Go 1.27以及后续版本的规划中。

2026 年,Go 团队的破局之战

面对这台越来越难以驾驭的硬件巨兽,Go 核心团队当然没有坐以待毙。在 Go 的官方 issue(#65694, #78044)中,核心成员 Michael Pratt 已经明确表态:解决超高核数和 NUMA 下的性能瓶颈,是今年 Go 团队的头等任务之一。

我们即将看到 Go 团队打出的几记重拳:

  • 修复“失忆症”(强化亲和性锁链)

就在去年10月份,Go 团队合并了一个关键的底层补丁(CL 714801)。现在,STW 结束后,runtime 会拼命尝试将 P 重新分配给它在 STW 之前绑定的那个 M。把你牢牢按在原来的工位上,死死护住你的 CPU Cache。

  • 驯服 GC 抢占(减少驱逐)

新的调度逻辑将尽量避免 GC worker “鸠占鹊巢”,强行驱逐正在运行业务逻辑的 Goroutine,保证业务代码执行环境的连贯性。

  • 探索 NUMA 感知的内存分配(软性偏好)

这是目前最艰难但也最激动人心的探索。未来的 Go 有望实现:优先在本地 NUMA 节点分配内存;工作窃取时,优先偷取同一个 NUMA 节点内的任务。彻底斩断无意义的“跨省流量”。

小结:云原生开发者的自我修养

在摩尔定律彻底失效的今天,硬件发展的路线图已经极其明确:单核停滞,核心数将向 256 核、512 核无限狂飙。

这给我们所有 Go 开发者敲响了警钟:

在极致的性能调优面前,我们不能再仅仅满足于写出“业务正确”的代码,更要理解你的代码在真实硬件和操作系统上的物理足迹。

在 Go 1.27 或 Go 1.28 带来这些“性能怪兽级优化”落地之前,如果你发现你的高并发服务在顶级服务器上性能退化,请记住今天这篇文章:

  1. 不要急着改代码,先用 top 和 numastat 查一下你的 NUMA 命中率。
  2. 极端延迟敏感的场景下,可以临时考虑使用 runtime.LockOSThread() 或利用 cgroups 将进程绑定在特定的 NUMA 节点上运行。

打破对“加机器就能解决一切”的迷信,这是从初级码农走向资深架构师的必经之路。

参考资料

  • https://github.com/golang/go/issues/65694
  • https://github.com/golang/go/issues/78044

今日互动探讨:

你在生产环境中,遇到过哪些“加了机器/加了配置,性能反而变差”的诡异玄学事件?后来是怎么排查破解的?

欢迎在评论区分享你的血泪排查史!


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


原「Gopher部落」已重装升级为「Go & AI 精进营」知识星球,快来加入星球,开启你的技术跃迁之旅吧!

我们致力于打造一个高品质的 Go 语言深度学习AI 应用探索 平台。在这里,你将获得:

  • 体系化 Go 核心进阶内容: 深入「Go原理课」、「Go进阶课」、「Go避坑课」等独家深度专栏,夯实你的 Go 内功。
  • 前沿 Go+AI 实战赋能: 紧跟时代步伐,学习「Go+AI应用实战」、「Agent开发实战课」、「Agentic软件工程课」、「Claude Code开发工作流实战课」、「OpenClaw实战分享」等,掌握 AI 时代新技能。
  • 星主 Tony Bai 亲自答疑: 遇到难题?星主第一时间为你深度解析,扫清学习障碍。
  • 高活跃 Gopher 交流圈: 与众多优秀 Gopher 分享心得、讨论技术,碰撞思想火花。
  • 独家资源与内容首发: 技术文章、课程更新、精选资源,第一时间触达。

衷心希望「Go & AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚,享受技术精进的快乐!欢迎你的加入!

img{512x368}


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

© 2026, bigwhite. 版权所有.

🔲 ☆

点击图片放大查看交互效果的最佳实现

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12082
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。

一、先看跟随放大效果

请看下面的MP4录屏效果(不动点击播放):

除了视频看到的效果,相关实现还支持:

  1. ESC关闭;
  2. 地址栏回退关闭;

眼见为实,您可以狠狠地点击这里:点击缩略图以动画效果呈现大图demo

二、一步步原理说明

1. 跟随放大效果是如何实现的?

这个使用的是startViewTransition实现的,这个是页面级别的transition过渡效果API的语法之一,非常好用。

我们可以无需关注动画细节,只需要符合前后页面的快照,浏览器自动就会补全其中的动画效果,有点类似于keynote中的神奇移动。

无论是删除、移动、还是这里的放大效果,都会有很棒的效果。

这个我在之前详细介绍过,可以访问这里:“页面级可视动画View Transitions API初体验

此特性我已经大量在生产环境使用了。

在本效果中,只需要将viewTransitionName在合适的时机在缩略图和预览图元素上进行设置,就会自动有相关的效果了。

originImg.style.viewTransitionName = "dialogImg";
// 放大执行的时候
document.startViewTransition(() => {
  originImg.style.viewTransitionName = "";
  cloneImg.style.viewTransitionName = "dialogImg";
});

2. 为何使用dialog元素?

使用<dialog>元素主要是两个原因:

  1. 顶层特性;
  2. 无障碍访问天然支持;

顶层特性可以让我们无需关心层级,保证大图效果永远在上面,适用场景更广泛。

<dialog>元素天然聚焦,且支持ESC关闭,可以节约开发成本。

3. 地址栏回退如何实现?

每次弹框显示,我们使用history.pushState添加一条历史记录,当发生popstate变化的时候,判断当前的弹框状态,如果弹框正常展示,则执行关闭操作。

为了保证历史准确回退,可以在history.pushState执行的时候传递状态对象,在弹框关闭之后,对该状态对象进行判定,如果匹配,则执行history.back()

完整的交互逻辑参见:

// modal就是弹框元素
const handlePopState = () => {
  if (modal.isConnected) {
    modal.dispatchEvent(new Event("click"));
  }
};
// 弹框显示的时候
// 增加历史记录
history.pushState({ modal: true }, '', location.href);
// 监听地址栏变化
window.addEventListener("popstate", handlePopState);

// 弹框元素移除的时候
// 移除地址栏变化监听
window.removeEventListener("popstate", handlePopState);
// 历史回退
if (history.state && history.state.modal) {
  history.back();
}

4. 是否可以进行封装?

自然可以。

现在的DOM能力已经很强大了,我们无需关心点击事件等行为,也不需要用到Web Components这么重的东西,只需要通过一个简单的属性,就可以让元素拥有点击查看大图的效果了。

我花了点时间,把这个交互效果封装在了一个JS中,大家只需要引用这个JS文件,无需其他任何设置,就可以有对应的效果了。

三、交互封装与gitee开源

小玩具我都是放在gitee上的:https://gitee.com/zhangxinxu/image-preview

使用很方便:

  1. 引入 image-preview.js 文件,注意设置 type="module"
  2. 需要放大的图片元素设置 is-preview 属性即可
  3. 如果希望一次性预览多个图片,设置相同的 is-preview 属性值即可自动成组

如果希望缩略图和大图不是一个地址

如果希望缩略图是小图,点击查看的是大图,可以使用srcset属性,例如:

<img src="large.jpg" srcset="normal.jpg">

本文的demo页面有相关示意,本JS会在鼠标悬停图片的时候,提前预加载大图。

关于srcset更多知识,可以参见此文:“响应式图片srcset全新释义sizes属性w描述符

在我的书籍《HTML并不简单》中则有更加详细的介绍:

HTML并不简单

其他说明

注意,仓库代码使用了CSS嵌套、HTML5 dialog、Page Transition API等新特性,过于陈旧的浏览器运行可能会有问题。

不过这些问题都可以轻松适配,如果你有相关需求,可以fork项目,自行修改,例如CSS嵌套语法改为普通语法,dialog元素补全缺失的CSS。

四、新年快乐,开工大吉

好了,春节回来的第一篇文章。

用了很多学到的新特性,感受到了学习的价值,和新技术带来的开发体验和用户体验的提升。

在新的一年,祝大家万事顺利,节节高升。

春节快乐

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12082

(本篇完)

🔲 ☆

点击图片放大查看交互效果的最佳实现

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12082
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。

一、先看跟随放大效果

请看下面的MP4录屏效果(不动点击播放):

除了视频看到的效果,相关实现还支持:

  1. ESC关闭;
  2. 地址栏回退关闭;

眼见为实,您可以狠狠地点击这里:点击缩略图以动画效果呈现大图demo

二、一步步原理说明

1. 跟随放大效果是如何实现的?

这个使用的是startViewTransition实现的,这个是页面级别的transition过渡效果API的语法之一,非常好用。

我们可以无需关注动画细节,只需要符合前后页面的快照,浏览器自动就会补全其中的动画效果,有点类似于keynote中的神奇移动。

无论是删除、移动、还是这里的放大效果,都会有很棒的效果。

这个我在之前详细介绍过,可以访问这里:“页面级可视动画View Transitions API初体验

此特性我已经大量在生产环境使用了。

在本效果中,只需要将viewTransitionName在合适的时机在缩略图和预览图元素上进行设置,就会自动有相关的效果了。

originImg.style.viewTransitionName = "dialogImg";
// 放大执行的时候
document.startViewTransition(() => {
  originImg.style.viewTransitionName = "";
  cloneImg.style.viewTransitionName = "dialogImg";
});

2. 为何使用dialog元素?

使用<dialog>元素主要是两个原因:

  1. 顶层特性;
  2. 无障碍访问天然支持;

顶层特性可以让我们无需关心层级,保证大图效果永远在上面,适用场景更广泛。

<dialog>元素天然聚焦,且支持ESC关闭,可以节约开发成本。

3. 地址栏回退如何实现?

每次弹框显示,我们使用history.pushState添加一条历史记录,当发生popstate变化的时候,判断当前的弹框状态,如果弹框正常展示,则执行关闭操作。

为了保证历史准确回退,可以在history.pushState执行的时候传递状态对象,在弹框关闭之后,对该状态对象进行判定,如果匹配,则执行history.back()

完整的交互逻辑参见:

// modal就是弹框元素
const handlePopState = () => {
  if (modal.isConnected) {
    modal.dispatchEvent(new Event("click"));
  }
};
// 弹框显示的时候
// 增加历史记录
history.pushState({ modal: true }, '', location.href);
// 监听地址栏变化
window.addEventListener("popstate", handlePopState);

// 弹框元素移除的时候
// 移除地址栏变化监听
window.removeEventListener("popstate", handlePopState);
// 历史回退
if (history.state && history.state.modal) {
  history.back();
}

4. 是否可以进行封装?

自然可以。

现在的DOM能力已经很强大了,我们无需关心点击事件等行为,也不需要用到Web Components这么重的东西,只需要通过一个简单的属性,就可以让元素拥有点击查看大图的效果了。

我花了点时间,把这个交互效果封装在了一个JS中,大家只需要引用这个JS文件,无需其他任何设置,就可以有对应的效果了。

三、交互封装与gitee开源

小玩具我都是放在gitee上的:https://gitee.com/zhangxinxu/image-preview

使用很方便:

  1. 引入 image-preview.js 文件,注意设置 type="module"
  2. 需要放大的图片元素设置 is-preview 属性即可
  3. 如果希望一次性预览多个图片,设置相同的 is-preview 属性值即可自动成组

如果希望缩略图和大图不是一个地址

如果希望缩略图是小图,点击查看的是大图,可以使用srcset属性,例如:

<img src="large.jpg" srcset="normal.jpg">

本文的demo页面有相关示意,本JS会在鼠标悬停图片的时候,提前预加载大图。

关于srcset更多知识,可以参见此文:“响应式图片srcset全新释义sizes属性w描述符

在我的书籍《HTML并不简单》中则有更加详细的介绍:

HTML并不简单

其他说明

注意,仓库代码使用了CSS嵌套、HTML5 dialog、Page Transition API等新特性,过于陈旧的浏览器运行可能会有问题。

不过这些问题都可以轻松适配,如果你有相关需求,可以fork项目,自行修改,例如CSS嵌套语法改为普通语法,dialog元素补全缺失的CSS。

四、新年快乐,开工大吉

好了,春节回来的第一篇文章。

用了很多学到的新特性,感受到了学习的价值,和新技术带来的开发体验和用户体验的提升。

在新的一年,祝大家万事顺利,节节高升。

春节快乐

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12082

(本篇完)

🔲 ☆

告别insertBefore,使用moveBefore移动DOM元素

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12051
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。

一、新的moveBefore方法

以前我们要移动DOM元素或者Node节点都是使用insertBefore方法。

但是,insertBefore的移动是通过“删除” → “创建”实现的。

这就会有问题,包括:

  • 元素的动画中断;
  • :active状态丢失;
  • 触发Mutation Observer;

等。

实际上,我只是希望元素单纯地换一个位置。

于是就有了全新的moveBefore方法,语法和insertBefore几乎一致,例如:

Element.moveBefore(movedNode, referenceNode)
Document.moveBefore(movedNode, referenceNode)

其中,movedNode会变成调用对象的子元素,同时位置位于referenceNode的前面。

此时,以下这些状态变化都是不会触发的:

  • animation动画transition过渡状态;
  • <iframe>加载状态;
  • :focus或者:active等加载状态;
  • 元素全屏状态;
  • popover浮层的开关状态;
  • <dialog>元素的模态状态;

至于视频和音频的播放状态,这个无论是insertBefore还是moveBefore方法,都会保留。

以及moveBefore方法也会触发Mutation Observer,也就是可以检测到删除和添加,我觉得这个是合理的,否则会影响功能实现。

moveBefore使用限制

对于insertBefore方法,只要DOM元素在内存中(例如使用createElement创建),哪怕不在页面中,也是可以执行的。

但是moveBefore方法不行,moveBefore移动的节点元素必须在文档之中,而且不支持跨文档移动,否则会报错。

Web Components中的作用

之前我开发 LuLu UI 的Select组件,遇到了一个问题,那就是如果 Select 元素的DOM上下文环境变化,例如整体移动这种,运行状态就会有问题。

Select组件代码

就是因为元素移动触发了disconnectedCallback()connectedCallback()生命周期函数执行,导致状态出现问题。

moveBefore似乎就是为了这种情况设计的。

当然,在自定义元素场景下,需要使用其他的生命周期函数配合,叫做connectedMoveCallback()

是这样的:

如果在组件中添加connectedMoveCallback生命周期函数,就像下面这样:

class MyComponent {
  // ...
  connectedMoveCallback() {
    console.log("自定义移动逻辑,如果需要");
  }
  // ...
}

那么组件元素使用moveBefore移动的时候,disconnectedCallback()connectedCallback()生命周期函数是不会执行的。

注意,如果你没有添加connectedMoveCallback函数,无论是moveBefore还是insertBefore,依然遵循传统的生命周期逻辑。

二、moveBefore实践指南

直接说结论,页面内的元素移动,直接使用moveBefore,不需要有任何犹豫。

refNode.parentElement.moveBefore(movedNode, refNode);

不过moveBefore毕竟是新特性,存在兼容性问题,如下图所示:

moveBefore的兼容性

所以在生产环境使用,还需要Polyfill一下,很简单,使用insertBefore接济下,例如:

if (!document.moveBefore) {
  document.moveBefore = document.insertBefore;
}
if (!HTMLElement.prototype.moveBefore) {
  HTMLElement.prototype.moveBefore = HTMLElement.prototype.insertBefore;
}

就可以放心使用了。

案例

我们通过一个简单案例,感受下moveBefore的执行效果,想了下,点击列表置顶效果吧。

你可以点击下面的任意列表色块,看看有没有对应的移动效果。

1
2
3

完整的代码如下所示:

<div class="flex">
  <div class="item" style="view-transition-name: li-1">1</div>
  <div class="item" style="view-transition-name: li-2">2</div>
  <div class="item" style="view-transition-name: li-3">3</div>
</div>

CSS代码:

.flex {
  display: flex;
  gap: .5rem;
}
.item {
  aspect-ratio: 1;
  background: skyblue;
  height: 120px;
  display: grid;
  place-items: center;
}

JavaScript部分,前面都是新特性的Polyfill代码:

if (!document.moveBefore) {
  document.moveBefore = document.insertBefore;
}
if (!HTMLElement.prototype.moveBefore) {
  HTMLElement.prototype.moveBefore = HTMLElement.prototype.insertBefore;
}

if (!document.startViewTransition) {
  document.startViewTransition = function (callback) {
    setTimeout(callback, 1);
  };
}

document.querySelectorAll('.flex .item').forEach((item) => {
  item.onclick = function () {
    document.startViewTransition(() => {
      item.parentElement.moveBefore(item, item.parentElement.firstElementChild);
    });
  }
});

三、谢幕、敬礼

如果让AI实现一个列表点击置顶,同时带动画的效果,我不要看就知道,代码一定是洋洋洒洒。

说不定还有元素克隆,绝对定位,然后使用动画或过渡效果实现。

如果有元素移动,也一定是insertBefore这种传统的方法。

因为目前的AI编程还是基于历史代码训练而来,趋向于最传统稳健的实现,满足功能,创新能力不足。

也就是说,他能实现东西,但是不一定是最佳实践。

这就是目前开发人员的不可替代之处:

  1. 开发人员有不错的架构设计能力,能够很好地引导AI一步一步实现预期的代码;
  2. 开发人员有创新能力,眼界广泛,知道什么样的代码或者方法才是最好最优解。

所以回到很多开发人员问过的一个问题,都AI时代了,学这些细枝末节的东西有个屁用啊!

如果你的项目仅仅是功能完成就OK,说实话,给自己找个不学习的理由也说得过去。

可如果对业务和产品有更高的要求,无论何时,学习总是不能停的。

无论AI出现与否,我们身在职场,放眼整个行业,毕竟还是人与人的竞争。

即,我比你懂的更多,我能比你更好地使用AI,自然这个行业有我更好的一席之地。

好了,就叨这么多,有什么问题可以评论区交流,我们下个视频再见!

飞吻再见

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12051

(本篇完)

🔲 ☆

告别insertBefore,使用moveBefore移动DOM元素

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12051
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。

一、新的moveBefore方法

以前我们要移动DOM元素或者Node节点都是使用insertBefore方法。

但是,insertBefore的移动是通过“删除” → “创建”实现的。

这就会有问题,包括:

  • 元素的动画中断;
  • :active状态丢失;
  • 触发Mutation Observer;

等。

实际上,我只是希望元素单纯地换一个位置。

于是就有了全新的moveBefore方法,语法和insertBefore几乎一致,例如:

Element.moveBefore(movedNode, referenceNode)
Document.moveBefore(movedNode, referenceNode)

其中,movedNode会变成调用对象的子元素,同时位置位于referenceNode的前面。

此时,以下这些状态变化都是不会触发的:

  • animation动画transition过渡状态;
  • <iframe>加载状态;
  • :focus或者:active等加载状态;
  • 元素全屏状态;
  • popover浮层的开关状态;
  • <dialog>元素的模态状态;

至于视频和音频的播放状态,这个无论是insertBefore还是moveBefore方法,都会保留。

以及moveBefore方法也会触发Mutation Observer,也就是可以检测到删除和添加,我觉得这个是合理的,否则会影响功能实现。

moveBefore使用限制

对于insertBefore方法,只要DOM元素在内存中(例如使用createElement创建),哪怕不在页面中,也是可以执行的。

但是moveBefore方法不行,moveBefore移动的节点元素必须在文档之中,而且不支持跨文档移动,否则会报错。

Web Components中的作用

之前我开发 LuLu UI 的Select组件,遇到了一个问题,那就是如果 Select 元素的DOM上下文环境变化,例如整体移动这种,运行状态就会有问题。

Select组件代码

就是因为元素移动触发了disconnectedCallback()connectedCallback()生命周期函数执行,导致状态出现问题。

moveBefore似乎就是为了这种情况设计的。

当然,在自定义元素场景下,需要使用其他的生命周期函数配合,叫做connectedMoveCallback()

是这样的:

如果在组件中添加connectedMoveCallback生命周期函数,就像下面这样:

class MyComponent {
  // ...
  connectedMoveCallback() {
    console.log("自定义移动逻辑,如果需要");
  }
  // ...
}

那么组件元素使用moveBefore移动的时候,disconnectedCallback()connectedCallback()生命周期函数是不会执行的。

注意,如果你没有添加connectedMoveCallback函数,无论是moveBefore还是insertBefore,依然遵循传统的生命周期逻辑。

二、moveBefore实践指南

直接说结论,页面内的元素移动,直接使用moveBefore,不需要有任何犹豫。

refNode.parentElement.moveBefore(movedNode, refNode);

不过moveBefore毕竟是新特性,存在兼容性问题,如下图所示:

moveBefore的兼容性

所以在生产环境使用,还需要Polyfill一下,很简单,使用insertBefore接济下,例如:

if (!document.moveBefore) {
  document.moveBefore = document.insertBefore;
}
if (!HTMLElement.prototype.moveBefore) {
  HTMLElement.prototype.moveBefore = HTMLElement.prototype.insertBefore;
}

就可以放心使用了。

案例

我们通过一个简单案例,感受下moveBefore的执行效果,想了下,点击列表置顶效果吧。

你可以点击下面的任意列表色块,看看有没有对应的移动效果。

1
2
3

完整的代码如下所示:

<div class="flex">
  <div class="item" style="view-transition-name: li-1">1</div>
  <div class="item" style="view-transition-name: li-2">2</div>
  <div class="item" style="view-transition-name: li-3">3</div>
</div>

CSS代码:

.flex {
  display: flex;
  gap: .5rem;
}
.item {
  aspect-ratio: 1;
  background: skyblue;
  height: 120px;
  display: grid;
  place-items: center;
}

JavaScript部分,前面都是新特性的Polyfill代码:

if (!document.moveBefore) {
  document.moveBefore = document.insertBefore;
}
if (!HTMLElement.prototype.moveBefore) {
  HTMLElement.prototype.moveBefore = HTMLElement.prototype.insertBefore;
}

if (!document.startViewTransition) {
  document.startViewTransition = function (callback) {
    setTimeout(callback, 1);
  };
}

document.querySelectorAll('.flex .item').forEach((item) => {
  item.onclick = function () {
    document.startViewTransition(() => {
      item.parentElement.moveBefore(item, item.parentElement.firstElementChild);
    });
  }
});

三、谢幕、敬礼

如果让AI实现一个列表点击置顶,同时带动画的效果,我不要看就知道,代码一定是洋洋洒洒。

说不定还有元素克隆,绝对定位,然后使用动画或过渡效果实现。

如果有元素移动,也一定是insertBefore这种传统的方法。

因为目前的AI编程还是基于历史代码训练而来,趋向于最传统稳健的实现,满足功能,创新能力不足。

也就是说,他能实现东西,但是不一定是最佳实践。

这就是目前开发人员的不可替代之处:

  1. 开发人员有不错的架构设计能力,能够很好地引导AI一步一步实现预期的代码;
  2. 开发人员有创新能力,眼界广泛,知道什么样的代码或者方法才是最好最优解。

所以回到很多开发人员问过的一个问题,都AI时代了,学这些细枝末节的东西有个屁用啊!

如果你的项目仅仅是功能完成就OK,说实话,给自己找个不学习的理由也说得过去。

可如果对业务和产品有更高的要求,无论何时,学习总是不能停的。

无论AI出现与否,我们身在职场,放眼整个行业,毕竟还是人与人的竞争。

即,我比你懂的更多,我能比你更好地使用AI,自然这个行业有我更好的一席之地。

好了,就叨这么多,有什么问题可以评论区交流,我们下个视频再见!

飞吻再见

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12051

(本篇完)

🔲 ☆

20 年 Java 老店的“背叛”:WSO2 为何高呼“Goodbye Java, Hello Go”?

本文永久链接 – https://tonybai.com/2026/01/29/wso2-goodbye-java-hello-go-tech-stack-shift

大家好,我是Tony Bai。

“当我们 2005 年创办 WSO2 时,开发服务端企业级基础设施的正确语言毫无疑问是:Java。然而,当我们走过第 20 个年头并展望未来时,情况已经变了。”

近日,全球知名的开源中间件厂商 WSO2 发布了一篇震动技术圈的博文——《Goodbye Java, Hello Go!》。

这是企业级软件在云原生时代技术风向标的一次重要偏转。作为 Java 时代的既得利益者,WSO2 曾在 API 管理、集成中间件领域构建了庞大的 Java 帝国。为何在今天,他们会做出如此激进的转向?Java 真的不适合未来了吗?Go 到底赢在哪里?

让我们深入剖析这背后的技术逻辑、架构变迁与社区的激烈争议

时代的变迁——从“服务器”到“函数”

WSO2 的转向并非一时冲动,而是基于对过去 15 年基础设施软件形态深刻变化的洞察。其博文中极其精准地总结了这一变迁:

“服务器”概念的消亡

在 2010 年代之前,中间件是以独立“服务器”(Server)的形式交付的。

  • 应用服务器 (App Servers):如 WebLogic, WebSphere, Tomcat。
  • 企业服务总线 (ESB):集成了各种协议适配器的庞然大物。
  • 业务流程服务器 (Process Servers):管理长周期的业务状态。

那是一个“重量级”的时代。你部署一个服务器,然后把你的业务逻辑(WAR 包、JAR 包)扔进去运行。这正是 Java 和 JVM 的黄金时代——JVM 作为一个强大的运行时环境,提供了热加载、动态管理、JIT 优化等一系列高级功能,完美匹配了这种“长时间运行、多应用共享”的服务器模式。

然而,容器化时代终结了这一切。

现在的“服务器”不再是一个独立的实体,而变成了一个库 (Library)

  • 你的业务逻辑不再是“寄生”在服务器里,而是包含了服务器。
  • 整个应用打包成一个 Docker 镜像,作为一个独立的进程运行。
  • 任务完成后,容器销毁,进程结束。

在 WSO2 看来,“独立软件服务器的时代已经结束了”。这对于 Java 来说,是一个底层逻辑的打击。

生命周期:从“月”到“毫秒”

在过去,一个服务器启动慢点没关系,因为它一旦启动,可能会运行数月甚至数年。JVM 的 JIT(即时编译)机制通过预热来换取长期运行的高性能,这是一种非常合理的权衡。

但在 Kubernetes 和 Serverless 主导的今天,服务器变得极度短暂 (Ephemeral)。

  • 容器根据负载自动扩缩容,新实例必须瞬间就绪。
  • Serverless 函数可能只存活几秒钟。

在这种场景下,启动时间就是服务质量 (SLA)。

WSO2 指出:“容器应该在毫秒级内准备好起舞,而不是秒级。” Java 庞大的生态依赖(Spring 初始化、类加载、注解扫描)和 JVM 的启动开销,在云原生环境下显得格格不入。内存膨胀(Memory Bloat)也直接推高了云厂商的账单。

生态位的错位:修补 vs. 原生

面对挑战,Java 社区并非无动于衷。GraalVM Native Image 试图通过 AOT(提前编译)解决启动速度问题;Project Loom 试图通过虚拟线程解决并发资源消耗问题。

但在 WSO2 的架构师们看来,这些努力更像是一种“追赶式的修补”

“这些解决方案感觉就像是在为一个不同时代设计的语言和运行时进行翻新。”

GraalVM 虽然强大,但带来了构建时间的剧增、反射的限制以及调试的复杂性。相比之下,Go 语言在设计之初就原生 (Native) 地考虑了这些问题:编译即二进制,启动即巅峰,并发即协程。这是一种“原生契合”与“后天适配”的本质区别。

WSO2 的架构重构——前端不动,后端大换血

WSO2 并没有盲目地全盘推翻,他们对企业级软件的三层架构(前端、中间层、后端)进行了冷静的评估:

前端 (Frontend):维持现状

  • 现状:Web (JS/TS), iOS (Swift/Flutter), Android (Kotlin/Java)。
  • 未来No Change
  • 理由:前端技术栈受限于终端设备(浏览器、手机 OS),且更新换代极快(“fad-driven”,时尚驱动)。目前没有改变的必要。

中间层 (Middle Tier):Ballerina 的独角戏

  • 现状:Java, Ballerina。
  • 未来Ballerina
  • 核心逻辑:这一层通常被称为 BFF (Backend for Frontend),负责 API 聚合、编排。WSO2 自研的 Ballerina 语言正是为此而生,它将网络原语(Network Primitives)作为语言的一等公民,极其适合做集成工作。

后端 (Backend):Go 与 Python 的双雄会

  • 现状:Java, Go, NodeJS, Python。
  • 未来Go, Python
  • 核心逻辑:这是基础设施逻辑的核心。Python 将继续统治 AI/ML 领域,而 Go 将彻底接管原本属于 Java 的领地,成为构建高性能、高并发基础设施的首选。

为什么是 Go,而不是 Rust?

这是一个每个技术决策者都会面临的灵魂拷问:既然要追求性能和原生编译,为什么不选 Rust?它不是更快、更安全吗?

WSO2 的回答展现了极高的工程务实精神。他们确实评估了 Rust,但最终选择了 Go。理由如下:

抽象层级的匹配

  • Rust 的战场:操作系统内核、浏览器引擎、嵌入式设备。这些场景需要对内存布局、生命周期做极致的微操,且进程几乎永不重启。
  • Go 的战场:中间件、API 网关、编排系统。

WSO2 构建的是中间件基础设施(如 API Gateway, Identity Server)。在这个层级,“我们总是比裸金属 (Bare Metal) 高那么一点点”。Go 提供的自动垃圾回收 (GC) 和高效的并发原语,恰好处于这个“甜点”位置。

避免“过度杀伤” (Overkill)

Rust 的所有权模型 (Ownership) 和借用检查器 (Borrow Checker) 虽然保证了内存安全,但也带来了极高的学习曲线和开发摩擦。对于大多数企业级业务逻辑来说,Rust 提供的控制力是多余的,而为此付出的开发效率代价是昂贵的。

云原生生态的引力

这是一个无法忽视的因素。Go 是云原生的“普通话”。

Kubernetes、Docker、Prometheus、etcd、Terraform…… 几乎所有现代基础设施的基石都是用 Go 构建的。选择 Go,意味着:

  • 库的复用:可以直接调用 K8s 的库,而不是通过 API。
  • 人才的复用:DevOps 工程师和 SRE 通常都懂 Go,可以无缝参与开发。
  • 社区的共鸣:更容易融入 CNCF 生态,获得社区贡献。

实战验证——WSO2 的 Go 之旅

WSO2 并非纸上谈兵,他们在过去十年中已经在多个关键项目中验证了 Go 的能力:

OpenChoreo (CNCF Sandbox Project)

这是 WSO2 最具野心的项目之一,一个面向 Kubernetes 的开发者平台(IDP)。

  • 挑战:需要深度集成 K8s,处理复杂的 GitOps 流程,且自身必须轻量、快速。
  • Go 的价值:作为 K8s 原生语言,Go 让 OpenChoreo 能够像原生组件一样运行在集群中,资源占用极低。

Ballerina 编译器的彻底重写

这是一个惊人的决定。Ballerina 语言最初是基于 Java 实现的(运行在 JVM 上)。现在,WSO2 正在用 Go 完全重写 Ballerina 编译器。

  • 目标:摆脱 JVM 的束缚,实现瞬间启动。
  • 新架构:前端编译器用 Go 编写,直接生成基于 Go 的中间表示 (BIR),这让 CLI 工具的体验得到了质的飞跃。

Thunder:下一代身份认证平台

身份认证(IAM)通常处于请求链路的关键路径上,对延迟极其敏感。Thunder 利用 Go 的高并发处理能力,实现了在高负载下的低延迟认证,且在容器化环境中具备极快的冷启动能力。

社区激辩——理性的探讨与情绪的宣泄

这篇博文在 Reddit 的 r/golang 板块引发了数百条评论的激烈讨论。这不仅仅是语言之争,更是两种工程文化的碰撞。

反方阵营:Java 依然是王者

  1. “这是管理层的愚蠢决定”
    一位愤怒的网友评论道:“计算资源是廉价的,开发人员的时间才是昂贵的。” 他认为,虽然 Go 节省了内存,但在业务逻辑极其复杂的企业级应用中,Java 强大的 IDE 支持、成熟的设计模式和庞大的生态库能显著降低开发成本。强行切换到 Go,可能会导致开发效率的崩塌。

  2. “Java 并没有停滞不前”
    很多 Java 支持者指出,WSO2 对 Java 的印象似乎还停留在 Java 8 时代。现代 Java (21+) 引入了 Virtual Threads (Project Loom),在并发模型上已经可以与 Go 的 Goroutine 媲美;而 GraalVM 的成熟也让 Java 能够编译成原生镜像,启动速度不再是短板。

  3. “生态位的不可替代性”
    在处理遗留系统(如 SOAP, XML, 复杂的事务处理)方面,Java 积累了 20 年的库是 Go 无法比拟的。用 Go 去重写这些复杂的业务逻辑,无异于“重新发明轮子”,且容易引入新的 Bug。

正方阵营:Go 是未来的选择

  1. “运维友好才是真的友好”
    一位 DevOps 工程师反驳道:“在微服务架构下,运维成本是巨大的。” Go 生成的静态二进制文件(Static Binary)是运维的梦想——没有依赖地狱,没有 JVM 版本冲突,所有东西都打包在一个几 MB 的文件里。这种部署的便捷性,是 Java 永远无法达到的。

  2. “简洁是一种防御机制”
    Java 项目容易陷入“过度设计”的泥潭——层层叠叠的抽象、复杂的继承关系、魔法般的注解。Go 的强制简洁性(没有继承、显式错误处理)虽然写起来啰嗦,但读起来轻松。在人员流动频繁的大型团队中,Go 代码的可维护性往往优于 Java。

  3. “云原生的网络效应”
    正如 WSO2 所言,如果你在写 K8s Controller,如果你在写 Sidecar,如果你在写网关,Go 就是默认语言。这不仅仅是语言特性的问题,这是生态引力的问题。逆流而上使用 Java 编写这些组件,会让你失去整个社区的支持。

小结:没有终极语言,只有最适合的工具

WSO2 的声明并非要“杀死” Java。他们明确表示,现有的 Java 产品线将继续得到长期支持。但在新一代的云原生基础设施平台上,他们坚定地选择了 Go。

这一选择揭示了软件行业的一个趋势:通用编程语言的时代似乎正在结束,“领域专用语言”的时代正在到来。

  • 做前端?选 TS/JS。
  • 做 AI 模型训练?选 Python。
  • 做操作系统、浏览器或者嵌入式系统?选 C/Rust/C++。
  • 做企业级业务逻辑(尤其是遗留系统)?Java 依然稳健。
  • 做云原生基础设施、中间件、高并发服务?Go 是当之无愧的王者。

对于 Gopher 而言,WSO2 的转型是一个强有力的信号:你们选对了赛道。Go 不仅是 Google 的语言,它正在成为定义未来十年企业级基础设施的通用语。

资料链接:

  • https://wso2.com/library/blogs/goodbye-java-hello-go
  • https://www.reddit.com/r/golang/comments/1qomr6g/goodbye_java_hello_go/

你的技术栈“保卫战”

WSO2 的转身,是时代的缩影,也是个体的写照。在你的团队中,是否也发生过类似的“去 Java 化”或“拥抱 Go”的讨论?你认为在云原生时代,Java 还能守住它的江山吗?

欢迎在评论区分享你的观点或经历,无论是坚守者还是转型者,我们都想听听你的声音!

如果这篇文章引发了你的思考,别忘了点个【赞】和【在看】,并转发给你的架构师朋友,看看他们怎么选!


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

© 2026, bigwhite. 版权所有.

🔲 ☆

Clawdbot深度评测:全能AI助理的成本与实战避坑

一个复古机器人Clawdbot站在堆叠的Mac Mini主机上,周围环绕着爆炸式的社交媒体点赞图标,羊皮纸,钢笔彩色手绘的统一风格。

Clawdbot AI再进化,社交媒体又爆了。这一次是真的很厉害,还是尬吹?

大家好,欢迎收听老范讲故事的YouTube频道。被突然爆火的clawdbot给砸到头了,这是一种什么样的感觉?

我最近在X上面,看到很多人在晒他们新买的Mac mini,甚至有人晒了12台的Mac mini,摆满了办公桌。实在让我觉得很诧异,他们到底在干什么?后边都有一个词叫clawdbot。我一开始还没有太注意这件事情,昨天直播的时候有人问我:“最近最火的clawdbot你玩了没有?”哎呀我还没玩儿,因为最近在玩agent skills,还没有太关注到。这么神奇的东西我要去看一看。

突然爆火的原因

一个拟人化的AI小助手坐在办公桌前,旁边日历显示24小时工作,桌角有一堆正在燃烧的金币代表Token消耗,羊皮纸,钢笔彩色手绘的统一风格。

突然爆火的原因,是因为很多人跑出来吹了,说这个东西实在是太强了,又革命性了。2025年11月25日,这个产品就已经上线了,它是个开源产品,上线在GitHub上面。到2026年的1月,突然有很多位的网红博主开始非常用力的宣传这个产品,一下就火出圈了。这是一个住进聊天软件里面、7*24小时服务的助理,甚至有很多人给这个助理直接起了个名字。

大家要注意,我们一般不会给ChatGPT、Gemini或者是Anthropic Claude起名字,而像现在的这个clawdbot,很多人都给他们起名字了。这是一个非常非常划时代的事情,因为你一旦给它起名字了,它就人格化了。这不是那种情感陪伴型的聊天工具,这是一个帮你去办公的助理,这是非常重要的。这帮网络大V就出来吹了,说这是个人AI助理的未来形态。有人一周烧掉了1.8亿TOKEN。大家注意,这是非常关键的一个信息:使用clawdbot,你的TOKEN在燃烧

产品形态与体验的明显差异

1. 全时驻留

台Mac Mini安放在舒适的家居办公桌上,连接着漂浮的Word文档、Excel表格和iMessage聊天气泡,羊皮纸,钢笔彩色手绘的统一风格。

它的产品形态跟体验上,跟过去的产品有明显的差异。第一个特别重要的差异,就是全时驻留。像以前我要去跟ChatGPT聊天,我要点开APP,或者我要到网站上去打开这个网页;现在这个就不用了,它就永远在线,而且功能非常完整。这也是为什么Mac mini突然销量暴增的原因。你可以命令它:

  • 打开Word文档
  • 打开Excel
  • 去干任何事情,浏览网站
  • 用iMessage跟人聊天、给人发短信

它这个全能干,没有任何问题。但是你要保证所有功能都能使的话,特别是你要使用iMessage的话,你必须要有Mac的系统,要有Mac、要有Mac mini。这是一个自托管成本很低、部署很方便的系统。大家都是买个Mac mini放在家里头,甭管是放在办公桌上,还是放在机柜里,放在电视旁边,这都不重要。但是这是你放在自己家里头的,你不用再担心任何隐私问题了。

2. 持久记忆加上主动触达

AI助手手里拿着厚厚的记事本,正在主动轻拍一位正在休息的用户的肩膀进行提醒,背景充满记忆碎片,羊皮纸,钢笔彩色手绘的统一风格。

像我们现在都说ChatGPT也好,一些聊天工具也好,要有记忆,但是他们记住的东西其实非常少。原因也很简单,如果ChatGPT记住很多东西的话,他就不知道什么时候该用什么了。而现在的clawdbot他是全记忆,你跟他聊天的所有内容他都记得。

所以很多的博主上来用clawdbot之前,先会用很长的时间去跟他描述:

  • 我是谁
  • 我喜欢什么东西
  • 我在干什么
  • 我对什么东西感兴趣
  • 我对什么东西有什么样的要求
  • 为什么我喜欢这个球队、为什么我喜欢那首歌……

他会把这些东西通通都告诉这个clawdbot。他会记下来,记下完了以后再去跟你聊天的时候,这些通通都会变成系统资料,它就会很懂你。

而且clawdbot还有一点非常重要的是什么?就是它会主动的来去跟你聊天。原来是被动的,你不去跟ChatGPT说话,它就不会回答你任何问题,所以我们要先提出问题。而现在的话,你可以告诉他说:“什么什么时候记得提醒我干事”、“每天告诉我最近应该做一些什么什么样的事情”。不是说你列好计划让他做什么事情,而是说你觉得我应该做点什么,他会告诉你说我觉得你应该干点这个、应该干点那个,他会有很多这样的建议性的东西出来。甚至他每天早上起来说:“我今天早上起来了,把我认为你今天该干的活都给你列出来。”他可以干这样的事情。

开源且无所不能的系统

这个系统还是开源的,而且迭代的速度非常快。之所以突然爆火,还有一个很重要的原因,就是这个产品基本上是无所不能。你基本上能想到的活它全能干,包括你让它去做vibe coding,你让它指挥Claude code下去干活去,都没问题。它可以浏览各种网页替你买东西,通过agent skills和这个MCP,我们现在互联网上这些服务,它全都可以使用起来了。就是因为这些原因,这个产品突然就爆起来了。

但是你说这个里头有没有尬吹的部分?肯定有。你自己去安装的时候,你就会发现可能也没那么方便。而且如果有些人对于结果的格式要求非常严格的话,你可能会觉得他产出的东西依然是AI垃圾。但是方向是正确的,就是全时驻留、持久记忆、主动触达,这就是未来的AI助理的一个方向,而且还要最好能够全能一些,所有问题都可以解决掉。

有人说原来ChatGPT不是出过这种东西吗?原来ChatGPT你是可以通过WhatsApp跟他聊天的,为什么到这就突然爆了?因为很简单,ChatGPT你虽然可以通过WhatsApp跟他聊天,但是它只能调用ChatGPT里边这些东西。你说我想去调用外边这些东西,我想去写个Word文档、我想去做个PPT、我想去剪个视频、我想去搜集一些信息,它这个功能还是有一定局限的。他们家就是玩这套东西,所以就并没有推开。而现在clawdbot直接就爆了。

传奇的创始人:Peter Steinberger

一位睿智的程序员肖像,背景融合了PDF文档结构图和维也纳的建筑剪影,羊皮纸,钢笔彩色手绘的统一风格。

它的创始人很传奇,这个创始人的经历还让老范很有代入感,为什么?这哥们在维也纳是一位退休程序员。老范现在也可以算退休程序员了,但是人家还是比我厉害很多了。这个人叫Peter Steinberger,他是PSPDFKit的创始人。这个产品是什么?是面向开发者的PDF的SDK框架。它给你一套框架,然后你可以写程序,通过它这套框架去操作PDF,做PDF查看、PDF注释、PDF编辑、签署、填表单,做这些功能。它的产品在iOS、安卓、Web和桌面端全覆盖。它的公司主要是提供文档、PDF相关的SDK和框架能力的。因为它有这样的一个技术背景,所以对于配置系统、跨平台交付、可观测行为、安全边界等等这些方面,都是非常敏感的。这也是为什么clawdbot这样的一个产品突然会爆起来。

Clawdbot到底能干点什么?

什么都能干。就是这么简单的一个问题。但是你说真的什么都能干嘛?跟大家讲一个笑话。岳云鹏有一次出去参加综艺,人家问他你数学怎么样?说特别快没问题。然后就出了一个问题:

26*78等于多少?等于75。

人家说你这对不?

岳云鹏说:“我又没说我算的特别对,我就说我算的特别快,你就说我快不快吧?”

所以虽然clawdbot什么都能做,但是结果到底是不是能够让人满意,就是冷暖自知了。有些人很挑剔,他就觉得这不行;这些人可能提的问题也很模糊,对于结果又很挑剔,那么他就得不到满意的结果。有些人的问题提的非常详细、非常具体,对于结果特别是格式又要求不是很高,他们就会得到满意的结果。我觉得这样解释是相对比较清楚的。那种提问题、提要求的时候云山雾罩,经常玩这个“佛祖拈花一笑”,出来的这个结果还挑三拣四的这种领导,反正伺候起来比较难吧。比较难伺候的领导,clawdbot这样的助理他也搞不定。但是有一些领导就是提要求事无巨细,只要结果正确、格式无所谓的,这些领导,clawdbot就是你最好的助理。

部署Clawdbot的风险:TOKEN在燃烧

一个巨大的沙漏,里面的沙子是金币,正在快速流逝,象征昂贵的Token消耗,羊皮纸,钢笔彩色手绘的统一风格。

但是如果你去部署clawdbot,一定要小心的是什么?TOKEN在燃烧。前面有人一个礼拜烧了1.7亿TOKEN,那是非常非常贵的。通常使用clawdbot需要什么?就是买Anthropic Claude 4.5 Opus 200美金一个月的Max账号。如果没有这个账号的话,这个产品会很难用的。当然了现在我们就在看Anthropic会不会封他,因为前不久Anthropic刚刚把open code的账号给封了。原来我们使用open code的时候,也可以用Anthropic的20美金或者200美金的这种Pro或者是Max账号,但是Anthropic说不行,不让你用了。所以现在还要看,它到底能使到哪天。

千万千万不要干嘛?千万不要用Anthropic的API key,你真的会破产的。那个玩意非常非常的消耗TOKEN。GPT 5.2据说也还不错,但是跟Anthropic的Claude 4.5 Opus还是有一点点差距的,最好也是用200美金的Pro账号。用我现在这种plus账号可能是比较费劲的,我准备待会把它装上,把plus账号挂上试试。还有博主推荐Mini Max,Mini Max有10美金左右的月账号,它也是一种编程账号,效果再比open code再差一些,但是人便宜。大家也可以试一试。功能都是TOKEN烧出来的,你没有那么多TOKEN,就不要指望它有那么多功能。

为什么一定要Mac mini?

一个架子上整齐排列着多台Mac Mini组成的家庭服务器农场,指示灯闪烁,羊皮纸,钢笔彩色手绘的统一风格。

这么多人都去晒Mac mini,其实并不是必须要Mac mini,最好是使用闲置的非工作主力电脑。你说我这就是上班每天用的电脑,我把这个clawdbot挂在上头行不行?最好别这么干。为什么?因为你上班的电脑第一个,它的能力很强,晚上有可能还会关机,比如说你要把它合起来,这个电脑就会关掉。这个系统是要7*24小时工作的,所以你最好不要把它放在你的工作电脑上。很多家庭有这种闲置的Mac mini,放这个上面就挺方便的。价格也不贵,也还很省电,还很漂亮。特别是最新的Mac mini M4,很小、非常非常漂亮、非常精巧,放在家里头、放在各种地方都不显得突兀。

全功能的系统配置

最好是给clawdbot配这种叫“全功能的系统”。什么叫全功能系统?就是它可以直接使用浏览器、可以跑vibe coding、可以调用office,这些东西都是可以工作的。对于本地的算力其实并没有特别高的要求,所有的AI都是调云端的算力。它通过即时通讯工具来工作。我们想去跟clawdbot聊天的时候,你可以打开:

  • Telegram
  • Discord
  • iMessage
  • WhatsApp
  • 或者是给你发短信

都是可以的。国内的不行,像什么微信搞不定这事,因为微信对于这种机器人是封闭的,比较严格的,怕各种黑灰产。

很多人想去用iMessage,就是苹果系统的这种iMessage,这个就没办法,你必须使用Mac mini。你说我现在想整个Windows、想整个Linux上iMessage?上不去。这个iMessage也不是一个开放系统。很多苹果全家桶的玩家,特别是在程序员和AI玩家里头,苹果全家桶玩家的比例是很大的,肯定是喜欢上Mac mini的。家里头其他的闲置电脑其实也可以跑,Windows电脑也可以。但是如果你要在Windows电脑里跑,最好是装WSL。WSL就是Windows里面的Linux,现在Windows新的系统里边都是可以装一个Linux系统的。然后Linux电脑,这个肯定也是没问题的。我准备上NAS了,家里NAS已经跑了一大堆的各种各样的Docker了,它也是可以跑上去的。

云主机也没毛病。你都花了200刀去买套餐了,那你一个月花5刀去租个云主机跑这个clawdbot肯定也是没问题的。Oracle云上有免费的主机,大家可以上去玩耍一下。NAS、瘦服务器或者是在云主机上跑clawdbot,浏览器也是能用的,但是会比较费劲。vibe coding就要稍微克制一点了。如果是在你的Mac mini上,你就可以给它下指令,说打开哪个vibe coding的工具,然后在里边去给我写一什么产品出来,他自己吭哧吭哧就干活去了。你可以每天晚上睡觉之前给他布置一大堆任务,早上起来看看,完成几个、没有完成的部分你还可以去辅助一下。他是这样来干活的。你要是在云主机上,就不能干这活了。

Clawdbot是不是一次革命性的创新?

方向上肯定是。这个方向也很明确,就是无限记忆、私有部署、绝对隐私保护、7*24小时驻留、随时待命、主动沟通和提醒,基本可以解决各种问题。随着模型能力的提升、agent skills的发展,他的能力一定还会继续爆炸式增长。大模型厂商应该会争先恐后的推出新套餐了。因为有了前车之鉴,Anthropic估计过一段时间还是会封他的。这个咱们预言一下,咱们打个赌,猜一猜会不会把他封掉?前面open code用户量上去以后,Anthropic就直接把他封掉了。

因为现在买TOKEN基本上是两套玩法:一套就是你具体按100万TOKEN多少钱去算;另外一套就是给你套餐,这个编程套餐。因为现在编程实在是太烧TOKEN了,所以Anthropic出了这种编程套餐,OpenAI、谷歌都出了这种编程套餐。但是Anthropic还是希望,如果你想要去买它的编程套餐,你就只能用Claude code,你不能用其他的东西。像咱们现在讲这个clawdbot,这就不允许用。那么OpenAI跟谷歌应该会继续支持你。像open code这块,在Anthropic说我封闭它之后,OpenAI说我们准备继续支持。没毛病,你买我的plus套餐、Pro套餐,我都继续支持你。谷歌在这一块其实是放的比较宽松的,只要你愿意用,谷歌还是愿意笑脸相迎的。

国内的模型平台的话,也应该会推出一些专门的套餐,应该是会像code套餐这样,都是可以挂上使的。国内平台的code套餐基本上有5美金一个月的、10美金一个月的,甚至可能最便宜的有3美金一个月的。他们都是去仿真Anthropic的这个API形式,只要我仿真好了,就往上挂就完了,都是可以用的。

硬件与巨头的新机会

几个代表科技巨头的彩色球体悬浮在一张棋盘上方,象征市场博弈,羊皮纸,钢笔彩色手绘的统一风格。

家庭瘦服务器应该有新的应用场景,以后的NAS也可以配更好的CPU GPU了。至于家里是不是要买一台Mac mini,让我再犹豫几天吧,反正我目前为止还没有下决心再去买一个Mac mini。至于Mac mini农场,也许会在一段时间内流行起来。什么叫Mac mini农场?就是在一个房间里边装一大堆的Mac mini,允许大家从远程去访问它、替你去维护,我们只管去付租金就可以了。这可能也是一种未来的服务形式。

黑苹果可能会焕发第二春。什么叫黑苹果?就是在一些比较便宜的Intel这种架构上,使用macOS系统重新去破解,然后给你装上,让你去使用。这个东西叫黑苹果。其实黑苹果随着后来苹果出M系列芯片以后,已经不是那么活跃了,但是现在的话,应该会重新再活跃起来。

腾讯、Meta、苹果、谷歌机会来了,就看谁能抓得住了。为什么他们机会来了?他们做即时通讯工具的。既然大家觉得以后的这些个人助理应该是活在WhatsApp、活在Telegram、活在Discord里头了,腾讯说我这有微信,干脆我在这边给你配一个助理不就完事了吗?你有什么事跟助理说不就完了吗?我觉得他们未来是有机会的。至于说Meta的话,你像WhatsApp是它的,Facebook Messenger也是它的,全世界最大的两个即时通讯工具都是它的。苹果自己也是有iMessage的。它们都是有机会去腾飞一下的。

最后总结一下

又一个神奇的AI工具发布了,赶快玩起来吧。甭管好不好使,大家一定要去玩起来。7*24小时永久驻留、永久记忆、主动提醒、全能助理,这应该就是未来的方向了,这个基本上可以确定。助理已经这么强大了,具体做什么就是留给我们的问题了。网上的介绍视频里头经常是这样的,他问clawdbot:“你觉得我该干点什么?”还是要有自己的想法。只要烧得起TOKEN,我们每个人都会得到一个强大的全能助理。

好,这一期就讲到这里。感谢大家收听,请帮忙点赞、点小铃铛,参加DISCORD讨论群。也欢迎有兴趣、有能力的朋友加入我们的付费频道。再见。


背景图片

🔲 ☆

AI 时代,Go 语言会“失宠”还是“封神”?—— GopherCon 2025 圆桌深度复盘

本文永久链接 – https://tonybai.com/2026/01/20/ai-and-go-opportunities-and-challenges

大家好,我是Tony Bai。

在 AI 的滔天巨浪面前,每一位 Go 开发者心中或许都曾闪过一丝不安:Python 似乎统治了一切,我的 Go 语言技能树还值钱吗?AI 会取代我写代码吗?我该如何在这个喧嚣的时代保持清醒?

GopherCon 2025 的压轴圆桌会议上,一场名为“AI 与 Go:机遇与挑战”的深度对话给出了答案。

嘉宾阵容堪称豪华(从左二到右分别是):

  • Ian Cottrell: Google工程师,现从事 AI Agent 开发
  • Katie Hawkman: 前 Go 团队成员,现 Mercari 平台工程师
  • David Soria Parra: Anthropic 技术专家,MCP (Model Context Protocol) 联合创始人
  • Jaana Dogan: 前 Go团队成员,Google Gemini Serving 团队专家, adk-go项目成员
  • Samir Ajmani: Google Go 团队工程总监

他们没有贩卖焦虑,也没有盲目吹捧,而是用冷静、务实的工程师视角,为我们描绘了 Go 在 AI 时代的真实版图。

Go 的新机遇:AI 基础设施的“基石”

当被问及“Go 能提供什么 Python以及其他编程语言 无法提供的价值”时,嘉宾们的回答出奇一致:生产级的可靠性与并发能力。

Samir Ajmani 提出了一个精准的洞察:Go 的崛起得益于云原生时代的爆发,而 AI 正在带来“第二次云原生机遇”。

  • 现状:目前的 AI/ML 基础设施大量依赖 Python,适合快速原型和实验。
  • 痛点:当这些原型需要走向大规模生产,需要处理高并发推理、构建复杂的 Agent 编排、或者实现像 MCP (Model Context Protocol) 这样需要高度可靠性的协议时,Python 的动态特性和性能瓶颈开始显现。
  • Go 的位置:Go 语言天生的高并发模型、静态类型安全、以及构建大规模分布式系统的基因,使其成为构建 AI 生产基础设施(Serving, Orchestration, Agent Protocols)的完美选择。

Katie 分享了一个真实案例:她在黑客马拉松中选择用 Go 而非 TypeScript 来编写 MCP Server,因为 Go 的代码在处理复杂协议逻辑时更易读、更易维护。

David(Anthropic)就个人经验和观察,认为Go 是目前AI最擅长生成的语言代码之一,这也是Go的一大优势!

Python 也许是 AI 的“训练语言”,但 Go 有望成为 AI 的“运行语言”

职业焦虑:AI 会取代我们吗?

面对“AI 取代程序员”的言论,嘉宾们的态度是——“这只是另一种生产力工具,它改变了工作方式,但提升了人的价值。”

  • Samir Ajmani:未来的软件构建方式可能会变成“组件组装”。但这依然需要懂系统设计、安全性和可靠性的专业人士来构建这些高质量的组件。对于初级开发者,门槛确实变高了(简单的代码生成不再是技能壁垒),但对于具备系统思维的工程师,这是最好的时代。
  • Jaana Dogan (Google):她提出了一个令人耳目一新的视角——“代码写得快了,不仅没让我失业,反而让我更强大了。” AI 极大地缩短了编码时间,这意味着工程师可以更快地去“连接点” (connect the dots):将孤立的组件串联成系统,与更多人协作,验证更多设计想法。个人的产出能力被放大了,你不再是一个单纯的“螺丝钉制造者”,而更容易成为一名“系统架构师”。
  • David Suryapara (Anthropic):作为一名非 Go 核心开发者,David 的观察更为冷静。他认为,纯粹的“代码编写”技能(例如熟练背诵 API、手写 CSS)确实面临贬值。但核心工程能力——如拆解复杂需求、设计分布式系统、处理边缘情况——将变得前所未有的重要。 AI 抬高了入行的地板,但也让那些拥有深厚解决问题能力的工程师变得更加不可替代。
  • Katie Hawkman:写代码从来不是工作中“最难”的部分,而是“最有趣”的部分。真正的难点在于——如何渐进式交付?如何设计良好的 UX?如何优化系统性能?这些是 AI 短期内无法完全替代的工程智慧。
  • Ian Cottrell:我有 40 年的开发经验,每一次生产力工具的飞跃(从汇编到 C,从 IDE 到自动补全),人们都说“不需要程序员了”。结果呢?我们的需求量反而更大了。我们只是在提升期望值,尝试解决更难的问题。

不要试图成为每一个 AI 工具的专家。选择一个工具(如 Cursor 或 Claude Code),深入掌握它,让它服务于你的工作流,而不是被它淹没。

理性审视:算力、能源与负责任的 AI

主持人提出了一个尖锐的问题:在区块链曾因高能耗饱受诟病之后,我们该如何理性看待 AI 巨大的算力和能源消耗?作为开发者,我们该如何权衡使用 AI 工具的成本?

嘉宾们的回答,揭示了工程优化在 AI 时代的巨大潜力:

  • Samir Ajmani (Google) 分享了一个令人振奋的实验:Go 团队尝试将 MCP 支持集成到 Go 语言服务器 (LSP) 中。结果发现,当 AI 能够直接调用精确的工具(Tools)而不是在那“空想”时,任务完成率提高了,延迟降低了,最重要的是——Token 消耗量减少了近 50%。 这意味着,通过优秀的工程工具(如 Go),我们可以显著降低 AI 的运行成本和碳排放。
  • Jaana Dogan (Google) 认为我们正处于优化的早期阶段。就像当年的数据库优化一样,模型推理 (Inference) 的效率优化将是接下来的重头戏。缓存、量化、专用硬件,这些工程手段将大幅抵消模型增长带来的成本。
  • David Suryapara (Anthropic) 提到了“小模型与蒸馏”。我们不需要每次都动用最昂贵、最慢的“超大模型”来解决所有问题。未来,针对特定领域(如代码生成)进行微调和蒸馏的小模型,将在效能和成本之间找到完美的平衡点。

不要盲目堆砌算力。“负责任的 AI”不仅是道德要求,更是工程优化的必然方向。 用更少的 Token 做更多的事,这本身就是 Go 开发者擅长的“资源优化”技能的延伸。

务实派的生存指南:过滤噪音,回归本质

在 AI 炒作的喧嚣中,如何保持清醒?

  1. 从“小”开始:不要被“AGI 即将到来”的宏大叙事吓倒。像 Katie 建议的那样,承认自己是初学者,哪怕是 MCP 的创始人也说“现在没有所谓的专家”。放下包袱,去尝试写一个简单的 Agent,去用 Go 写一个 MCP Server。
  2. 关注“确定性”:Jaana 和 Ian 都提到,AI 模型本质上是概率性的(非确定性),而工程系统需要确定性。Go 语言强大的静态分析、测试工具链和类型系统,是约束 AI 幻觉、构建可靠系统的最佳防线。用 Go 的“确定性”去包裹 AI 的“不确定性”,是未来的核心工程模式之一。
  3. 解决实际问题:不要为了 AI 而 AI。如果老板让你“加点 AI 进去”,试着去寻找那些真正能通过 AI 提升效率的痛点(比如自动化文档更新、复杂日志分析),而不是生搬硬套。

小结:Go 社区的“绿地”时刻

这场圆桌会议传递出的最强烈信号是:乐观

我们正处于一个类似于 2013 年 Docker 诞生前夜的时刻。AI 领域的“Kubernetes”、“Prometheus”还没有被写出来。这片巨大的空白,正是 Go 开发者施展拳脚的“绿地” (Greenfield)。

正如 Samir 所言:

“如果我想让 AI 真正能够与现实世界进行交易(比如订购 Pizza 并且真的送到),这中间需要大量的、可靠的基础设施。而 Go,是构建这一层的绝佳语言。”

所以,Gopher 们,别慌。带上你的并发模型,带上你的工程智慧,去构建 AI 时代的钢铁地基吧。

资料链接:https://www.youtube.com/watch?v=r40Mwdvg38M


你的 AI 实践

听了这些顶级专家的观点,你是否对 Go 在 AI 时代的未来更有信心了?在你目前的开发工作中,是否已经开始尝试用 Go 构建 AI 应用或基础设施?你认为 Go 在 AI 领域最大的短板是什么?

欢迎在评论区分享你的实战经验或困惑!让我们一起探索 Go + AI 的无限可能。

如果这篇文章为你扫除了职业焦虑,别忘了点个【赞】和【在看】,并转发给身边迷茫的 Gopher 朋友!


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

© 2026, bigwhite. 版权所有.

🔲 ☆

Wing FTP Server 8.0.3 专业跨平台FTP服务

Wing FTP Server是一个专业的跨平台FTP服务器端,它拥有不错的速度、可靠性和一个友好的配置界面。Wing FTP Server 它除了能提供FTP的基本服务功能以外,还能提供管理员终端、任务计划、基于Web的管理端和脚本支持等,它还支持 …
🔲 ☆

Soft AP on Linux with WPS

I’ve recently upgraded my home WiFi setup with Ubiquiti (UniFi) access points (I bought a U7 Pro AP). All is good, except that UniFi doesn’t support WPS. On the other hand, my old Canon TS3150 printer can only be setup using WPS. Now, it wouldn’t have been an issue, had I not changed the SSID. The easiest workaround seems to be setting up a WiFi that has the same SSID and password, and supports WPS (so the printer can connect to it).

Some people on the Internet have suggested buying a cheap router/AP that supports WPS, which seems like a waste to me. Not only it costs minimally £30 or so, but also it’ll literally become an e-waste one minute later. Bad for the environment!

With the help of Gemini 2.0, and this post on StackExchange, I’ve managed to open a Hotspot by using SoftAP via hostapd that supports WPS (WiFi Protected Setup), and my printer is now happily connected to the WiFi network created by my UniFi AP.

I’m using Fedora Kinoite on my desktop, and hostapd is not Flatpak’d, therefore rpm-ostree is needed to get hostapd installed:

sudo rpm-ostree install hostapd

Then create this hostapd.conf file:

interface=wlo1  # check the actual device name, ip link
ieee80211d=1  # make sure the frequency conforms to the regulation
country_code=GB  # United Kingdom
eap_server=1
ap_setup_locked=1
ssid=&lt;ACTUAL SSID>
channel=1
hw_mode=g
wpa=2
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
wpa_passphrase=&lt;ACTUAL PASSWORD>
wps_state=2
ap_max_inactivity=300
auth_algs=3
ctrl_interface=/var/run/hostapd
config_methods=label virtual_display virtual_push_button keypad

Now you need to first switch off the AP/WiFi so there is no conflict. Then stop NetworkManager and start hostapd,

sudo systemctl stop NetworkManager
sudo hostapd hostapd.conf

In a different Terminal window/tab,

sudo hostapd_cli wps_pbc

Immediately, press the WPS button on the printer (or other devices that need to be setup in this way). You can query whether WPS has been successful by running

sudo hostapd_cli wps_get_status

A successful setup should have a message like:

Selected interface 'wlo1'
PBC Status: Disabled
Last WPS result: Success
Peer Address: 34:XX:XX:XX:XX:00

Once it’s done, you can turn off the SoftAP (pressing Ctrl+C in the terminal window where hostapd is running), switch back the normal AP/WiFi, and start NetworkManager again – sudo systemctl start NetworkManager

A screenshot showing now my printer is now happily connected to the Ubiquiti U7 AP
🔲 ☆

在 Kubernetes 部署 Jumpserver 跳板机

1. 部署 Jumpserver 需要提前准备好 StorageClass,用于存储 Jumpserver 的数据。除了下面提到的数据库,各个组件 jms-core、jms-web、jms-koko、jms-lion、jms-chen 都需要一个 PV 存储。 1.1 部署 MySQL 参考 https://github.com/shaowenchen/ops-hub/blob/main/database/mysql8.yaml ,部署 MySQL。 需要调整
🔲 ⭐

给家里云装上 Fedora 41 KDE 后,我是如何配置的

前两天给自己的 N100 小主机重装成了最近发布的 Fedora 41 ( KDE ),也是花了不少时间把整个系统调成自己熟悉的样子,因此开一篇博客记录一下。以下仅为我个人的 HomeServer 小主机使用,不具有普适性。

换官方源

我这里比较适合用上交的源,直接参考他们的文档

sudo sed -e 's/^metalink=/#metalink=/g' -e 's|^#baseurl=http://download.example/pub/|baseurl=https://mirror.sjtu.edu.cn/|g' -i.bak /etc/yum.repos.d/{fedora.repo,fedora-updates.repo}

加 rpmfusion 源

参考 help.cernet.edu.cn 提供的文档

安装源配置文件

sudo yum install --nogpgcheck https://mirror.sjtu.edu.cn/rpmfusion/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm https://mirror.sjtu.edu.cn/rpmfusion/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm

换源

sudo sed -e 's!^metalink=!#metalink=!g' \
         -e 's!^mirrorlist=!#mirrorlist=!g' \
         -e 's!^#baseurl=!baseurl=!g' \
         -e 's!https\?://download1\.rpmfusion\.org/!https://mirror.sjtu.edu.cn/rpmfusion/!g' \
         -i.bak /etc/yum.repos.d/rpmfusion*.repo

dnf 操作默认使用 [Y/n]

sudo sh -c "echo 'defaultyes=True' >> /etc/dnf/dnf.conf"

移除不想要的软件

libreoffice

sudo dnf remove libreoffice*

discover, flatpak

sudo dnf remove discover flatpak

podman

sudo dnf remove podman

关闭 selinux

sudo sed -i "s|SELINUX=enforcing|SELINUX=disabled|" /etc/selinux/config

vlc,mpv,ffmpeg (补全大部分编码器)

sudo dnf install vlc mpv ffmpeg --allowerasing

docker

sudo dnf install docker

rustdesk

直接去官方的 Github Release 下载安装包

sudo dnf install https://github.com/rustdesk/rustdesk/releases/download/1.3.2/rustdesk-1.3.2-0.x86_64.rpm

尽管 Rustdesk 支持被控端使用 wayland,但因为权限原因需要被控端手动选择被控区域,不适合无人值守的环境,因此还是要换 x11。

安装 x11 支持

sudo dnf install plasma-workspace-x11

使用 x11 启动 sddm

sudo sed -i "s|^#DisplayServer=wayland|DisplayServer=x11|" /etc/sddm.conf

开发相关

sudo dnf install gcc g++ python3-devel

解除 systemd-resolved 53 端口占用

编辑 /usr/lib/systemd/resolved.conf,取消注释,yes 改 no

[Resolve]
# Some examples of DNS servers which may be used for DNS= and FallbackDNS=:

//...

#DNS=
#FallbackDNS=
#Domains=
#DNSSEC=no
#DNSOverTLS=no
#MulticastDNS=yes
#LLMNR=yes
#Cache=yes
#CacheFromLocalhost=no
-#DNSStubListener=yes
+DNSStubListener=no
#DNSStubListenerExtra=
#ReadEtcHosts=yes
#ResolveUnicastSingleLabel=no
#StaleRetentionSec=0

配置 fcitx5

sudo dnf install fcitx5-chinese-addons kcm-fcitx5 fcitx5-autostart

在 设置 - 输入法 中添加拼音

安装词库

https://github.com/felixonmars/fcitx5-pinyin-zhwiki

https://github.com/outloudvi/mw2fcitx

在 wayland 下使用

参考 处理 fcitx5 的文字候选框在 tg 客户端上闪烁的问题

if [ ! "$XDG_SESSION_TYPE" = "tty" ]   # if this is a gui session (not tty)
then
    # let's use fcitx instead of fcitx5 to make flatpak happy
    # this may break behavior for users who have installed both
    # fcitx and fcitx5, let then change the file on their own

    export INPUT_METHOD=fcitx
    export GTK_IM_MODULE=fcitx
    export QT_IM_MODULE=fcitx
    export XMODIFIERS=@im=fcitx
fi
+if [ "$XDG_SESSION_TYPE" = "wayland" ]
+then
+        unset QT_IM_MODULE
+fi

然后仍然要去 设置 - 键盘 - 虚拟键盘 中选中 fcitx5

安装 vscode

sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com/yumrepos/vscode\nenabled=1\ngpgcheck=1\ngpgkey=https://packages.microsoft.com/keys/microsoft.asc" | sudo tee /etc/yum.repos.d/vscode.repo > /dev/null
sudo dnf install code

网络优化工具

禁用防火墙

sudo systemctl disable firewalld --now

RPM 构建

参考 以 Archlinux 中 makepkg 的方式打开 rpmbuild

🔲 ☆

kubernetes 部署 metric-server


作者:SRE运维博客
博客地址:https://www.cnsre.cn
文章地址:https://www.cnsre.cn/posts/210922013357/
相关话题:https://www.cnsre.cn/tags/kubernetes/


K8S 使用 kubectl top 看K8S监控
kubectl top 是基础命令,但是需要部署配套的组件才能获取到监控值
部署 metric-server

  • Kubernetes Metrics ServerCluster 的核心监控数据的聚合器,kubeadm 默认是不部署的。

  • Metrics ServerDashboard 等其他组件使用,是一个扩展的 APIServer,依赖于 API Aggregator。所以,在安装 Metrics Server 之前需要先在 kube-apiserver 中开启 API Aggregator

  • Metrics API 只可以查询当前的度量数据,并不保存历史数据。

  • Metrics API URI/apis/metrics.k8s.io/,在 k8s.io/metrics 下维护。

  • 必须部署 metrics-server 才能使用该 APImetrics-server 通过调用 kubelet Summary API 获取数据.

  • 不指定pod 名称,则显示命名空间下所有 pod,–containers可以显示 pod 内所有的container

  • 指标含义:

  • 和k8s中的request、limit一致,CPU单位100m=0.1 内存单位1Mi=1024Ki

  • pod的内存值是其实际使用量,也是做limit限制时判断oom的依据。pod的使用量等于其所有业务容器的总和,不包括 pause 容器,值等于cadvisr中的container_memory_working_set_bytes指标

  • node的值并不等于该node 上所有 pod 值的总和,也不等于直接在机器上运行 top 或 free 看到的值

要求

注意:使用 Metrics Server 有必备两个条件:

  • API Server 启用 Aggregator Routing 支持
  • API Server 能访问 Metrics Server Pod IP
  • 启用API Aggregator

安装 metric-server

下载yaml文件

可以通过运行以下命令下载最新的 Metrics Server 版本:

1
wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

兼容性:

指标服务器 指标 API 组/版本 支持的 Kubernetes 版本
0.6.x metrics.k8s.io/v1beta1 *1.19+
0.5.x metrics.k8s.io/v1beta1 *1.8+
0.4.x metrics.k8s.io/v1beta1 *1.8+
0.3.x metrics.k8s.io/v1beta1 1.8-1.21
  • 对于 <1.16 需要--authorization-always-allow-paths=/livez,/readyz

修改镜像地址

国内无法下载 k8s.gcr.io/metrics-server/metrics-server:v0.4.1镜像
需要修改为一下镜像地址

1
cnsre/metrics-server-metrics-server:v0.4.1

添加 –kubelet-insecure-tls参数

1
2
3
4
5
6
7
8
    spec:
      containers:
      - args:
        - --cert-dir=/tmp
        - --secure-port=4443
        - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
        - --kubelet-use-node-status-port
        - --kubelet-insecure-tls  # 新增内容

部署metrics-server服务

1
kubectl apply -f components.yaml

查看状态

1
2
3
[root@master ~]# kubectl -n kube-system get deploy metrics-server
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
metrics-server   1/1     1            1           9m39s

测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[root@master ~]# kubectl  top  node
NAME     CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%   
master   294m         8%     5971Mi          41%       
node1    198m         5%     3800Mi          26%       
[root@master ~]# kubectl  top  pods 
NAME                                     CPU(cores)   MEMORY(bytes)   
cnsre-deployment-cf6fddb9f-bvfdk         1m           1Mi             
cnsre-deployment-cf6fddb9f-d8spc         1m           1Mi             
cnsre-deployment-cf6fddb9f-nrrhz         1m           1Mi             
nfs-client-provisioner-fd74f99b4-gk8l4   4m           8Mi             
tomcat-deployment-66dc86bb8f-6xj28       2m           974Mi           
tomcat-deployment-66dc86bb8f-db66l       2m           884Mi  

作者:SRE运维博客
博客地址:https://www.cnsre.cn
文章地址:https://www.cnsre.cn/posts/210922013357/
相关话题:https://www.cnsre.cn/tags/kubernetes/


🔲 ⭐

在 Arch Linux 下配置使用 HP Laser 103w 打印机无线打印

我寝室有一台使用 wifi 连接的 HP Laser 103w 打印机,这些天刚好布置了新的 HomeServer,因此来记录一下这台打印机的配置过程,根据 HP 官网驱动包的名字「HP Laser 100 and HP Color Laser 150 Printer series Print Driver」推断,此过程应该能适用于所有的 HP Laser 100 及 HP Color Laser 150 系列的打印机。

打印机联网

首先使用 Windows 操作系统完成打印机的联网工作,在路由器的网页管理界面可以看到这台打印机的局域网 ip 是 192.168.123.20 ,记录备用。如果有条件的话,尽量将打印机的 MAC 地址与 IP 地址绑定,避免路由器将该 IP 分配给别的设备。

路由器的网页管理界面

安装 CUPS

随后按照 ArchWiki 的 CUPS 页面进行相关配置,CUPS 是苹果公司开源的打印系统,是目前 Linux 下最主流的打印方案。

首先安装 cups ,如果需要「打印为 pdf」的功能,可以选装 cups-pdf。

pacman -S cups
pacman -S cups-pdf

接着需要启动 cups 的服务,如果需要使用 cups 自带的 webui,可以直接启用 cups.service,这样就能在 http://localhost:631 看到对应的配置页面。

systemctl enable cups.service --now

而如果你正在使用一些集成度较高的 DE 如 KDE 或 GNOME,可以安装 DE 对应的打印机管理程序。在 Arch Linux 下,KDE 自带的打印机管理程序包名为 print-manager,此外还需要安装安装 system-config-printer 打印机功能支持软件包。这种方案则不需要启动 cups.service,只需要启动 cups.socket 即可。

pacman -S print-manager system-config-printer
systemctl enable cups.socket

杂项

在常规的流程中,通常会安装 ghostscript 来适应 Non-PDF 打印机,这台 HP Laser 103w 也不例外。

pacman -S ghostscript

如果是 PostScript 打印机可能还需要安装 gsfonts 包,但我这里不需要。

安装驱动

OpenPrinting 维护的 foomatic 为很大一部分打印机提供的驱动文件,Gutenprint 维护的 gutenprint 包也包含了佳能(Canon)、爱普生(Epson)、利盟(Lexmark)、索尼(Sony)、奥林巴斯(Olympus) 以及 PCL 打印机的驱动程序。如果你的打印机型号和我的不同,可以尝试安装这些组织维护的驱动。具体的安装方法同样可以在 ArchWiki 的 CUPS 页面找到。我上一台打印机 HP LaserJet 1020 所需的驱动是在 AUR/foo2zjs-nightly 中取得的。

但 HP Laser 103w 的驱动程序都不在这些软件包中,在 HP 的官网我们可以找到这个页面,包含了 HP Laser 103w 的 Linux 驱动下载地址(已在 web.archive.org 存档)。通过下载下来的文件名,我们可以看见名字为 uld-hp,理论上可以直接通过压缩包内的安装脚本进行安装,但我通过这个名字顺藤摸瓜,找到了 AUR/hpuld 可以直接进行安装。

yay -S hpuld

添加打印机

打开设置中的打印机设置后,选择添加打印机,CUPS 直接帮我们找到了局域网下的打印机,并自动开始搜索驱动程序(虽然没搜到)。

自动搜索

但如果没能自动检测到打印机,也可以使用手动选项中的 AppSocket/HP JetDirect 手动输入打印机的 ip 地址进行配置。

紧接着就到了选择驱动程序的阶段,厂商选择 HP,能够找到「HP Laser 10x Series」的选项,直接选择。

选择驱动

接着就可以完成打印机的添加。

完成添加

随后便能正常打印文件啦!

🔲 ⭐

使用动态公网 ip + ddns 实现 rustdesk 的 ip 直连

最近跟风整了一台 n100 的迷你主机装了个 Archlinux 当 HomeServer,搭配上了显卡欺骗器,平常一直远程使用,因此需要实现稳定的远程桌面连接。开源软件 Rustdesk 本身对 Linux 的适配尚可,可惜官方提供的服务器位于境外,且前一阵子因为诈骗相关的风波使得官方对连接做出了一些限制,应当使用自建服务器或者 ip 直连。

单从网络安全的角度出发,最佳实践应该是通过 wireguard 或者别的协议先接入局域网,然后使用局域网内的 ip 直连,这是最稳妥的,但我有点懒,而且我可能会在多个设备上都有控制 HomeServer 的需求,给所有设备配置 wireguard 是一件挺麻烦的事情,因此我决定放弃安全性,直接公网裸奔。

在学校宿舍的电信宽带提供了一个动态公网 ip,因此只需要设置好 ddns 和端口转发就可以拿到一个固定的 domain + port 提供给 rustdesk 直连。

在被控端 Rustdesk 允许直连访问

在「设置」中的「安全」一栏选择「解锁安全设置」,拉到最下面的「安全」栏,勾选「允许 IP 直接访问」,并选择一个端口,范围在 1000 ~35535 之间且不要被本地的其他程序占用,Rustdesk 的默认值为 21118。

可以直接在局域网内的另一台设备进行测试,直接在 Rustdesk 中输入被控端的局域网 ip 和刚刚设置的端口,看看能不能访问得通,如果不行可能需要排查一下被控端访问墙设置的问题。

ddns

由于我的域名是交给 cloudflare 进行解析的,就找了个支持 cloudflare 的 ddns 脚本,大致的部署过程可以参考 「自建基于Cloudflare的DDNS」,不过我小改了一下脚本中获取公网 ipv4 的方式,直接 ssh 到路由器上获取当前的 ipv4 地址,不依赖外部的服务。

WAN_IP=`ssh -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa root@192.168.1.1 'ip -br a' | grep pppoe-wan | awk '{print $3}'`

理论上来说,有不少路由器自身就支持不少域名解析商

端口转发

端口转发需要在路由器的后台设置进行,我这里路由器使用的是 openwrt 系统,大部分路由器应该都支持这个操作。

在「网络」-「防火墙」

选择「端口转发」

新建端口转发,共享名随便填,外部端口是你最终要在主控端输入的端口,内部 IP 地址是被控机 的 IP 地址,可以用 ip -br a 命令看到,内部端口就是上文在 Rustdesk 指定的端口号。

效果

可以直接在主控端口输入 ddns 的域名和端口号,实现远程控制

🔲 ⭐

在Windows Server上启用Webdav

为什么要用Webdav

首先,你得知道我们平常用的SMB文件共享服务用的445端口,一般是不对外开放的。云服务器也是如此。而webdav可以通过http服务来访问你的文件,甚至我在家里用Cloudflare Tunnel都可以穿出去访问,非常地方便。

所以对于我家里的小霸王服务器,自我换成Windows Server后,需要在外访问的话就需要打开Webdav

安装Webdav服务

本次使用的是Windows Server 2022

首先先打开Windows Server自带的服务器管理器,选择添加角色和功能,在服务器角色选项卡添加web 服务器(IIS)

然后跳到功能选项卡,勾选Webdav重定向服务

再在下面Web 服务器角色(IIS)选项卡下的角色服务添加Windows 身份验证WebDAV 发布

然后点下一步,把这些功能装上,这个过程可能有点长,装好了记得重启一下,记得先保存一下工作(把虚拟机啥的挂起一下)

添加WebDAV服务器

重启完了以后,我们还是打开服务器管理器,在右上角的工具里面找到IIS工具

然后添加一个网站,物理路径就是你想要共享的文件夹位置,记得改下端口(当然你不介意用80的话可以不改)

设置好了点确定,然后双击我们刚刚添加的网站,找到WebDav创作规则

然后在右侧添加一个创作规则,具体配置按需配置,你也可以按照我这么选,然后点确定

设置身份验证

既然是WebDAV,那肯定得加上身份验证

我们双击左边树状图里我们的网站,然后选择身份验证

如果你需要匿名登录你就保持匿名为启用(注意做好目录限制),然后把第二个Windows 身份验证给打开

开启目录浏览

再次双击左边的树状图里的网站,找到目录浏览,点击右边的启用

搞定了以后,我们双击树状图里我们的网站,在右边重启一下我们的网站

测试网站

重启了以后,我们在浏览器里访问我们的网址(IP地址:端口),如果弹出像我这样的身份验证页面(或者直接不弹,直接看到了目录,取决于你是否开了匿名访问),登陆完进去看到目录树,就是成功了

如果出现了500错误,然后详细信息里面写由于权限不足而无法读取配置文件的话,你就需要右键你网站的目录,选择安全,把你的用户加进去

设置MINE类型

如果你不设置MINE类型的话,会导致你点一个非主流后缀名的文件,出现404(注:web.config文件本身请求就会404,不是MINE问题)

我们还是去到IIS,在自己的网站配置下找到MINE类型这个选项

在右边点添加,然后按我这么填(一劳永逸)

上面填.*,下面填application/octet-stream

然后重启一下网站就可以了

使用Cloudflare Tunnel映射

首先你电脑得装Cloudflared,关于这东西的用法在这里不多讲,这里假设你已经装好了

我们去到Zero Trust,然后去到Tunnel

找到自己的服务器,进去后在Public Hostname添加一个网站,可以按我这么填(上面域名啥的就填自己的了)

然后我们再测试从Cloudflare的Tunnel那边访问一下,确认正常

❌