5分鐘APIG實戰: 使用Rust語言快速構建API能力開放

序言:Rust語言簡介
參與過C/C++大型項目的同學可能都經歷過因爲Null Pointer、Memory Leak等問題“被” 加班了不知道多少個晚上。別沮喪,你不是一個人,Mozilla Firefox的開發者們同樣經歷過這個問題。瀏覽器可以說是我們日常使用最爲頻繁的軟件了,目前主流的瀏覽器主要 有Google Chrome、Internet Explorer、Mozilla Firefox。爲了提升用戶體驗,Mozilla就已經啓動了多線程渲染的計劃。然而,面對大型的C/C++工程,Mozilla的開發者們也堅持不住了。此時,Rust進入了開發者的眼中,與C/C++ ABI 兼容、多編程範式支持、無GC及獨特的所有權系統,使得Mozilla與Rust語言一拍即合,並迅速啓動了 Mozilla 的下一代瀏覽器引擎項目:servo,到目前爲止(2018年8 月),servo已經成爲了除Rust編譯器自身外,社區中最大的Rust項目。servo目前已經部 分應用在Firefox 57之後的版本中。
Rust語言的設計目標是安全、高效、併發以及實用性。Rust 從一定程度上解決了C++的以 下痛點:

  1. 容器/數組越界訪問;
  2. 動態內存分配的泄露與double free問題;
  3. 難以對依賴進行管理;
    其中前兩點在C/C++項目中是最容易引發Bug以及安全問題的原因,依靠人來對這些問題進行檢查往往不是最佳的解決方案。Rust通過其獨特的所有權系統,簡化這個所研究的對象,使得一些隱晦的問題在編譯期間便暴露出來。任何事情都是有兩面性的,由於嚴格的編譯期檢查以及工程實現上的取捨,Rust在一定程度上犧牲了編譯速度以及靈活性,對“靈活性”的捨棄並不代表Rust語言的表現力下降,只是我們在編寫Rust程序時,可能需要 改變一下以往的思路。
    在Rust圈子中,有一句調侃:“C++是調試的時候想撞牆,而Rust是編譯的時候想撞牆”。
    接下來我們將通過一個簡單的例子來建立Rust中所有權系統的一個基本印象。

核心概念:所有權系統
Rust 的所有權系統包括三個核心概念:所有權、借用以及生命週期。我們首先來通過一個 簡單的例子來建立對所有權以及生命週期的直觀概念。
#[derive(Debug)]
struct Foo;

fn main() {
let foo = Foo; // Note: Foo not implement Copy trait
let bar = foo;

println!("{:?}", bar);
// println!("{:?}", foo);

}
首先創建了一個Foo類型的變量foo,然後我們執行let bar = foo;,然後我們嘗試 輸出這兩個變量的值,如果我們將第9行的註釋去掉,程序將無法通過編譯,這是因爲在 Rust中,對於沒有實現Copy trait的類型,如果我們將一個綁定賦給另一個綁定,默認 使用的是move語義,也即對於任意給定的資源,當且僅當有一個變量綁定與之對應。
想要進一步學習Rust的小哥哥×××姐,可以參考Rust Learning

使用Rust進行HTTP Web後端應用開發
在Rust生態中進行HTTP Web後端應用開發目前主要依賴兩個基礎庫:http 以及hyper,其中 http 提供HTTP標準相關的基礎類型,如Request<T> 、Response<T>以及StatusCode和常用的Header等;hyper的定位是一個高效、準確的 HTTP底層庫,它封裝了HTTP的報文解析、報文編碼處理、連接控制等內容,對於用戶而言 只需要實現一個類似於Fn(Request) -> Response的映射,就可以完HTTP Web服務端的開 發。
基於http以及hyper,社區中還有很多用於Web應用開發的框架,常用的有:
• rocket
• iron
• actix-web
• tower-web
值得一提的是上週剛發佈的tower-web,因爲這是官方net團隊2018年工作計劃的一部分, 這個庫在未來會爲Rust生態提供一個靈活、高效、易於使用的Web開發框架。那麼事不宜遲, 我們通過實戰演練來一睹爲快。

在本月月底,tower-web將會集成到wrap項目中,成爲wrap框架的一部分,開發的重心將會轉移到wrap上。

實戰演練
登錄華爲雲,並創建彈性雲服務器作爲我們的後端應用 服務器
5分鐘APIG實戰: 使用Rust語言快速構建API能力開放
實戰中使用的系統版本爲Ubuntu 16.04,如果選擇不同的系統需要根據情況調整命令。

安裝相關的工具鏈
apt update && apt install build-essential

安裝Rust工具鏈

curl https://sh.rustup.rs -sSf | sh
這一步結束後,我們就可以開始編寫我們的應用服務了。

編寫後端Web應用
這次分享我們來構建一個RESTful中文分詞API,首先我們來創建一個Rust工程 cargo new --bin chinese_segmentation
接下來在Cargo.toml中添加相關依賴
[dependencies]
tower-web = "0.2"

