Categories
程式開發

如何使用Rust來構建微服務?


Rust是一門很棒的語言,也是我在2019年和2020年(截止當前)學的最多的語言。 Rust幾乎可以和任何語言互操作,同時對於容器和在Kubernetes上運行也非常友好。

今天,我想展示下如何使用Rust構建一個簡單的微服務。本文中,我們將使用ActixTokio-Postgress和其他一些庫,使用Postgres作為唯一數據源,同時為了便於開發,我們會將其運行在Docker容器中。另外,我還會使用自己開發的Barrel作為數據庫遷移工具。代碼將全部使用異步和非阻塞IO實現。

總體架構

如何使用Rust來構建微服務? 1

這裡我們採用多層架構,業務規則和REST請求定義在news-contract中實現。 SOA約定定義在news-contract + news-service,數據結構(News)定義在news-contract。 REST請求的endpoint和服務定義在news-service中。 Postgres持久化相關功能定義在news-dao中。

代碼結構

如何使用Rust來構建微服務? 2

我們一個有5個工程,最頂層是一個全局工作空間,作為第一個工程。其餘工程有:

  • news-contract:SOA約定部分,這裡定義了其他工程使用的News結構體。

  • news-dao:包含響應式持久化代碼,基於tokio-postgres實現對News資源的增刪改查操作。

  • news-migrations:我們使用barrel和自定義邏輯來創建表結構和初始化測試數據。

  • news-service:這裡我們有endpoint、服務實現和包含actix-web框架配置的入口代碼。

每個工程都有自己的依賴,定義在其Cargo.toml文件中。

同時,工程中還有2個處理Docker容器的腳本,一個用於運行Postgres,另一個用於運行psql。

數據遷移

現在,讓我們來看看如何實現數據遷移(在Postgres SQL中創建表和插入記錄)。

如何使用Rust來構建微服務? 3

首先,我們需要連接運行在Docker容器中的Postgres數據庫,創建一個向量,向其中添加所有需要運行的數據遷移邏輯。然後循環執行其中的所有數據遷移邏輯,並檢查結果是否正常。

現在,讓我們看下一段代碼,數據遷移邏輯。

如何使用Rust來構建微服務? 4

我創建了一個名為NewsMigration的trait,其中包含new函數(用於創建結構體)和run函數(用於運行數據遷移)。如你所見,然後創建了CreateTableNewsMigration結構體,使用impl關鍵字實現了這個trait。這裡我使用了barrel來創建表結構,barrel將會生成Postgres SQL的INSERT語句。最後,我們使用pg_client在Postres中運行生成的腳本。這段代碼看上去很繞:&news_table[…],這裡我們在傳遞String類型news_table的引用,將其變成slice之後,傳給pg_client的execute函數。

SOA約定

首先讓我們來看下約定的第一部分,News結構體。

如何使用Rust來構建微服務? 5

我們定義了一個名為News的結構體,同時使用了serde和serde_json,以便於該結構體的序列化和反序列化。這個結構體還實現了Display trait,用於打印結構體內容。最後在文件的末尾有一個單元測試,用於測試結構體的打印。

Endpoint和服務

如何使用Rust來構建微服務? 6

這裡我定義了一個基於actix的HttpServer,然後定義了一系列處理器:index、list_news、insert_news、get_news_by_id和delete_news_by_id。服務將會運行在本地的8080端口上。所有的信息都使用log和env_logger creates進行日誌記錄。

現在讓我們來看下endpoint.rs,這裡有REST請求的定義。

如何使用Rust來構建微服務? 7

這裡我們使用宏來定義REST操作,例如PUT、DELETE和GET。每個函數處理器都定義成公有且非常簡單,僅僅調用對應的服務,將返回結果序列化成json結構返回。

如何使用Rust來構建微服務? 8

這是服務的實現,這裡沒有任何REST或者actix框架的依賴。這裡是實現校驗、業務邏輯和代理請求dao crate的地方。所有函數的增刪改查操作都是異步的。

DAO

這裡是魔法發生的地方,我們使用了tokio-postgress庫。先來看下代碼。

如何使用Rust來構建微服務? 9

這是DAO層的實現。這裡有一個名為connect()的函數用於連接Postgres數據庫,它使用異步非阻塞的方式實現。然後展示的是如何實現find_by_id功能。在Postgres中ID使用UUID來生成,因此需要將其轉成字符串類型,這就是為什麼代碼中會看見id::text=$1。同樣在這一行中,我將從入參獲取到的ID轉換成了&[&id]傳入。 DAO層還有很多函數,有興趣的話可以在我的GitHub上查看完整代碼

視頻:代碼走讀和功能演示

https://vimeo.com/384505355

完整代碼:

https://github.com/diegopacheco/rust-playground/tree/master/rust-microservice

原文鏈接:

http://diego-pacheco.blogspot.com/2020/01/building-microservice-with-rust.html