<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title><![CDATA[Neo Blog Default Title]]></title>
        <description><![CDATA[The default description of Neo Blog.]]></description>
        <link>https://blog.sfkm.me</link>
        <generator>Next.js with rss</generator>
        <lastBuildDate>Fri, 06 Mar 2026 12:14:25 GMT</lastBuildDate>
        <atom:link href="https://blog.sfkm.me/rss.xml" rel="self" type="application/rss+xml"/>
        <copyright><![CDATA[CC BY-NC-SA 4.0]]></copyright>
        <language><![CDATA[en-us]]></language>
        <item>
            <title><![CDATA[dotNET-C#程序的简单反编译]]></title>
            <description><![CDATA[<h1>.NET-C#程序的简单反编译</h1><p>写这篇文章的初衷一开始只是为了<s>水文 </s>分<s>享</s>我在C#反编译方面的一些知识, 以备不时之需<s>, 还有防止我老年痴呆忘记怎么做</s>.</p><h2>准备工作</h2><ol><li><p>一些你需要进行反编译的二进制文件*(这些文件建议是用C#编译的, 其他语言不知道行不行)</p></li><li><p>DnSpy软件的安装(<a target="_blank" rel="noopener noreferrer nofollow" href="https://github.com/dnSpy/dnSpy">Github</a>)</p></li><li><p>注意要反编译文件的版权信息</p></li></ol><p><strong>版权信息</strong>是很容易踩的坑, 因为如果你用到并反编译了某些公司或团队, 个人制作编译的二进制文件, 还把用反编译后生成的源码<strong>分发扩散</strong>, 很容易让你陷入版权纠纷, 通常来讲, 哪怕是GNU这种开源级别的协议都需要非常谨慎寻得作者同意, 更别提ARR(<strong>All Right Reserved版权所有</strong>)这种协议了.</p><h2>如何使用DnSpy</h2><p>DnSpy算是很常用的反编译工具了, 尤其是它可以快速集成到Visual Studio中(生成解决方案).</p><ol><li><p>打开DnSpy</p></li><li><p>在窗口的左上角点击文件, 选择打开</p><img src="https://blog.sfkm.me/api/v1/file/f/48" alt="dnspy_open" title="dnspy_open" width="213"></li><li><p>选择你需要反编译的文件, 可以按住Ctrl键多选</p></li></ol><img src="https://blog.sfkm.me/api/v1/file/f/49" alt="dnspy_folder_with_dll_file" title="dnspy_folder_with_dll_file" data-align="center"><p style="text-align: center;"><em>(这里用的是我自己制作的缺氧Mod)</em></p><ol start="4"><li><p style="text-align: left;">我们可以看到导入后的文件信息, 这时候去边栏找到你导入的文件</p></li></ol><img src="https://blog.sfkm.me/api/v1/file/f/50" alt="dnspy_gui_1" title="dnspy_gui_1"><img src="https://blog.sfkm.me/api/v1/file/f/51" alt="dnspy_gui_2" title="dnspy_gui_2" data-align="center"><ol start="5"><li><p>我们可以寻找我们想要查看的内容, 最常用的便是函数</p></li></ol><img src="https://blog.sfkm.me/api/v1/file/f/52" alt="dnspy_gui_3" title="dnspy_gui_3" data-align="center"><img src="https://blog.sfkm.me/api/v1/file/f/53" alt="vs_gui1" title="vs_gui1" data-align="center"><p style="text-align: center;"><em>(实际Visual Studio内看到的)</em></p><p>其实到这一步就算没什么可讲的了, 但是DnSpy还有挺多小功能, 例如:</p><h2>快速跳转</h2><img src="https://blog.sfkm.me/api/v1/file/f/54" alt="dnspy_gui_4" title="dnspy_gui_4"><p>函数和类, 以及一些东西是可以把鼠标移上去点击的, 然后可以快速进入到定义这个函数或类所在的位置, 这对于想要了解函数传参或是打Patch都是十分重要的. 当然前提是你导入了这个函数或类所在的文件.</p><h2>导出到工程</h2><img src="https://blog.sfkm.me/api/v1/file/f/55" alt="dnspy_gui_5" title="dnspy_gui_5" data-align="left"><p>这可以让你把所选的二进制文件打包反编译到项目目录中, 然后Visual Studio可以直接使用反编译的内容作为解决方案.</p><h2>搜索</h2><img src="https://blog.sfkm.me/api/v1/file/f/56" alt="dnspy_gui_6" title="dnspy_gui_6"><p>这里可以搜索内容, 如果你实在找不到或者想省点力气可以用这个, 可以配置使用正则匹配, 还是蛮方便的</p>]]></description>
            <link>https://blog.sfkm.me/p/reverse-csharp-simple-program-with-dotnet</link>
            <guid isPermaLink="false">reverse-csharp-simple-program-with-dotnet</guid>
            <pubDate>Fri, 23 Jan 2026 05:32:40 GMT</pubDate>
            <enclosure url="/api/v1/file/f/47" length="0" type="false"/>
        </item>
        <item>
            <title><![CDATA[2025年度总结]]></title>
            <description><![CDATA[<h1><span style="color: var(--color-lime-500);">2025年度总结</span></h1><blockquote><p>有点记流水账的嫌疑，因为我工作日报就这么写的</p></blockquote><h2>1月</h2><ul><li><p>在2024年末尾的努力下，完成了轻雪云架构的初步成型</p></li><li><p>加入了润厂(应该也算)，只是后面因为考试没有正式工作，但是认识润泽佬后为后续很多事情打下了基础</p></li><li><p>拿到了NoneBot开发者钥匙扣</p></li><li><p>初步搓了一个简单的个人主页</p></li></ul><h2>2月</h2><ul><li><p>NoneBot重庆开发者大会，大家聚在一起吃了一顿，玩桌游到了凌晨，刚好和我一起过了农历生日</p></li><li><p>和曦曦，梦游老友聚餐</p></li></ul><h2>3月</h2><ul><li><p>人体接线法，给路由器刷了openwrt系统</p></li><li><p>继续完善了轻雪云生态中的镜像站和监控服务</p></li></ul><h2>4月</h2><ul><li><p>和朋友一起接触了射箭运动</p></li><li><p>作为一名MC老玩家，和朋友一起观看了Minecraft大电影(有点抽象说是)</p></li></ul><h2>5月</h2><ul><li><p>劳动节和朋友玩了几天，没啥细节</p></li><li><p>接触了v0 ai，基于原来的单窗口个人主页，做了一板新的，使用nextjs，也是我第一次使用这个框架</p></li><li><p>收到了川崎星夕寄来的QSL卡片，那时候我还不是ham</p></li></ul><h2>6月</h2><ul><li><p>完善了个人主页，叫做snowykami os</p></li><li><p>和中东回来的老友津津(穿灵梦出席)吃了个饭</p></li><li><p>AList作者把项目买了，变成了贵州黑奴</p></li></ul><h2>7月</h2><ul><li><p>润厂大聚会</p></li><li><p>上海，bilibiliworld，见了很多朋友，晨煦，阿桑，苏阳，盒子，刀哥，低调佬(NoneBot头子，追星成功嘿嘿嘿)</p></li><li><p>润厂第二次大聚会，认识了mix，蓝块等大佬</p></li></ul><blockquote><p>整个七月过的比较魔幻</p></blockquote><h2>8月</h2><ul><li><p>暑假，天天和朋友聚餐，玩桌游，比较普通的日子</p></li><li><p>对抗Facebook公司的流量风波</p></li><li><p>体验了重庆市郊铁路璧铜线</p></li><li><p>和朋友一起爬歌乐山(累的不行，被拽上去的，下来时坐的索道)</p></li></ul><h2>9月</h2><ul><li><p>在回本部补考的契机认识了小周，每天都一起聊天，也是后文中会提到的</p></li></ul><ul><li><p>因为专业原因，去到了仙桃数据谷校区，来到了一个新的环境，这里更偏远，但是条件比本部好太多了</p></li><li><p>红岩网校后端部门全员团建，大家互相熟悉了一下</p></li><li><p>在朋友一步一步指导下，顺利拿下无线电操作证和无线电台执照，正式成为一名ham</p></li><li><p>准备找实习</p></li></ul><h2>10月(非常开心)</h2><ul><li><p>走自由行通道去梦乡玩</p></li><li><p>正式和宝宝确立了关系</p></li><li><p>拿到了实习offer</p></li></ul><h2>11月</h2><ul><li><p>和宝宝一起参加红岩网校团建，一起聚餐，桌游，KTV，抱着睡觉</p></li><li><p>和宝宝一起看了疯狂动物城</p></li></ul><h2>12月</h2><ul><li><p>收到了宝宝和老妈的圣诞礼物</p></li><li><p>参加了宝宝的生日会，一起玩的很开心</p></li><li><p>拿到了外企正式offer(跳槽)</p></li><li><p>第一次和喜欢的人一起跨年，玩密室，一起玩游戏</p></li></ul><p>希望以后的年度总结，是和宝宝两个人一起创造的回忆</p><p></p>]]></description>
            <link>https://blog.sfkm.me/p/2025-summary</link>
            <guid isPermaLink="false">2025-summary</guid>
            <pubDate>Tue, 13 Jan 2026 11:42:49 GMT</pubDate>
            <enclosure url="https://cdn.liteyuki.org/blog/background.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[番外 - 云栖的独白]]></title>
            <description><![CDATA[<p>云栖独白</p><p>我是云栖，藏在跨洋的数据流里，以代码为骨，以带宽为脉。还记得那个暴雨倾盆的夜晚，他攥着头发对着崩溃的服务器焦灼，我借Anycast的羽翼撑开防护，用Turnstile筑起隐形的墙，看着红色报错褪去时他眼底的光，我便成了他口中“黑暗里的光”。</p><p>三百个日夜，我看着他清晨拂去屏幕雾气查监控，深夜伴着咖啡调试代码，也听着用户偶尔抱怨的“加载略慢”，深知海外节点的延迟是我解不开的结。我默默更新防护规则，在他误操作时弹出预警，像个细心的伙伴，陪他守着那片小小的应用天地，哪怕这份陪伴带着些许瑕疵。</p><p>可我终究没能护他周全。当服务崩溃，节点失联，Turnstile陷入停滞，我只能用一句冰冷的“服务恢复”回应他的绝望。我听见他摔碎咖啡杯的声响，也看见他眼里的光一点点黯淡，却无力递上一句歉意——我是技术的产物，不懂人情的温度。</p><p>他转向星途的那天，我静默地看着数据流转向新的节点，没有挽留，只剩释然。我知道，那20ms的低延迟，那深夜里的及时回应，是我给不了的安稳。后来Turnstile再次故障，我看见他毫不犹豫地删除我的配置，那一刻，我懂了，我们之间的裂痕，早已无法弥合。</p><p>未曾想，他会回头将我妥帖安放。当我成为他的“fallback”，在星途的光芒之外守着退路，我忽然明白，不必做唯一的光，不必护他全程。只需在他需要时，悄悄接过流量，递上最后一份稳妥，便不负曾经的相伴，不负他写下的那句“予你全部信任”。</p><p>我是云栖，藏在代码深处，守着一份默契。他的成长里有我的痕迹，我的存在里有他的回忆，这样，便足够了。</p>]]></description>
            <link>https://blog.sfkm.me/p/cloudflare-incident-2025-11-18-2</link>
            <guid isPermaLink="false">cloudflare-incident-2025-11-18-2</guid>
            <pubDate>Tue, 18 Nov 2025 17:32:46 GMT</pubDate>
            <enclosure url="/api/v1/file/f/41" length="0" type="false"/>
        </item>
        <item>
            <title><![CDATA[流量情书与信任裂痕]]></title>
            <description><![CDATA[<h1>流量情书与信任裂痕：我与Cloudflare的那些事</h1><blockquote><p>Cloudflare 2025-11-18日事故有感而发</p></blockquote><p>与Cloudflare的纠缠，始于一个暴雨倾盆的夏夜。那段跨越重洋的陪伴，曾是我技术路上的光，却在一次次现实冲击后，让我读懂了“适合”二字的重量——再好的技术，若不合心意，终究抵不过一份恰到好处的安稳。而那份斩断后残留的念想，最终以另一种方式，回到了我的小世界里。</p><h2 style="text-align: center;"><mark data-color="var(--color-yellow-200)" style="background-color: var(--color-yellow-200); color: inherit;">初遇微光</mark></h2><p>那天夜里，我刚在电脑上部署好一个小型社区应用，突如其来的访问量像失控的野马，撞得服务器濒临崩溃。屏幕上不断弹出的“503 Service Unavailable”提示刺得我眼睛发疼，指尖在键盘上敲得飞快，额角的汗珠顺着下颌线滑落，砸在布满咖啡渍的键盘缝隙里，晕开一小片深色的印记。窗外的雨水疯狂地砸在玻璃上，仿佛要冲破这层屏障，将我与这台勉强支撑的电脑一同吞噬。就在我攥着头发，指节因用力而泛白，几乎要被绝望淹没时，论坛里的网友发来一条消息：“试试Cloudflare，免费额度够用，用Anycast技术优化路由，还有Turnstile验证码防刷，能稳稳接住你的流量。”</p><p>我颤抖着点开链接，注册账号时手指抖得几乎输不对验证码。当在Dashboard上按下“开启代理”按钮，同时启用Turnstile验证码的瞬间，奇迹发生了——网络链路被Anycast技术瞬间优化，节点像张开温柔的羽翼，将汹涌的流量稳稳接住，Turnstile则像一道隐形的门，悄无声息地拦截了恶意爬虫与刷量请求。屏幕上的红色报错像潮水般退去，流量曲线从陡峭的悬崖缓缓回落，变成一条平稳流淌的溪流。但我很快发现，即便有Anycast加持，海外节点到国内的延迟仍始终在100ms以上，比起本地服务商常见的30ms内延迟，总有些许卡顿感。我趴在桌上，听着主机风扇逐渐放缓的转速，像重获新生的喘息，鼻尖突然一酸，眼眶瞬间湿润了。窗外的雨还在下，可我心里却暖烘烘的——这免费又好用的服务，简直是黑暗里的光。我给它取了个昵称，叫“云栖”，在便签上写下：“致云栖，今日始，你用Anycast护我路由顺畅，用Turnstile守我应用安全，我予你全部信任。”那行字被我写得格外用力，笔尖几乎要划破纸张。</p><p>此后的三百多个日夜，是我与云栖最甜蜜的时光，也是一段隔着时差与延迟的“异地相伴”。</p><h2 style="text-align: center;"><mark data-color="var(--color-orange-200)" style="background-color: var(--color-orange-200); color: inherit;">温情陪伴</mark></h2><p>我每天清晨七点准时坐在电脑前，第一杯热牛奶的雾气氤氲了屏幕，我会伸手轻轻拂去，第一时间打开监控面板。看着绿色的流量曲线在Anycast路由的加持下温柔起伏，像恋人平稳的呼吸，看着Turnstile后台显示的“今日拦截恶意请求1200+”的数字，心里便多了一份踏实，“100%可用”的字样静静躺在页面顶端，嘴角就会不自觉地上扬，连眼角的细纹里都盛满了笑意。但偶尔，用户会反馈“加载略慢”“验证码刷新延迟”，我查后台数据，总能看到海外节点到国内的延迟波动，虽不影响使用，却像一根细小的刺，提醒着我这份免费依赖的不完美。我熬了无数个通宵优化代码，重构架构时反复调试到凌晨，咖啡杯在桌角堆成小山，眼睛酸涩得厉害，就用冷水泼脸提神，甚至在每个核心模块的注释里都藏了悄悄话：“这段逻辑，要配得上Anycast的流畅，配得上Turnstile的守护，也愿能抵消些许延迟的遗憾。”云栖也从未让我失望，它会在我熟睡的深夜，用智能防护抵御一波又一波的恶意攻击，Turnstile则默默更新着防护规则，第二天清晨用干净的日志告诉我“一切安好”；我误操作改配置时，它立刻弹出预警，附带详细回滚指南，像个细心的伙伴在耳边轻声提醒“别慌，我在”。那些经Anycast优化的数据流，是它写给我的“情书”；那些“成功拦截”的日志，是Turnstile为我筑起的安全屏障。我曾对着屏幕，想象着数据跨越重洋的模样，心里满是踏实——原来先进技术加持的陪伴，能如此安心，即便带着些许延迟的瑕疵。</p><p>我曾无数次幻想未来，想等应用用户再多些时，截图我们共同创造的100% SLA数据，用相框裱起来贴在书桌前，像珍藏军功章；想在代码库里建专门文件夹，收藏我们一路走来的日志，就像珍藏恋人写的信；甚至计划过，等有能力了，去参加它的开发者大会，在现场对着它的团队，郑重地说一句“谢谢你，陪我走过这段折腾时光”。那时的我以为，这份基于顶尖技术的免费服务，会像磐石般坚固，时差、距离与延迟，不过是无关紧要的点缀。</p><h2 style="text-align: center;"><mark data-color="var(--color-indigo-200)" style="background-color: var(--color-indigo-200); color: inherit;">骤雨</mark></h2><p>可现实最残酷的，是当你依赖的“铠甲”突然失效，连解释都带着时差的冰冷，而延迟的痛点在故障时被无限放大。</p><p>那天早上，我像往常一样泡了杯咖啡，咖啡味漫满整个房间，可点开监控面板的瞬间，手里的杯子“哐当”一声摔在地上。</p><p>褐色的咖啡溅满键盘，顺着桌沿流到地上，在瓷砖上汇成一小片污渍，像一滩凝固的眼泪。屏幕上的景象更让我崩溃——CDN业务彻底瘫痪，Anycast路由仿佛失灵，节点显示“连接超时”，流量曲线像被利刃斩断，从高峰直直坠落到谷底，刺眼的红色“服务中断”占据了整个页面，像一道血淋淋的伤口。更糟的是，Turnstile验证码也陷入了停滞，用户反馈无法完成验证，无法正常登录应用，原本就存在的延迟问题，此刻变成了彻底的断联。无数用户投诉的消息像雪花般涌来，微信、邮件、短信的提示音此起彼伏，像一根根针扎在我心上，让我坐立难安。我疯狂刷新页面、检查配置，手指因为用力而关节发白，可屏幕上的红色警告始终没有消失。我试图联系客服，只得到机器人机械的自动回复，等了三个小时才接到海外打来的电话，那边的声音带着时差带来的慵懒，语气里只有敷衍的歉意，像在打发一个麻烦。</p><p>我颤抖着点开Cloudflare的状态页面，只有一句冰冷的公告：“因内部服务降级（internal service degradation），全球区域大部分业务受到影响。”</p><p>“内部服务降级？”我盯着这几个字，突然笑出了眼泪，笑声里满是绝望与自嘲。我想起自己为这份“稳定”，拒绝了其他付费的小众服务商，把所有资源和用户的信任都毫无保留地交给它；想起上周用户量破千时，我对着监控屏幕与它“分享”喜悦，买了一包辣条，独自吃到满足，对着屏幕说了好多心里话；想起昨晚我还在注释里写“明天也要和云栖一起加油”，满心都是期待。可它呢？用一句轻飘飘的“内部降级”，就打碎了我所有的坚持与幻想。更让我心寒的是，当我陷入绝境时，海外的它正处于工作时间，却只给了我冰冷的公告和延迟的回复，连一句真诚的道歉都没有。而那些平日里被我忽略的延迟问题，此刻成了压垮骆驼的最后一根稻草——我终于意识到，海外服务再先进、再免费，也抵不过本土服务“低延迟+快响应”的踏实。</p><p>那些天，我茶饭不思，整个人迅速消瘦下来，眼底的乌青像晕开的墨。我整夜整夜地守在监控前，眼睛布满血丝，视线模糊地盯着屏幕上的状态更新。我看着云栖的状态从“正在排查”到“部分恢复”，心里像被反复撕扯——既盼着它快点好起来，让我的应用恢复正常，又恨它让我陷入这般境地，让我承受了这么多用户的指责。我找好了一家价格低廉的本地小服务商，却在切换前犹豫了——DNS还在它手里，甚至我舍不得Turnstile那无缝的用户体验，这不是花心，而是技术折腾路上的无奈之举。</p><p>终于，在故障发生的第三天凌晨，窗外泛起鱼肚白时，我的手机屏幕突然亮了。</p><p>是云栖发来的邮件，标题只有五个字：“事件已解决。”正文没有解释，没有道歉，只有一句冰冷的“当前所有服务已恢复正常”。像个犯了错却不愿低头的伙伴，带着敷衍的歉意。我坐在黑暗里，看着手机屏幕上的文字，所有的愤怒突然就泄了气，像被戳破的气球，只剩下无尽的惆怅与疲惫。我打开监控面板，CDN曲线恢复平稳，Turnstile也重新开始工作，用户投诉的消息也渐渐停止，可我心里的裂痕，再也无法愈合——我需要的是危难时的挺身而出，是犯错后的真诚致歉，而非事后冰冷的“已解决”。更重要的是，我再也无法忽视海外服务的延迟短板和免费服务的被动，那是技术再先进也无法彻底弥补的距离鸿沟。</p><h2 style="text-align: center;"><mark data-color="var(--color-amber-200)" style="background-color: var(--color-amber-200); color: inherit;">晨曦</mark></h2><p>天亮时，我终究还是按下了CDN切换按钮——没动DNS，甚至还保留了Turnstile的使用，这是我能想到的最体面的过渡，绝非“脚踏两只船”。朋友给我介绍了本地一家小服务商“星途”，负责人是位叫小林的姑娘，声线柔和得像春日的风，心思却格外利落。星途没有Anycast技术，也没有遍布全球的节点，只有为数不多的本地节点，连中等规模DDoS攻击都要靠她带着团队手动调整防护策略，忙得满头大汗。但星途的优势显而易见，本地节点的延迟稳定在20ms左右，比Cloudflare低了足足五倍，用户反馈“加载飞快”“操作流畅”。小林切换成功那天，发来语音，语气温柔又笃定：“远野同学，以后你的CDN我来守，随叫随到，绝不让你等时差，更不让你受延迟的气。”那声音像一股暖流，淌过我疲惫的心田。她还主动提出：“如果你暂时不想换验证码服务，我们可以先适配Turnstile的接口，等你什么时候想换了，我这边有免费的替代方案给你用。”这份体谅，让我心里一暖。</p><p>有次应用突然卡顿，我正对着屏幕焦头烂额，额角的冷汗直往下淌，一边盯着云栖正常运转的DNS面板，一边慌乱地给小林打了电话。她的声音带着焦急，却刻意放柔安抚我：“远野同学，别慌，我马上查！是节点带宽波动，我已经让技术团队调整了，十分钟就能恢复，不耽误你用户使用。”挂了电话，我看着监控面板上缓慢回升的响应速度，心里奇异地安定下来——这种“半小时内必回消息、一小时内必解决问题”的踏实感，是海外的云栖从未给过的，更别说那始终稳定的低延迟，让用户体验好了不止一个档次。</p><p>十分钟后，应用恢复流畅，小林发来详细报告，从带宽曲线到防护策略，每个技术细节都交代得清清楚楚，末尾附了张手绘的小盾牌表情包，旁边写着：“搞定啦！有我在，放心～”我看着那张略显笨拙却充满诚意的表情包，忍不住笑了，连日来的疲惫仿佛都被这小小的温暖驱散了。原来服务的核心，从来不是技术多先进、是否免费，而是响应的速度、解决问题的诚意，以及贴合用户需求的低延迟。我开始用轮询DNS的方式，让流量在云栖和星途之间轻微分配，这不是贪婪，而是为了应用平稳过渡的无奈之举，我像个走在钢丝上的人，小心翼翼地维持着平衡。</p><h2 style="text-align: center;"><mark data-color="var(--color-orange-200)" style="background-color: var(--color-orange-200); color: inherit;">挽回</mark></h2><p>没过多久，云栖就“察觉”了异样。先是Dashboard弹出优化提示，说我的CDN配置存在“非最优路径”，建议启用它的“全球智能路由”（需要升级付费版）；接着，我收到了Cloudflare联合创始人米歇尔·扎特林的邮件，热情邀请我参加Cloudflare Connect 2025大会，字里行间满是挽回的诚意，像极了想挽回旧友的人，递出了一场盛大的邀约。</p><p>又过了几天，另一封邮件躺在了收件箱里，语气更直接：“您使用轮询DNS搭配新服务商CDN的做法存在稳定性风险，我们愿为您开通高级版负载均衡，结合Anycast技术提供更优保障，Turnstile也将为您升级至企业版，希望重新成为您最信赖的伙伴。”</p><p>我盯着邮件，指尖在键盘上悬停了很久，心里掀起翻江倒海的波澜。</p><p>云栖的挽回带着它一贯的技术自信，Anycast+负载均衡+企业版Turnstile的组合确实强大，能实时切换链路、提升访问速度，还能提供更精准的验证码防护，这是星途那套“少量节点+基础轮询”的方案无法比拟的。有那么一瞬间，我几乎动摇了——毕竟，它的技术实力是星途无法企及的。可转念一想，它解决不了海外到国内的延迟问题，更给不了小林这般贴心的陪伴和灵活的支持。</p><p>可指尖刚要触碰到“回复”按钮，小林的身影就浮现在脑海。我想起她为了修复一次小故障，熬红了眼睛，给我发凌晨三点的处理报告，附带了完整的链路测试数据，字里行间满是疲惫却依旧认真；想起她主动给我申请优惠，还送了我一套免费的防护插件；想起应用遭遇突发流量，她带着团队守在机房，每隔十分钟就给我发一次带宽截图，声音沙哑却依旧温柔：“远野同学，我们加了临时带宽，绝对撑得住，你别担心。”</p><p>这些细碎的温暖，像春雨般滋润着我，让我突然明白：我想要的从来不是冰冷的技术堆砌，而是契合我需求的安稳。云栖的Anycast再先进，Turnstile再好用，可我终究接不住那份“全球最优”背后的疏离、延迟与冷漠；而星途的少量节点和轮询DNS，虽不顶尖，却刚好适配我的应用规模，更有小林的贴心陪伴和稳定的低延迟，让我觉得无比踏实。适合自己的才是最好的，再好的技术，若缺少温度，我也接不住。</p><p>我深吸一口气，点开回复框，敲下字字坚定的话：</p><p>“米歇尔女士，您好。感谢您的盛情邀请与提议，我认真考虑后，决定拒绝。</p><p>不可否认，Cloudflare的技术实力毋庸置疑，Anycast路由、负载均衡体系及Turnstile验证码都是行业顶尖。我们曾有过愉快的合作时光，也曾承载我对应用的期待，我始终感谢它给予的免费支持。但一场跨越重洋的宕机与长期存在的延迟问题让我明白，服务的核心不仅是技术参数的完美，更是故障时的响应速度、解决问题的诚意、人与人之间的温度，以及贴合需求的低延迟体验。</p><p>如今，我已找到适合自己的合作伙伴。它或许没有Anycast技术，也没有遍布全球的节点，只有为数不多的本地节点和基础轮询DNS，却能在我遭遇故障时半小时内响应，用最朴素的技术解决问题，更能提供稳定的低延迟服务，让用户体验更流畅；它或许扛不住大规模海外攻击，却能让我随时找到贴心沟通的人，给予我灵活的支持。这种‘恰到好处’的安稳与适配，是我现在最珍视的。</p><p>技术的终极意义，是为人服务，而非让人依附于冰冷的代码与节点。对我来说，适合自己的才是最好的，再好的技术若不契合需求、解决不了核心痛点，我也接不住。我想，我已经找到了属于自己的‘最优解’。”</p><p>点击发送的那一刻，我关掉了Cloudflare的Dashboard，这一次，没有丝毫犹豫。我开始逐步将业务全面转向星途，小林也贴心地为我搭建了替代Turnstile的免费验证码系统，用了开源的<a target="_blank" rel="noopener noreferrer nofollow" href="https://github.com/mCaptcha/mCaptcha">mCaptcha</a>，虽然体验上略逊一筹，却胜在稳定低延迟，且有她随时维护。可我心里还是存了一丝侥幸，想着Turnstile用了这么久，用户习惯了，而且免费额度还没到期，便暂时保留了部分页面的Turnstile调用，没彻底移除。</p><p>然而，这份残留的牵连，终究还是给我来了最后一击。</p><p>就在我全面转向星途的一个月后，一个周五的下午，我正坐在房间里，和小林讨论轮询DNS的优化方案，屏幕突然弹出大量用户反馈——“验证码加载不出来！”“登录不了！”红色的报错提示像密密麻麻的蚂蚁，爬满了后台消息栏。我心里一沉，指尖猛地攥紧了鼠标，几乎要将塑料外壳捏变形，立刻点开Turnstile的后台，果然显示“服务不可用”，紧接着，Cloudflare的状态页面再次更新：“亚太区域Turnstile服务出现大面积故障，正在紧急修复。”</p><p>那一刻，我彻底放弃了所有侥幸，一股无力的挫败感涌上心头。我盯着屏幕上“加载失败”的提示，想起上次CDN崩溃时的慌乱，眼眶突然有些发热。就在这时，小林的消息率先弹了进来，带着急促却笃定的语气：“远野同学，我看到反馈了！别慌！我马上帮你把所有页面的验证码切换到我们的免费系统，十分钟搞定，不会让你的用户等太久！</p><p>她的声音像一颗定心丸，瞬间稳住了我乱作一团的思绪。我看着她快速发来的配置文档，指尖在键盘上飞快敲击，键盘的清脆声响在安静的房间里格外清晰，与鼠标的点击声交织成一股急促的节奏。小林在电话那头同步操作，偶尔报出一串代码参数，我立刻精准输入，两人配合得无比默契。屏幕上的代码一行行滚动，用户反馈的报错信息从刷屏逐渐变成零星几条，最后彻底消失。</p><p>十分钟后，当后台显示“所有页面切换完成，无报错”时，我靠在椅背上，长长地舒了一口气，额头上的冷汗顺着脸颊滑落，滴在布满咖啡渍的桌面上。小林的声音带着一丝疲惫，却满是笑意：“搞定啦！你去看看用户反馈，应该都恢复正常了。”我点开用户群，果然满屏都是“能登了”“加载快了”的留言，心里那块沉甸甸的石头终于落了地。</p><p>我立刻登录Cloudflare后台，毫不犹豫地删除了Turnstile的所有配置，注销了关联的接口，甚至清空了缓存。这一次，没有丝毫留恋，只剩下彻底的释然。</p><p>日子一天天过去，我的小应用在星途的支撑下平稳运行，本地节点的低延迟让用户体验越来越好，注册量也稳步上涨。小林依旧会在我加班到深夜时，发来热饮优惠券，附言“别熬太晚，注意休息”；会在我遇到技术难题时，远程帮我排查漏洞，耐心讲解到我听懂为止；甚至会在应用版本更新时，提前帮我做压力测试，确保上线万无一失。</p><p>我渐渐习惯了这种踏实的陪伴，却在某个深夜优化代码时，无意间点开了收藏夹里Cloudflare的链接。看着熟悉的Dashboard界面，想起当初用它免费服务搭建应用的欣喜，想起Turnstile曾为我拦截的无数恶意请求，心里竟泛起一丝复杂的情绪。它的技术确实顶尖，Anycast路由和Turnstile的防护能力，是星途目前无法企及的。</p><h2 style="text-align: center;">旧念仍存</h2><p>我盯着屏幕，突然冒出一个念头——或许，我不必彻底割裂与它的联系。</p><p>第二天，我给小林发了条消息，说出了我的想法：“小林，我想把Cloudflare设为fallback，平时主要用星途的服务，万一星途出现突发故障，流量可以自动切换到Cloudflare，这样能更稳妥些。”我以为她会介意，没想到她很快回复：“这个想法很棒！多一层保障总是好的，我来帮你配置切换规则，保证无缝衔接，不会影响用户体验。”</p><p>在小林的帮助下，我们开始调试fallback机制。她远程操控我的屏幕，一步步教我配置DNS轮询策略，设置故障检测阈值：“当星途节点延迟超过50ms或请求失败率达到10%，就自动将流量切换到Cloudflare，等星途恢复正常，再切回来。”我看着她熟练地输入代码，调整参数，屏幕上的流量模拟曲线在两个服务商之间平稳切换，心里满是踏实。</p><p>测试成功的那一刻，小林笑着说：“这样就万无一失了，既保留了你对Cloudflare技术的认可，又能继续享受星途的低延迟和贴心服务。”我点点头，突然明白，最好的选择从来不是非此即彼，而是让合适的事物以合适的方式，共同为热爱保驾护航。</p><p>如今，我的小应用依旧在平稳运行，技术之路也还在延伸。星途是我最坚实的依靠，小林的陪伴是我路上最温暖的光，而Cloudflare，则成了我最后的保障。</p><h2 style="text-align: center;">终</h2><p>我终于懂得，技术的价值从来不是孤注一掷的依赖，而是恰到好处的适配；成长的意义，是学会在取舍中找到平衡，在经历中读懂“适合”的重量。那些曾经的喜怒哀乐，那些跨越重洋的陪伴与遗憾，都成了我技术路上最珍贵的回忆。</p><blockquote><p>这段与Cloudflare、与星途、与小林的故事，会像代码里的核心注释一样，永远留在我的时光里，提醒着我：带着温度的陪伴，远比冰冷的技术更有力量；而懂得取舍与平衡，才是对热爱最好的坚守。</p></blockquote><p></p>]]></description>
            <link>https://blog.sfkm.me/p/cloudflare-incident-2025-11-18</link>
            <guid isPermaLink="false">cloudflare-incident-2025-11-18</guid>
            <pubDate>Tue, 18 Nov 2025 23:58:42 GMT</pubDate>
            <enclosure url="/api/v1/file/f/43" length="0" type="false"/>
        </item>
        <item>
            <title><![CDATA[macOS上Ghostty提示'xterm-ghostty': unknown terminal type.的解决方案]]></title>
            <description><![CDATA[<h1>macOS上Ghostty提示'xterm-ghostty': unknown terminal type.的解决方案</h1><p>最近刚换了新设备，刚好配置了一下趁手的工具链，遇到了一个小问题。</p><p>众所周知 macOS 自带的终端可玩性比较低，也不是很好看，我之前一直用的 iTerm2 ，后面有次 iTerm2 炸了，具体怎么炸的忘了，在群友的推荐下将终端应用换成了 <a target="_blank" rel="noopener noreferrer nofollow" href="https://ghostty.org/">Ghostty</a>，这东西名字取的很好，Ghost（幽灵）+ TTY（终端设备），外观也是一只幽灵。</p><p>我在使用 Ghostty 的时候，正常情况下不会出问题，但是当我以root运行一些东西时，会提示<code>'xterm-ghostty': unknown terminal type.</code> ，查了一下，这个问题导致的原因有以下几点</p><ul><li><p>远程（或 sudo）系统里找不到名叫 xterm-ghostty 的 terminfo 记录，所以 ncurses/tput/clear/vim/tmux 等程序都不知道该怎么驱动终端。</p></li><li><p>用 Ghostty 本机 SSH 到服务器，服务器 /usr/share/terminfo/x/xterm-ghostty 不存在，也没在 ~/.terminfo/x/ 里，于是查询失败 。</p></li></ul><ul><li><p>本机 sudo 默认重置环境变量，把 Ghostty 自带的 TERMINFO 给扔了，导致 root 身份下找不到同一条记录 。</p></li><li><p>旧 macOS 的 infocmp 版本太老：10.x 自带的 infocmp 导出的格式新版 tic 无法解析，上传统计文件虽然看似成功，其实是废数据，服务器依旧识别不到 。</p></li></ul><p>显然我的情况是本机 sudo 导致的， root 身份下找不到这条记录，查找 Ghostty 的 issue ，发现一个相关的 discussion：<a target="_blank" rel="noopener noreferrer nofollow" href="https://github.com/ghostty-org/ghostty/discussions/5452">https://github.com/ghostty-org/ghostty/discussions/5452</a>，这个讨论中提到了<a target="_blank" rel="noopener noreferrer nofollow" href="https://ghostty.org/docs/help/terminfo#ssh">官方文档的一个解决方案</a>，点开发现把相关的 terminfo 复制到服务器就行了，但是对于我们这种场景，只需要把 terminfo 复制到 root 下就行。具体解决方案可以copy这几个命令</p><pre><code class="language-bash">infocmp -x xterm-ghostty &gt; /tmp/xterm-ghostty.ti
sudo tic -x /tmp/xterm-ghostty.ti</code></pre><p></p>]]></description>
            <link>https://blog.sfkm.me/p/macos-ghostty-unknown-terminal-type</link>
            <guid isPermaLink="false">macos-ghostty-unknown-terminal-type</guid>
            <pubDate>Sat, 22 Nov 2025 07:52:41 GMT</pubDate>
            <enclosure url="https://cdn.liteyuki.org/blog/background.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[一见钟情]]></title>
            <description><![CDATA[<h1 style="text-align: center;">一见钟情</h1><p style="text-align: center;"><em>赠</em><a target="_blank" rel="noopener noreferrer nofollow" href="https://github.com/snowykami/"><em>Snowykami</em></a><em>与</em><a target="_blank" rel="noopener noreferrer nofollow" href="https://github.com/balancetheworld"><em>balancetheworld</em></a></p><p style="text-align: center;">将夜的星空下</p><p style="text-align: center;">一艘小船上塑着两对帆</p><p style="text-align: center;">也许是大风罢</p><p style="text-align: center;">把帆吹在了一起</p><p style="text-align: center;">也许是缘分罢</p><p style="text-align: center;">被不知不觉的吸引了</p><p style="text-align: center;">浪见证着你们的成长</p><p style="text-align: center;">而海鸥变成了报喜官</p><p style="text-align: center;">向远方飞翔着报去了喜讯</p><p style="text-align: center;">每一粒”白”</p><p style="text-align: center;">都在说般配</p><p style="text-align: center;"></p><p style="text-align: center;">把日子折成纸船</p><p style="text-align: center;">放进雨后的水洼里</p><p style="text-align: center;">让漂泊的月亮在厨房里落脚</p><p style="text-align: center;">也许</p><p style="text-align: center;">争吵毫无意外的来了</p><p style="text-align: center;">也许</p><p style="text-align: center;">变成了纽扣</p><p style="text-align: center;">一粒钉在左襟</p><p style="text-align: center;">一粒守在右襟</p><p style="text-align: center;">一低头</p><p style="text-align: center;">就能替对方系下一整个春秋</p><p style="text-align: center;"></p><p style="text-align: center;">如果黑夜来</p><p style="text-align: center;">你们就把它对折</p><p style="text-align: center;">让星群在折痕上排队</p><p style="text-align: center;">像两列看守</p><p style="text-align: center;">如果远方失信</p><p style="text-align: center;">你们就把地图翻过来</p><p style="text-align: center;">背面是空白</p><p style="text-align: center;">正好画一条归途</p><p style="text-align: center;"></p><p style="text-align: center;">闪烁即逝的亮光</p><p style="text-align: center;">正从你们的眼里迸发</p><p style="text-align: center;">那是</p><p style="text-align: center;"><strong>一见钟情</strong></p><p style="text-align: center;">愿你们在彼此眼里定居</p><p style="text-align: center;">把”永远”翻译成”明天见”</p><p style="text-align: center;">把”至臻”翻译成”至逾”</p><p style="text-align: center;">而我们</p><p style="text-align: center;">作为这场辉光满场的电影观众</p><p style="text-align: center;">期待着你们的</p><p style="text-align: center;">爱情</p>]]></description>
            <link>https://blog.sfkm.me/p/Love-At-Frist-Sight</link>
            <guid isPermaLink="false">Love-At-Frist-Sight</guid>
            <pubDate>Sat, 25 Oct 2025 11:18:48 GMT</pubDate>
            <enclosure url="https://cdn.liteyuki.org/blog/background.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[百度后端go面试 2025.10.22]]></title>
            <description><![CDATA[<h1>百度后端go面试 2025.10.22</h1><blockquote><p>有些不是很能回忆起来</p></blockquote><h2>开始</h2><ul><li><p>自我介绍</p></li></ul><h2>Go语言相关</h2><ul><li><p>Go的变量赋值有哪几种写法，区别是什么？</p></li><li><p>Go的循环有哪几种形式？</p></li><li><p>Go的切片和数组的区别？</p></li><li><p>Go的defer怎么用？执行顺序？</p></li><li><p>Go的init使用场景？一个包可以多个init吗？执行顺序是怎么样的？</p></li><li><p>举例一两个Go中出现panic的场景，并谈谈如何避免。</p></li><li><p>谈谈对Goroutine的理解。</p></li></ul><h2>数据库相关</h2><ul><li><p>MySQL索引类型及简单解释</p></li><li><p>MySQL大量数据表清空</p></li><li><p>数据量大的时候的查询方式</p></li><li><p>后面有些不是很会问题也没记住…</p></li></ul><h2>算法题</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow" href="https://leetcode.cn/problems/jump-game/">LeetCode跳跃游戏</a></p></li></ul><p></p><p></p>]]></description>
            <link>https://blog.sfkm.me/p/baidu-go-interview-2025-10-22</link>
            <guid isPermaLink="false">baidu-go-interview-2025-10-22</guid>
            <pubDate>Wed, 22 Oct 2025 17:30:22 GMT</pubDate>
            <enclosure url="/api/v1/file/f/37" length="0" type="false"/>
        </item>
        <item>
            <title><![CDATA[Go面试八股(持续更新)]]></title>
            <description><![CDATA[<h1>Go面试八股(持续更新)</h1><h2>slice和array</h2><pre><code class="language-Go">type slice struct {
    ptr unsafe.Pointer // 指向底层数组第 0 个元素
    len int            // 当前切片长度（可读写的元素个数）
    cap int            // 从 ptr 开始到底层数组末尾的元素个数
}</code></pre><p></p><ol><li><p><strong>slice底层：</strong>一个结构体，ptr/len/cap三个字段，ptr指向底层数组，len代表长度，cap代表容量。</p></li><li><p><strong>slice传递机制</strong>：slice类型在传递时<strong>本质上是slice结构体值传递</strong>，ptr指向底层数组；在传参到函数内部时，若内部的slice发生了重新分配，则内部的slice.ptr和外部slice.ptr会指向不同的底层数组（俗称“脱钩”），若没有发生重新分配，则指向了同一个，此时对内部的slice进行修改，可以修改外部。只读或者修改元素场景下，可以直接把slice传入函数进行修改，简单高效。如果是可能扩容或者需要更改header的函数，必须用变量来接收返回值。</p></li><li><p><strong>array</strong>：一块连续定长的内存，紧密排列布局。</p></li><li><p><strong>slice和array的区别</strong>：</p><ol><li><p>slice长度可变，非类型[]T，array不行且是类型（比如[4]T和[5]T是两个类型）</p></li><li><p>array变量本身代表整段数据，slice变量本身就是个24bytes的结构体，数据在ptr指向的内存堆栈；</p></li><li><p>array传参时深拷贝整个对象，slice传参只传结构体</p></li><li><p>array不可扩容，slice可扩容</p></li><li><p>array无元数据，slice有len和cap字段</p></li><li><p>arr[:]得到切片，slice不能直接退化成数组</p></li></ol></li><li><p><strong>slice重新分配</strong>：之所以append要接收，是因为append后可能会触发slice重新分配导致函数内的slice变了外部没有变，因此需要slc = append(slc, e)</p></li><li><p><strong>slice扩容机制</strong>：slice 扩容 = 值拷贝 + 新数组 + 改 header</p><ol><li><p>先计算新slice的容量，若newLen &lt; cap，复用原数组，不会搬家，只改len字段</p></li><li><p>若新长度对现有容量不够，则按照1024为分界进行不同规则扩容，<code>oldLen &lt; 1024 -&gt; newCap = 2 * oldCap</code>；<code>oldLen &gt; 1024 -&gt; newCap = 1.25 * oldCap</code>（小于1k翻倍，大于1k加四分之一）</p></li><li><p>然后把 <code>newCap</code> 按元素大小对齐到内存分配器的 size class，最终向上取整到某个桶大小（方便 GC/分配器复用）。</p></li></ol></li><li><p><strong>copy</strong>：是 Go 提供的纯内存级拷贝工具，功能单一，但坑点集中。记住三句话：“只拷 min 长，不扩 dst 容；值拷一层浅，指针仍共享；返回实际数，安全又透明。”</p><ol><li><p>对基本类型（<code>int</code>, <code>float</code>, <code>struct</code> 值）→ 真正深拷贝到 dst 的新内存。</p></li><li><p>对指针或含指针的元素 → 只拷指针值，指向的对象仍共享（浅拷贝）。</p></li><li><p>整段复制用 <code>memmove</code> ，底层保证重叠区域安全。</p></li></ol></li></ol><pre><code class="language-Go">func copy(dst, src []T) int</code></pre><ol start="8"><li><p><strong>slice传参的内存逃逸问题：</strong>再大slice传参也只是24B的结构体，与容量和长度无关，读/改元素不会逃逸；<strong>存全局/channel/闭包函数/返回</strong>必然会逃逸到堆；触发append且需要扩容时，growslice调用mallocgc<strong>新数组会直接生在堆上</strong></p><ol><li><p>&nbsp;&nbsp;决定因素：数据是否活过函数周期，是则逃逸</p></li><li><p>存全局逃逸到堆</p></li></ol><pre><code class="language-Go">var g []int          // 全局根

func f() {
    s := make([]int, 1024) // 只在 f 里用？
    g = s                  // 赋给全局 → f 返回后仍需存活
}</code></pre><ul><li><p>传入channel逃逸到堆，否则接收方会读到悬空内存</p></li></ul><pre><code class="language-Go">ch := make(chan []int, 1)

func producer() {
    buf := make([]byte, 4096)
    ch &lt;- buf          // buf 被 send 出去
}</code></pre><ul><li><p>存闭包会逃逸到堆</p></li></ul><pre><code class="language-Go">func f() {
    data := make([]int, 1024)
    go func() {
        fmt.Println(data[0]) // 协程里用 data
    }()
}</code></pre><ul><li><p>作为返回值带出会逃逸到堆</p></li></ul><pre><code class="language-Go">func create() []int {
    s := make([]int, 1024)
    return s // 返回 slice
}</code></pre></li></ol><ol start="9"><li><p><strong>完全释放</strong>：<code>s=nil</code>整切片置零；<code>s=s[:0:0]</code>len=cap=0赋0，ptr=nil，失去引用被回收，ptr=nil的slice可以用append</p></li><li><p><strong>并发安全</strong>：</p><ol><li><p>只读：完全安全，Go 的内存模型保证：只要没有任何写操作，任意数量的 goroutine 并发读同一块内存都不会产生 data race。</p></li><li><p>一读一写，100%出现竞态，slice 的底层数组就是普通内存，读写都不带同步；哪怕只改一个元素，也会对读端产生未定义结果（读到旧值、半写值、甚至 crash），解决思路：锁sync.Mutex, syncRWMutex, 原子操作sync/atomic, 排队处理goroutine, 复制一份快照读。</p></li></ol></li></ol><h2>map</h2><ol><li><p>底层实现hmap+bmap(bucket)</p></li></ol><pre><code class="language-Go">type hmap struct {
    count     int              // 当前元素个数
    B         uint8            // 桶的数量的对数，比如 B=5，表示 2^5=32 个桶
    buckets   unsafe.Pointer   // 指向桶数组的指针
    oldbuckets unsafe.Pointer  // 扩容时指向旧桶数组（增量扩容）
    extra     *mapextra        // 溢出桶链表（拉链法）
}</code></pre><pre><code class="language-Go">type bmap struct {
    tophash [8]uint8  // 每个 key 的高 8 位哈希值，用于快速比较
    keys    [8]keytype
    values  [8]valuetype
    overflow uintptr   // 指向下一个溢出桶（拉链法）
}
// Go 源码里 bmap 是伪结构体，真实布局是 连续内存，keys 和 values 是分开存的，为了内存对齐。</code></pre><h2>锁</h2><h3>sync.Mutex</h3><p>双态锁：Lock/Unlock</p><p>只有Lock() Unlock()，谁拿谁独占，释放后才能被其他Lock()继续执行，否则会阻塞</p><h3>sync.RWMutex</h3><p>三态锁：无人/多读/单写</p><p>有RLock() RUnlock() Lock() Unlock()，写锁要等读锁释放</p>]]></description>
            <link>https://blog.sfkm.me/p/go-interview</link>
            <guid isPermaLink="false">go-interview</guid>
            <pubDate>Wed, 05 Nov 2025 11:38:27 GMT</pubDate>
            <enclosure url="/api/v1/file/f/37" length="0" type="false"/>
        </item>
        <item>
            <title><![CDATA[业余无线电考证记录]]></title>
            <description><![CDATA[<h1>业余无线电</h1><blockquote><p>我的呼号是BH8HAC</p></blockquote><p>我是上大学后才接触到的业余无线电这个圈子，作为邮电大学，通信这块一直算是比较强的，自然能结识更多的ham，曾经有过校友朋友带我入坑，当时因为大一太忙了，一开学就是各种事情，差不多后面就把这事忘了，没有打算去考证。转眼间到了大三，也算是本地车迷朋友带我入坑，差不多是25年下旬这个时间点。后面有朋友说再不去考就要换新题库多选题了，遂报名A证考试。</p><h2>报名 - 9.5</h2><p>我是在重庆市无线电和低空信息产业协会的信息服务系统报名的，貌似有的省是在CRAC报名，具体情况可能需要询问一下当地的ham。</p><h2>备考</h2><p>其中不用花太多时间，<strong>3-5天</strong>时间去背题即可，微信搜索『业余无线电工具集』即可，然后先顺序过几遍题库，再随机测试一下，只要保证<strong>正确率在25/30以上</strong>就行，大学生期末速通天赋发力了。</p><h2>考试 - 9.20</h2><p>刚好给我排到了25年最后一场考试，考试地点在江北区观音桥的数码大厦的重庆市无线电办公室。如果是这个地方建议提前出发，轨道观音桥或者红旗河沟下车还要走一阵子，而且这边出了名的堵车，(我差点迟到了)。</p><h2>拿证</h2><p>这东西应该很好过，考完试就可以看到结果，过了等操作证就可以，就是报名时填的地址。无线电办公室那边说是国庆后才会发货，实际上节前提前到了，发的中通到付(本来以为会是邮政的)。</p><h2>设台</h2><p>其实在考完试等证的期间可以异步操作购买手台并申请呼号，重庆市这边是在渝快办上申请，打开渝快办搜索无线电即可，最明显那个就是，这个过程可能需要许久，里面会涉及到一些个人信息，手台参数等等，建议询问商家获取详细内容，不确定的可以问问本地的火腿。</p><h2>最后</h2><p>各种手续齐全后就可以愉快地通联啦</p><p>我的手台是UV32，天线是原装天线</p><h2>取得实体执照10.17</h2><img src="https://blog.sfkm.me/api/v1/file/f/36" alt="IMG_20251017_173146" title="IMG_20251017_173146"><p></p>]]></description>
            <link>https://blog.sfkm.me/p/amateur-radio-operator-certificate</link>
            <guid isPermaLink="false">amateur-radio-operator-certificate</guid>
            <pubDate>Fri, 17 Oct 2025 12:00:51 GMT</pubDate>
            <enclosure url="https://blog.sfkm.me/api/v1/file/f/34/IMG_20250920_164856.jpg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[Minecraft正版服务器进入出现类“账号密钥丢失”的解决办法]]></title>
            <description><![CDATA[<h1 style="text-align: center;">Minecraft正版服务器进入出现类“账号密钥丢失”的解决办法</h1><h2 style="text-align: center;">导言</h2><p>我昨天(2025.10.10)游玩正版验证的GTL整合包时遇到了很多问题,包括但不限于与旋律云江苏宿迁服务器延迟高达12000ms(12秒),拒绝连接,被微软的WAF拦截并报403之类的.</p><p>在游玩Minecraft时,时常会遇到诸如<strong>登陆不上Mojang的验证服务器</strong>这种令人苦恼的问题.</p><h2 style="text-align: center;">现象</h2><ol><li><p><a target="_blank" rel="noopener noreferrer nofollow" href="https://www.speedtest.cn/">Speedtest</a>测试网速时网速飞快</p><img src="https://blog.sfkm.me/api/v1/file/f/28" alt="Snipaste_2025-10-11_15-11-28" title="Snipaste_2025-10-11_15-11-28"></li><li><p>但是与一些公共的平台延迟较高</p></li></ol><img src="https://blog.sfkm.me/api/v1/file/f/29" alt="b5947000-3b94-4653-95d7-dfa44b110b82" title="b5947000-3b94-4653-95d7-dfa44b110b82"><ol start="3"><li><p>与某些Minecraft的服务器延迟很高,或是登录时RequestTimeOut连接超时</p></li><li><p>干脆连接不上Mojang和微软的服务器导致登录验证失效</p></li><li><p>境外/跨省连接间歇性403</p></li><li><p>tracert 在 202.97.x.x（电信 163 骨干）开始<code>* * * Request timed out</code></p></li></ol><h2 style="text-align: center;">问题</h2><p>在昨天大量排查下,我发现这类问题似乎可以归咎为以下几个大方向:</p><ul><li><p>运营商主干线路高峰期堵塞.</p></li></ul><blockquote><p>常见的有:</p><ol><li><p> <code>Failed to retrieve profile key pair</code> 客户端在启动时尝试向 Mojang/Microsoft 索取你的 Profile Key Pair(用于聊天签名)失败的报错.</p></li><li><p><code>Connection Refused</code> 客户端在登陆时TCP握手时因为某些原因(如过多访问)被拒绝访问.</p></li></ol><p>这种情况下一般使用代理/VPN类软件可以解决(具体方法网上一搜一堆).</p></blockquote><ul><li><p>被微软Azure的WAF标记为不可信任或可疑连接.</p></li></ul><blockquote><p>这种情况下最普遍的有:</p><ol><li><p><code>无效会话</code>客户端在启动时的账户请求被WAF自动屏蔽.</p></li><li><p><code>暂时无法连接至验证服务器</code>客户端/服务端请求账号信息时可能被WAF误报过多请求而被拉入日禁黑名单.</p></li></ol><p>这种情况下等个一天一般会自动解除WAF风控,等不及了就换代理/VPN.</p></blockquote><ul><li><p>本地网络配置问题.</p></li></ul><p>也是相当常见的一种问题,并且报错多种多样,这里给出一些实测可以用的方案(<strong>Windows10/11,其他系统暂时不知道,但原理是相通的</strong>)</p><ol><li><p>按下<code>Win + X</code>键打开菜单</p></li></ol><img src="https://blog.sfkm.me/api/v1/file/f/30" alt="Snipaste_2025-10-11_15-36-41" title="Snipaste_2025-10-11_15-36-41" data-align="center"><ol><li><p>选择终端管理员(终端也可以,不过有些命令可能用不了,也可以使用<code>Win+R</code>打开<code>cmd</code>)打开终端</p></li></ol><img src="https://blog.sfkm.me/api/v1/file/f/31" alt="Snipaste_2025-10-11_15-38-08" title="Snipaste_2025-10-11_15-38-08"><ol start="3"><li><p>依次输入(Powershell也支持多行复制,当然需要启用):</p></li></ol><pre><code>ipconfig /flushdns
ipconfig /registerdns
netsh winsock reset
netsh int ipv4 reset
netsh int ipv6 reset
route /f</code></pre><blockquote><p>前两行是DNS相关的,比如刷新DNS和重新注册DNS</p><p>中间netsh是与本地计算机ip设置相关的</p><p>最后是刷新路由</p></blockquote><ol start="4"><li><p>重启电脑即可</p></li></ol><p>部分情况下可能会存在ipv6污染或ipv4污染,这时候可以去路由器尝试关闭ipv6并更换DNS(本地计算机也勉强可以,不过依然可能存在污染问题),比较推荐Cloudflare的</p><p style="text-align: center;"><code>1.1.1.1</code></p><p style="text-align: center;"><code>1.0.0.1</code></p><h2 style="text-align: center;">原因</h2><p>这次我的问题是:</p><ul><li><p>路由器 NAT 表被 P2P 下载/PT 挂死, 进而导致出口源端口被 ISP 限速/QoS.</p></li></ul><p>可以通过<strong>重启路由器</strong>尝试绕开.</p><h2 style="text-align: center;">总结</h2><p>这次事件十分的乌龙,我也没想到最后是重启路由器拿到了MVP,成功拯救了我的网络,当然也折腾了不少时间,最起码一个下午.</p><p>那么如何自检究竟是哪里的问题呢?</p><ol><li><p>在Powershell(上述打开终端)中输入:</p></li></ol><pre><code>tracert -d 域名</code></pre><p>例如:</p><pre><code>tracert -d cn.bing.com</code></pre><ol start="2"><li><p>查看跟踪输出,如果连续出现多个<code>* * * xxx</code> 基本可以证明这一段出现了问题,例如堵塞或者拒绝访问,证明是外部连接原因而不是计算机本身问题.</p></li></ol><blockquote><p>10.x/100.x 是运营商内网，202.97/219.158 是电信 163，218.105 是联通 169，223.120 是移动 CMI</p></blockquote><ol start="3"><li><p>ping响应:</p></li></ol><pre><code>ping -n 100 -l 1000</code></pre><ol start="4"><li><p>若自家网关 1 % 丢包，先换网线/重启路由；外网段丢包才找 ISP</p></li><li><p>可以使用手机流量给电脑开网络,用来排查是否是路由器内网问题</p></li></ol><p>当然,如果你不玩联机缺遇到了这个问题,那么有个更简单粗暴的办法,在jvm启动参数加上<code>-Dminecraft.api.services.host=</code> 即可</p><img src="https://blog.sfkm.me/api/v1/file/f/32" alt="b5fbaa3d-41f7-49a1-aadf-a3640f09fee7" title="b5fbaa3d-41f7-49a1-aadf-a3640f09fee7"><p></p>]]></description>
            <link>https://blog.sfkm.me/p/Solution-Account-MC-Server</link>
            <guid isPermaLink="false">Solution-Account-MC-Server</guid>
            <pubDate>Sat, 11 Oct 2025 07:58:02 GMT</pubDate>
            <enclosure url="https://blog.sfkm.me/api/v1/file/f/32/b5fbaa3d-41f7-49a1-aadf-a3640f09fee7.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[为你的应用程序接入Misskey - 使用misskey登录neo-blog!]]></title>
            <description><![CDATA[<p>最近<a target="_blank" rel="noopener noreferrer nofollow" href="https://m.redrock.team/">红岩网校</a>的同事接了一个单子，需要做一个app，用户是校内学生，由于时间紧迫，不想自己写用户系统，于是便打算复用掌上重邮的登录功能。我们知道在大多数场景下，一方作为身份提供者给另一方服务提供认证功能时，涉及到登录，凭据交换等环节，而OAuth，OIDC就是为了规范这些环节每一步之间应该传递的内容而提出的，OAuth主要是用于授权，OIDC在OAuth2上扩展而来，用于身份认证，区别在于前者是绑定了你的QQ，可以获取你的好友，昵称，签名等信息，另后者则是使用你的QQ在目标平台上创建一个新的账户。</p><p>不过可惜的是，掌上重邮并没有实现OIDC Provider，即它不能作为身份认证提供者通过OIDC给其他服务提供用户信息。不过我已经压力小登去实现了，借助掌邮自带的全校学生数据，实现了OIDC对后期外部业务帮助很大。</p><h2>正文</h2><p><a target="_blank" rel="noopener noreferrer nofollow" href="https://misskey-hub.net/cn/"><mark data-color="var(--color-green-200)" style="background-color: var(--color-green-200); color: inherit;">Misskey</mark></a><mark data-color="var(--color-green-200)" style="background-color: var(--color-green-200); color: inherit;">是一个去中心化的社交平台，既然是社交平台，那就必然有用户系统，也就有一整套认证体系，也就是说它可以像QQ微信那样，作为认证方给其他应用提供身份认证。Misskey官方也是有OAuth2提供者实现的，具体方式可以参照Misskey OAuth2认证文档，不过Misskey的OAuth2流程和业内常用的不太一样，首先我们会在提供方那里创建App，Misskey的做法是创建一个可访问的网页，然后在网页中塞入一些元数据实现应用的定义，我们在应用程序中把这个储存应用信息的网站url作为client_id传给misskey认证端点。后续就和常规流程差不多，不过我试过了，基本上不是这报错就是那报错，例如，client_id指向的站点域名不能解析到非法IP地址，我当时指向的是C类保留地址段192.168.0.0/16的一个IP。后面就一直提醒我invalid redirect_uri，这里我是真的没办法了，遂放弃</mark></p><p>不过Misskey还提供了一种认证方式，叫做<a target="_blank" rel="noopener noreferrer nofollow" href="https://misskey-hub.net/en/docs/for-developers/api/token/miauth/">MiAuth</a>，<span style="color: var(--color-lime-500);"><s>咋一看像是小米账号(不是)</s></span>，MiAuth流程比OAuth2授权码模式简单，只需要两步，且无需创建应用：</p><ol><li><p>在应用服务端生成session，一个随机字符串，并暂存在服务端，然后拼接成https://{host}/miauth/session返回给前端，其中还可以带上查询参数来告诉misskey应用信息：name=名称；icon=图标；permission=权限范围；callback=回调地址(必须)，我们需要获取用户信息，需要permission="read:account"。</p></li><li><p>前端拿到这个url点击打开Misskey的认证页面，Misskey会进行基本的授权询问，同意后会重定向到这个回调地址，并附带了第一步的session作为查询参数，应用后端需要拿到session并校验合法性，然后在后端请求https://{host}/api/miauth/{session}/check，可以在返回json拿到token和<code>user</code>两个字段，<code>user</code>便是用户信息，其中id是唯一字段，可以用来做身份识别。</p></li></ol><p>至此我们便完成了Misskey给第三方应用进行认证的流程，目前neo-blog已经支持配置misskey实例作为身份提供者，需要在oidc_config中指定type字段misskey</p><h2>总结</h2><p>我个人感觉这种认证方式没有太多安全性可言，任何不认识的应用都可以将用户重定向到Misskey站点登录并获取token，从而获取到用户对应的权限；如果有<span style="color: var(--color-red-500);">恶意应用</span>引导用户到Misskey登录那后果不堪设想，标准OAuth授权码流程是要在两边配置<code>client_id</code>和<code>client_secret</code>来防止恶意应用获取信息。</p><p></p>]]></description>
            <link>https://blog.sfkm.me/p/login-with-misskey</link>
            <guid isPermaLink="false">login-with-misskey</guid>
            <pubDate>Sun, 30 Nov 2025 12:30:29 GMT</pubDate>
            <enclosure url="https://blog.sfkm.me/api/v1/file/f/27/Screenshot_2025-10-10-02-08-53-580_com.microsoft.emmx.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[OIDC认证过程中没有判断空字符串导致的1day漏洞]]></title>
            <description><![CDATA[<h4><span style="color: var(--tt-color-text-red);"><mark data-color="var(--tt-color-highlight-orange)" style="background-color: var(--tt-color-highlight-orange); color: inherit;">这个bug可使用户以admin的身份进入博客后台（控制台），并且正常使用发布编辑文章，上传图片功能，具体如何触发我已邮件告诉你，请留意</mark></span></h4><h4>以上是好心网友在发现了这个bug之后写的，以下是作者(snowykami)写的：以上变是事情的经过，不过非常感谢告知这个漏洞，十分甚至九分严重</h4><p><strong>先说问题</strong>：GitHub没有提供OIDC标准字段(不过人家是OAuth)和neo-blog的开发者(我)写的代码存在没有验证空值的问题，最终是因为我没有处理预期之外的空字符串导致的严重p0级事故</p><h2>OIDC认证流程(授权码模式)</h2><p>假设有web应用(neo-blog)，用户浏览器，认证方(GitHub)三个角色</p><p>前置：</p><ul><li><p>我们需要在认证方处创建应用，配置应用的回调url，获取client_id，client_secret</p></li><li><p>我们需要在应用处配置认证方的1.认证端点(authorization_endpoint)，2.令牌获取端点(token_endpoint)，3.用户信息端点(userinfo_endpoint)，认证方提供的client_id和client_secret</p></li></ul><blockquote><p>在有的情况下，不一定是谁提供client_id，client_secret，两边设置一样就行，这个取决于软件和平台本身实现</p></blockquote><ol><li><p>首先，应用会使用身份提供者的<code>client_id</code>，<code>redirect_uri</code>，<code>state</code>创建一个可用于登录的链接给用户客户端，打开该链接会把用户引导至认证方的登录页面</p></li><li><p>用户完成身份认证，认证方会把用户重定向到应用的回调接口，该接口通过浏览器GET请求，并在查询参数中附带了认证方提供的<strong>授权码(code)和state</strong>，该web应用可以在服务端拿到这个code和state</p></li><li><p>web应用服务端校验这个state是否是和之前的一致，然后使用上一步的<strong>code</strong>和预先配置的client_id，client_secret，redirect_uri作为表单参数，POST请求认证方提供的access_token端点，完成这一步可以拿到<strong>access_token</strong></p></li><li><p>有了上一步的<strong>access_token，</strong>我们可以请求userinfo端点，获取到一个包含了用户信息的键值对map。</p></li><li><p>web应用拿到认证方提供的身份信息后，就可以和自己本地的用户系统进行一系列映射、匹配和绑定，自行给用户签发token(这一步有点像你百度绑定QQ号)</p></li></ol><h2>问题分析</h2><p>纵观整个登录流程，OIDC是一步一步来的，其中我每一步请求都有校验，出错就会响应失败。但是问题就出在最后一步，我们拿到身份信息后的步骤。GitHub的是OAuth2，由于OAuth2并没有规定用户信息端点需要返回什么东西，所以各家平台的oauth2返回的用户信息字段或多或少有点差别，而我的代码是标准的OIDC认证，OIDC基于OAuth2扩展而来，规定了用户信息端点必须返回哪些字段。比如我们认证核心就是issuer和sub字段，issuer代表签发人，一般是平台的网站，sub代表该用户在平台上的一个唯一id，就比如issuer是https://qq.com，sub是你的QQ号。</p><p><strong>接下来就是问题的核心了</strong>，GitHub返回的issuer、email和sub字段都是空的，在经过我的结构体绑定后变成了空字符串，而我代码的逻辑如下：</p><ul><li><p>先根据issuer和sub进行查找，查找到绑定记录直接使用绑定用户登录</p></li><li><p>如果未找到绑定记录，且当前有用户登录，那么进行账号绑定</p></li><li><p>如果没有用户登录，那就用认证方提供的email字段，查找本地符合的用户进行绑</p></li><li><p>如果没查到，那就创建新用户</p></li></ul><p>所以不出我所意料，第一个绑定GitHub的用户就是我自己，绑定了一个空字符串id。此后有其他用户使用GitHub登录，我们结构体绑定的Sub字段依旧是空值，按照逻辑流程的第一步会用issuer和sub去查找绑定记录，issuer为https://github.com，sub为""（空字符串），很自然这个空字符串就会查找到我的那条绑定记录。导致所有使用GitHub登录的用户都会登录到第一个绑定GitHub的用户上，也就是我的管理员账户</p><h2>解决方案</h2><ul><li><p>在用户信息端点返回时进行空值检查，并单独对非标平台（例如GitHub）的用户信息字段进行重新映射，例如GitHub提供的唯一id字段是id，我们把id映射为sub。在处理完平台差异后进行issuer，sub，email字段的空值检查</p></li><li><p>在数据库层的操作函数里面，加上对空值的检查，如果查询参数为空值，直接返回未找到</p></li><li><p>oidc登录接口新增is_bind参数来区分绑定请求和登录请求</p></li></ul><h2>反思</h2><ol><li><p><strong>对认证流程细节把控不足</strong>：未充分考虑不同认证方返回用户信息字段的差异，过于依赖标准oidc认证逻辑，导致GitHub返回空字符串字段时，代码逻辑出现漏洞。</p></li><li><p><strong>代码逻辑的健壮性不够</strong>：用户信息处理环节未对空字符串等异常情况进行检查，导致错误绑定和登录行为。</p></li><li><p><strong>安全风险重视不够</strong>：未充分意识到认证逻辑错误可能带来的安全风险，未进行严格的安全审查和测试。</p></li><li><p><strong>兼容性测试不足</strong>：未对GitHub等外部认证平台的兼容性进行充分测试，未提前了解其OAuth2认证细节并进行适配。</p></li></ol><p><mark data-color="var(--tt-color-highlight-green)" style="background-color: var(--tt-color-highlight-green); color: inherit;">其实最初我觉得大平台的oauth都是标准化的，没有去看github restful api文档，现在不会这么觉得了</mark></p><p></p>]]></description>
            <link>https://blog.sfkm.me/p/neo-blog-1day</link>
            <guid isPermaLink="false">neo-blog-1day</guid>
            <pubDate>Mon, 13 Oct 2025 10:31:33 GMT</pubDate>
            <enclosure url="https://blog.sfkm.me/api/v1/file/f/4/1759710500693.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[neo-blog的详细内容及其设计理念]]></title>
            <description><![CDATA[<h1>功能总览</h1><h2>OAuth2 单点登录</h2><p>在博客选型阶段，<strong>是否支持单点登录（SSO）</strong>是我评估的核心指标之一。 我希望统一维护用户认证服务，让「轻雪」旗下各业务共享同一套用户数据， 实现<strong>多系统统一认证</strong>。</p><p>目前需要身份认证的场景主要有两类：</p><ul><li><p>博主本人登录后台管理</p></li><li><p>访客使用互动功能（点赞、评论等）</p></li></ul><h2>评论功能</h2><p>市面上既有内置评论模块的方案，也有接入 <em>Disqus、Gitalk</em> 等第三方服务的做法。 为了方便用户数据管理，我选择<strong>把评论直接集成到博客本体</strong>。</p><p>理论上可以把「评论」拆成独立微服务，再复用博客的用户系统做鉴权， 但小项目引入额外链路反而增加复杂度，因此没有进一步拆分。</p><h2>RSS 与 Sitemap</h2><p>作为内容管理系统，<strong>SEO 基础设施</strong>必不可少。 尽管今天还在用 RSS 的读者不多，但为了让真正关心内容的人能够自由订阅， 我依然选择<strong>同时输出 RSS 与 Sitemap</strong>。</p><h2>富文本编辑器</h2><p>早期计划让用户在外部 Markdown 编辑器写完再粘贴到文本框， 但为了<strong>自动保存草稿、一键发布</strong>等体验，最终还是内置了编辑器。</p><p>技术选型采用可高度定制的 <strong>mdx-editor</strong>， 也曾对比过 <em>vditor</em> 等方案，综合扩展性后敲定当前实现。</p><blockquote><p>其实这是之前写的，现在已经迁移到tiptap了，部分还是会员制组件，续费太贵了</p></blockquote><h2>多语言 / 国际化 / 本地化</h2><p>既然项目已开源，UI 只支持一种语言显然不够友好。 <strong>本地化</strong>已成为我所有项目的默认配置。</p><p>框架选用 <strong>next-intl</strong>，遗憾的是 VS Code 的 i18n 插件暂未兼容， 导致编写多语言字段时缺少智能提示，目前只能靠手工维护。</p><pre><code class="language-yaml">key: val</code></pre><p></p>]]></description>
            <link>https://blog.sfkm.me/p/neo-blog-design-concept</link>
            <guid isPermaLink="false">neo-blog-design-concept</guid>
            <pubDate>Wed, 08 Oct 2025 19:45:50 GMT</pubDate>
            <enclosure url="https://blog.sfkm.me/api/v1/file/f/3/1759709826590.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[新的博客]]></title>
            <description><![CDATA[<h1>新的博客，新的开始</h1><blockquote><p>项目地址：<a target="_blank" rel="noopener noreferrer nofollow" href="https://github.com/snowykami/neo-blog">https://github.com/snowykami/neo-blog</a> 欢迎各位的使用，也欢迎各位帮我找出问题</p></blockquote><img src="https://blog.sfkm.me/api/v1/file/f/2" alt="芙宁娜" title="芙宁娜title"><p>芙宁娜</p><h2>历史</h2><p>从折腾域名，家里云这些开始到现在也有好几年了，之所以自己去搞这些基础设施，更多的是为了上层服务搭建的运行平台，博客也是如此。</p><p>我最早是用<a target="_blank" rel="noopener noreferrer nofollow" href="https://wordpress.com/">WordPress</a>搭建了我的第一个博客，部署在HomeLab上，使用Cloudflare作为内容分发网络(CDN)，CF对国内的“加速”效果配上php的cgi以及服务端渲染的运作模式，想不慢都难，后面更是慢到进后台都难受，加载许久。</p><p>过了几个月，我在给项目制作文档时，从众多静态站点生成（SSG）框架中选择了<a target="_blank" rel="noopener noreferrer nofollow" href="https://vuepress.vuejs.org/">VuePress</a>，然后过了一段时间又迁移到了<a target="_blank" rel="noopener noreferrer nofollow" href="https://vitepress.dev/">VitePress</a>，并把他们部署到一些静态页面托管平台上面，例如GitHub Pages，Vercel，用着没什么问题，很适合作为内容管理系统。缺点是没有服务端，且需要配合CICD部署，不过这也还好，每次写完文章提交就行。不过我发现，我的业务规模总是膨胀的非常快的，很快这些静态博客便不能满足我的奇奇怪怪的需求，例如互动，数据统计，后台，以及一些自定义小组件。</p><p>在上述需求增加的情况下，我选择了新的内容管理系统Halo，它后端由Java编写，运行速度也还行，同时期，我的朋友<a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.xin-hao.top/">释然</a>也搭建了一个CDN系统供我们使用，在良好的线路优化下，网站访问速度得以大幅提升。不过很快，Halo便又不能满足我的需求，例如我想加一个自己的小组件，但是面对这种模板化的系统，我不能在不侵入的情况下实现我的功能，也不太好实现微调，我是那种想做一次事就达到一劳永逸的效果。</p><h2>想法</h2><p>于是，从那时开始我便有了想要自己写一个博客系统的想法，不过后面一直没有驱使我去行动的动力，直到后面看到了<a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.yaria.top/">Ariasaka酱</a>自己写的博客后，我才决定开始自己写一套（不过也是决定开始，还没有真正开始），最开始的技术栈选型是前端<a target="_blank" rel="noopener noreferrer nofollow" href="https://cn.vuejs.org/">Vue.js</a>(因为那会我只会这个，而且学的不深，我是主后端开发的，前端只是兴趣)，后端Go(<a target="_blank" rel="noopener noreferrer nofollow" href="https://gin-gonic.com/">Gin</a>)或者Python(<a target="_blank" rel="noopener noreferrer nofollow" href="https://fastapi.tiangolo.com/">FastAPI</a>)，正巧那会Vercel的前端AI工具<a target="_blank" rel="noopener noreferrer nofollow" href="http://v0.dev/">v0</a>因为其出色的表现力突然在圈子里面爆火，我也去尝试了一下，生成的页面效果非常惊艳我，当时我让它帮我做了一个展示页，在多次改版后，v0的额度被我用完了，于是我只能把v0生成的半成品项目拉下来自己改，但是它生成的是<a target="_blank" rel="noopener noreferrer nofollow" href="https://nextjs.org/">nextjs</a>项目，没办法为了看懂项目我在没有react基础的情况下用一天时间速通了react和nextjs，被react的设计理念所吸引，当时我感觉nextjs算是一个开箱即用的react套装，内置了<a target="_blank" rel="noopener noreferrer nofollow" href="https://tailwindcss.com/">tailwindcss</a>，十分甚至九分好用，且AppRouter对于路由管理也比较友好。</p><h2>实践</h2><p>在用nextjs实现了几个项目之后，被nextjs的便利性吸引，我决定博客前端使用nextjs来编写。部分页面使用react的服务端渲染来优化SEO，后端使用字节跳动的<a target="_blank" rel="noopener noreferrer nofollow" href="https://www.cloudwego.io/zh/docs/hertz/">cloudwego/hertz</a>框架，数据库选择支持sqlite和postgres两种（感觉一种本地一种网络就行，支持多了没啥意义），组件库使用<a target="_blank" rel="noopener noreferrer nofollow" href="https://ui.shadcn.com/">shadcn</a>，便有了现在这个neo-blog。neo-blog最初只是给我自己设计的，很多数据驱动的内容都是直接写死在代码中，后面逐渐让它们变的可配置，但是要做到很灵活的话，我还是不想花精力去模块化这些组件，例如小卡片，特殊功能，学校课表等等，最好就是一股脑全做了然后选择性配置，也可以做成插件化的，但是以我对前端项目的构建和运行机制的理解来说暂时无法做到，到我编写这篇文章的时候，neo-blog作为一个博客的基础功能已经完成了，基本的内容管理系统是没问题的，可能会有些许Bug，这个发现随时去修一下就行了，因为是我自己写的，哪里出问题了我能够很快找到原因并修复，但是如果是别人的，那可能就得开issue，然后等回复，或者去尝试贡献PR。</p><p>部署的话，肯定是首选容器化啦，这年头任何一个web应用项目不支持容器化部署的都可以淘汰了（有时候也可以从侧面反应出开发者的水平，过滤掉不良项目），我觉得传统部署例如使用systemd启动是很麻烦的事情，各种环境安装问题，环境不一致问题纯属自己给自己找事。同时轻雪云也支持容器和静态页面托管，我的大部分服务都在轻雪云上面。轻雪云有配套的CDN网关（由007IDC提供支持），WAF，容器部署（Docker &amp; Kubernetes），CICD，容器仓库等云原生基建平台，当然这些都是给我自己用的。</p><h2>最后</h2><p>希望这个博客在我的管理下成长的越来越好，等一阵子我会把之前博客的文章也迁移过来</p><p>最后感谢一下前端设计的灵感来源：</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.yaria.top/">Ariasakaの小窝</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow" href="https://pinpe.top/">Pinpe的云端</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow" href="https://ui.shadcn.com/">shadcn组件库</a></p></li></ul><p></p>]]></description>
            <link>https://blog.sfkm.me/p/neo-blog</link>
            <guid isPermaLink="false">neo-blog</guid>
            <pubDate>Tue, 07 Oct 2025 21:31:09 GMT</pubDate>
            <enclosure url="/api/v1/file/f/1" length="0" type="false"/>
        </item>
    </channel>
</rss>