實(shí)現(xiàn)一個(gè) RESTful API 服務(wù)器

RESTful 是目前最為流行的一種互聯(lián)網(wǎng)軟件結(jié)構(gòu)。因?yàn)樗Y(jié)構(gòu)清晰淤刃、符合標(biāo)準(zhǔn)栽燕、易于理解、擴(kuò)展方便台盯,所以正得到越來越多網(wǎng)站的采用。

什么是 REST

REST(REpresentational State Transfer)畏线,首次出現(xiàn)在 2000 年 Roy Thomas Fielding 的博士論文中爷恳,它指的是一組架構(gòu)約束條件和原則。滿足這些約束條件和原則的應(yīng)用程序或設(shè)計(jì)就是 RESTful 的象踊。

  • 資源(Resources)温亲,REST 是“表現(xiàn)層狀態(tài)轉(zhuǎn)化”,其實(shí)它省略了主語杯矩≌恍椋“表現(xiàn)層”其實(shí)指的是“資源”的“表現(xiàn)層”。那么什么是資源呢史隆?我們平時(shí)網(wǎng)上訪問到圖片魂务、文字、文檔、多媒體等就是資源粘姜,一般通過 URI 來定位鬓照。也就是說,一個(gè) URI 就表示一個(gè)資源孤紧。
  • 表現(xiàn)層(Representation)豺裆,資源是作為一個(gè)具體的實(shí)體信息,它可以有多種的展現(xiàn)方式号显。而把實(shí)體展現(xiàn)出來就是表現(xiàn)層臭猜。例如一個(gè) txt 文本信息,它可以輸出成 html押蚤、json 等蔑歌。
  • 狀態(tài)轉(zhuǎn)化(State Transfer),訪問一個(gè)網(wǎng)站揽碘,就代表了客戶端和服務(wù)器的一個(gè)互動(dòng)過程次屠。在這個(gè)過程中,就涉及到數(shù)據(jù)和狀態(tài)的變化雳刺。而 HTTP 協(xié)議是無狀態(tài)的劫灶,那么這些狀態(tài)肯定保存在服務(wù)器端,所以如果客戶端想要通知服務(wù)器端改變數(shù)據(jù)和狀態(tài)的變化煞烫,就要通過某種方式來通知它浑此。客戶端能通知服務(wù)器端的手段滞详,只能是 HTTP 協(xié)議凛俱。具體來說,就是 HTTP 協(xié)議里面料饥,四個(gè)表示操作方式的動(dòng)詞:GET蒲犬、POST、PUT岸啡、DELETE原叮。它們分別對應(yīng)四種基本操作:GET 用來獲取資源,POST 用來新建資源(也可以用于更新資源)巡蘸,PUT 用來更新資源奋隶,DELETE 用來刪除資源。

綜上所述悦荒,我們總結(jié)一下什么是 RESTful 架構(gòu):

1唯欣、每一個(gè) URI 代表一種資源

2、客戶端和服務(wù)端之間搬味,傳遞這種資源的某種表現(xiàn)層

3境氢、客戶端通過四個(gè) HTTP 動(dòng)詞蟀拷,對服務(wù)端資源進(jìn)行操作,實(shí)現(xiàn)“表現(xiàn)層狀態(tài)轉(zhuǎn)化”

將它們概述為圖片形式萍聊,則 REST 架構(gòu)圖為:

REST架構(gòu)圖.png

REST 的擴(kuò)展性:

REST的擴(kuò)展性.png

什么是 RPC

RPC(Remote Procedure Call Protocol)遠(yuǎn)程過程調(diào)用協(xié)議问芬,是一種通過網(wǎng)絡(luò)從遠(yuǎn)程計(jì)算機(jī)程序上請求服務(wù),而不需要了解底層網(wǎng)絡(luò)技術(shù)的協(xié)議寿桨。它假定某些傳輸協(xié)議的存在此衅,如 TCP 或 UDP,以便為通信程序之間攜帶信息數(shù)據(jù)牛隅。通過它可以使函數(shù)調(diào)用模式網(wǎng)絡(luò)化炕柔。在 OSI 網(wǎng)絡(luò)通信模型中酌泰,RPC 跨越了傳輸層和應(yīng)用層媒佣。RPC 使得開發(fā)包括網(wǎng)絡(luò)分布式多程序在內(nèi)的應(yīng)用程序更加容易。

