API網(wǎng)關(guān)選型:我用OpenResty!
今天跟大家聊一下關(guān)于網(wǎng)關(guān)選型的話題,為什么我最后選擇了 OpenResty?
圖片來自 包圖網(wǎng)
互聯(lián)網(wǎng)公司,不論體量大小如何,其內(nèi)部的技術(shù)架構(gòu)基本都是相似的,體現(xiàn)在以下幾個(gè)方面:
- 數(shù)據(jù)量過大,如何定制化存儲(chǔ)
- 訪問量高了,如何集群化部署,流量負(fù)載均衡
- 響應(yīng)速度慢了,如何提高處理速度,引入多級(jí)緩存
- 如果機(jī)器多了,如何保證某臺(tái)服務(wù)器突然抽風(fēng),不影響業(yè)務(wù)集群的服務(wù)穩(wěn)定性
麻雀雖小五臟俱全,今天要講的網(wǎng)關(guān),就是其中的關(guān)鍵一環(huán),不論公司規(guī)模大小如何,基本都要有這個(gè)系統(tǒng)。那么網(wǎng)關(guān)是干什么用的?
什么是網(wǎng)關(guān)
網(wǎng)關(guān)是連接客戶端與服務(wù)端的中間橋梁,將很多通用地、非業(yè)務(wù)邏輯抽離,前置到網(wǎng)關(guān)系統(tǒng),減少了很多重復(fù)性開發(fā)工作,是整個(gè)網(wǎng)站的唯一流量入口。
為了提高系統(tǒng)的擴(kuò)展性,網(wǎng)關(guān)通常采用組件式架構(gòu),高內(nèi)聚低耦合。
常用的組件功能:
- 黑名單攔截
- 日志
- 參數(shù)校驗(yàn)
- 鑒權(quán)
- 限流
- 負(fù)載均衡
- 路由轉(zhuǎn)發(fā)
- 監(jiān)控
- 灰度分流
- 多協(xié)議支持
- 熔斷、降級(jí)、重試、數(shù)據(jù)聚合等
系統(tǒng)設(shè)計(jì)上一般采用責(zé)任鏈設(shè)計(jì)模式,定義好抽象接口,每個(gè)組件實(shí)現(xiàn)自己的專屬功能,職責(zé)單一。
并且根據(jù)不同的業(yè)務(wù)請(qǐng)求 API,添加、刪除一些節(jié)點(diǎn),動(dòng)態(tài)構(gòu)建新的節(jié)點(diǎn)鏈,從而滿足多樣化的業(yè)務(wù)需求。
網(wǎng)關(guān)選型
目前比較常見的網(wǎng)關(guān)如下:
- Tomcat/Jetty+NIO+Servlet3
- Nginx
- Spring WebFlux
- Soul
- Spring cloud Gateway
- Zuul
- OpenResty
- Kong
- Netty 自建
各個(gè)框架的資料網(wǎng)上基本都有,考慮到篇幅限制,這里就不展開分析每個(gè)框架的優(yōu)缺點(diǎn)。
我們計(jì)劃選型 OpenResty,主要有以下幾個(gè)原因:
- 能實(shí)現(xiàn)跨網(wǎng)絡(luò)的 gRPC 請(qǐng)求轉(zhuǎn)發(fā),底層采用 HTTP/2 協(xié)議。
- 支持 SSL/TLS 證書加密,通訊安全。
- 性能方面,支持較高的并發(fā)請(qǐng)求。
- 性能開銷低,延遲少。
翻牌 OpenResty
下圖是 Netcraft 最新統(tǒng)計(jì)的 2021年10月 Web 服務(wù)器排行榜:
Netcraft 是一家總部位于英國巴斯始于 1995 年的互聯(lián)網(wǎng)服務(wù)公司。該公司官網(wǎng)每月發(fā)布的調(diào)研數(shù)據(jù)報(bào)告:Web Server Survey 系列,已成為當(dāng)今人們了解全球網(wǎng)站的服務(wù)器市場(chǎng)份額。
OpenResty 現(xiàn)在全球排名第三,由于將 Nginx 擴(kuò)展成動(dòng)態(tài)服務(wù)器,發(fā)展勢(shì)頭很猛。
我們常用的 12306 的余票查詢功能,京東的商品詳情頁,這些高流量的背后,其實(shí)都是 OpenResty 在提供服務(wù)。
OpenResty 最擅長的是部署在流量入口處,處理各種高并發(fā)流量。接下來,我們來深入了解下這個(gè)框架。
OpenResty 誕生起因
Nginx 采用 master-worker 進(jìn)程模型,分工明確,職責(zé)單一,也是其具備高性能的原因之一。
①master 進(jìn)程
管理進(jìn)程,處理指令如:-s reload、-s stop,通過進(jìn)程間通信,將管理指令發(fā)送給 worker 進(jìn)程,從而實(shí)現(xiàn)對(duì) worker 進(jìn)程的控制。
②worker 進(jìn)程
工作進(jìn)程,不斷接收客戶端的連接請(qǐng)求,處理請(qǐng)求。數(shù)量通常設(shè)置為與 CPU 核數(shù)一致,Nginx 也會(huì)將每個(gè)進(jìn)程與每個(gè) CPU 進(jìn)行綁定,充分利用其多核特性。
多個(gè) worker 進(jìn)程會(huì)競(jìng)爭(zhēng)一個(gè)共享鎖,只有搶到鎖的進(jìn)程才能處理客戶端的請(qǐng)求。
如果請(qǐng)求是 accept 事件,則會(huì)將其添加到 accept 隊(duì)列中;如果是 read 或者 write 事件,則會(huì)將其添加到 read-write 隊(duì)列。
可能有人問,OpenResty 為什么要基于 Nginx 框架?
主要還是看重了 Nginx 的高并發(fā)能力,反向代理服務(wù)器通常流量很大,本身不涉及復(fù)雜計(jì)算,屬于 I/O 密集型服務(wù)。
Nginx 采用基于 epoll 機(jī)制的事件驅(qū)動(dòng),異步非阻塞,大大提高并發(fā)處理能力。
但是 Nginx 采用 C 語言開發(fā),二次開發(fā)門檻較高。市場(chǎng)應(yīng)用廣泛,更多是基于 nginx.conf 預(yù)留配置參數(shù),如:反向代理、負(fù)載均衡、靜態(tài) web 服務(wù)器等。
如果想讓 Nginx 訪問 MySQL ,定制化開發(fā)一些業(yè)務(wù)邏輯,難度很高。
OpenResty 通過嫁接方式,將 Nginx 和 Lua 腳本相結(jié)合,既保留 Nginx 高并發(fā)優(yōu)勢(shì),也擁有腳本語言的開發(fā)效率,也大大降低了開發(fā)門檻。
Lua 是最快的、動(dòng)態(tài)腳本語言,接近 C 語言運(yùn)行速度。LuaJIT 將一些常用的 lua 函數(shù)和工具庫預(yù)編譯并緩存,下次調(diào)用時(shí)直接使用緩存的字節(jié)碼,速度很快。
另外,Lua 支持協(xié)程,這個(gè)很重要。協(xié)程是用戶態(tài)的操作,上下文切換不用涉及內(nèi)核態(tài),系統(tǒng)資源開銷小;另外協(xié)程占用內(nèi)存很小,初始 2KB。
OpenResty 核心架構(gòu)
OpenResty 是一個(gè)基于 Nginx 的 Web 平臺(tái),內(nèi)部嵌入 LuaJIT 虛擬機(jī)運(yùn)行 Lua 腳本。使用 Lua 編程語言對(duì) Nginx 核心以及各種 Nginx C 模塊進(jìn)行腳本編程。
如上圖:
- 每接到一個(gè)客戶端請(qǐng)求,通過搶占鎖,由一個(gè) worker 進(jìn)程來跟進(jìn)處理。
- worker 內(nèi)部會(huì)創(chuàng)建一個(gè) lua 協(xié)程,綁定請(qǐng)求,也就是說一個(gè)請(qǐng)求對(duì)應(yīng)一個(gè) lua 協(xié)程。
- lua 協(xié)程將請(qǐng)求通過網(wǎng)絡(luò)發(fā)出,并添加一個(gè) event 事件到 nginx。然后,當(dāng)前協(xié)程就處于 yield,讓出 CPU 控制權(quán)。
- 當(dāng)服務(wù)端響應(yīng)數(shù)據(jù)后,網(wǎng)絡(luò)流程會(huì)創(chuàng)建一個(gè)新的 event 事件,將之前的協(xié)程喚醒,將結(jié)果返回。
注意:不同的 lua 協(xié)程之間數(shù)據(jù)隔離,從而保證了不同的客戶端請(qǐng)求不會(huì)相互影響。另外,一個(gè) worker 中同一時(shí)刻,只會(huì)有一個(gè)協(xié)程在運(yùn)行。
cosocket 將 Lua 協(xié)程 + Nginx 事件通知兩個(gè)重要特性組合。cosocket 是 OpenResty 世界中技術(shù)、實(shí)用價(jià)值最高部分。
讓我們可以用非常低廉的成本,優(yōu)雅的姿勢(shì),比傳統(tǒng) socket 編程效率高好幾倍的方式進(jìn)行網(wǎng)絡(luò)編程。無論資源占用、執(zhí)行效率、并發(fā)能力都非常出色。
為了方便開發(fā),OpenResty 將一個(gè) HTTP 請(qǐng)求劃分為 11 個(gè)階段,每個(gè)階段有自己的專屬職責(zé)。
函數(shù)功能說明:
- set_by_lua,用于設(shè)置變量。
- rewrite_by_lua,用于轉(zhuǎn)發(fā)、重定向等。
- access_by_lua,用于準(zhǔn)入、權(quán)限等。
- content_by_lua,用于生成返回內(nèi)容。
- balancer_by_lua,負(fù)載均衡,路由轉(zhuǎn)發(fā)。
- header_filter_by_lua,用于響應(yīng)頭過濾處理。
- body_filter_by_lua,用于響應(yīng)體過濾處理。
- log_by_lua,日志記錄。
OpenResty 提供了大量的 Lua API 接口,用于操作 Nginx 。只要熟悉 lua 語法,同時(shí)對(duì) Nginx 的運(yùn)行流程有較清楚理解,那么就可以輕松的在 Nginx 上做二次開發(fā)。
無論是作為應(yīng)用網(wǎng)關(guān),還是高性能的 web 應(yīng)用,支持連接各種豐富的后端存儲(chǔ),如:MySQL、Redis、Memcache、PostgreSQL 等,周邊生態(tài)非常豐富。
- https://github.com/openresty/lua-nginx-module/#accessbylua
注意:OpenResty 的 API 有使用范圍限制,每個(gè) API 都有與之對(duì)應(yīng)的使用階段列表,如果你超范圍使用就會(huì)報(bào)錯(cuò)。
①部署安裝
本文以 CentOS 系統(tǒng)中為例。
添加 openresty 倉庫,這樣以后可以通過 yum updata 命令安裝或更新我們的軟件包:
- yum install yum-utils -y
- yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
安裝軟件:
- yum install openresty -y
安裝命令行工具 resty:
- yum install openresty-resty -y
②項(xiàng)目實(shí)戰(zhàn)
修改 nginx.conf 配置文件:
- worker_processes auto;
- worker_rlimit_nofile 1000000;
- events {
- use epoll;
- worker_connections 150000;
- }
- http {
- include mime.types;
- default_type application/octet-stream;
- log_format main '$remote_addr - $remote_user [$time_local] "$request" '
- '$status $content_length $body_bytes_sent "$http_referer" '
- '"$http_user_agent" "$http_x_forwarded_for" "$upstream_response_time" "$request_time"';
- access_log logs/access.log main;
- server {
- listen 8080;
- location / {
- access_by_lua_block {
- local headers = ngx.req.get_headers(0)
- local trace_id= headers["X-Trace-Id"]
- ngx.log(ngx.ERR, trace_id)
- }
- # ngx.say("<p>hello !</p>")
- proxy_pass http://168.12.8.10:8080;
- }
- }
- server {
- listen 8082;
- location / {
- default_type text/html;
- content_by_lua_block {
- ngx.say("<p>Hello Openresty!</p>")
- }
- }
- }
- }
nginx.conf 內(nèi)部分為三層嵌套:
- 最外層的 http,表示處理 HTTP 協(xié)議。
- http 內(nèi)部 的 server 監(jiān)聽端口,會(huì)啟動(dòng)一個(gè) LuaJIT 虛擬機(jī),執(zhí)行l(wèi)ua代碼
- 同一個(gè)端口內(nèi)部,區(qū)分不同的業(yè)務(wù)功能,采用 location 配置,通過不同的 path 路徑,處理不同的業(yè)務(wù)邏輯。
添加環(huán)境變量:
- echo "export PATH=$PATH:/usr/local/openresty/nginx/sbin" >> /etc/profile
- source /etc/profile
啟動(dòng) openresty,啟動(dòng)命令和 nginx 一致:
- nginx -c /usr/local/openresty/nginx/conf/nginx.conf
訪問 Web 服務(wù):
- curl http://localhost:8082/
如果正常,瀏覽器頁面會(huì)輸出 Hello Openresty!
如果 nginx.conf 配置項(xiàng)做了修改,我們可以重新啟動(dòng):
- nginx -s reload
作者:Tom哥
編輯:陶家龍
來源:轉(zhuǎn)載自公眾號(hào)微觀技術(shù)(ID:weiguanjishu)