Jieba Chinese Work Segmentation

jieba-rs = "0.2"

logging utils

log = "0.4.0"
env_logger = "0.5.12"

Serializing responses, deserializing requests

serde = "1.0.70"
然後是我們的main.rs,與其他語言一樣,在文件開始的部分引入外部依賴以及相關聲明:
extern crate jieba_rs;
#[macro_use]
extern crate tower_web;

#[macro_use]
extern crate log;
extern crate env_logger;

use std::iter::FromIterator;
use std::collections::HashSet;

use jieba_rs::Jieba;
use tower_web::ServiceBuilder;
接下來我們定義我們的服務資源ChineseTokenizer:
#[derive(Debug)]
struct ChineseTokenizer {
inner: Jieba,
}

impl ChineseTokenizer {
pub fn new() -> ChineseTokenizer {
ChineseTokenizer { inner: Jieba::new() }
}

// 對傳入的字符串進行分詞,並返回一個字符串向量
pub fn cut(&self, text: &String) -> Vec<String> {
    let words = self.inner.cut(&text, true)
        .into_iter()
        .map(|word| word.to_owned())
        .collect::<HashSet<String>>();

    let mut words = Vec::from_iter(words.into_iter());

    // 由於使用HashSet進行去重會引入不確定性,
    // 因此對結果進行重排,使輸出的結果有序。
    words.sort();

    words
}

}
定義了我們的服務資源後,我們來定義輸入Web API的輸入輸出類型:
#[derive(Debug, Extract)]
struct TokenizeRequest {
text: String
}

#[derive(Debug, Response)]
#[web(status = "200")] // 當 handler 返回 Ok(xx) 時,返回 200 狀態碼
struct TokenizeResponse {
words: Vec<String>,
}
到目前爲止,我們已經有了我們的服務資源,輸入輸出類型,接下來就到我們的重頭戲了, Web 部分的實現,別擔心,因爲真的很簡單。
impl_web! {
impl ChineseTokenizer {
#[post("/tokenize")]
#[content_type("application/json")]
fn tokenize(&self, body: TokenizeRequest) -> Reqult<TokenizeResponse, ()> {
Ok(TokenizeResponse {
words: self.cut(&body.text),
})
}
}
}
最後是我們的main函數:
fn main() {
// 初始化Logger
env_logger::init();
let addr = "0.0.0.0:8081".parse().expect("invalid address");
info!("listening on http://{}", addr);

ServiceBuilder::new()
    .resource(ChineseTokenizer::new()) // 註冊我們的服務資源
    .run(&addr)                        // 讓我們的服務跑起來
    .unwrap();

}
現在,我們通過命令RUST_LOG=chinese_segmentation=info cargo run --release來檢驗 一下我們的成果了。服務在本地跑起來之後,我們可以通過命令 curl -H "Content-Type: application/json" -X POST -d '{"text":"中間件小哥"}' <url> 來測試一下我們的接口。
本地測試通過之後,就需要着手開始部署了,我們檢查一下彈性雲服務器的安全組的入方向 是否放開8081端口。

API 部署
API 網關集成了監控、流控、負載均衡等一系列功能,爲開發者提供高性能、高可用的API 託管服務,在本次實踐中,我們將我們的API部署在API網關中。
5分鐘APIG實戰: 使用Rust語言快速構建API能力開放
登錄華爲雲API網關服務,選擇“新建API”

  1. 填寫API的基本信息
    在本次實驗中,選擇無認證。

5分鐘APIG實戰: 使用Rust語言快速構建API能力開放

  1. 定義API請求
    請求路徑填爲 /segment,方法爲 POST
    5分鐘APIG實戰: 使用Rust語言快速構建API能力開放

  2. 定義後端服務
    請求方式設置爲POST,在VPC通道這一項中,我們需要新建VPC通道。端口設置爲8081, 並將其與彈性雲服務器關聯。

5分鐘APIG實戰: 使用Rust語言快速構建API能力開放
5分鐘APIG實戰: 使用Rust語言快速構建API能力開放

創建完VPC通道後,回到API創建頁面,填入相關信息:
5分鐘APIG實戰: 使用Rust語言快速構建API能力開放

網關創建完成後,我們需要回到我們的彈性雲服務器,將我們的後端服務器先跑起來:
RUST_LOG=chinese_segmentation=info nohup ./target/release/chinese_segmentation 2>&1 ~/api.log &
作爲示例,這裏使用nohup命令來跑我們的服務。但在生產環境中,建議使用 systemd等工具來跑服務。
服務在雲服務器運行起來之後,將API發佈至RELEASE環境中。
5分鐘APIG實戰: 使用Rust語言快速構建API能力開放

然後我們就可以和我們的API愉快地玩耍啦。
5分鐘APIG實戰: 使用Rust語言快速構建API能力開放

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章