使用 Rust 構建個人部落格(1) – Warp 後端框架

blank

使用 Rust 構建個人部落格(1) - Warp 後端框架

需求背景

一個朋友買了一個雲主機(就是300元3年的那種)

雲主機設定是:

  1. CPU:vCPU 2
  2. 記憶體:2G
  3. 硬碟:40G
  4. 帶寬:1M
  5. 公網IP:1個
  6. 預裝的系統是:Windows 2008 R2

這台機器上,跑了一些他自己的東西,雖然機器整體性能不咋的,但是總覺得還可以再"挖掘"一些性能(擠一擠總是會有的),所以還在跑一個自己的部落格。

技術選型

最初他問我的時候,我就讓他用 WAMP + WordPress,方便、省事兒,而且WP的外掛程式資源一大推,教程也很多。

他說他嘗試過,裝上了以後,機器莫名卡得厲害(不知道原因)。
把他之前跑的東西都弄慢了,後來導致重裝了一次系統。。

好的,這個方案幹掉。

要省資源,可以用 Github 的 Pages,託管免費,還可以綁定域名。
試運行了幾天以後,朋友反饋,覺得訪問速度時快時慢,用戶體驗不能保證。
(我心想,你部落格有啥使用者? )

EN,那麼再換一個吧。。

那麼用靜態網站生成器吧,這樣,速度和資源,都有保證了。

於是,扔給他了一個連結:Static Site Generators - Top Open Source SSGs | Jamstack

說這裡收集了應該是市面上最全的,什麼口味兒的都有,包君滿意!

連結扔過去了以後,朋友幾天沒有聯繫了。

有一天,右下角,他的頭像又開始一閃一閃的。。。
有點點不好的預感。

  1. 朋友:我看了幾種,後來看得眼睛都花了,最後反而不知道選啥了。
  2. 我:那你可以選比較流行的 Hugo,教程也多
  3. 朋友:嗯,看了。
  4. 我:如何?
  5. 朋友:Windows 用習慣了,看到文字性的配置,就頭疼
  6. 我:那我這兒暫時還想不到有啥其它的方案了。
  7. 朋友:那麼
  8. 我:啥?
  9. 朋友:我們自己擼一個呢?
  10. 我:啥? (為什麼是我們? )
  11. 朋友:最近看了些CSS,想自己試試。 (朋友做設計的)
  12. 朋友:之前看的那些框架、靜態網站,都好複雜,想自己從 0 開始。 慢慢摸索。
  13. 我:(嗯,你學 CSS 從 0 開始,起點是不錯的! 但是除了 CSS,還有其它一大坨呢。 )

正想怎麼回復,回頭看到一本 Rust 教程,就給朋友說:
我來做後端和前端交互。 前端HTML 和 CSS 由你來吧。

最後的方案就是:

後端:Warp + Sqlite + Sled
前端:Yew + 朋友的 HTML 和 CSS

選擇的理由?

Rust

我是 2017 年的時候瞭解到 Rust 的,那時已經發佈了 2 年了。

看了它的設計理念、語言設計以及官方教程,我的內心就告訴我,這個就是我需要的語言。

然後,經歷了從入門到糾結,再到放棄,再入門到放棄,期望借這個機會再準備入門。

Warp

Rust 不乏優秀的 web 框架,我嘗試過 Actix-web -> Tide -> Warp。
Warp 是目前用起最順手的。

Sqlite

就朋友那點流量,Sqlite 足以應付了

Sled

部落格是寫少,讀多的。 用 Sled 來充當一個緩存還是不錯的

Yew

用於建構前端的,很方便的將朋友寫的 HTML 嵌入到我的代碼裡。
我就專注於前端與後端交互邏輯了

動工

在那邊還在邊學邊整HTML CSS 的時候,我這裡先開始後端的工作。

資料庫,有 3 張表

部落格內容

CREATETABLE"blog"("id"INTEGERNOTNULL,"title"TEXT(64)NOTNULL,"markdown_content"TEXT(20480)NOTNULL,"parsed_content"TEXT(65535)NOTNULL,"tags"TEXT(256)NOTNULL,"created_at"INTEGERNOTNULL,PRIMARYKEY("id"ASC));

標籤數據

CREATETABLE"blog_tag"("id"INTEGERPRIMARYKEYAUTOINCREMENTNOTNULL,"name"TEXT(32)NOTNULL,CONSTRAINT"name"UNIQUE("name"ASC));

