使用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 · 江波·林沂

关于TempFile中tempfile()使用比较迷惑一点的解释

在我最初向meilisearch提交PR3164时,我是使用NamedTempFile来创建临时文件的。当时审核人员在审核之后给出了建议: “从需求上看,这里使用tempfile()可能更合适,因为我们不需要去知道文件名字或者持久化该文件” 在查看了Tempfile这个crate之后,我认可了这个建议,同时也使用tempfile()调整了相关代码。 在调整的过程中我遇到了一个非常奇怪的问题。在使用NamedTempFile时,我写入文件之后,再进行读取,中间不需要任何其他的步骤。但是在我调整直接使用tempfile()方法之后,直接进行读取,那么读到的是一个空的文件,这个问题花费了我好多时间,终于找到了一个可以使用的方案,那就是在写入和读取之间加入如下一行代码: // Seek to start buffer.seek(std::io::SeekFrom::Start(0)).await 如果你是直接使用tempfile进行读写,那么下面这个代码正是你需要的: // Seek to start tmpfile.seek(SeekFrom::Start(0)).unwrap(); seek()方法做的事情很简单,就是把文件里面的cursor(游标)指向它开头的位置。这个就让人很疑惑了,感觉这里也没有什么实际的动作呢。怎么就让原本写入读取不到数据的文件,从而读取到数据呢?这个问题困扰了我很长时间,即使在相关的PR已经合并完之后。 后续我在研究milli的源码的时候,发现里面使用tempfile()的地方同样使用了相同的代码。我意识到这应该不是一个偶然的用法,而是必须添加这一步才能够正常的读取到tempfile()创建的文件。我又去翻阅了一遍Tempfile crate,在它的github的README中看到了相同的写法,但是也没有进一步说明为什么要这么做。 最终我在tempfile的github issues中找到了明确的答案。具体issue号是142。 2021年的时候有人跟我提出了相同的疑问,作者做出了以下的回复: “After you write to the temporary file, the file “cursor” well be at the end. You need to seek back to the beginning before you can copy.” 意思吗就是在tempfile写入之后,文件的cursor(游标)指向了文件结束的位置,这个时候如果需要进行copy,需要重新将cursor指向到文件开头的位置。这就很明确了,读取的时候其实也是类似的问题,读取的时候也是要通过cursor进行读取,从文件的末尾进行读取,当然读取的是0字节。当使用seek()将cursor重新指向文件开头,就可以正确的读取到所有数据了。

2023-01-14 07:22 · 1 min · 江波·林沂

Dyn trait的性能影响及其相应的性能提升方案

本篇文章受meilisearch的issue #2456 启发。并主要参考以下两篇英文文章 a basic explanation of how dyn Traits work internally a basice explanation of enum_dispath 。 Dyn trait一般用于多态操作,这块在面向对象编程中比较常见。我们想象一种场景,我们现在需要一个取值范围,这个范围既可以是线性的,即0, 1, 2, 3 ….. 1000; 也可以是对数范围。那么我们可以用一下代码来表示。 trait RangeControl { fn set_range(&mut self, value: f64); fn get_value(&self) -> f64; } struct LinerRange{ position: f64; } struct LogarithmickRange { position: f64; } impl RangeControl for LinerRange { fn set_range(&mut self, value: f64) { self.position = value; } fn get_value(&self) -> f64 { self....

2023-01-08 22:11 · 1 min · 江波·林沂