關於Nginx mmap(MAP_ANON|MAP_SHARED, 314572800)報錯

飛翔碼農發表於2020-09-12

mmap 報錯解決

今天修改了一下測試環境的Nginx的nginx.conf,然後做檢測的時候報了一個錯誤

/usr/local/bin/nginx -c /usr/local/etc/openresty/conf/nginx.conf -t

nginx: [alert] mmap(MAP_ANON|MAP_SHARED, 314572800) failed (12: Cannot allocate memory)
nginx: configuration file /usr/local/etc/openresty/conf/nginx.conf test failed

報錯提示很清楚,不能分配記憶體了。為什麼不能分配記憶體了?基本上就是實體記憶體不夠使用了,先查了下記憶體

free -m
              total        used        free      shared  buff/cache   available
Mem:            990         568          75          54         347         224
Swap:             0           0           0

可以看到,真正可以被使用的記憶體大概就是224M。那就是Nginx此次檢查的配置需要使用大於224M的記憶體。按理說,Nginx自身不需要多少記憶體。我們系統中大量使用openresty,首先懷疑可能openresty的某個引用申請記憶體過多了,然後查詢了下配置,果然發現openresty的共享記憶體的使用。

lua_shared_dict xxx 300m;
lua_shared_dict yyy 100m;

總共需要400M記憶體,這兩個共享記憶體該小點應該就可以了。改為

lua_shared_dict xxx 100m;
lua_shared_dict yyy 30m;

然後檢查通過

/usr/local/bin/nginx -c /usr/local/etc/openresty/conf/nginx.conf -t
nginx: the configuration file /usr/local/etc/openresty/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/openresty/conf/nginx.conf test is successful

檢測是成功,reload Nginx成功生效。

Nginx 申請記憶體的模組

可能有的不一定是openresty共享記憶體的鍋。Nginx配置需要配置記憶體的地方其實不多,一個個排查就可以做到。

openresty lua_shared_dict

lua_shared_dict 定義在http模組。
宣告共享記憶體區,共享記憶體區始終由當前Nginx伺服器例項中的所有Nginx程式共享。
引數接受大小單位,比如K 和M 等等。

http {
     lua_shared_dict dogs 10m;
     ...
 }

proxy_cache_path

proxy_cache_path path [levels=levels] keys_zone=name:size
path 設定快取的路徑
levels 設定快取檔案的層級,當levels=1:2時,表示是兩級目錄,1和2表示用1位和2位16進位制來命名目錄名稱。
keys_zone 設定共享記憶體的名稱和大小,keys_zone=one:10m,表示共享記憶體名稱是one,大小是10M,這裡設定記憶體過高,就會出現開頭的報警,mmap(MAP_ANON|MAP_SHARED, 314572800)
配置設定成這樣
proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=one:10m;
路徑和層級就是下面的
/data/nginx/cache/c/29/b7f54b2df7773722d382f4809d65029c

limit_req_zone

limit_req_zone key zone=name:size rate=rate
設定共享記憶體的限流引數
主要看下應用
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

使用客戶端的IP作為限流的key,共享記憶體分配了10M,共享記憶體的名稱是one,速度是每個客戶端IP每秒1個請求,超過1個請求可能會延遲或者直接拒絕,要看limit_req的配置。
$binary_remote_addr變數的大小為4個位元組。在64位平臺上始終佔據128位元組。 一個1M的區域可以保留約約8000個客戶端IP。所以10M大概可以準確儲存8萬個客戶端IP。如果限流的客戶端IP超過了8萬個,就應該擴大共享記憶體。

limit_conn_zone

limit_conn_zone key zone=name:size;
設定連線數限流的引數
limit_conn_zone $binary_remote_addr zone=addr:10m;

連線數限流的引數跟請求數引數類似。
使用客戶端的IP作為限流的key,共享記憶體分配了10M,共享記憶體的名稱是addr。10M可以儲存8萬個客戶端IP。限流的具體數量限制是由limit_conn配置
比如

limit_conn addr 1;

就表示每個客戶端IP同時只能有一個連線存在。

總結Nginx mmap

所以,如果以後遇到關於Nginx mmap(MAP_ANON|MAP_SHARED, 314572800)報錯,首先應該檢查可能分配大記憶體的配置,首要檢查的就是這兩個
lua_shared_dict
proxy_cache_path
修改這兩個引數就可以解決問題。

一般
limit_req_zone
limit_conn_zone
分配的記憶體很小,至多幾十M,不會直接導致系統沒有記憶體可分配。

相關文章