拥抱AI

最近重新整理了一下本站,这次的特别之处在于:我没有像过去那样一个文件一个文件地慢慢改,而是直接把 AI 当成了结对工程师来用。 这件事本身也挺适合作为一篇记录。毕竟本站从 2016 年开始存在,中间经历过 Hexo、Hugo、主题迁移、Docker 化、Nginx 配置调整,也算是一个小型但长期运行的个人项目。以前每次改版,最大的成本并不是写几行 CSS,而是重新进入上下文:目录结构是什么、主题怎么覆盖、哪些文件会影响构建、哪些配置不能乱动。 这次 AI 帮我把这个成本降了很多。 没有推翻 Hugo 一开始我也在想,既然现在有 AI Coding,是不是已经不需要 Hugo 这种静态站点系统了。让 AI 直接写一个静态页面,似乎也能很快完成。 但真正拆开来看,Hugo 仍然有价值。它负责的是长期内容管理:Markdown、文章列表、多语言、RSS、sitemap、分类标签、URL 结构和静态构建。这些能力不是 AI 写几个 HTML 文件就能自然获得的。 所以最后选择的是一个更稳妥的方向:保留 Hugo 作为内容底座,同时用 AI 来重写关键页面和视觉表达。 这次没有动文章系统,也没有丢掉 PaperMod 主题,而是在 Hugo 的模板覆盖机制上做定制: 新增自定义首页模板 layouts/index.html 新增关于页模板 layouts/about.html 新增站点扩展样式 assets/css/extended/ai-site.css 保留原来的文章、分类、标签、RSS 和多语言能力 这样改完以后,站点仍然是 Hugo 站点,但首页和关于页已经不再像默认主题。 AI 参与了什么 这次 AI 做得比较好的地方,不是“替我生成一段好看的文案”,而是完整参与了工程过程。 它先读了仓库结构,确认这是一个 Hugo + PaperMod + Nginx 的静态站点。然后根据现有配置判断应该走模板覆盖,而不是直接复制一整套主题出来改。这个判断很重要,因为个人站点最怕的就是为了一个页面效果,把后续维护复杂度拉高。 接着它生成了首页和关于页的模板。首页突出我现在的定位:AI 技术专家、AI 方向探索、Agent 架构和 AI Coding。关于页则改成了更像个人资料页的结构,左侧是身份摘要和社交入口,右侧继续使用 Markdown 内容。 ...

2026年5月23日

2021新年快乐&随手记

其实1月份已经过去了半个多月了,马上眼看就要二月到了。以前每个元旦都会好好的写篇日志,但是今年忘了当时干什么去了(大雾,好像打DotA跨年了)。 2020一年其实过得非常的魔幻,前半年突然被已经弄得在家工作,其实在我看来就是非常的舒适,恨不得一年待在家里。有些人在家工作久了竟然会抑郁,真是两个围城啊。 还记得年初的时候,女朋友一天只上两天班,真的是让人太羡慕了,感觉应该换个这样的工作才对。不过最近听说可能过年不让回了,这就是欲戴其冠必承其重吧2333333。 工作上我感觉自己又进步了很多,低头走路久了,还是要看看远处的山峰,知道自己的目标在哪边。同事们今年给了我很大的帮助,很感谢。 疫情的出现,让我觉得这个世界并不是想象中的那么稳定,时代的变化,可能说来就来。 自己又默默地长了一岁了,身边有的人越走越远,已经不再是之前熟悉的样子,真的是话不投机半句多。有的人又忽然出现,带着像是憋了几百年没说出来的话,在这些天里一股脑的想跟你说完。 感觉好些时间没有这样一个朋友可以随心所欲的说话了,可能我的想法里都已经默认放弃了这种可能性了,觉得不会有人可以跟我好好说话了。毕竟随着年岁的增长,心墙也越来越厚了,不想说的东西越来越多。 但有的人就是会让你觉得特别的真实、真诚。感觉可以直接说很多有的没的的话。在2020的末尾,2021的开始,还是有人可以直接跳进来,我可能有了一个可以灵魂共鸣的人了。 很开心。我会再珍惜一次,好好维系。希望不要渐行渐远,毕竟这样的人说不定可能是最后一个了。 2020不能说过得有多好,但是总是在忙碌中进行,帮女朋友装修房子装修了半年,每周末都搞得灰头土脸的,工作也天天让我头大,可能都要大到某些大头怪那么大了。 玩了半年美股,最后以+10%收官…中间有很多机会,也中了很多陷阱,感觉自己的心脏都强健了不少。不知道,可能不太合适这种吧。慢慢玩耍好了。 在这个大家都习惯快节奏的时代,每天充斥着微博、状态、小视频的生活里,可能这篇日志就还算我一点点的初心吧。 前两天高速出个事故,事儿不大,惊吓性很强,一货车尼玛居然把桥撞掉一块。万一要是撞下来大点儿我岂不是凉了,后面后怕还是比较久。不过我还是相信那句话的,平时你对车好,关键时候车会对你好的。 不枉平时给它洗车都调贵的,总算是把我护住了。等车修好了再带它去打个蜡~ 加油了2021,希望一切都更好

