返利插件后台服务器崩溃与恢复的一点感想

去年5,6月份的时候,临时兴起写了一个京东返利插件。放到chrome和edge的插件市场,没啥流量,不过每天也有点人用,高峰期大概可以给我攻陷个差不多200的收入。 前两天研究rspress,想把我的返利插件的文档页面重构一下,更美观一点。当时手残的就在返利插件的后台服务器上使用vscode尝试。心想一个静态网站生成器吗,没啥大问题,我之前在上面跑rust开发也没出现什么大事。谁知道就坏在这个上面。 在修改一个文件重新编译生成的时候,系统竟然没反应了,我以为可能是网络卡顿了,谁知道差不多10分钟也没恢复,登录到腾讯云控制台,发现cpu和内存同时爆到到99%以上。没办法,只能重启。谁知道重启之后,ssh竟然链接不上,通过控制台的vnc连接工具连接到机器上,发现启动的时候死在了cloudflared服务上。不知道我当初怎么安装的,竟然自启动cloudflared,然后还没其他成功,导致整个系统完全进入不来kernel. 由此而来第一个教训就是不能在生产机器上进行无关生产操作的其他任务,说不定什么时候这个任务就会导致生产服务器崩溃。 这就坑了,本来就是个重启解决的问题,现在要解决系统问题了。拜托了google和chatgpt搞了半天也没解决怎么进入系统问题。还好腾讯还有个救援模式,通过vnc进入之后还可以挂载原来机器的数据,否则数据丢失就坑了。虽然用户不是很多,涉及的钱也不多,但是也不能白白浪费了。 vnc连接机器虽然可以挂载原来机器数据,但是不能直接下载。好在我有台nas,通过cloudflare可以webdav暴露到公网。简单的通过curl将重要数据传递到nas上之后我重装了服务器,然后很快的就恢复了服务。 从系统崩溃到最终恢复过去了大概12小时,仅操作尝试就花了大概4个小时。主要还是不太了解linux的启动过程,导致在排查启动问题时就消耗了很长时间,最终还没有解决。其次,就是数据备份不存在,导致后续通过救援模式进入系统之后要重新备份数据,因为救援模式不太好安装新的工具,只能通过curl命令上传webdav简单的备份数据,如果遇到大量的数据,那么就花费更多的时间。最终的纯系统重装及恢复服务,其实到并没有花费太多的时间。 由此也可见数据备份的重要性。也就是我这个服务用户量小,影响面低,如果换成其他重要服务,这么长的恢复时间就会造成很大的损失。如果有完善的数据备份,那么完全可以开一台新机器很快的恢复服务。 在最终备份数据的时候还发现另外一个问题。因为这个插件的后台我开发的有段时间且后续也没有怎么维护,导致数据备份的时候差点忘记一块重要的数据。这个时候就体现出项目readme.md的重要性了。核心内容及操作一定要记录其中,否则过了一段时间之后,即使你是当初的开发者,有意外情况发生时也会忘记重要步骤,从而导致事故。

2024-02-20 16:11 · 1 min · 江波·林沂

使用rust入门discord机器人遇到的一些坑

最近几天在研究discord,其实之前也玩过,只不过当时没怎么玩下去,感觉怎么用都不知道。这两天感觉突然开悟了,用起来非常顺手,突发奇想弄个discord的bot玩玩。 快进到官网的教程,在进入开发之前的步骤非常流程,新加的机器人顺利的加入了群组,但是接下来的就看不懂了。 官网的示例是用的http做的,我研究了一下rust下的相关库,找到了serenity。看了看它的hello world,倒是也正常跑起来了,但是这鬼东西,连个http的端口都没有,怎么让discord访问呢? 接着又去研究官网,研究了了一两天,我发现discord还有一种使用websocket的连接方案。猜一下,大概率serenity大概率用的这种方案,这也就解释了它为什么不需要http的端口了。直接都连上了discord服务器的websocket,还需要什么自行车。暴露什么端口徒增风险呢。 仔细一想,serenity使用websocket其实是满明智的,Im这种应用消息的频率是相当高的,如果使用http的话,目标服务器的负载基本上都是很大的,毕竟很大一部分bot是需要读取频道里面的每条消息的,一个成熟的bot是会有相当多的频段和用户消息的,这个时候可以复用tcp通道,主动接受推送的websocket简直不要太合适。 知道了serenity是使用websocket的,那么它的示例应该是可以正常让机器人在线的啊,我应用都跑起来了,不科学啊,这么广泛使用的类库不应该会犯这么低级的错误。 这个时候尝试了一下telnet discord的wss服务器,我靠,果然是不通的,这个示例怎么还一直正常运行,这不是坑爹吗。调整日志级别,打印出serenity的DEBUG日志,看日志果然是连接discord的wss服务器异常,而且DEBUG级别日志下,应用自己报错关闭了,真坑。 知道网络不通剩下就好办了,打开clash for windows的tun模式,再次启动程序。我的discord bot终于正常上线了。 在频道中输入!ping, bot自动回复了Pong。终于discord的入门级bot完成了!!!!

