API 流程和代碼結構
為了使讀者在開始實戰之前對 API 開發有個整體的了解,這里選擇了兩個流程來介紹:
- HTTP API 服務器啟動流程
- HTTP 請求處理流程
本小節也提前給出了程序代碼結構圖,讓讀者從宏觀上了解將要構建的 API 服務器的功能。
本小節視頻教程和代碼:百度網盤,密碼gdas
可先下載視頻和源碼到本地,邊看視頻邊結合源碼理解后續內容,邊學邊練。
HTTP API 服務器啟動流程
如上圖,在啟動一個 API 命令后,API 命令會首先加載配置文件,根據配置做后面的處理工作。通常會將日志相關的配置記錄在配置文件中,在解析完配置文件后,就可以加載日志包初始化函數,來初始化日志實例,供后面的程序調用。接下來會初始化數據庫實例,建立數據庫連接,供后面對數據庫的 CRUD 操作使用。在建立完數據庫連接后,需要設置 HTTP,通常包括 3 方面的設置:
- 設置 Header
- 注冊路由
- 注冊中間件
之后會調用 net/http
包的 ListenAndServe()
方法啟動 HTTP 服務器。
在啟動 HTTP 端口之前,程序會 go 一個協程,來ping HTTP 服務器的 /sd/health
接口,如果程序成功啟動,ping 協程在 timeout 之前會成功返回,如果程序啟動失敗,則 ping 協程最終會 timeout,並終止整個程序。
解析配置文件、初始化 Log 、初始化數據庫的順序根據自己的喜好和需求來排即可。
HTTP 請求處理流程
一次完整的 HTTP 請求處理流程如上圖所示。(圖片出自《HTTP 權威指南》,推薦想全面理解 HTTP 的讀者閱讀此書。)
1. 建立連接
客戶端發送 HTTP 請求后,服務器會根據域名進行域名解析,就是將網站名稱轉變成 IP 地址:localhost -> 127.0.0.1,Linux hosts文件、DNS 域名解析等可以實現這種功能。之后通過發起 TCP 的三次握手建立連接。TCP 三次連接請參考 TCP三次握手詳解及釋放連接過程,建立連接之后就可以發送 HTTP 請求了。
2. 接收請求
HTTP 服務器軟件進程,這里指的是 API 服務器,在接收到請求之后,首先根據 HTTP 請求行的信息來解析到 HTTP 方法和路徑,在上圖所示的報文中,方法是 GET,路徑是 /index.html,之后根據 API 服務器注冊的路由信息(大概可以理解為:HTTP 方法 + 路徑和具體處理函數的映射)找到具體的處理函數。
3. 處理請求
在接收到請求之后,API 通常會解析 HTTP 請求報文獲取請求頭和消息體,然后根據這些信息進行相應的業務處理,HTTP 框架一般都有自帶的解析函數,只需要輸入 HTTP 請求報文,就可以解析到需要的請求頭和消息體。通常情況下,業務邏輯處理可以分為兩種:包含對數據庫的操作和不包含對數據的操作。大型系統中通常兩種都會有:
- 包含對數據庫的操作:需要訪問數據庫(增刪改查),然后獲取指定的數據,對數據處理后構建指定的響應結構體,返回響應包。數據庫通常用的是 MySQL,因為免費,功能和性能也都能滿足企業級應用的要求。
- 不包含對數據庫的操作:進行業務邏輯處理后,構建指定的響應結構體,返回響應包。
4. 記錄事務處理過程
在業務邏輯處理過程中,需要記錄一些關鍵信息,方便后期 Debug 用。在 Go 中有各種各樣的日志包可以用來記錄這些信息。
HTTP 請求和響應格式介紹
一個 HTTP 請求報文由請求行(request line)、請求頭部(header)、空行和請求數據四部分組成,下圖是請求報文的一般格式。
- 第一行必須是一個請求行(request line),用來說明請求類型、要訪問的資源以及所使用的 HTTP 版本
- 緊接着是一個頭部(header)小節,用來說明服務器要使用的附加信息
- 之后是一個空行
- 再后面可以添加任意的其他數據(稱之為主體:body)
HTTP 響應格式跟請求格式類似,也是由 4 個部分組成:狀態行、消息報頭、空行和響應數據。
目錄結構
├── conf # 配置文件統一存放目錄
│ ├── config.yaml # 配置文件
├── config # 專門用來處理配置和配置文件的Go package
│ └── config.go
├── db.sql # 在部署新環境時,可以登錄MySQL客戶端,執行source db.sql創建數據庫和表
├── handler # 類似MVC架構中的C,用來讀取輸入,並將處理流程轉發給實際的處理函數,最后返回結果
│ ├── handler.go
│ ├── sd # 健康檢查handler
│ │ └── check.go
│ └── user # 核心:用戶業務邏輯handler
│ ├── create.go # 新增用戶
│ └── user.go # 存放用戶handler公用的函數、結構體等
├── main.go # Go程序唯一入口
├── Makefile # Makefile文件,一般大型軟件系統都是采用make來作為編譯工具
├── model # 數據庫相關的操作統一放在這里,包括數據庫初始化和對表的增刪改查
│ ├── init.go # 初始化和連接數據庫
│ ├── model.go # 存放一些公用的go struct
│ └── user.go # 用戶相關的數據庫CURD操作
├── pkg # 引用的包
│ ├── constvar # 常量統一存放位置
│ │ └── constvar.go
│ ├── errno # 錯誤碼存放位置
│ │ ├── code.go
│ │ └── errno.go
├── README.md # API目錄README
├── router # 路由相關處理
│ ├── middleware # API服務器用的是Gin Web框架,Gin中間件存放位置
│ │ ├── header.go
│ │ ├── logging.go
│ │ └── requestid.go
│ └── router.go # 路由
├── service # 實際業務處理函數存放位置
│ └── service.go
├── util # 工具類函數存放目錄
│ ├── util.go
│ └── util_test.go
└── vendor # vendor目錄用來管理依賴包
├── github.com
├── golang.org
├── gopkg.in
└── vendor.json
Go API 項目中,一般都會包括這些功能項:配置文件目錄、RESTful API 服務器的 handler 目錄、model 目錄、工具類目錄、vendor 目錄,以及實際處理業務邏輯函數所存放的 service 目錄。這些都在上述的代碼結構中有列出,新加功能時將代碼放入對應功能的目錄/文件中,可以使整個項目代碼結構更加清晰,非常有利於后期的查找和維護。
小結
本小節通過介紹 API 服務器啟動流程和 HTTP 請求處理流程,來讓讀者對 API 服務器中的關鍵流程有個宏觀的了解,更好地理解 API 服務器是如何工作的。API 服務器源碼結構也非常重要,一個好的源碼結構通常能讓邏輯更加清晰,編寫更加順暢,后期維護更加容易,本小冊介紹了筆者傾向的源碼組織結構,供讀者參考。