使用者訊息(只有朋友一個使用者數據,離線初始化進去)

CREATETABLE"user"("id"INTEGERNOTNULL,"username"TEXT(32)NOTNULL,"password"TEXT(256)NOTNULL,PRIMARYKEY("id"));

Rust工程

先初始化一個 git 目錄,然後創建 workspace,包含 3 個目錄

  1. backend 部落格後端
  2. frontend 部落格前端
  3. common 公共類

進入 backend 目錄,創建一個 Rust bin 專案

Cargo.toml 引入依賴項

[package]
name = "blog-backend"
version = "0.1.0"
authors = ["Songday <[email protected]>"]
edition = "2018"

[lib]
name = "blog_backend"

[dependencies]
blog-common = { path = "../common" }

ahash = "0.4"
base64 = "0.12"
bytes = "0.5"
chrono = { version = "0.4", features     = ["serde"] }
comrak = "0.8"
futures = "0.3"
hyper = "0.13"
image = { version = "0.23", features = ["jpeg", "png", "gif"] }
lazy_static = "1.4"
lazy-static-include = "3.0"
parking_lot = "0.11"
once_cell = "1.4"
rand = "0.7"
v_htmlescape = "0.10"
subtle = "2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sled = "0.34"
sqlx = { version = "0.4.0-beta.1", default-features = false, features = [ "runtime-tokio", "macros", "sqlite"], optional = false }
scrypt = "0.4"
tokio = { version = "0.2", features = ["fs", "io-util", "macros", "rt-core", "rt-threaded", "signal", "time"] }
uuid = { version = "0.8", features = ["v5"] }
urlencoding = "1.1"

然後,定義一下個人部落格的路由。

Warp 最自以為豪的就是它的 Filter 概念(在定義路由的時候就到的)

Thanks to its Filter system, warp provides these out of the box:

Path routing and parameter extraction
Header requirements and extraction
Query string deserialization
JSON and Form bodies
Multipart form data
Static Files and Directories
Websockets
Access logging
Gzip, Deflate, and Brotli compression

我定義的路由如下:

使用者登錄letuser_login=warp::post().and(warp::path("user")).and(warp::path("login")).and(warp::path::end()).and(warp::cookie::optional(var::AUTH_HEADER_NAME)).and(warp::body::json::<LoginParams>()).and_then(controller::user_login);用戶登出letuser_logout=warp::get().and(warp::path("user")).and(warp::path("logout")).and(warp::path::end()).and(warp::cookie::optional(var::AUTH_HEADER_NAME)).and_then(controller::user_logout);部落格清單letblog_list=warp::get().and(warp::path("blog")).and(warp::path("list")).and(warp::path::param::<u8>()).and(warp::path::end()).and_then(controller::blog_list);部落格標籤清單letblog_tags=warp::get().and(warp::path("blog")).and(warp::path("tags")).and(warp::path::end()).and_then(controller::blog_tags);根據部落格標籤展示部落格清單letblog_list_by_tag=warp::get().and(warp::path("blog")).and(warp::path("tag")).and(warp::path::param::<String>()).and(warp::path::param::<u8>()).and(warp::path::end()).and_then(controller::blog_list_by_tag);保存部落格數據letblog_save=warp::post().and(warp::path("blog")).and(warp::path("save")).and(warp::path::end()).and(auth()).and(warp::body::json::<NewBlog>()).and_then(controller::blog_save);展示某一篇部落格文章letblog_show=warp::get().and(warp::path("blog")).and(warp::path("show")).and(warp::path::param::<u64>()).and(warp::path::end()).and_then(controller::blog_show);

剛剛定義好了路由,朋友發來了登錄頁面框架。

我打開看了,還有登錄驗證碼。 於是就準備查一下如何讓Rust讀取字體再在畫布上畫出來。

這時朋友又發了一個zip,說裡面是用於顯示驗證碼的圖片。

好吧,省得我去查資料了。

於是,先新增一個顯示圖片的介面

letverify_image=warp::get().and(warp::path("tool")).and(warp::path("verify-image")).and(warp::path::end()).and(warp::cookie::optional(var::AUTH_HEADER_NAME)).and_then(controller::verify_image);

畫驗證圖,我用的是 image crate,效果如下:

好了,後端框架搭建得差不多了。

下一篇,我們一起用 Yew 來搭建前端。

What do you think?

Written by marketer

blank

決勝未來,2020年前端開發十大戰略性技術佈局

blank

Drupal vs WordPress:團隊選擇CMS的方法