本文翻译自 URL Shortener - Shuttle ,有少许修改。当个小软文看吧
现在,星期三晚上,我试图在入睡 —— 我看了下手机,现在是凌晨 2 点 54 分。当我意识到我将无法再次获得超过 5 小时的睡眠时,一种恐惧感笼罩着我。
我做软件开发人员已经将近 10 年了,我是怎么走来的?
我的部署在晚上十点崩溃了(因为我从不学习),我不得不在接下来的 3 小时内应对我们的基础设施和巨大的压力。当我坐在那里反思人生选择时,我有了一个想法。我们能做得更好吗?
我不想在午夜处理 Terraform 和 Kubernetes。我在部署的同时编写可扩展的代码。我希望为我的依赖会帮我自动生成和管理。我希望晚上能够入睡。
现在是 2022 年。当然,我们可以做得更好。当我茫然地盯着我的白色天花板时,我决定看看这是否可能。
我突然在床上坐了起来。我是否可以创建一个有用的应用程序,具有某种数据库状态,一个自定义子域,只关注我的应用程序代码,而不需要担心基础设施,并在 10 分钟或更短的时间内完成它?我会写一个 URL 缩短器什么的。我会用 Rust 写。我今晚会写它。
设计
我下了床,打开了办公室的灯。我坐在符合人体工程学的椅子上,打开了一个滑稽的大曲面显示器。Arch 启动!我赶紧在 Signal 上给我的一个朋友发消息,提醒他们我用的是 Arch。现在我已经准备好编码了。让我们来构建这个东西。
API 将很简单。没有理由使用 GUI 或类似的东西 - 我是工程师,因此我说服自己 UI 在 1970 年代的电传打字机中达到了顶峰。
生命是短暂的,所以我要构建一个 HTTP API。我能想到的最简单的事情。
您可以像这样缩短 URL:
1 | curl -X POST -d 'https://www.google.com' https://myapp.com |
你被重定向如下:
1 | curl https://myapp.com/uvAivJ |
是的,那会起作用的。
接下来,我需要某种数据库来存储 URL。我曾短暂考虑过在不需要数据库状态的情况下使用双射压缩方案,但让我们面对现实吧,我不太确定什么是双射,而且已经是凌晨 3 点 02 分了。
我将只获得一个具有基本架构的 Postgres 实例:
1 | CREATE TABLE urls ( |
天才。
我将添加一个索引或其他东西,以便数据库不会对每个请求进行线性搜索。没有人会真正使用它 - 但我已经可以感觉到任何碰巧浏览我的源代码的人的判断。我需要能够向人们解释,你可以在恒定的时间内搜索网址,这意味着我理解复杂性理论。
好的,我准备好了。现在是凌晨 3 点 05 分。我有 10 分钟。我拿起我的黑色电子烟,受到了很大的打击。烟雾弥漫在房间里,我再也看不见屏幕了。无论什么。我试着掰开我的手指和脖子以获得一些戏剧性的天赋,失败了,然后打开了一个终端。
构建 Barebones - 剩余 09:59
我正在为这个项目使用 Shuttle。这是一个为 Rust 构建的无服务器平台,我不必处理配置数据库、子域或任何类似的问题。我已经安装了 CLI。
我停下来想一想 - 我想使用哪个 Web 框架?我想是 Rocket。它几乎已经准备好使用一个甜蜜的 API 进行生产,而且我对它相当精通。
要使用样板初始化火箭项目,我可以使用 Shuttle CLI init
命令:
1 | cargo shuttle init --template rocket url-shortener |
这给我留下了以下 main.rs
文件:
1 |
|
正如你所看到的,这导入了一些依赖项。幸运的是,该 init
命令也处理了这个问题,给我们留下了以下内容 Cargo.toml
:
1 | [package] |
该 init
命令还为我们创建了一个新的 Shuttle 项目。这将在 Shuttle 的基础结构中启动一个隔离的容器。
现在就可以部署:
1 | cargo shuttle deploy |
还行。。。这似乎有点太容易了,让我们看看它是否有效。
1 | curl -X https://url-shortener.shuttleapp.rs/hello |
嗯,还不错。我又给自己倒了一杯……
添加 Postgres - 剩余 07:03
这是我旅程中我通常会有点慌张的部分。我以前设置过数据库,但这总是很痛苦。您需要预配 VM,确保存储不是临时的,安装并启动数据库,创建具有正确权限和安全密码的帐户,将密码存储在 CI 中的某种机密管理器中,将 IP 地址和 VM 的 IP 地址添加到可接受的主机列表中等。哎呀,这听起来像是很多工作。
Shuttle 为你做了很多这样的事情——我只是不记得是怎么做的。我快速转到文档中的 Shuttle 共享数据库 部分。我在 Cargo.toml
添加了 sqlx
依赖,并更改了 main.rs
中的一行:
1 |
|
通过向 rocket
主函数添加参数,Shuttle
将自动为您预置 Postgres 数据库,创建一个帐户并返回一个经过身份验证的连接池,该连接池可从您的应用程序代码中使用。
让我们部署它,看看会发生什么:
1 | cargo shuttle deploy |
我有一个数据库!我忍不住笑了一下。目前为止,一切都好。
设置 Schema - 剩余 06:30
配置的 Shuttle 数据库是完全空的 - 我需要连接到 Postgres 并自己创建架构,或者编写某种代码来自动执行迁移。当我开始思考这个看似存在的问题时,我决定不去想太多。我只想用最简单的方法。
我使用提供的数据库 URI 使用 pgAdmin 连接到 Shuttle 配置的数据库,并运行以下脚本:
1 | CREATE TABLE urls ( |
当我准备在谷歌上搜索“postgres 如何创建索引”时,我意识到由于用于 url 查找的 id 的是主键,这是一个隐含的“唯一”约束,Postgres 会为我创建索引。Cool。
编写 Endpoints - 剩余 05:17
应用将需要两个 endpoints - 一个用于 URL,另一个用于 shorten
检索 URL 和 redirect
用户。
在考虑实际实现时,我快速为 endpoints 创建了两个实现:
1 |
|
我决定从缩短方法开始。我能想到的最简单的实现是使用 nanoid crate 动态生成一个唯一的 id,然后运行一个 INSERT 语句。嗯 - 重复呢?我决定不去想太多🤷.
1 |
|
接下来,我本着类似的精神实现了该 redirect 方法。在这一点上,我开始恐慌,因为它真的接近 10 分钟大关。我将执行一个 SELECT * 操作并提取与查询 ID 匹配的第一个 URL。如果 id 不存在,则返回:404
1 |
|
哎呀,SQL 查询中有一个拼写错误。
在我修复了我的错别字并通过让我的 IDE 为我完成繁重的工作来整理各种未解决的依赖关系之后,我最后一次部署到了 Shuttle。
关键时刻 - 剩余 00:25
我感觉自己像一个在不可能完成的任务中的冒牌阿汤哥,我专心致志地盯着倒计时的时钟,因为 Shuttle 部署了我的 url 缩短器。19.3 秒,我们就开始了。DEPLOYED 对话框一出现,我就立即测试了一下:
1 | curl -X POST -d "https://google.com" https://url-shortener.shuttleapp.rs |
然后,我将缩短的 URL 复制/粘贴到我的浏览器中,瞧,它被重定向到 Google。
我成功了。
回顾 - 剩余 00:00
我松了一口气,把自己从办公桌上推了回来。我重新装满了杯子,拿起它,走向我废弃的阳台。当我推开窗户,冷空气流进我的公寓时,我向前走了两步,把胳膊肘和杯子放在栏杆上。
我坐在那里思考了一会儿刚刚发生的事情。我成功了。我成功地快速构建了一个有点微不足道的应用程序,而不需要担心配置数据库或网络以及诸如此类。
但这在现实世界中如何衡量呢?真正的软件工程是复杂的,涉及具有不同技能组合的不同团队之间的协作。整个软件世界几乎无法将其保持在一起。用一种完全不必处理基础设施的新范式取代我们现有的、久经考验的云范式真的可行吗?我可以肯定的是,今晚我不会深入了解这个问题。
当我回到卧室,再次躺在床上时,我注意到我在咧嘴笑。我们真的有机会做得更好。也许我们还没有完全到达那里,但我今晚的经历给了我某种乐观,我们并没有像我曾经想象的那么远。怀着对明天更光明的承诺,我侧过身睡着了。