2021年1月24日

Scrapy在Windows下的坑爹安装

今天要装一个Scrapy来爬点内容,因为连到VPS上去搞有点麻烦,就说本地装一个吧。加速开启,然后很easy的pip3 install scrapy,本来等它装好,结果一看powershell里,居然报红字了。 刚开始还以为是不是这东西需要管理员权限,开了个管理员窗口还是装不上,仔细看看错误,发现了这么一段 error: Microsoft Visual C++ 14.0 is required. Get it with "Build Tools for Visual Studio": https://visualstudio.microsoft.com/downloads 这意思是少东西呗,那咱也别废话,既然给了链接就打开看看得了。看报错应该就是少一个c++编译器而已。 好家伙,打开这网页懵逼了。这不是一个完整的VS2019么??这东西好久之前用过,大概有个15G差不多,难道我就装个Scrapy就要这么大动干戈么。 这东西装上我估摸着最多用一次。所以还是不要装这种有的没得了。 去StackOverflow上看大佬们怎么整,结果也只是给了一个什么说明都没有的vc++2005的包。觉得有点不靠谱。 最后去微软那边找了很久,找到了这个页面。https://visualstudio.microsoft.com/zh-hans/visual-cpp-build-tools/ 看起来很科学啊!!只是单独安装工具而已。实际下来以后还下载了大概1.5G的内容,感觉比之前的15G已经强多了。 装好这个重启以后终于能正常安装Scrapy了。

2020年11月21日

老老实实来阿里云了

去年买了搬瓦工的CN2-GIA-E线路,也就是DC6机房。刚买的时候体验很好,虽然ping值稳定在138左右,但是带宽大,稳定啊。 但时过境迁,没想到过了半年,开始间歇性抽风。原来会偶尔5~10分钟的无法ping通,或者延迟高达300。忍了很久。 这次五一它完全给我罢工了,一天能抽风十几次,每次都是同样症状。而且ping值居然稳定到198了??这是逗我吗。 开网页的体验实在是太差了。 假期最后一天打开了阿里云香港,哇。真的是好厉害。38的ping,100M的带宽。按量付费。爱了爱了。 所以迁来了阿里云,核算了下成本,屌丝机器1C0.5G20G,配置费250/年,流量1元/1G,一年下来应该1000之内吧。先试试看~~

2020年5月5日

源代码迁移到github咯~