2023-08-07 17:29 · 1 min · 江波·林沂

用rust调用pytorch的一点记录

最近在研究openAI的CLIP模型,但是python的水平不够,写起来总感觉别扭。就随手搜了一下,发现正好tch做了一层libtorch的包装,使用rust来调用pytorch还是相当顺手的,就跟着tch的README来做一点尝试。 解释一下:pytorch的底层其实就是libtorch,这个是使用c++写的,可以理解pytorch是libtorch的python包装。 以下所有尝试都是在windows的wsl上操作的,具体linux版本为ubuntu/22:04 使用tch的前提: conda(我使用的是miniconda3) rust(1.71.0) python(3.11.4) pytorch(2.0.1) 我在这里没有使用miniconda3的默认env,而是创建了一个tch的专用env,使用了以下命令 conda create -n rust_pytorch1 pytorch 命令的意思就是就是创建一个名字叫做rust_pytorch1的env,并在其中安装pytorch。 下面,我们使用命令激活这个env conda activate rust_pytorch1 按照tch的README我们还需要下载libtorch,这个需要在页面根据自己的环境进行选择,我这边只有cpu环境,因此选择的是libtorch的cpu版本。我们可以在页面https://pytorch.org/get-started/locally/自行选择自己需要的版本并下载,然后解压到指定目录即可。 我们通过export导入环境变量,以便程序知道libtorch的路径,使用以下命令 export LIBTORCH=/home/jiangbo212/libtorch 这个时候我们cargo run运行README上的Tensor的例子时,可能会得到一个cc的编译错误。这时需要执行以下命令,导入一个变量。具体原因暂时未知。 export LIBTORCH_CXX11_ABI=0 此时,我们运行cargo run,依旧可能得到以下错误: error while loading shared libraries: libtorch_cpu.so: cannot open shared object file: No such file or directory 这是由于cargo找不到libtorch.so的路径,虽然我们在前面导入了LIBTORCH的位置。需要执行以下命令, 让cargo可以在LD_LIBRARY_PATH下找到libtorch的lib路径 export LD_LIBRARY_PATH=/home/jiangbo212/libtorch//lib:$LD_LIBRARY_PATH 最后,我们运行cargo run, 可以看到正常输出了Tensor。

2023-07-28 09:39 · 1 min · 江波·林沂

从Vue2迁移到Svelte

本文翻译自https://escape.tech/blog/from-vue2-to-svelte/ 在使用Vue2作为我们的前端框架差不多快两年后,它被宣布不再继续维护,因此我们决定迁移到一个新的框架。但是哪一个是我们应该选择的呢?Vue3 OR Svelte。 需要注意的是在这次迁移中我们也需要提升我们的开发体验,特别是在类型检查, 高性能以及构建时间方面。我们没有考虑React,因为我们没有太多的时间去学习。同时相对于Vue和Svelte,它也没有提供一个开箱即用的方案。此外,Vue和Svelte使用了相同的单文件组件概念:逻辑(javascript), 结构(html), 样式(CSS)在同一个文件中。 我们做了一些研究,最终选定了Svelte。下面一些解释关于为什么选择Svelte: Svelte PK Vue3 Svelte拥有更好的学习留存率。我们选择了市场上两个新的前端框架,Vue3和Svelte。下面是一个插图,显示的是不同框架在过去5年的留存率。从State of JS survey收集的该领域的开发人员的数据,我们可以看到Svelte来到了第2的位置,而Vue3仅排名第4。 In 2021 Svelte was in the 2nd position and Vue 3 in 4th position (source: State of JS) 这张图显示过去使用Svelte的开发人员在将来更愿意使用他们 更好的类型检查 Svelte 通过更简单的组件设计过程和内置类型化事件提供更好的类型检查体验,这对我们来说非常人性化。 严格的全局访问 在Svelte中可以从其他文件导入枚举,并在模板中使用它们,在Vue3中是不存在这种情形的。 Escape Benchmark about frontend stack 语法 主观的,我认为Svelte的语法相比于Vue更加的人性化和友好。你可以看看下面这些代码块,想下自己是什么感觉。 Svelte <script> let firstName = ""; let town = ""; $: fullName = "Full name: " + firstName + ' ' + lastName; const reset = () => { firstName = ""; lastName = ""; } </script> <main> <div> <label>First name</label> <input type="text" bind:value={firstName}> <label>Last name</label> <input type="text" bind:value={lastName}> <button on:click={reset}>Reset</button> </div> <div> {fullName} </div> </main> <style> main{ background-color: white; } </style> Vue...