工作原理

RPC工作流程圖.png

運(yùn)行時(shí)陵刹,一次客戶端對服務(wù)器的 RPC 調(diào)用默伍,其內(nèi)部操作大致有如下步驟:

1、調(diào)用客戶端句柄衰琐;執(zhí)行傳送參數(shù)

2也糊、調(diào)用本地系統(tǒng)內(nèi)核發(fā)送網(wǎng)絡(luò)消息

3、消息傳送到服務(wù)端

4羡宙、服務(wù)器句柄得到消息并取得參數(shù)

5狸剃、執(zhí)行遠(yuǎn)程過程

6、執(zhí)行的過程將結(jié)果返回服務(wù)器句柄

7狗热、服務(wù)器句柄返回結(jié)果钞馁,調(diào)用遠(yuǎn)程系統(tǒng)內(nèi)核

8、消息傳回本地主機(jī)

9匿刮、客戶端句柄由內(nèi)核接收消息

10僧凰、客戶端接收句柄返回的數(shù)據(jù)

REST vs RPC

在做 API 服務(wù)器開發(fā)時(shí),很多人都會(huì)遇到這個(gè)問題 —— 選擇 REST 還是 RPC熟丸。RPC 相比 REST 的優(yōu)點(diǎn)主要有 3 點(diǎn):

1训措、RPC+Protobuf 采用的是 TCP 做傳輸協(xié)議,REST 直接使用 HTTP 做應(yīng)用層協(xié)議光羞,這種區(qū)別導(dǎo)致 REST 在調(diào)用性能上會(huì)比 RPC+Protobuf 低

2绩鸣、RPC 不像 REST 那樣,每一個(gè)操作都要抽象成對資源的增刪改查纱兑,在實(shí)際開發(fā)中呀闻,有很多操作很難抽象成資源,比如登錄操作萍启。所以在實(shí)際開發(fā)中并不能嚴(yán)格按照 REST 規(guī)范來寫 API总珠,RPC 就不存在這個(gè)問題

3屏鳍、RPC 屏蔽網(wǎng)絡(luò)細(xì)節(jié)、易用局服,和本地調(diào)用類似

但是 REST 相較 RPC 也有很多優(yōu)勢:

1钓瞭、輕量級(jí),簡單易用淫奔,維護(hù)性和擴(kuò)展性都比較好

2山涡、REST 相對更規(guī)范,更標(biāo)準(zhǔn)唆迁,更通用鸭丛,無論哪種語言都支持 HTTP 協(xié)議,可以對接外部很多系統(tǒng)唐责,只要滿足 HTTP 調(diào)用即可鳞溉,更適合對外,RPC 會(huì)有語言限制鼠哥,不同語言的 RPC 調(diào)用起來很麻煩

3熟菲、JSON 格式可讀性更強(qiáng),開發(fā)調(diào)試都很方便

4朴恳、在開發(fā)過程中抄罕,如果嚴(yán)格按照 REST 規(guī)范來寫 API,API 看起來更清晰于颖,更容易被大家理解

其實(shí)業(yè)界普遍采用的做法是呆贿,內(nèi)部系統(tǒng)之間調(diào)用用 RPC,對外用 REST森渐,因?yàn)閮?nèi)部系統(tǒng)之間可能調(diào)用很頻繁做入,需要 RPC 的高性能支撐。對外用 REST 更易理解章母,更通用些母蛛。

一個(gè)基本的 Web Server

一個(gè) RESTful 服務(wù)本質(zhì)上首先是一個(gè) Web service。下面是一個(gè)最簡單的 Web server乳怎,對于任何請求都簡單的直接返回請求鏈接:

package main

import (
        "fmt"
        "html"
        "log"
        "net/http"
)

func main() {
        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
            fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
    })

    log.Fatal(http.ListenAndServe(":8080", nil))
}

編譯運(yùn)行之后彩郊,使用 curl 測試,結(jié)果如下:

$ curl -v -XGET -H "Content-Type: application/json" http://127.0.0.1:8080/user

Hello, "/user"

路由功能

