<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Rust 中實(shí)現(xiàn) API 健康檢查

          共 8610字,需瀏覽 18分鐘

           ·

          2021-12-12 17:06

          當(dāng)我準(zhǔn)備將基于 Rust 的后端服務(wù)部署到 Kubernetes 集群時,我意識到我還沒有配置我的后端服務(wù)以供 kubelet[1] 探測以進(jìn)行活性[2]就緒[3]檢查。我能夠通過添加一個/healthAPI 端點(diǎn)來滿足此要求,該端點(diǎn)根據(jù)你的服務(wù)的當(dāng)前狀態(tài)以OkServiceUnavailableHTTP 狀態(tài)進(jìn)行響應(yīng)。

          /healthAPI 端點(diǎn)解決方案是 Health Check API 模式的實(shí)現(xiàn)[4],該模式用于檢查 API 服務(wù)的健康狀況。在像 Spring[5] 這樣的 Web 框架中,像 Spring Actuator[6] 這樣的嵌入式[7]解決方案可供你集成到 Spring 項目中。但是,在許多 Web 框架中,你必須自己構(gòu)建此 Health Check API 行為。

          在這篇博文中,我們將使用 actix-web[8] Web 框架實(shí)現(xiàn)健康檢查 API 模式,該框架使用 sqlx[9] 連接到本地 PostgreSQL 數(shù)據(jù)庫實(shí)例。

          01 先決條件

          在開始之前,請確保你的機(jī)器上安裝了 Cargo[10]Rust[11]。安裝這些工具的最簡單方法是使用 rustup[12]

          還要在你的機(jī)器上安裝 Docker[13],以便我們可以輕松創(chuàng)建并連接到 PostgreSQL 數(shù)據(jù)庫實(shí)例。

          如果這是你第一次看到 Rust[14] 編程語言,我希望這篇博文能激勵你更深入地了解這門有趣的靜態(tài)類型語言和生態(tài)系統(tǒng)。

          GitHub 上[15] 可以找到本文完整的源代碼。

          02 創(chuàng)建一個新的 Actix-Web 項目

          打開你最喜歡的 命令行終端[16] 并通過cargo new my-service --bin 創(chuàng)建一個 Cargo 項目。

          --bin 選項會告訴 Cargo 自動創(chuàng)建一個main.rs文件,讓 Cargo 知道這個項目不是一個庫,而是會生成一個可執(zhí)行文件。

          接下來,我們能夠通過運(yùn)行以下命令來運(yùn)行項目:cargo run。運(yùn)行此命令后,應(yīng)該打印如下文本。

          ????Finished?dev?[unoptimized?+?debuginfo]?target(s)?in?0.00s
          ?????Running?`target/debug/health-endpoint`
          Hello,?world!

          是不是很容易?!

          接下來,讓我們創(chuàng)建并運(yùn)行 PostgreSQL 實(shí)例。

          03 運(yùn)行 PostgreSQL

          在使用 Docker Compose 創(chuàng)建 PostgreSQL 實(shí)例之前,我們需要創(chuàng)建一個用于創(chuàng)建數(shù)據(jù)庫的初始 SQL 腳本。我們將以下內(nèi)容添加到在項目根目錄下的 db 目錄的 init.sql 文件中。

          SELECT?'CREATE?DATABASE?member'
          WHERE?NOT?EXISTS?(SELECT?FROM?pg_database?WHERE?datname?=?'member')\gexec

          此腳本將檢查是否已存在名為“member”的數(shù)據(jù)庫,如果不存在,它將為我們創(chuàng)建數(shù)據(jù)庫。接下來,我們將以下 YAML 復(fù)制到docker-compose.yml文件中并運(yùn)行docker compose up.

          version:?'3.1'

          services:
          ??my-service-db:
          ????image:?"postgres:11.5-alpine"
          ????restart:?always
          ????volumes:
          ??????-?my-service-volume:/var/lib/postgresql/data/
          ??????-?./db:/docker-entrypoint-initdb.d/
          ????networks:
          ??????-?my-service-network?
          ????ports:
          ??????-?"5432:5432"
          ????environment:
          ????????POSTGRES_HOST:?localhost
          ????????POSTGRES_DB:?my-service
          ????????POSTGRES_USER:?root
          ????????POSTGRES_PASSWORD:?postgres

          volumes:
          ??my-service-volume:

          networks:
          ??my-service-network:

          在控制臺窗口打印出一些彩色文本 ?? 之后,表示已經(jīng)啟動了 PostgreSQL。

          現(xiàn)在我們已經(jīng)確認(rèn)服務(wù)已經(jīng)運(yùn)行,并且我們有一個本地運(yùn)行的 PostgreSQL 實(shí)例,打開你最喜歡的文本編輯器[17]IDE[18],并將我們的項目依賴項添加到我們的Cargo.toml文件中。

          [dependencies]
          actix-web = "4.0.0-beta.8"
          serde = { version = "1.0", features = ["derive"] }
          serde_json = "1.0"
          sqlx = { version = "0.5.7", features = [ "runtime-actix-native-tls", "postgres" ] }

          對于sqlx,我們希望確保在編譯期間包含 “postgres” 功能,因此我們有 PostgreSQL 驅(qū)動程序來連接到我們的 PostgreSQL 數(shù)據(jù)庫。接下來,我們要確保包含 ?runtime-actix-native-tls 特性,以便 sqlx 可以支持actix-web使用 tokio[19] 運(yùn)行時的框架。最后,包含serdeserde_json序列化我們的 Health Check API 響應(yīng)主體,以供稍后在文章中使用。

          注意:對于 Rust 的新手,你可能會想,“到底什么是 heck ?Actix 運(yùn)行時?我認(rèn)為 actix-web 只是 Rust 的一個 Web 框架。” 的確是,但不全是。由于 Rust 的設(shè)計沒有考慮任何特定的運(yùn)行時[20],因此你當(dāng)前所在的問題域需要一個特定的運(yùn)行時。有專門用于處理客戶端/服務(wù)器通信需求的運(yùn)行時,例如 Tokio[21],一種流行的事件驅(qū)動,非- 阻塞 I/O 運(yùn)行時。Actix[22] 是 actix-web 背后的底層運(yùn)行時,是一個構(gòu)建在 tokio 運(yùn)行時之上的基于 actor 的[23]消息傳遞框架。

          所以,現(xiàn)在我們已經(jīng)添加了依賴項,繼續(xù)創(chuàng)建我們的actix-web服務(wù)。為此,我們用以下 Rust 代碼替換src/main.rs文件中的內(nèi)容:

          use?actix_web::{web,?App,?HttpServer,?HttpResponse};

          async?fn?get_health_status()?->?HttpResponse?{
          ????HttpResponse::Ok()
          ????????.content_type("application/json")
          ????????.body("Healthy!")
          }

          #[actix_web::main]
          async?fn?main()?->?std::io::Result<()>?{
          ????HttpServer::new(||?{
          ????????App::new()
          ????????????.route("/health",?web::get().to(get_health_status))
          ???????????//?^?Our?new?health?route?points?to?the?get_health_status?handler
          ????})
          ????.bind(("127.0.0.1",?8080))?
          ????.run()
          ????.await
          }

          上面的代碼為我們提供了一個在端口 8080 上運(yùn)行的 HTTP 服務(wù)器和一個 /health 端點(diǎn),它始終返回 Ok 這個 HTTP 響應(yīng)狀態(tài)代碼。

          回到終端,運(yùn)行cargo run 啟動服務(wù)。在新 tab 終端中,繼續(xù)運(yùn)行 curl -i localhost:8080/health 并查看你收到如下響應(yīng):

          $?curl?-i?localhost:8080/health
          HTTP/1.1?200?OK
          content-length:?8
          content-type:?application/json
          date:?Wed,?22?Sep?2021?17:16:47?GMT

          Healthy!%

          現(xiàn)在我們已經(jīng)啟動并運(yùn)行了基本的健康檢查 API 端點(diǎn),現(xiàn)在更改我們的健康 API 的行為,當(dāng)與 PostgreSQL 數(shù)據(jù)庫的連接處于活動狀態(tài)時讓其返回 OK 這個 HTTP 響應(yīng)狀態(tài)代碼。為此,我們需要先使用sqlx建立一個數(shù)據(jù)庫連接。

          04 創(chuàng)建數(shù)據(jù)庫連接

          在我們可以使用 sqlx 的 connect[24] 方法建立數(shù)據(jù)庫連接之前,我們需要創(chuàng)建一個數(shù)據(jù)庫連接字符串,格式類似 ://:@:/,與我們本地的 PostgreSQL 設(shè)置相匹配。

          此外,與其對我們的數(shù)據(jù)庫連接字符串進(jìn)行硬編碼,不如通過一個名為 ?DATABASE_URL 的環(huán)境變量對其進(jìn)行配置[25]DATABASE_URL 在每次cargo run調(diào)用之前添加該變量,如下所示:

          DATABASE_URL=postgres://root:postgres@localhost:5432/member?sslmode=disable?cargo?run

          有了 DATABASE_URL 環(huán)境變量,我們在main函數(shù)中添加一行來獲取我們新導(dǎo)出的環(huán)境變量。

          #[actix_web::main]
          async?fn?main()?->?std::io::Result<()>?{
          ????let?database_url?=?std::env::var("DATABASE_URL").expect("Should?set?'DATABASE_URL'");
          ????...

          接下來,在 main 函數(shù)中編寫更多代碼來創(chuàng)建數(shù)據(jù)庫連接。

          ...
          let?db_conn?=?PgPoolOptions::new()
          ??.max_connections(5)
          ??.connect_timeout(Duration::from_secs(2))
          ??.connect(database_url.as_str())?//?<-?Use?the?str?version?of?database_url?variable.
          ??.await
          ??.expect("Should?have?created?a?database?connection");
          ...

          在我們可以將數(shù)據(jù)庫連接傳遞給我們的健康端點(diǎn)處理程序之前,我們首先需要創(chuàng)建一個struct代表我們服務(wù)的共享可變狀態(tài)的 。Actix-web 使我們能夠在路由之間共享我們的數(shù)據(jù)庫連接,這樣我們就不會在每個請求上創(chuàng)建新的數(shù)據(jù)庫連接,這是一項高成本的操作,并且會真正降低我們的服務(wù)性能。

          為了實(shí)現(xiàn)這一點(diǎn),我們需要創(chuàng)建一個 Rust struct(在我們的main函數(shù)上方),名為 AppState,有一個字段 db_conn,對數(shù)據(jù)庫連接的引用。

          ...
          use?sqlx::{Pool,?Postgres,?postgres::PgPoolOptions};
          ...

          struct?AppState?{
          ????db_conn:?Pool
          }

          現(xiàn)在,在我們的db_conn實(shí)例化之下,將創(chuàng)建一個包裝在web::Data包裝器中的 AppState 數(shù)據(jù)對象。該web::Data包裝在請求處理程序中訪問我們的 AppState。

          ...
          let?app_state?=?web::Data::new(AppState?{
          ??db_conn:?db_conn
          });
          ...

          最后,我們設(shè)置 App 的app_data為我們的克隆app_state的變量,并使用move語句更新我們的HttpServer::new閉包。

          ????...
          ????let?app_state?=?web::Data::new(AppState?{
          ????????db_conn:?db_conn
          ????});

          ????HttpServer::new(move?||?{
          ????????App::new()
          ????????????.app_data(app_state.clone())?//?<-?cloned?app_state?variable
          ????????????.route("/health",?web::get().to(get_health_status))
          ????})
          ????.bind(("127.0.0.1",?8080))?
          ????.run()
          ????.await

          如果我們不克隆app_state變量,Rust 會提出我們的app_state變量不是在我們的閉包內(nèi)部創(chuàng)建的,并且 Rust 無法保證app_state在調(diào)用時不會被銷毀。有關(guān)更多信息,請查看 Rust Ownership[26]Copy trait[27] 文檔。

          到目前為止,我們的服務(wù)代碼應(yīng)該如下所示:

          use?actix_web::{web,?App,?HttpServer,?HttpResponse};
          use?sqlx::{Pool,?Postgres,?postgres::PgPoolOptions};

          async?fn?get_health_status()?->?HttpResponse?{
          ????HttpResponse::Ok()
          ????????.content_type("application/json")
          ????????.body("Healthy!")
          }

          struct?AppState?{
          ????db_conn:?Pool
          }

          #[actix_web::main]
          async?fn?main()?->?std::io::Result<()>?{
          ????let?database_url?=?std::env::var("DATABASE_URL").expect("Should?set?'DATABASE_URL'");

          ????let?db_conn?=?PgPoolOptions::new()
          ????????.max_connections(5)
          ????????.connect_timeout(Duration::from_secs(2))
          ????????.connect(database_url.as_str())
          ????????.await
          ????????.expect("Should?have?created?a?database?connection");

          ????let?app_state?=?web::Data::new(AppState?{
          ????????db_conn:?db_conn
          ????});

          ????HttpServer::new(move?||?{
          ????????App::new()
          ????????????.app_data(app_state.clone())
          ????????????.route("/health",?web::get().to(get_health_status))
          ????})
          ????.bind(("127.0.0.1",?8080))?
          ????.run()
          ????.await
          }

          現(xiàn)在我們已經(jīng)將app_state對象,包含我們的數(shù)據(jù)庫連接傳遞到我們的App實(shí)例中,繼續(xù)更新我們的get_health_status函數(shù)以檢查我們的數(shù)據(jù)庫連接是否有效。

          數(shù)據(jù)庫連接檢查

          為了從我們的get_health_status函數(shù)中捕獲AppState數(shù)據(jù),我們需要添加一個Data參數(shù)到get_health_status函數(shù)中。

          async?fn?get_health_status(data:?web::Data)?->?HttpResponse?{
          ????...

          接下來,讓我們編寫一個輕量級的 PostgreSQL 查詢 SELECT 1 來檢查我們的數(shù)據(jù)庫連接。

          async?fn?get_health_status(data:?web::Data)?->?HttpResponse?{
          ????let?is_database_connected?=?sqlx::query("SELECT?1")
          ????????.fetch_one(&data.db_conn)
          ????????.await
          ????????.is_ok();
          ????...

          然后,我們更新HttpResponse響應(yīng)以在我們的數(shù)據(jù)庫連接時返回一個Ok,當(dāng)它沒有連接時返回ServiceUnavailable。此外,為了調(diào)試的目的,我們有一個更有用的響應(yīng)主體,不是簡單的 healthy 或者not healthy,使用 serde_json 序列化 Ruststruct,描述為什么我們的健康檢查是成功還是失敗。

          ????...
          ????if?is_database_connected?{
          ????????HttpResponse::Ok()
          ????????????.content_type("application/json")
          ????????????.body(serde_json::json!({?"database_connected":?is_database_connected?}).to_string())
          ????}?else?{
          ????????HttpResponse::ServiceUnavailable()
          ????????????.content_type("application/json")
          ????????????.body(serde_json::json!({?"database_connected":?is_database_connected?}).to_string())
          ????}
          }

          最后,我們使用以下cargo run命令運(yùn)行我們的服務(wù):

          DATABASE_URL=postgres://root:postgres@localhost:5432/member?sslmode=disable?cargo?run

          打開另一個終端選項卡并運(yùn)行以下curl命令:

          curl?-i?localhost:8080/health

          應(yīng)該返回以下響應(yīng):

          HTTP/1.1?200?OK
          content-length:?27
          content-type:?application/json
          date:?Tue,?12?Oct?2021?15:56:00?GMT

          {"database_connected":true}%

          如果我們通過docker compose stop 關(guān)閉我們的數(shù)據(jù)庫,那么兩秒鐘后,當(dāng)你再次調(diào)用以上 curl命令時,你會看到一個ServiceUnavailable的 HTTP 響應(yīng)。

          HTTP/1.1?503?Service?Unavailable
          content-length:?28
          content-type:?application/json
          date:?Tue,?12?Oct?2021?16:07:03?GMT

          {"database_connected":false}%

          05 結(jié)論

          我希望這篇博文能成為實(shí)現(xiàn) Health Check API 模式的有用指南。你可以將更多信息應(yīng)用到您的/healthAPI 端點(diǎn),例如,在適用的情況下,當(dāng)前用戶的數(shù)量、緩存連接檢查等。需要任何信息來確保你的后端服務(wù)看起來“健康”。這因服務(wù)而異。

          原文鏈接:https://dev.to/tjmaynes/implementing-the-health-check-api-pattern-with-rust-29ll

          參考資料

          [1]

          kubelet: https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/

          [2]

          活性: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-startup-probes

          [3]

          就緒: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes

          [4]

          Health Check API 模式的實(shí)現(xiàn): https://microservices.io/patterns/observability/health-check-api.html

          [5]

          Spring: https://spring.io/

          [6]

          Spring Actuator: https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html

          [7]

          嵌入式: https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html

          [8]

          actix-web: https://actix.rs/

          [9]

          sqlx: https://github.com/launchbadge/sqlx

          [10]

          Cargo: https://doc.rust-lang.org/cargo/getting-started/installation.html

          [11]

          Rust: https://www.rust-lang.org/

          [12]

          rustup: https://rustup.rs/

          [13]

          Docker: https://docs.docker.com/get-docker/

          [14]

          Rust: https://rust-lang.org/

          [15]

          GitHub 上: https://github.com/tjmaynes/health-check-rust

          [16]

          命令行終端: https://github.com/alacritty/alacritty

          [17]

          文本編輯器: https://code.visualstudio.com/

          [18]

          IDE: https://www.jetbrains.com/idea/

          [19]

          tokio: https://tokio.rs/

          [20]

          運(yùn)行時: https://en.wikipedia.org/wiki/Runtime_system

          [21]

          Tokio: https://docs.rs/tokio/1.12.0/tokio/

          [22]

          Actix: https://docs.rs/actix/

          [23]

          基于 actor 的: https://en.wikipedia.org/wiki/Actor_model

          [24]

          connect: https://docs.rs/sqlx/0.5.7/sqlx/postgres/struct.PgConnection.html#method.connect

          [25]

          配置: https://12factor.net/config

          [26]

          Rust Ownership: https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html

          [27]

          Copy trait: https://hashrust.com/blog/moves-copies-and-clones-in-rust/




          往期推薦


          我是 polarisxu,北大碩士畢業(yè),曾在 360 等知名互聯(lián)網(wǎng)公司工作,10多年技術(shù)研發(fā)與架構(gòu)經(jīng)驗!2012 年接觸 Go 語言并創(chuàng)建了 Go 語言中文網(wǎng)!著有《Go語言編程之旅》、開源圖書《Go語言標(biāo)準(zhǔn)庫》等。


          堅持輸出技術(shù)(包括 Go、Rust 等技術(shù))、職場心得和創(chuàng)業(yè)感悟!歡迎關(guān)注「polarisxu」一起成長!也歡迎加我微信好友交流:gopherstudio

          瀏覽 58
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  丁香五月天网站 | 日韩国产精品在线看 | www.色综合 | 欧洲激情亚洲 | 98精品国产乱码久久久久久 |