2023-02-10 16:07 · 2 min · 江波·林沂

使用Cloudflare Pages及Meilsearch搭建一个Linux Git提交日志查询网站

本文章主要受网站https://linux-commits-search.typesense.org/启发,并根据其源码进行修改。 文章重点讲述项目部署在Cloudflare Pages过程,其他细节只做简略的叙述。目前可通过域名https://linux-commit-search.jiangbo.space进行访问。 网站本身是一个前后端分离的网站, 后台有meilsearch提供http服务。meilisearch是一个开源的搜索服务,可以理解为一个简单的ES服务,便于搭建和使用。 前端使用Parcel进行打包,js方面使用的还是比较老的jquery。使用instant-meilisearch和instantsearch进行搜索控件的构建,整体来说结构比较简单。如果进行常规的部署,还是相当简单的。之前我是在自己的轻量级服务器上进行的部署,只是用了nginx就轻松的部署完成。为什么要使用cloudflare呢?主要是考虑到服务的稳定性以及访问的广度,毕竟cloudflare的机器遍布全球,而我的服务器只有一台。同时,使用cloudflare pages可以轻松的做到部署自动化。 Cloudflare pages部署前端项目还是非常简单的,选择好git项目以及构建步骤之后,前端代码就轻松的部署到了Cloudflare的全球网络上了,配置好相应的域名之后,测试访问,前端页面展示的非常完美。 接下来问题就来了, 在自有服务器上可以简单实现的代理功能,在Cloudflare Pages上就比较麻烦了。我先后尝试了以下三种方式: 使用Cloudflare Pages提供的Functions功能进行代理功能的实现。 这里遇到的问题是,Functions提供的fetch功能,只能使用env.ASSET进行调用。开始的时候,我没有自己,认为fetch并不是什么大问题,直接将前端页面域名中的域名替换成meilisearch服务的后端页面进行调用。functions部署之后,进行简单调用,发现返回的竟然还是前端页面。这是什么鬼啊,我又详细的检查了一遍functions文档, 返现evn.ASSET只能访问CloudFlare Pages域名下的静态文件。这就太坑了,只能放弃这种处理方式了。 使用Cloudflare Pages提供的Rediects功能进行代理功能的实现。 这里面耽误的时间到不是很多,看文档的时候,我就看到了proxy功能是不支持的,说是未来将会支持。但是总归不信邪,试一下吧。打包阶段就通知我这种配置方式是不支持的。哎! 使用Cloudflare Pages提供的Functions与Workers绑定的功能实现。 不得不说,Cloudflare Pages中的Functions的文档写的有点稀碎,同时很多措辞感觉有点模棱两可的感觉,也不知道是不是我英语不过关的原因。Functions与Workers绑定的文档我看了好多次,也尝试了多次,才最终完成绑定的工作。 在配置界面进行Functions和Workers(Service)进行绑定的时候,页面显示的Variable Name。在文档界面,又显示的是select the environment, 同时文档界面Service bindings下面就是Environment variables, 搞的我一直以为设置界面设置的参数是环境变量而已,是用来区分环境的。真正调用workers直接使用文档里面的例子就可以了。 Functions与Service绑定配置页面的配置: Functions与Service绑定配置页面 Functions与Service绑定的文档页面: Functions与Service绑定的文档页面 文档中的调用服务的例子如下: export async function onRequestGet(context) { return context.env.SERVICE.fetch(context.request); } 我一直认为其中的SERVICE是固定写法,文档中是这样写的:“Here is an example of how to use service bindings in your Function, our service binding is named “SERVICE”"。同样的写法我怎么调用,都直接告诉我系统异常。困扰了我好久,后来我突然想到,是不是我在设置页面设置的变量值才是env.后面应该跟随的参数,而不是固定的SERVICE。我进行了尝试,果然一下就成功了。后续再看了文档,才有点理解。 解决了functions与service绑定的问题之后,网站终于完整的部署好了。整体过程还是相当流程的,使用workers进行调用的过程也是几乎没有延迟,非常完美。 整个过程中workers的开发环境真的是非常友好,对于简单的需求,完全可以直接在网页上进行开发,并进行调试,整个过程相当流畅,有些简单的需求在workers上可以快速的实现。

2023-01-30 13:46 · 1 min · 江波·林沂