其实很早就看到github开放免费的私有仓库了(翻了翻资料是19年初了),但是因为这套gitlab-ci用的很好的,所以没有迁移的动力。 五一终于腾出时间看了一下github上对标的workflow功能,也是蛮有意思的。趁着这个空档,也就一并迁过来了。 为啥会非要私有仓库?那是因为翻墙代码也在上面啊,考虑再三还是把密码啥的写到代码里最好维护,部署起来也方便。但这个要是公开了,蹭我翻墙也就不说了,喝茶去了就搞笑了。。 迁移了四个仓库过来,总共花费了6小时…研究了下workflow有点费劲。顺手也把老掉牙的hexo和next版本升了一下,看起来没啥大问题。 几个仓库 gaia 这是一个总体的网关,里面包含了一个核心nginx,嵌了多个域名和转发。同时本hexo工程也在里面,主要是考虑到单独拆一个还需要再搞个nginx,麻烦。单纯的配置,就公开了。 chronos 这个是闲的没事儿用go写的,本来想实现一个网站登录,但是后来一直没继续写下去,之后会继续在上面搞的。但我最近又觉得Python不错,后面看会不会开个Python的东西出来了,也没啥敏感内容。 v2ray 这东西就不说了。就是个翻墙了,迁过来也是私有了。主要是有私有的key之类的东西,这东西也是容器化了。基于v2ray的官方镜像叠了一层配置,主要是可以简单的重新打包来升级。不开放是因为认证key也写在配置文件里,开放了我怕不是我机器要被各位大佬爆掉了。 deploy 这玩意儿就是几个简单的docker-compose.yml了,其他几个工程每个的机器上的部署都在这个里面写着。因为我偷懒所以把cloudflare的key也放进去了…所以没法公开。 有相同需求的可以去github上看看参考一下。 后面再开个文章把现在网站的结构记一记好了。 workflow和gitlab-ci 之前好好研究了一下gitlab-ci的方式,gitlab-ci可以全部基于docker镜像,这个比较方便,我自己封装了好几个私有的image,用来打包、部署等等。 切换到workflow发现思路不太一样,workflow里面有个action的概念,乍看起来也是封装的一系列脚本,基本基于shell,但是不能私有是个问题。毕竟我想把机器的deploy key写进去,还是有私有化的需求的。但是看文档,这东西只能public状态才能用。 两者比起来我还是觉得gitlab-ci好用……可能是用久了,workflow我觉得设计思路挺独特,具体还没发现很好用的地方。 之前还自己部署了一台gitlab-runner来跑ci,这次也懒得弄了,直接薅github的羊毛算了。 有点蛋疼的部署 所有的部署镜像都是我在docker hub上的私有仓库,这本身没什么问题,但是gitlab-ci可以在runner里面直接访问到私有仓库,拉到我的私有仓库进行部署,这个在workflow里没法这么干,只能把ssh key写到secrets里面,代码动态的保存下来进行远程deploy。看起来有点不科学,不过先这么用了。 - name: Create the key run: echo "${{ secrets.PRIVATE_KEY }}" > key && chmod 600 key - name: Deploy run: ssh -i key -o StrictHostKeychecking=no root@muxin.io "cd /mnt/deploy/gaia && docker-compose down --rmi all && docker-compose up -d" 没错代码就是这么的妖!话说这个key应该跑完workflow会被删掉吧???要是没删掉下一个workflow还能用就gg了。我也没细看,相信不会这么蠢的设计的。 第二个不爽的地方是github里面不能建group,导致我每个项目相同的secrets都要手动配一次,这个感觉也不科学。。 基本初步体验就这样,后面看看有新项目要部署了再仔细研究下。

2020年5月4日

MySQL死锁排查