很顯然蚪缀,我們的線上項(xiàng)目不可能使用這么簡單的 API 服務(wù)器秫逝。當(dāng)用戶增加,請求也會(huì)不斷上漲询枚,該如何處理好這些請求违帆?作者使用了一個(gè)開源路由框架 mux。這是一個(gè)小巧高效金蜀,且使用較廣的第三方框架刷后。接下來的篇幅里的畴,作者會(huì)使用 mux 搭建一個(gè) API 服務(wù)器框架。

安裝 mux

$go get github.com/gorilla/mux

Router

//Router.go
import (
    "net/http"

    "github.com/gorilla/mux"
)

type Route struct {
    Name        string
    Method      string
    Pattern     string
    HandlerFunc http.HandlerFunc
}

type Routes []Route

func NewRouter() *mux.Router {
    router := mux.NewRouter().StrictSlash(true)
    for _, route := range routes {
        var handler http.Handler

        handler = route.HandlerFunc
        handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            handler.ServeHTTP(w, r)
        })

        router.Methods(route.Method).Path(route.Pattern).Name(route.Name).Handler(handler)
    }
    return router
}

var routes = Routes{
    Route{
        "DeleteItem",
        "DELETE",
        "/v1/delete",
        v1_deleteItem,
    },
    ...
}

Handler

//Handler.go
func v1_deleteItem(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json; charset=UTF-8")

    w.WriteHeader(http.StatusOK)
    if err := json.NewEncoder(w).Encode(jsonErr{Code: http.StatusOK, Text: "操作成功"}); err != nil {
        log.print("%s\n%s", err.Error(), debug.Stack())
    }
}

整體 mux 功能實(shí)現(xiàn):

//main.go
func main() {
    router := NewRouter()

    log.print("service running(PID:%d)...", os.Getpid())
    log.Fatal(http.ListenAndServe(":8080", router))
}

API 基本框架已經(jīng)實(shí)現(xiàn)尝胆,接下來就是將相應(yīng)功能實(shí)現(xiàn)模塊與相應(yīng)接口對接即可丧裁。

寫在最后

對于想要學(xué)習(xí)作為一個(gè)客戶端開發(fā)者如何獨(dú)立完成一個(gè)具有 API 服務(wù)器功能的線上 APP,可以參考專欄《如何獨(dú)立開發(fā)一個(gè)完整應(yīng)用》含衔,專欄中使用線上 APP 靚手藝 作為案例煎娇,詳細(xì)分享了筆者如何實(shí)現(xiàn) APP 全部功能。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贪染,一起剝皮案震驚了整個(gè)濱河市缓呛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌杭隙,老刑警劉巖哟绊,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異寺渗,居然都是意外死亡匿情,警方通過查閱死者的電腦和手機(jī)兰迫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門信殊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人汁果,你說我怎么就攤上這事涡拘。” “怎么了据德?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵鳄乏,是天一觀的道長。 經(jīng)常有香客問我棘利,道長橱野,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任善玫,我火速辦了婚禮水援,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘茅郎。我一直安慰自己蜗元,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布系冗。 她就那樣靜靜地躺著奕扣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掌敬。 梳的紋絲不亂的頭發(fā)上惯豆,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天池磁,我揣著相機(jī)與錄音,去河邊找鬼楷兽。 笑死框仔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拄养。 我是一名探鬼主播离斩,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瘪匿!你這毒婦竟也來了跛梗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤棋弥,失蹤者是張志新(化名)和其女友劉穎核偿,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體顽染,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡漾岳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了粉寞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尼荆。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖唧垦,靈堂內(nèi)的尸體忽然破棺而出捅儒,到底是詐尸還是另有隱情,我是刑警寧澤振亮,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布巧还,位于F島的核電站,受9級(jí)特大地震影響坊秸,放射性物質(zhì)發(fā)生泄漏麸祷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一褒搔、第九天 我趴在偏房一處隱蔽的房頂上張望阶牍。 院中可真熱鬧,春花似錦站超、人聲如沸荸恕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽融求。三九已至,卻和暖如春算撮,著一層夾襖步出監(jiān)牢的瞬間生宛,已是汗流浹背县昂。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留陷舅,地道東北人倒彰。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像莱睁,于是被迫代替她去往敵國和親待讳。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容