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

序言:Rust語言簡介html

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

 

Rust語言的設計目標是安全、高效、併發以及實用性。Rust 從必定程度上解決了C++的以 下痛點:github

1容器/數組越界訪問;web

2動態內存分配的泄露與double free問題;編程

3難以對依賴進行管理。json

 

其中前兩點在C/C++項目中是最容易引起Bug以及安全問題的緣由,依靠人來對這些問題進行檢查每每不是最佳的解決方案。Rust經過其獨特的全部權系統,簡化所研究的對象,使得一些隱晦的問題在編譯期間便暴露出來。任何事情都是有兩面性的,因爲嚴格的編譯期檢查以及工程實現上的取捨,Rust在必定程度上犧牲了編譯速度以及靈活性,對「靈活性」的捨棄並不表明Rust語言的表現力降低,只是咱們在編寫Rust程序時,可能須要改變一下以往的思路。後端

 

在Rust圈子中,有一句調侃:「C++是調試的時候想撞牆,而Rust是編譯的時候想撞牆」。接下來咱們將經過一個簡單的例子來創建Rust中全部權系統的一個基本印象。api

 

核心概念:全部權系統數組

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(https://github.com/ctjhoa/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將會集成到warp項目中,成爲warp框架的一部分,開發的重心將會轉移到warp上。

 

實戰演練

登陸華爲雲,並建立彈性雲服務器做爲咱們的後端應用服務器:

實戰中使用的系統版本爲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網關中。

一、登陸華爲雲API網關服務,選擇「新建API」 

 

二、填寫API的基本信息。

在本次實驗中,選擇無認證。

 

三、定義API請求。

請求路徑填爲 /segment,方法爲 POST。

 

四、定義後端服務。

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

 

建立完VPC通道後,回到API建立頁面,填入相關信息:

 

網關建立完成後,咱們須要回到咱們的彈性雲服務器,將咱們的後端服務器先跑起來:

RUST_LOG=chinese_segmentation=info nohup ./target/release/chinese_segmentation 2>&1 ~/api.log &

 

做爲示例,這裏使用nohup命令來跑咱們的服務。但在生產環境中,建議使用 systemd等工具來跑服務。服務在雲服務器運行起來以後,將API發佈至RELEASE環境中。

 

而後咱們就能夠和咱們的API愉快地玩耍啦。

相關文章
相關標籤/搜索