之前一直没有机会碰到与MySQL相关的问题,缺乏一些对于线上环境排错的经验,今天刚好碰到这个一个例子,以此来做一些记录。 问题起因 某数据库中一张business表,当尝试update某一条记录时,会卡住一段时间,然后报一个获取锁错误。 问题初步确定 遇到这个问题第一反应是这个记录被加锁了,首先update了一下同一张表的其他数据,确定可以执行,因此初步确定是行锁引起的。 那么最直接的想法就是执行show full processlist;,看一下能否找到蛛丝马迹。 然而结果并不乐观,上面命令告诉我当前这个数据库没有任何连接在执行SQL,唯一看到的就是我这条命令本身了。 但虽说没有执行SQL,但是有很多Sleep中的连接,这种情况下,在此尝试update被锁死的数据,依然处于锁死状态。 进一步排查 这时想到了SHOW ENGINE INNODB STATUS;查看一下InnoDB引擎的情况,可以看到我刚才执行的语句在尝试加锁的时候超时了,更印证了之前的推测。 首先考虑是不是出现了死锁,现在的MySQL基本比较智能了,如果一般的简单死锁会自动回滚一个事务。通过show status like '%lock%';查看了一下,当前等待的锁数目为0,当我尝试update时数量变为1,说明只有当前一个锁在等待,并不是简单的一个死锁问题。 那么看一下SELECT * FROM information_schema.INNODB_TRX;当前trx的结果,发现了令人卧槽的结果。 居然有一个事务运行了几个小时……并且加锁居然加了200多个行锁。这显然就是罪魁祸首了。 后续排查 那么原因很明了了,找到这个事务的线程,直接kill掉可以强行回滚释放掉锁,但是究竟是什么业务导致的这个问题,还需要细致排查。 首先看调用方,根据show full processlist;的结果可以找到调用方,是一台Java服务,但这个很难知道究竟是什么业务引起的超长时间事务。 一开始想到去看binlog,但是谷歌过发现binlog并不会记录事务的操作,只有事务提交后才会被写入,然而我们这个超长时间没有提交的事务就无法看到内容了。 最后找到了general_log,这个日志文件会将事务中的动作也记录下来。 然而执行一下show variables like '%general%';后发现居然是OFF状态,也就是没有在记录general_log(后来我反应过来,这是很正常的,开启了这个选项会在短时间内用这个日志把你的磁盘爆掉…) 那么此处我们set global general_log=1;后,之后产生的动作便会被记录了。 最终解决 首先当前第一要务是把对应的线程kill掉,先释放行锁,恢复业务。 其次是打开general_log的记录,随时观察数据库,当数据库复现这个问题时,使用thread_id到general_log中搜索便可以一目了然了。 最后一定要记得观察一下general_log的情况,毕竟详细记录所有操作,日志量也是巨大的,用完之后要将开关关掉。

2018年7月2日

本站全面启用CDN

今日起本站全面启用CDN! 之前一直纠结于境外服务器访问速度有限,境内服务器限制多而且不能科学上网的问题。刚好看到Dnspod的CDN推荐,于是亲自尝试了一下,发现国内访问效果确实显著。 主要做了如下的修改: 取消源站的HTTPS访问方式,关闭防火墙443端口 将HTTPS证书部署在CDN节点,由CDN节点负责建立到用户的HTTPS连接 取消原先www.muxin.io通过nginx做301重定向的操作,改为由DNS阶段直接301重定向 目前你们所看到的结果是来自阿里云CDN的加速效果,话说现在CDN价格确实便宜,阿里云价格大概在0.4元/GB左右,对于小站来说基本可以忽略这个费用。 在此不得不吐槽一下阿里云,开启CDN居然还要人工审核,花了一天时间,而腾讯云则可以立刻开通,最后选择阿里云的原因还是出于情感和信任吧,访问上感觉和腾讯云差不多。 CDN的优势 用上CDN之后本站的真实IP被隐藏,虽然说目前没有遭到过任何攻击,最多被端口扫描过几次,不过终究还是安全了些,目前没有方法可以获得本站的真实IP。 云加速效果确实有效,因为原本国内数据中心到本站的速度并不慢,慢的是某些地区,经过CDN加速后可以完全打到秒开的效果,大大提升了体验。 CDN启用方法也十分简单,只需要提供源站域名和真实IP,由提供商生成加速域名,将自己站点的域名CNAME解析到加速域名即可。 期间也遇到了裸域名muxin.io下CNAME和MX记录冲突的问题,由于Dnspod支持同时设置,暂时就这么搞了,测试了几封邮件没有什么问题,其他还有待观察。

2017年2月16日

HTML 中固定 footer 的方法

