OpenResty 搭建高性能服務端

Socke 介紹
Linux Socket 編程領域爲了處理大量連接請求場景,需要使用非阻塞 I/O 和複用,select、poll、epoll 是 Linux API 提供的 I/O 複用方式,自從 Linux2.6 中加入了 epoll 之後,高性能服務器領域得到廣泛的應用,Nignx 就是使用 epoll 來實現 I/O 複用支持高併發。

對於“高性能”服務端而言,我們所關注的並不是語言的性能,而是緩存和語言支持異步非阻塞。

緩存
針對緩存要明白通信速度的快慢順序:

  • 內存>SSD>機械磁盤
  • 本機>網絡
  • 進程內 > 進程間
    緩存系統的目標是希望在進程內的命中率是最高的,那麼此時緩存系統整體的效率也是最高的。

異步非阻塞
希望訪問數據庫、訪問網絡,訪問一些比較慢的IO設備時,不要在等待上耗費大量時間。而是使用事件驅動的方式,當系統完成某項任務後再來通知我們。這樣就可以將服務器CPU的空閒資源,用來服務客戶端連接。

OpenResty
OpenResty是基於Ngnix和Lua的高性能web平臺,內部集成精良的LUa庫、第三方模塊、依賴項。用於方便搭建能夠處理高併發、擴展性極高的動態web應用、web服務、動態網關。可以使用Lua腳本調用Ngnix支持的C以及Lua模塊,快速構建10K~1000K單機併發連接的高性能web應用系統。OpenResty的目標是讓web服務直接運行在Nginx服務內部,利用Ngnix的非阻塞IO模型,對HTTP客戶端請求和後端DB進行一致的高性能響應。

OpenResty的出現可以說是顛覆了高性能服務端的開發模式。OpenResty實際上是Nginx+LuaJIT的完美組合。

由於Nginx採用的是master-worker模型,也就是一個master主進程管理多個worker進程,基本的事件處理都是放在worker中,master僅負責一些全劇初始化,以及對worker的管理。在OpenResty中,每個worker使用一個LuaVM,每個請求被分配到worker時,將在這個LuaVM中創建一個coroutine協程。協程之間數據隔離,每個協程具有獨立的全局變量_G。

Lua中的協程和多線程下的線程類似,都有自己的堆棧、局部變量、指令指針…,但是和其他協程程序共享全局變量等信息。線程和協程主要不同在於:多處理器的情況下,概念上來說多線程是同時運行多個線程,而協程是通過代碼來完成協程的切換,任何時刻只有一個協程程序在運行。並且這個在運行的協程只有明確被要求掛起時纔會被掛起。

根據實際測試,OpenResty性能接近於Nginx 性能之王c module,甚至超過。

OpenResty 架構
負載均衡
LVS+HAProxy將流量轉發給核心Nginx1和Nginx2,即實現了流量的負載均衡。

單機閉環
所有想要的數據都能從本服務器直接獲取,大多數時候無需通過網絡或去其他服務器獲取。

分佈式閉環
單機閉環會遇到2個主要問題

數據不一致
例如沒有主從架構導致不同服務器數據不一致
遇到存儲瓶頸
磁盤或內存遇到天花板
解決數據不一致比較好的辦法是採用主從或分佈式集中存儲,而遇到存儲瓶頸就需要進行按業務鍵進行分片,將數據分散到多臺服務器。

接入網關
接入網關又叫接入層,即接收流量的入口,在入口處做如下事情:

OpenResty環境搭建
安裝前準備,必須安裝perl、libpcre、libssl庫。

# 從系統路徑中查看必備庫是否已經安裝
$ sudo ldconfig -v

# 安裝必備庫
$ sudo apt install libpcre3-dev libssl-dev perl make build-essential curl libreadline-dev libncurses5-dev

下載並解壓OpenResty後進入其目錄

$ wget https://openresty.org/download/ngx_openresty-1.13.6.1.tar.gz
$ tar -zxvf ngx_openresty-1.13.6.1.tar.gz
$ mv openresty-1.13.6.1 openresty
$ cd openresty
$ ./configure

默認會被安裝到/usr/local/openresty目錄下

編譯並安裝

$ sudo make && make install
$ cd /usr/local/openresty

啓動Nginx

$ sudo /usr/local/openresty/nginx/sbin/nginx
$ ps -ef | grep nginx
$ service nginx status

Nginx啓動若出現

nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] still could not bind()

說明80端口並佔用,查看80端口被佔用的端口並重啓。原因在於nginx先監聽了ipv4的80端口之後又監聽了ipv6的80端口,於是就重複佔用了。

$ sudo netstat -ntlp | grep 80
$ sudo killall -9 nginx

重新編輯Nginx配置文件

