使用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。我进行了尝试,果然一下就成功了。后续再看了文档,才有点理解。...

2023-01-30 13:46 · 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 · 江波·林沂

使用Automata和Rust索引1,600,000,000Keys(5)

FST构造 构造确定性非循环有限状态转换器的工作方式与构造确定性非循环有限状态接受器的工作方式大致相同。关键区别在于转换时输出的放置和共享。 Constructing deterministic acyclic finite state transducers works in much the same way as constructing deterministic acyclic finite state acceptors. The key difference is the placement and sharing of outputs on transitions. 为了降低心理负担,我们将重用上一节中的示例,其中包含key mon,tues和thurs。由于使用FST表示map,我们将一周中的数字日期2,3,5分别与每个key相关联。 To keep the mental burden low, we will reuse the example in the previous section with keys mon, tues and thurs. Since FSTs represent maps, we will associate the numeric day of the week with each key: 2, 3 and 5, respectively....

2022-09-30 11:04 · 8 min · 江波·林沂

使用Automata和Rust索引1,600,000,000Keys(4)

在前两节中,我一直小心避免谈论用于表示有序集或map的有限状态机的构造。也就是说,构造比简单的遍历要复杂一些。 In the previous two sections, I have been careful to avoid talking about the construction of finite state machines that are used to represent ordered sets or maps. Namely, construction is a bit more complex than simple traversal. 为了简单起见,我们对 set 或 map 中的元素进行了限制:它们必须按字典顺序添加。这是一个繁重的限制,但我们稍后会看到如何减轻它。 To keep things simple, we place a restriction on the elements in our set or map: they must be added in lexicographic order. This is an onerous restriction, but we will see later how to mitigate it....

2022-09-29 16:43 · 6 min · 江波·林沂