有时在 Html 中我们需要固定底部的版权信息等内容,期望的展示方式是页面撑满浏览器,最下方是 footer 的内容。 <div class="main"> <div class="content"> ... </div> <footer>版权所有</footer> </div> 如到所示,我们希望的是footer永远占据页面的最下方,而content内容则填满页面的其他部分。 想当然的方法 想当然的方法去实现,是直接将div.main部分的height设置为100%,这样可以直接撑满页面,虽然在内容少的时候确实达到了我们想要的效果,但弊端也十分明显——在页面内容多于一页的时候,内容会展示不全,因为设置了固定的height,所以内容并不会将div.main进一步撑开。所以这样是不行的。 使用min-height Html 中可以使用min-height来指定内容高度。根据这个思路我们可以将div.conent的min-height设置为80%,同时将footer的min-height设置为20%。 然而这个解决方案看起来很完美,实际上用起来的时候还是会有问题,原因如下: 一个元素如果要指定min-height,那么它的父元素必须指定height,指定min-height也无济于事。也就是说div.main必须指定height这一属性。然而还是那个height不能自动变大的问题。 最终方案——使用position 那么最终的解决方案应该怎样呢?答案是使用position 先给出最终的Html和CSS代码: <div class="main"> <div class="content"> ... </div> <footer>版权所有</footer> </div> div.main { min-height: 100%; position: relative; padding-bottom: 10rem; } footer { height: 10rem; position: absolute; bottom: 0; } 首先将div.main的最小高度设置为100%,随后将其位置指定为relative;随后指定footer的位置为absolute,这样footer会相对于上一个位置被指定为relative的元素进行布局,随后bottom: 0;将footer设置在了div.main的最下方。 当然不要忘记了div.main需要有一个padding-bottom,大小等于footer的高度,这样内容才不会错位。 到此我们就实现了最完美的固定footer的样式。

2017年1月2日

2017——新年快乐

不知不觉又是新的一年开始了,自己也没有像之前想象中的一样,对跨年这个一般人看来充满意义的事情抱有太大的期待。虽说如此,也觉得此时的自己已经开始了新一年的旅程。 回首2016,还是可以鲜明的感觉到自己各方面的进步的,不能说是很多,但是起码见识和思维都比前一年有所拓展。像某人说过的那句话一样——“如果失去了好奇心,那么人就死了”。我可以昂首挺胸的说,我活着走过了2016,同时也会在2017年活得更好。对更多地事情不遗余力的去探索,去学习,去让自己更有深度。 在这里祝大家新年快乐!同时新年也会多抽一些时间来维护这里,把更多的知识和经验记录下来,和别人一起分享,进步。 再见了,2016。你好!2017。

2017年1月1日

Java callback 回调学习

最近接触了一些前端 JavaScript 方面的东西,觉得 JS 最方便的地方便是各种回调函数,无论是 Ajax 还是与用户的前端交互,都是通过回调函数的形式实现的。于是乎来到 Java 中搜索了一下有关回调的相关内容,结果发现还真的有。查了很多文章,总算是弄清楚了 Java 中回调的使用方法,然而大部分讲解都没有说到其实际的用途,于是便再次进行记录,也是给自己一个备忘。 什么是回调 接触过 JS 或者做过服务端接口的人应该都会对回调十分熟悉。按我的理解,假设有 A 和 B 两个模块,A 调用 B 来执行某一个功能,这个功能执行可能会花费一些时间,而且 A 希望在 B 执行完之后得到通知,那么 B 执行完相关功能之后再对 A 进行通知的这个过程,就叫做回调。通俗来讲就是甲乙两个人,甲需要一把梯子来换灯泡,让乙去拿来,乙把梯子拿来以后需要告诉甲:梯子已经到了,可以换灯泡了。 在 JS 中的回调十分便捷,只要将收到回调后需要执行的方法传入调用的方法中即可,这种回调方式的调用不仅限于 JS,Ruby 中也有类似的语法,然而对于 Java 来说,实现回调虽然有些麻烦,但是仍然有着解决办法。 Java 中的回调 在 Java 中实现一个回调不像脚本语言那样简洁优雅,但是会有一种更加规范的感觉。 此处还是用之前提到的的 A 和 B,生命这两个模块为两个类,主要归结为以下步骤: 定义一个 Callback 接口,用来指明回调时候需要执行的方法。 让 A 类实现 callback 接口,并且 A 类需要持有一个 B 的实例对象 b(因为只有持有才能调用)。 让 B 类中实现一个参数带有 callback 接口参数的方法 f(Callback callback, Object args)。 A 类的实例对象 a 调用 b.f,并且将 a 自身作为 callback 参数传入。 b.f 此时可以通过 callback 调用 A 中定义的 Callback 接口中的问题。 乍看下来感觉整个人都不好了,乱七八糟的,其实结合代码的话会看着很简单。此处以刚才说到的甲让乙搬梯子,然后甲用梯子换灯泡的情景,写一个 Demo。 ...

2016年6月28日