$ sudo vim /etc/nginx/conf/nginx.conf

listen 80;
listen [::]:80 ipv6only=on default_server;

使用curl工具或在瀏覽器訪問默認80端口

$ curl 127.0.0.1
瀏覽器輸入http://127.0.0.1/

將Nginx工具配置到當前用戶的系統環境變量中

$ sudo vim ~/.bashrc
export PATH=$PATH:/usr/local/openresty/nginx/sbin
$ source ~./bashrc
$ cd ~
$ nginx -s reload
nginx: [alert] kill(12267, 1) failed (1: Operation not permitted)

開發文檔
https://www.nginx.com/resources/wiki/modules/lua/ ubuntu 安裝 vcode 或 sublime text 編輯器

content_by_lua

$ vim /usr/local/openresty/nginx/conf/nginx.conf
location /test {
  default_type text/html;
  content_by_lua 'ngx.say("hello openresty")';
}
# 重啓Nginx
$ /usr/local/openresty/nginx/sbin/nginx -s reload

# 瀏覽器訪問 127.0.0.1/test
content_by_lua_file

$ vim nginx.conf
location /test {
  content_by_lua_file 'html/test.lua';
}
$ vim ../html/test.lua
ngx.say("hello lua")
$ sudo /usr/local/nginx/sbin/nginx -s reload
$ curl 127.0.0.1/test
hello lua

爲避免每次修改都需要重啓Nginx,可在Nginx的server選項中配置lua_code_cache選項。

$ vim nginx.conf
server{
  lua_code_cache off;
  location /test{
    content_by_lua_file 'html/test.lua';
  }
}
$ sudo /usr/local/openresty/nginx/sbin/nginx -s reload
nginx: [alert] lua_code_cache is off; this will hurt performance in /usr/local/openresty/nginx/conf/nginx.conf:48

OpenResty入門
創建工作目錄

OpenResty安裝之後就有配置文件及相關目錄,爲了工作目錄和安裝目錄互不干擾,另外創建OpenResty工作目錄,並另寫配置。

$ mkdir -p ~/openresty/test/logs ~/openresty/test/conf
$ vim ~/openresty/test/conf/nginx.conf

# 設置Nginx worker工作進程數量,即CPU核數。
worker_processes 1;

# 設置錯誤日誌文件路徑
error_log logs/error.log;
# 配置Nginx服務器與用戶的網絡連接
events{
    # 設置每個工作進程的最大連接數
    worker_connections 10224;
}

http{
    # 虛擬機主機塊定義
    server{
        # 監聽端口
        listen 8001;
        # 配置請求的路由
        location /{
            default_type text/html;
            content_by_lua_block{
                ngx.say("hello world");
            }
        }
    }
}

$ nginx -p ~/openresty/test
$ curl 127.0.0.1:8001
hello world
$ vim nginx.conf
location /test{
  content_by_lua_file "lua/test.lua";
}
$ cd .. && mkdir lua && cd lua
$ vim test.lua
local args = ngx.req.get_uri_args()
local salt = args.salt
if not salt then
  ngx.exit(ngx.HTTP_BAD_REQUEST)
end
local md5str = ngx.md5(ngx.time()..salt)
ngx.say(md5str)

$ sudo /usr/local/openresty/nginx/sbin/nginx -s reload
$ curl -i 127.0.0.1/test?salt=lua
HTTP/1.1 200 OK
Server: openresty/1.13.6.2
Date: Sun, 27 Jan 2019 10:07:17 GMT
Content-Type: application/octet-stream
Transfer-Encoding: chunked
Connection: keep-alive

b55b77f75e46b96b11778ca7edfe8d55

若代碼中出現錯誤則需要直接查看Nginx的錯誤日誌進行查看

$ vim nginx/logs/error.log
2019/01/27 17:37:15 [error] 15764#0: *6 failed to load external Lua file "/usr/local/openresty/nginx/test.lua": 
cannot open /usr/local/openresty/nginx/test.lua: No such file or...

Windows系統下查看Nginx進程

λ tasklist /fi "imagename eq nginx.exe"

映像名稱                       PID 會話名              會話#       內存使用
========================= ======== ================ =========== ============
nginx.exe                     9072 Console                    1      7,840 K
nginx.exe                     7692 Console                    1     12,304 K
nginx.exe                     8120 Console                    1      7,840 K
nginx.exe                     4552 Console                    1     12,188 K
nginx.exe                     9588 Console                    1      7,828 K
nginx.exe                     6256 Console                    1     12,216 K
nginx.exe                     7308 Console                    1      7,828 K
nginx.exe                    10192 Console                    1     12,212 K

λ taskkill /im nginx.exe /f

成功: 已終止進程 "nginx.exe",其 PID 爲 9072。

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