Nginx高級

Nginx高級 第一部分:擴容

通過擴容提升整體吞吐量

1.單機垂直擴容:硬件資源增加

云服務資源增加
整機:IBM、浪潮、DELL、HP等
CPU/主板:更新到主流
網卡:10G/40G網卡
磁盤:SAS(SCSI) HDD(機械)、HHD(混合)、SATA SSD、PCI-e SSD、 MVMe SSD
SSD
多副本機制
系統盤/熱點數據/數據庫存儲
HDD
冷數據存儲

2.水平擴展:集群化

會話管理

Nginx高級負載均衡

ip_hash

hash $cookie_jsessionid;

hash $request_uri;

使用lua邏輯定向分發

Redis + SpringSession

   upstream httpds {ip_hash;server 192.168.44.102 ;server 192.168.44.103 ;}server {listen       80;server_name  localhost;location / {proxy_pass http://httpds;# root   html;}location ~*/(css|img|js) {root   /usr/local/nginx/html;}
使用sticky模塊完成對Nginx的負載均衡

使用參考

http://nginx.org/en/docs/http/ngx_http_upstream_module.html#sticky

tengine中有session_sticky模塊我們通過第三方的方式安裝在開源版本中

sticky是第三方模塊,需要重新編譯Nginx,他可以對Nginx這種靜態文件服務器使用基于cookie的負載均衡

1.下載模塊

項目官網

https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/src/master/

另外一個版本

https://github.com/bymaximus/nginx-sticky-module-ng

下載

https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/get/1.2.6.zip

2.上傳解壓
3.重新編譯Nginx

依賴openssl-devel

進到源碼目錄重新編譯

./configure --prefix=/usr/local/nginx --add-module=/root/nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d

執行make

如遇報錯修改源碼

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

打開 ngx_http_sticky_misc.c文件

在12行添加

#include <openssl/sha.h>
#include <openssl/md5.h>

備份之前的程序

mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old

把編譯好的Nginx程序替換到原來的目錄里

cp objs/nginx /usr/local/nginx/sbin/

升級檢測

make upgrade

檢查程序中是否包含新模塊

nginx -V

配置方法

upstream httpget {sticky name=route expires=6h;server 192.168.44.102;
server 192.168.44.103;
}

KeepAlive

在http協議header中可以看到當前連接狀態

測試工具charles

下載地址

https://www.charlesproxy.com/assets/release/4.6.2/charles-proxy-4.6.2-win64.msi?k=fc1457e312

官網

https://www.charlesproxy.com

什么時候使用?

明顯的預知用戶會在當前連接上有下一步操作

復用連接,有效減少握手次數,尤其是https建立一次連接開銷會更大

什么時候不用?

訪問內聯資源一般用緩存,不需要keepalive

長時間的tcp連接容易導致系統資源無效占用

對客戶端使用keepalive

keepalive_time

限制keepalive保持連接的最大時間

1.19.10新功能

keepalive_timeout

用于設置Nginx服務器與客戶端保持連接的超時時間

用于踢出不活動連接

keepalive_timeout = 0 即關閉

  • send_timeout 10; 10秒
  • send_timeout 10 10; 同時下發一個header 告訴瀏覽器

send_timeout

兩次向客戶端寫操作之間的間隔 如果大于這個時間則關閉連接 默認60s

此處有坑,注意耗時的同步操作有可能會丟棄用戶連接

該設置表示Nginx服務器與客戶端連接后,某次會話中服務器等待客戶端響應超過10s,就會自動關閉連接。

keepalive_request

默認1000

單個連接中可處理的請求數

keepalive_disable

不對某些瀏覽器建立長連接

默認msie6

http {include       mime.types;default_type  application/octet-stream;sendfile        on;keepalive_timeout  65 65; #超過這個時間 沒有活動,會讓keepalive失效 keepalive_time 1h; # 一個tcp連接總時長,超過之后 強制失效send_timeout 60;# 默認60s  此處有坑!! 系統中 若有耗時操作,超過 send_timeout 強制斷開連接。 注意:準備過程中,不是傳輸過程keepalive_requests 1000;  #一個tcp復用中 可以并發接收的請求個數

對上游服務器使用keepalive

首先需要配置使用http1.1協議。以便建立更高效的傳輸,默認使用http1.0,在http1.0中需要配置header才能

在Upstream中所配置的上游服務器默認都是用短連接,即每次請求都會在完成之后斷開

相關配置
upstream中配置

配置

keepalive 100;

向上游服務器的保留連接數

**keepalive_timeout **

連接保留時間

**keepalive_requests **

一個tcp復用中 可以并發接收的請求個數

server中配置
proxy_http_version 1.1;
配置http版本號
默認使用http1.0協議,需要在request中增加”Connection: keep-alive“ header才能夠支持,而HTTP1.1默認支持。
proxy_set_header Connection "";
清楚close信息

AB安裝

yum install httpd-tools

參數說明:

  • -n 即requests,用于指定壓力測試總共的執行次數。
  • -c 即concurrency,用于指定的并發數。
  • -t 即timelimit,等待響應的最大時間(單位:秒)。
  • -b 即windowsize,TCP發送/接收的緩沖大小(單位:字節)。
  • -p 即postfile,發送POST請求時需要上傳的文件,此外還必須設置-T參數。
  • -u 即putfile,發送PUT請求時需要上傳的文件,此外還必須設置-T參數。
  • -T 即content-type,用于設置Content-Type請求頭信息,例如:application/x-www-form-urlencoded,默認值為text/plain。
  • -v 即verbosity,指定打印幫助信息的冗余級別。
  • -w 以HTML表格形式打印結果。
  • -i 使用HEAD請求代替GET請求。
  • -x 插入字符串作為table標簽的屬性。
  • -y 插入字符串作為tr標簽的屬性。
  • -z 插入字符串作為td標簽的屬性。
  • -C 添加cookie信息,例如:“Apache=1234”(可以重復該參數選項以添加多個)。
  • -H 添加任意的請求頭,例如:“Accept-Encoding: gzip”,請求頭將會添加在現有的多個請求頭之后(可以重復該參數選項以添加多個)。
  • -A 添加一個基本的網絡認證信息,用戶名和密碼之間用英文冒號隔開。
  • -P 添加一個基本的代理認證信息,用戶名和密碼之間用英文冒號隔開。
  • -X 指定使用的和端口號,例如:“126.10.10.3:88”。
  • -V 打印版本號并退出。
  • -k 使用HTTP的KeepAlive特性。
  • -d 不顯示百分比。
  • -S 不顯示預估和警告信息。
  • -g 輸出結果信息到gnuplot格式的文件中。
  • -e 輸出結果信息到CSV格式的文件中。
  • -r 指定接收到錯誤信息時不退出程序。
  • -h 顯示用法信息,其實就是ab -help。
直連nginx
Server Software:        nginx/1.21.6
Server Hostname:        192.168.44.102
Server Port:            80Document Path:          /
Document Length:        16 bytesConcurrency Level:      30
Time taken for tests:   13.035 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Total transferred:      25700000 bytes
HTML transferred:       1600000 bytes
Requests per second:    7671.48 [#/sec] (mean)
Time per request:       3.911 [ms] (mean)
Time per request:       0.130 [ms] (mean, across all concurrent requests)
Transfer rate:          1925.36 [Kbytes/sec] receivedConnection Times (ms)min  mean[+/-sd] median   max
Connect:        0    0   0.4      0      12
Processing:     1    3   1.0      3      14
Waiting:        0    3   0.9      3      14
Total:          2    4   0.9      4      14Percentage of the requests served within a certain time (ms)50%      466%      475%      480%      490%      595%      598%      699%      7100%     14 (longest request)
反向代理
Server Software:        nginx/1.21.6
Server Hostname:        192.168.44.101
Server Port:            80Document Path:          /
Document Length:        16 bytesConcurrency Level:      30
Time taken for tests:   25.968 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Total transferred:      25700000 bytes
HTML transferred:       1600000 bytes
Requests per second:    3850.85 [#/sec] (mean)
Time per request:       7.790 [ms] (mean)
Time per request:       0.260 [ms] (mean, across all concurrent requests)
Transfer rate:          966.47 [Kbytes/sec] receivedConnection Times (ms)min  mean[+/-sd] median   max
Connect:        0    0   0.2      0      13
Processing:     3    8   1.4      7      22
Waiting:        1    7   1.4      7      22
Total:          3    8   1.4      7      22Percentage of the requests served within a certain time (ms)50%      766%      875%      880%      890%      995%     1098%     1299%     13100%     22 (longest request)
直連Tomcat
Server Software:        
Server Hostname:        192.168.44.105
Server Port:            8080Document Path:          /
Document Length:        7834 bytesConcurrency Level:      30
Time taken for tests:   31.033 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Total transferred:      804300000 bytes
HTML transferred:       783400000 bytes
Requests per second:    3222.38 [#/sec] (mean)
Time per request:       9.310 [ms] (mean)
Time per request:       0.310 [ms] (mean, across all concurrent requests)
Transfer rate:          25310.16 [Kbytes/sec] receivedConnection Times (ms)min  mean[+/-sd] median   max
Connect:        0    0   0.3      0      15
Processing:     0    9   7.8      7     209
Waiting:        0    9   7.2      7     209
Total:          0    9   7.8      7     209Percentage of the requests served within a certain time (ms)50%      766%      975%     1180%     1390%     1895%     2298%     2799%     36100%    209 (longest request)
nginx反向代理Tomcat + keepalive

Server Software:        nginx/1.21.6
Server Hostname:        192.168.44.101
Server Port:            80Document Path:          /
Document Length:        7834 bytesConcurrency Level:      30
Time taken for tests:   23.379 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Total transferred:      806500000 bytes
HTML transferred:       783400000 bytes
Requests per second:    4277.41 [#/sec] (mean)
Time per request:       7.014 [ms] (mean)
Time per request:       0.234 [ms] (mean, across all concurrent requests)
Transfer rate:          33688.77 [Kbytes/sec] receivedConnection Times (ms)min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       9
Processing:     1    7   4.2      6     143
Waiting:        1    7   4.2      6     143
Total:          1    7   4.2      6     143Percentage of the requests served within a certain time (ms)50%      666%      775%      780%      790%      895%     1098%     1399%     16100%    143 (longest request)
nginx反向代理Tomcat
Server Software:        nginx/1.21.6
Server Hostname:        192.168.44.101
Server Port:            80Document Path:          /
Document Length:        7834 bytesConcurrency Level:      30
Time taken for tests:   33.814 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Total transferred:      806500000 bytes
HTML transferred:       783400000 bytes
Requests per second:    2957.32 [#/sec] (mean)
Time per request:       10.144 [ms] (mean)
Time per request:       0.338 [ms] (mean, across all concurrent requests)
Transfer rate:          23291.74 [Kbytes/sec] receivedConnection Times (ms)min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       9
Processing:     1   10   5.5      9     229
Waiting:        1   10   5.5      9     229
Total:          1   10   5.5      9     229Percentage of the requests served within a certain time (ms)50%      966%     1075%     1180%     1190%     1395%     1498%     1799%     19100%    229 (longest request)

UpStream工作流程

proxy_pass 向上游服務器請求數據共有6個階段

  • 初始化
  • 與上游服務器建立連接
  • 向上游服務器發送請求
  • 處理響應頭
  • 處理響應體
  • 結束

set_header

設置header

proxy_connect_timeout

與上游服務器連接超時時間、快速失敗

proxy_send_timeout

定義nginx向后端服務發送請求的間隔時間(不是耗時)。默認60秒,超過這個時間會關閉連接

proxy_read_timeout

后端服務給nginx響應的時間,規定時間內后端服務沒有給nginx響應,連接會被關閉,nginx返回504 Gateway Time-out。默認60秒

緩沖區

proxy_requset_buffering

是否完全讀到請求體之后再向上游服務器發送請求

proxy_buffering

是否緩沖上游服務器數據

proxy_buffers 32 64k;

緩沖區大小 32個 64k大小內存緩沖塊

proxy_buffer_size

header緩沖區大小

proxy_requset_buffering on;
proxy_buffering on;proxy_buffer_size 64k;proxy_buffers 32 128k;
proxy_busy_buffers_size 8k;
proxy_max_temp_file_size 1024m;

proxy_temp_file_write_size 8k

當啟用從代理服務器到臨時文件的響應的緩沖時,一次限制寫入臨時文件的數據的大小。 默認情況下,大小由proxy_buffer_size和proxy_buffers指令設置的兩個緩沖區限制。 臨時文件的最大大小由proxy_max_temp_file_size指令設置。

proxy_max_temp_file_size 1024m;

臨時文件最大值

proxy_temp_path

proxy_temp_path /spool/nginx/proxy_temp 1 2;

a temporary file might look like this:

/spool/nginx/proxy_temp/7/45/00000123457
對客戶端的限制

可配置位置

  • http
  • server
  • location

client_body_buffer_size

對客戶端請求中的body緩沖區大小

默認32位8k 64位16k

如果請求體大于配置,則寫入臨時文件

client_header_buffer_size

設置讀取客戶端請求體的緩沖區大小。 如果請求體大于緩沖區,則將整個請求體或僅將其部分寫入臨時文件。 默認32位8K。 64位平臺16K。

client_max_body_size 1000M;

默認1m,如果一個請求的大小超過配置的值,會返回413 (request Entity Too Large)錯誤給客戶端

將size設置為0將禁用對客戶端請求正文大小的檢查。

client_body_timeout

指定客戶端與服務端建立連接后發送 request body 的超時時間。如果客戶端在指定時間內沒有發送任何內容,Nginx 返回 HTTP 408(Request Timed Out)

client_header_timeout

客戶端向服務端發送一個完整的 request header 的超時時間。如果客戶端在指定時間內沒有發送一個完整的 request header,Nginx 返回 HTTP 408(Request Timed Out)。

client_body_temp_path path [level1 [level2 [level3`]]]

在磁盤上客戶端的body臨時緩沖區位置

client_body_in_file_only on;

把body寫入磁盤文件,請求結束也不會刪除

client_body_in_single_buffer

盡量緩沖body的時候在內存中使用連續單一緩沖區,在二次開發時使用$request_body讀取數據時性能會有所提高

client_header_buffer_size

設置讀取客戶端請求頭的緩沖區大小

如果一個請求行或者一個請求頭字段不能放入這個緩沖區,那么就會使用large_client_header_buffers

large_client_header_buffers

默認8k

反向代理中的容錯機制

參考文檔

https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/

http://nginx.org/en/docs/stream/ngx_stream_proxy_module.html#proxy_bind

proxy_timeout

重試機制

proxy_next_upstream

作用:

當后端服務器返回指定的錯誤時,將請求傳遞到其他服務器。

error與服務器建立連接,向其傳遞請求或讀取響應頭時發生錯誤;

timeout在與服務器建立連接,向其傳遞請求或讀取響應頭時發生超時;

invalid_header服務器返回空的或無效的響應;

http_500服務器返回代碼為500的響應;

http_502服務器返回代碼為502的響應;

http_503服務器返回代碼為503的響應;

http_504服務器返回代碼504的響應;

http_403服務器返回代碼為403的響應;

http_404服務器返回代碼為404的響應;

http_429服務器返回代碼為429的響應;

不了解這個機制,在日常開發web服務的時候,就可能會踩坑。

比如有這么一個場景:一個用于導入數據的web頁面,上傳一個excel,通過讀取、處理excel,向數據庫中插入數據,處理時間較長(如1分鐘),且為同步操作(即處理完成后才返回結果)。暫且不論這種方式的好壞,若nginx配置的響應等待時間(proxy_read_timeout)為30秒,就會觸發超時重試,將請求又打到另一臺。如果處理中沒有考慮到重復數據的場景,就會發生數據多次重復插入!(當然,這種場景,內網可以通過機器名訪問該服務器進行操作,就可以繞過nginx了,不過外網就沒辦法了。)

獲取客戶端真實IP

X-Real-IP

額外模塊,不推薦使用

setHeader
proxy_set_header X-Forwarded-For $remote_addr;

Gzip

作用域 http, server, location

gzip on;

開關,默認關閉

gzip_buffers 32 4k|16 8k

緩沖區大小

gzip_comp_level 1;

壓縮等級 1-9,數字越大壓縮比越高

gzip_http_version 1.1;

使用gzip的最小版本

gzip_min_length

設置將被gzip壓縮的響應的最小長度。 長度僅由“Content-Length”響應報頭字段確定。

gzip_proxied 多選

off 為不做限制

作為反向代理時,針對上游服務器返回的頭信息進行壓縮

expired - 啟用壓縮,如果header頭中包含 “Expires” 頭信息
no-cache - 啟用壓縮,如果header頭中包含 “Cache-Control:no-cache” 頭信息
no-store - 啟用壓縮,如果header頭中包含 “Cache-Control:no-store” 頭信息
private - 啟用壓縮,如果header頭中包含 “Cache-Control:private” 頭信息
no_last_modified - 啟用壓縮,如果header頭中不包含 “Last-Modified” 頭信息
no_etag - 啟用壓縮 ,如果header頭中不包含 “ETag” 頭信息
auth - 啟用壓縮 , 如果header頭中包含 “Authorization” 頭信息
any - 無條件啟用壓縮

gzip_vary on;

增加一個header,適配老的瀏覽器 Vary: Accept-Encoding

gzip_types

哪些mime類型的文件進行壓縮

gzip_disable

禁止某些瀏覽器使用gzip

完整實例
  gzip on;gzip_buffers 16 8k;gzip_comp_level 6;gzip_http_version 1.1;gzip_min_length 256;gzip_proxied any;gzip_vary on;gzip_types text/plain application/x-javascript text/css application/xml;gzip_typestext/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xmltext/javascript application/javascript application/x-javascripttext/x-json application/json application/x-web-app-manifest+jsontext/css text/plain text/x-componentfont/opentype application/x-font-ttf application/vnd.ms-fontobjectimage/x-icon;gzip_disable "MSIE [1-6]\.(?!.*SV1)";
HTTP/1.1 200
Server: nginx/1.21.6
Date: Wed, 18 May 2022 17:42:35 GMT
Content-Type: text/html;charset=utf-8
Content-Length: 7832
Connection: keep-alive
Keep-Alive: timeout=65

ngx_http_gunzip_module

幫助不支持gzip的客戶端解壓本地文件

http_gzip_static_module

需要重新編譯nginx

./configure --with-http_gzip_static_module

Brotli

安裝
  • 官網

    • https://github.com/google/ngx_brotli
    • https://codeload.github.com/google/brotli/tar.gz/refs/tags/v1.0.9
  • 下載 兩個項目

  • 解壓縮

模塊化編譯

./configure --with-compat --add-dynamic-module=/root/ngx_brotli-1.0.0rc --prefix=/usr/local/nginx/

--add-dynamic-module=brotli目錄
  • make

  • ngx_http_brotli_filter_module.so ngx_http_brotli_static_module.so拷貝到/usr/local/nginx/modules/

  • 復制nginx主程序

  • 配置文件中添加

load_module "/usr/local/nginx/modules/ngx_http_brotli_filter_module.so";
load_module "/usr/local/nginx/modules/ngx_http_brotli_static_module.so";
brotli on;brotli_static on;brotli_comp_level 6;brotli_buffers 16 8k;brotli_min_length 20;brotli_types text/plain text/css text/javascript application/javascript text/xml application/xml application/xml+rss application/json image/jpeg image/gif image/png;
  • 測試

默認http協議是沒有br的

curl -H 'Accept-Encoding: gzip' -I http://localhost

合并客戶端請求

Concat模塊

Tengine

Nginx官方介紹

https://www.nginx.com/resources/wiki/modules/concat/

git地址

https://github.com/alibaba/nginx-http-concat

  • 安裝

下載源碼解壓縮編譯安裝

  • 配置
    concat on;concat_max_files 30;

資源靜態化

?高并發系統資源靜態化方案

?一致性問題

?合并文件輸出

?集群文件同步

SSI合并服務器端文件

官方文檔

http://nginx.org/en/docs/http/ngx_http_ssi_module.html

配置

ssi_min_file_chunk

向磁盤存儲并使用sendfile發送,文件大小最小值

ssi_last_modified

是否保留lastmodified

ssi_silent_errors

不顯示邏輯錯誤

ssi_value_length

限制腳本參數最大長度

ssi_types

默認text/html;如果需要其他mime類型 需要設置

include file
<!--# include file="footer.html" -->

靜態文件直接引用

include virtual

可以指向location,而不一定是具體文件

include wait

阻塞請求

include set

在virtual基礎上設置變量

set

設置臨時變量

block

可以聲明一個ssi的命令塊,里面可以包裹其他命令

config errmsg

在模板中配置報錯情況

config timefmt

日期格式化

echo

直接輸出變量

  • var變量名稱
  • encoding 是否使用特殊編碼格式
  • default 變量沒有值的時候使用默認值
if

邏輯判斷

rsync

https://www.samba.org/ftp/rsync/rsync.html

remote synchronize是一個遠程數據同步工具,可通過 LAN/WAN 快速同步多臺主機之間的文件。也可以使用 rsync 同步本地硬盤中的不同目錄。
rsync 是用于替代 rcp 的一個工具,rsync 使用所謂的 rsync算法 進行數據同步,這種算法只傳送兩個文件的不同部分,而不是每次都整份傳送,因此速度相當快。

rsync 基于inotify 開發

Rsync有三種模式:

  • 本地模式(類似于cp命令)
  • 遠程模式(類似于scp命令)
  • 守護進程(socket進程:是rsync的重要功能)

rsync 常用選項

選項含義
-a包含-rtplgoD
-r同步目錄時要加上,類似cp時的-r選項
-v同步時顯示一些信息,讓我們知道同步的過程
-l保留軟連接
-L加上該選項后,同步軟鏈接時會把源文件給同步
-p保持文件的權限屬性
-o保持文件的屬主
-g保持文件的屬組
-D保持設備文件信息
-t保持文件的時間屬性
–delete刪除DEST中SRC沒有的文件
–exclude過濾指定文件,如–exclude “logs”會把文件名包含logs的文件或者目錄過濾掉,不同步
-P顯示同步過程,比如速率,比-v更加詳細
-u加上該選項后,如果DEST中的文件比SRC新,則不同步
-z傳輸時壓縮
安裝

兩端安裝

yum install -y rsync

密碼文件

創建文件/etc/rsync.password

內容

hello:123

修改權限

chmod 600 /etc/rsync.password

修改配置

auth users = sgg
secrets file = /etc/rsyncd.pwd
開機啟動

/etc/rc.local文件中添加

rsync --daemon
  • 修改權限

echo “sgg:111” >> /etc/rsyncd.passwd

查看遠程目錄

rsync --list-only 192.168.44.104::www/

拉取數據到指定目錄

rsync -avz rsync://192.168.44.104:873/www

rsync -avz 192.168.44.104::www/ /root/w

使用SSH方式

rsync -avzP /usr/local/nginx/html/ root@192.168.44.105:/www/

客戶端免密

客戶端只放密碼

echo "111" >> /etc/rsyncd.passwd

此時在客戶端已經可以配合腳本實現定時同步了

如何實現推送?

修改配置

rsync -avz --password-file=/etc/rsyncd.passwd.client /usr/local/nginx/html/ rsync://sgg@192.168.44.105:/www

--delete 刪除目標目錄比源目錄多余文件

實時推送

推送端安裝inotify

依賴

yum install -y automake
wget http://github.com/downloads/rvoicilas/inotify-tools/inotify-tools-3.14.tar.gz
./configure --prefix=/usr/local/inotify
make && make install

監控目錄

/usr/local/inotify/bin/inotifywait -mrq --timefmt '%Y-%m-%d %H:%M:%S' --format '%T %w%f %e' -e close_write,modify,delete,create,attrib,move //usr/local/nginx/html/
簡單自動化腳本
#!/bin/bash/usr/local/inotify/bin/inotifywait -mrq --timefmt '%d/%m/%y %H:%M' --format '%T %w%f %e' -e close_write,modify,delete,create,attrib,move //usr/local/nginx/html/ | while read file
dorsync -az --delete --password-file=/etc/rsyncd.passwd.client /usr/local/nginx/html/ sgg@192.168.44.102::ftp/
done
inotify常用參數
參數說明含義
-r–recursive#遞歸查詢目錄
-q–quiet#打印很少的信息,僅僅打印監控事件信息
-m–monitor#始終保持事件監聽狀態
–excludei#排除文件或目錄時,不區分大小寫
–timefmt#指定事件輸出格式
–format#打印使用指定的輸出類似格式字符串
-e–event[ -e|–event … ]accessmodifyattribcloseopenmove_tomove createdeleteumount#通過此參數可以指定要監控的事件 #文件或目錄被讀取#文件或目錄的內容被修改#文件或目錄屬性被改變#文件或目錄封閉,無論讀/寫模式#文件或目錄被打開#文件或目錄被移動至另外一個目錄#文件或目錄被移動另一個目錄或從另一個目錄移動至當前目錄#文件或目錄被創建在當前目錄#文件或目錄被刪除#文件系統被卸載

多級緩存

靜態資源緩存
瀏覽器緩存
什么時候可以用緩存?
  1. 不常改變的內容
  2. 過期時間
  3. 針對post/get請求都可以
  4. 存儲位置
  5. 磁盤使用空間限制

觀察京東緩存及加載速度

  • deskcache

字面理解是從內存中,其實也是字面的含義,這個資源是直接從內存中拿到的,不會請求服務器一般已經加載過該資源且緩存在了內存當中,當關閉該頁面時,此資源就被內存釋放掉了,再次重新打開相同頁面時不會出現from memory cache的情況

  • memorycache

是從磁盤當中取出的,也是在已經在之前的某個時間加載過該資源,不會請求服務器但是此資源不會隨著該頁面的關閉而釋放掉,因為是存在硬盤當中的,下次打開仍會from disk cache

Age

是CDN添加的屬性表示在CDN中緩存了多少秒

via

用來標識CDN緩存經歷了哪些服務器,緩存是否命中,使用的協議

Nginx默認緩存

Nginx版本不同會默認配置

強制緩存與協商緩存

強制緩存:直接從本機讀取,不請求服務器

協商緩存:發送請求header中攜帶Last-Modified,服務器可能會返回304 Not Modified

瀏覽器強制緩存
cache-control

http1.1的規范,使用max-age表示文件可以在瀏覽器中緩存的時間以秒為單位

標記類型功能
public響應頭響應的數據可以被緩存,客戶端和代理層都可以緩存
private響應頭可私有緩存,客戶端可以緩存,代理層不能緩存(CDN,proxy_pass)
no-cache請求頭可以使用本地緩存,但是必須發送請求到服務器回源驗證
no-store請求和響應應禁用緩存
max-age請求和響應文件可以在瀏覽器中緩存的時間以秒為單位
s-maxage請求和響應用戶代理層緩存,CDN下發,當客戶端數據過期時會重新校驗
max-stale請求和響應緩存最大使用時間,如果緩存過期,但還在這個時間范圍內則可以使用緩存數據
min-fresh請求和響應緩存最小使用時間,
must-revalidate請求和響應當緩存過期后,必須回源重新請求資源。比no-cache更嚴格。因為HTTP 規范是允許客戶端在某些特殊情況下直接使用過期緩存的,比如校驗請求發送失敗的時候。那么帶有must-revalidate的緩存必須校驗,其他條件全部失效。
proxy-revalidate請求和響應和must-revalidate類似,只對CDN這種代理服務器有效,客戶端遇到此頭,需要回源驗證
stale-while-revalidate響應表示在指定時間內可以先使用本地緩存,后臺進行異步校驗
stale-if-error響應在指定時間內,重新驗證時返回狀態碼為5XX的時候,可以用本地緩存
only-if-cached響應那么只使用緩存內容,如果沒有緩存 則504 getway timeout

在瀏覽器和服務器端驗證文件是否過期的時候,瀏覽器在二次請求的時候會攜帶IF-Modified-Since屬性

Expires

過期時間

expires 30s;   #緩存30秒
expires 30m;  #緩存30分鐘   
expires 2h;     #緩存2小時
expires 30d;    #緩存30天
協商緩存
last-modified
etag

http1.1支持

在HTTP協議中If-Modified-Since和If-None-Match分別對應Last-Modified和ETag

Entity Tag 的縮寫,中文譯過來就是實體標簽的意思.

HTTP中并沒有指定如何生成ETag,哈希是比較理想的選擇。

在計算Etag的時候,會產生CPU的耗費,所以也可以用時間戳,但這樣直接使用Last-Modified即可。

ETag 用來校驗用戶請求的資源是否有變化,作用和lastmodified很像,區別是lastmodified精確到秒,ETag可以用hash算法來生成更精確的比對內容。

當用戶首次請求資源的時候返回給用戶數據和200狀態碼并生成ETag,再次請求的時候服務器比對ETag,沒有發生變化的話返回304

Cache-Control直接是通過不請求來實現,而ETag是會發請求的,只不過服務器根據請求的東西的內容有無變化來判斷是否返回請求的資源

總結:

cache-control expires 強制緩存

頁面首次打開,直接讀取緩存數據,刷新,會向服務器發起請求

etag lastmodify 協商緩存

沒發生變化 返回304 不發送數據

last-modified 與ssi的沖突
瀏覽器緩存原則
  • 多級集群負載時last-modified必須保持一致

  • 還有一些場景下我們希望禁用瀏覽器緩存。比如輪訓api上報數據數據

  • 瀏覽器緩存很難徹底禁用,大家的做法是加版本號,隨機數等方法。

  • 只緩存200響應頭的數據,像3XX這類跳轉的頁面不需要緩存。

  • 對于js,css這類可以緩存很久的數據,可以通過加版本號的方式更新內容

  • 不需要強一致性的數據,可以緩存幾秒

  • 異步加載的接口數據,可以使用ETag來校驗。

  • 在服務器添加Server頭,有利于排查錯誤

  • 分為手機APP和Client以及是否遵循http協議

  • 在沒有聯網的狀態下可以展示數據

  • 流量消耗過多

  • 提前下發 避免秒殺時同時下發數據造成流量短時間暴增

  • 兜底數據 在服務器崩潰和網絡不可用的時候展示

  • 臨時緩存 退出即清理

  • 固定緩存 展示框架這種,可能很長時間不會更新,可用隨客戶端下發

    • 首頁有的時候可以看做是框架 應該禁用緩存,以保證加載的資源都是最新的
  • 父子連接 頁面跳轉時有一部分內容不需要重新加載,可用從父菜單帶過來

  • 預加載 某些邏輯可用判定用戶接下來的操作,那么可用異步加載那些資源

  • 漂亮的加載過程 異步加載 先展示框架,然后異步加載內容,避免主線程阻塞

GEOip

1 下載數據庫

官網需注冊登錄

下載數據庫

maxmind.com

2 安裝依賴
官方git

https://github.com/maxmind/libmaxminddb

下載后執行編譯安裝之后

$ echo /usr/local/lib  >> /etc/ld.so.conf.d/local.conf 
$ ldconfig
Nginx模塊

https://github.com/leev/ngx_http_geoip2_module

更完整的配置可參考官方文檔

http://nginx.org/en/docs/http/ngx_http_geoip_module.html#geoip_proxy

Nginx配置
    geoip2 /root/GeoLite2-ASN_20220524/GeoLite2-ASN.mmdb {
$geoip2_country_code country iso_code;}
add_header country $geoip2_country_code;

正向代理與反向代理緩存

正向代理配置
proxy_pass $scheme://$host$request_uri;
resolver 8.8.8.8;
代理https請求

需要第三方模塊

https://github.com/chobits/ngx_http_proxy_connect_module

配置

 server {listen                         3128;# dns resolver used by forward proxyingresolver                       8.8.8.8;# forward proxy for CONNECT requestproxy_connect;proxy_connect_allow            443 563;proxy_connect_connect_timeout  10s;proxy_connect_read_timeout     10s;proxy_connect_send_timeout     10s;# forward proxy for non-CONNECT requestlocation / {proxy_pass http://$host;proxy_set_header Host $host;}}

proxy緩存

官網解釋

http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache

配置
http模塊:
proxy_cache_path /ngx_tmp levels=1:2 keys_zone=test_cache:100m inactive=1d max_size=10g ;
location模塊:
add_header  Nginx-Cache "$upstream_cache_status";
proxy_cache test_cache;
proxy_cache_valid 168h;

proxy_cache_use_stale

默認off

在什么時候可以使用過期緩存

可選error | timeout | invalid_header | updating | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | http_429 | off

proxy_cache_background_update

默認off

運行開啟子請求更新過期的內容。同時會把過期的內容返回給客戶端

proxy_no_cache proxy_cache_bypass

指定什么時候不使用緩存而直接請求上游服務器

proxy_no_cache $cookie_nocache $arg_nocache$arg_comment;
proxy_no_cache $http_pragma    $http_authorization;

如果這些變量如果存在的話不為空或者不等于0,則不使用緩存

proxy_cache_convert_head

默認 on

是否把head請求轉換成get請求后再發送給上游服務器 以便緩存body里的內容

如果關閉 需要在 cache key 中添加 $request_method 以便區分緩存內容

proxy_cache_lock

默認off

緩存更新鎖

proxy_cache_lock_age

默認5s

緩存鎖超時時間

斷點續傳緩存 range

當有完整的content-length之后即可斷點續傳

在反向代理服務器中需向后傳遞header

proxy_set_header Range $http_range;

proxy_cache_key中增加range

proxy_cache_max_range_offset

range最大值,超過之后不做緩存,默認情況下 不需要對單文件較大的資源做緩存

proxy_cache_methods

默認 head get

proxy_cache_min_uses

默認1

被請求多少次之后才做緩存

proxy_cache_path

path 指定存儲目錄

以cache_key取md5值

  • levels=1:2

目錄層級數及目錄名稱位數

取mdb5后幾位

TMPFS

  • use_temp_path

默認創建緩存文件時,先向緩沖區創建臨時文件,再移動到緩存目錄

是否使用緩沖區

  • inactive

指定時間內未被訪問過的緩存將被刪除

緩存清理

purger

需要第三方模塊支持

https://github.com/FRiCKLE/ngx_cache_purge

配置

location ~ /purge(/.*) {proxy_cache_purge  test_cache  $1;}自定義cachekeyproxy_cache_key $uri;

proxy_cache_key

默認$scheme$proxy_host$request_uri

緩存的key

proxy_cache_revalidate

如果緩存過期了,向上游服務器發送“If-Modified-Since” and “If-None-Match來驗證是否改變,如果沒有就不需要重新下載資源了

proxy_cache_valid

可以針對不容http狀態碼設置緩存過期時間

不設置狀態碼會默認200, 301, 302

proxy_cache_valid 200 302 10m;
proxy_cache_valid 301      1h;
proxy_cache_valid any      1m;

any指其他任意狀態碼

第二部分 高效

Nginx內存緩存

strace

一般應用為靜態文件元數據信息緩存

sendfile執行過程

epoll_wait(8, [{EPOLLIN, {u32=1904243152, u64=140709327827408}}, {EPOLLIN, {u32=1904242704, u64=140709327826960}}], 512, 25215) = 2
recvfrom(10, "GET / HTTP/1.1\r\nHost: 192.168.44"..., 1024, 0, NULL, NULL) = 475
stat("/usr/local/nginx//html/index.html", {st_mode=S_IFREG|0644, st_size=1429, ...}) = 0
open("/usr/local/nginx//html/index.html", O_RDONLY|O_NONBLOCK) = 11
fstat(11, {st_mode=S_IFREG|0644, st_size=1429, ...}) = 0
writev(10, [{iov_base="HTTP/1.1 200 OK\r\nServer: nginx/1"..., iov_len=263}], 1) = 263
sendfile(10, 11, [0] => [1429], 1429)   = 1429
write(4, "192.168.44.1 - - [27/May/2022:14"..., 193) = 193
close(11) 

open_file_cache

open_file_cache max=500 inactive=60s
open_file_cache_min_uses 1; 
open_file_cache_valid 60s; 
open_file_cache_errors on

max緩存最大數量,超過數量后會使用LRU淘汰

inactive 指定時間內未被訪問過的緩存將被刪除

pen_file_cache_min_uses

被訪問到多少次后會開始緩存

open_file_cache_valid

間隔多長時間去檢查文件是否有變化

open_file_cache_errors

對錯誤信息是否緩存

Nginx外置緩存緩存

http://nginx.org/en/docs/http/ngx_http_memcached_module.html

error_page

指定狀態碼

	error_page 404 =302 http://www.atguigu.com;

默認指向location

匿名location

nginx + memcached

memcached安裝

yum -y install memcached

默認配置文件在

/etc/sysconfig/memcached

查看狀態

memcached-tool 127.0.0.1:11211  stats
nginx配置
    upstream backend {#   server 192.168.44.102 weight=8 down;server 192.168.44.104:8080;}location / {set            $memcached_key "$uri?$args";memcached_pass 127.0.0.1:11211;add_header X-Cache-Satus HIT;add_header Content-Type 'text/html; charset=utf-8'; # 強制響應數據格式為html# root   html;}

nginx + redis

Redis安裝

7.0下載地址

https://codeload.github.com/redis/redis/tar.gz/refs/tags/7.0.0

安裝
依賴
yum install -y tcl-devel解壓
make
make install 

redis2-nginx-module

redis2-nginx-module是一個支持 Redis 2.0 協議的 Nginx upstream 模塊,它可以讓 Nginx 以非阻塞方式直接防問遠方的 Redis 服務,同時支持 TCP 協議和 Unix Domain Socket 模式,并且可以啟用強大的 Redis 連接池功能。

https://www.nginx.com/resources/wiki/modules/redis2/

https://github.com/openresty/redis2-nginx-module

redis快速安裝

yum install epel-release
yum install -y redis

redis2-nginx-module 安裝

test
location = /foo {default_type text/html;redis2_query auth 123123;set $value 'first';redis2_query set one $value;redis2_pass 192.168.199.161:6379;}
get
location = /get {default_type text/html;redis2_pass 192.168.199.161:6379;redis2_query auth 123123;set_unescape_uri $key $arg_key;  # this requires ngx_set_miscredis2_query get $key;}
set
# GET /set?key=one&val=first%20valuelocation = /set {default_type text/html;redis2_pass 192.168.199.161:6379;redis2_query auth 123123;set_unescape_uri $key $arg_key;  # this requires ngx_set_miscset_unescape_uri $val $arg_val;  # this requires ngx_set_miscredis2_query set $key $val;}
pipeline
     set $value 'first';redis2_query set one $value;redis2_query get one;redis2_query set one two;redis2_query get one;redis2_query del key1;
list
    redis2_query lpush key1 C;redis2_query lpush key1 B;redis2_query lpush key1 A;redis2_query lrange key1 0 -1;
集群
upstream redis_cluster {server 192.168.199.161:6379;server 192.168.199.161:6379;}location = /redis {default_type text/html;redis2_next_upstream error timeout invalid_response;redis2_query get foo;redis2_pass redis_cluster;}

Stream模塊

http://nginx.org/en/docs/stream/ngx_stream_core_module.html

限流

QPS限制

官方文檔

http://nginx.org/en/docs/http/ngx_http_limit_req_module.html

測試工具

https://jmeter.apache.org/

配置

limit_req_zone $binary_remote_addr zone=test:10m rate=15r/s;
limit_req zone=req_zone_wl burst=20 nodelay;

日志

ngx_http_log_module

http://nginx.org/en/docs/http/ngx_http_log_module.html

ngx_http_empty_gif_module

http://nginx.org/en/docs/http/ngx_http_empty_gif_module.html

json
log_format  ngxlog json '{"timestamp":"$time_iso8601",''"source":"$server_addr",''"hostname":"$hostname",''"remote_user":"$remote_user",''"ip":"$http_x_forwarded_for",''"client":"$remote_addr",''"request_method":"$request_method",''"scheme":"$scheme",''"domain":"$server_name",''"referer":"$http_referer",''"request":"$request_uri",''"requesturl":"$request",''"args":"$args",''"size":$body_bytes_sent,''"status": $status,''"responsetime":$request_time,''"upstreamtime":"$upstream_response_time",''"upstreamaddr":"$upstream_addr",''"http_user_agent":"$http_user_agent",''"http_cookie":"$http_cookie",''"https":"$https"''}';
errorlog

http://nginx.org/en/docs/ngx_core_module.html#error_log

日志分割

1.腳本

2.Logrotate

重試機制

http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream

max_fails

最大失敗次數

0為標記一直可用,不檢查健康狀態

fail_timeout

失敗時間

當fail_timeout時間內失敗了max_fails次,標記服務不可用

fail_timeout時間后會再次激活次服務

proxy_next_upstream

proxy_next_upstream_timeout

重試最大超時時間

proxy_next_upstream_tries

重試次數,包括第一次

proxy_next_upstream_timeout時間內允許proxy_next_upstream_tries次重試

主動健康檢查

tengine版

https://github.com/yaoweibin/nginx_upstream_check_module

nginx商業版

http://nginx.org/en/docs/http/ngx_http_upstream_hc_module.html

配置

      upstream backend {#   server 192.168.44.102 weight=8 down;server 192.168.44.104:8080;server 192.168.44.105:8080;check interval=3000 rise=2 fall=5 timeout=1000 type=http;check_http_send "HEAD / HTTP/1.0\r\n\r\n";check_http_expect_alive http_2xx http_3xx;}location /status {check_status;access_log off;}location / {proxy_pass http://backend;root   html;}

Openresty

Lua

Lua 是由巴西里約熱內盧天主教大學(Pontifical Catholic University of Rio de Janeiro)里的一個研究小組于1993年開發的一種輕量、小巧的腳本語言,用標準 C 語言編寫,其設計目的是為了嵌入應用程序中,從而為應用程序提供靈活的擴展和定制功能。

官網:http://www.lua.org/

IDE

EmmyLua插件

https://github.com/EmmyLua/IntelliJ-EmmyLua

https://emmylua.github.io/zh_CN/

LDT 基于eclipse

https://www.eclipse.org/ldt/

Lua基礎語法

hello world
print("hello world!")
保留關鍵字

and break do else elseif end false for function if in local nil not or repeat return then true until while

注釋
-- 兩個減號是行注釋--[[這是塊注釋這是塊注釋.--]]
變量
數字類型

Lua的數字只有double型,64bits

你可以以如下的方式表示數字

num = 1024num = 3.0num = 3.1416num = 314.16e-2num = 0.31416E1num = 0xffnum = 0x56
字符串

可以用單引號,也可以用雙引號

也可以使用轉義字符‘\n’ (換行), ‘\r’ (回車), ‘\t’ (橫向制表), ‘\v’ (縱向制表), ‘\’ (反斜杠), ‘\”‘ (雙引號), 以及 ‘\” (單引號)等等

下面的四種方式定義了完全相同的字符串(其中的兩個中括號可以用于定義有換行的字符串)

a = 'alo\n123"'a = "alo\n123\""a = '\97lo\10\04923"'a = [[alo123"]]
空值

C語言中的NULL在Lua中是nil,比如你訪問一個沒有聲明過的變量,就是nil

布爾類型

只有nil和false是 false

數字0,‘’空字符串(’\0’)都是true

作用域

lua中的變量如果沒有特殊說明,全是全局變量,那怕是語句塊或是函數里。

變量前加local關鍵字的是局部變量。

控制語句
while循環
local i = 0local max = 10while i <= max doprint(i)i = i +1end
if-else
local function main()local age = 140local sex = 'Male'if age == 40 and sex =="Male" thenprint(" 男人四十一枝花 ")elseif age > 60 and sex ~="Female" thenprint("old man!!")elseif age < 20 thenio.write("too young, too simple!\n")elseprint("Your age is "..age)endend-- 調用
main()
for循環
sum = 0for i = 100, 1, -2 dosum = sum + iend
函數
function myPower(x,y)return      y+xendpower2 = myPower(2,3)print(power2)
function newCounter()local i = 0return function()     -- anonymous functioni = i + 1return iend
endc1 = newCounter()print(c1())  --> 1print(c1())  --> 2print(c1())
返回值
name, age,bGay = "yiming", 37, false, "yimingl@hotmail.com"print(name,age,bGay)
function isMyGirl(name)return name == 'xiao6' , name
endlocal bol,name = isMyGirl('xiao6')print(name,bol)
Table

key,value的鍵值對 類似 map

local function main()
dog = {name='111',age=18,height=165.5}dog.age=35print(dog.name,dog.age,dog.height)print(dog)
end
main()
數組
local function main()
arr = {"string", 100, "dog",function() print("wangwang!") return 1 end}print(arr[4]())
end
main()
遍歷
arr = {"string", 100, "dog",function() print("wangwang!") return 1 end}for k, v in pairs(arr) doprint(k, v)
end
成員函數
local function main()person = {name='旺財',age = 18}function  person.eat(food)print(person.name .." eating "..food)end
person.eat("骨頭")
end
main()

Openresty Nginx + Lua

Nginx是一個主進程配合多個工作進程的工作模式,每個進程由單個線程來處理多個連接。

在生產環境中,我們往往會把cpu內核直接綁定到工作進程上,從而提升性能。

安裝

預編譯安裝

以CentOS舉例 其他系統參照:http://openresty.org/cn/linux-packages.html

你可以在你的 CentOS 系統中添加 openresty 倉庫,這樣就可以便于未來安裝或更新我們的軟件包(通過 yum update 命令)。運行下面的命令就可以添加我們的倉庫:

? yum install yum-utils

? yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo

然后就可以像下面這樣安裝軟件包,比如 openresty:

? yum install openresty

如果你想安裝命令行工具 resty,那么可以像下面這樣安裝 openresty-resty 包:

? sudo yum install openresty-resty

源碼編譯安裝
下載

http://openresty.org/cn/download.html

最小版本基于nginx1.21

./configure

然后在進入 openresty-VERSION/ 目錄, 然后輸入以下命令配置:

./configure

默認, --prefix=/usr/local/openresty 程序會被安裝到/usr/local/openresty目錄。

依賴 gcc openssl-devel pcre-devel zlib-devel

安裝:yum install gcc openssl-devel pcre-devel zlib-devel postgresql-devel

您可以指定各種選項,比如

./configure --prefix=/opt/openresty \--with-luajit \--without-http_redis2_module \--with-http_iconv_module \--with-http_postgres_module

試著使用 ./configure --help 查看更多的選項。

make && make install

服務命令
啟動

Service openresty start

停止

Service openresty stop

檢查配置文件是否正確

Nginx -t

重新加載配置文件

Service openresty reload

查看已安裝模塊和版本號

Nginx -V

測試lua腳本

在Nginx.conf 中寫入location /lua {default_type text/html;content_by_lua 'ngx.say("<p>Hello, World!</p>")';}

lua-nginx-module

創建配置文件lua.conf
   server {listen       80;server_name  localhost;location /lua {default_type text/html;content_by_lua_file conf/lua/hello.lua;}
}
在Nginx.conf下引入lua配置

include lua.conf;

創建外部lua腳本

conf/lua/hello.lua

內容:

ngx.say("<p>Hello, World!</p>")

獲取Nginx uri中的單一變量
    location /nginx_var {default_type text/html;content_by_lua_block {ngx.say(ngx.var.arg_a)}}
獲取Nginx uri中的所有變量
local uri_args = ngx.req.get_uri_args()  for k, v in pairs(uri_args) do  if type(v) == "table" then  ngx.say(k, " : ", table.concat(v, ", "), "<br/>")  else  ngx.say(k, ": ", v, "<br/>")  end  
end
在處理http請求時還可以使用
  • set_by_lua

修改nginx變量

  • rewrite_by_lua

修改uri

  • access_by_lua

訪問控制

  • header_filter_by_lua

修改響應頭

  • boy_filter_by_lua

修改響應體

  • log_by_lua

日志

代碼熱部署
lua_code_cache off
獲取Nginx請求頭信息
local headers = ngx.req.get_headers()                         ngx.say("Host : ", headers["Host"], "<br/>")  ngx.say("user-agent : ", headers["user-agent"], "<br/>")  ngx.say("user-agent : ", headers.user_agent, "<br/>")for k,v in pairs(headers) do  if type(v) == "table" then  ngx.say(k, " : ", table.concat(v, ","), "<br/>")  else  ngx.say(k, " : ", v, "<br/>")  end  end  
獲取post請求參數
ngx.req.read_body()  ngx.say("post args begin", "<br/>")  local post_args = ngx.req.get_post_args()  for k, v in pairs(post_args) do  if type(v) == "table" then  ngx.say(k, " : ", table.concat(v, ", "), "<br/>")  else  ngx.say(k, ": ", v, "<br/>")  end  
end
http協議版本
ngx.say("ngx.req.http_version : ", ngx.req.http_version(), "<br/>")
請求方法
ngx.say("ngx.req.get_method : ", ngx.req.get_method(), "<br/>")  
原始的請求頭內容
ngx.say("ngx.req.raw_header : ",  ngx.req.raw_header(), "<br/>")  
body內容體
ngx.say("ngx.req.get_body_data() : ", ngx.req.get_body_data(), "<br/>")

Nginx緩存

Nginx全局內存緩存
lua_shared_dict shared_data 1m;local shared_data = ngx.shared.shared_data  local i = shared_data:get("i")  if not i then  i = 1  shared_data:set("i", i)  ngx.say("lazy set i ", i, "<br/>")  
end  i = shared_data:incr("i", 1)  ngx.say("i=", i, "<br/>")
lua-resty-lrucache

Lua 實現的一個簡單的 LRU 緩存,適合在 Lua 空間里直接緩存較為復雜的 Lua 數據結構:它相比 ngx_lua 共享內存字典可以省去較昂貴的序列化操作,相比 memcached 這樣的外部服務又能省去較昂貴的 socket 操作

https://github.com/openresty/lua-resty-lrucache

引用lua文件

content_by_lua_block {require("my/cache").go()}

自定義函數

local _M = {}lrucache = require "resty.lrucache"c, err = lrucache.new(200)  -- allow up to 200 items in the cache
ngx.say("count=init")if not c thenerror("failed to create the cache: " .. (err or "unknown"))
endfunction _M.go()count = c:get("count")c:set("count",100)
ngx.say("count=", count, " --<br/>")if not count then  c:set("count",1)ngx.say("lazy set count ", c:get("count"), "<br/>")  elsec:set("count",count+1)ngx.say("count=", count, "<br/>")
endend
return _M

打開lua_code_cache

lua-resty-redis訪問redis

https://github.com/openresty/lua-resty-redis

常用方法
local res, err = red:get("key")local res, err = red:lrange("nokey", 0, 1)ngx.say("res:",cjson.encode(res))
創建連接
red, err = redis:new()ok, err = red:connect(host, port, options_table?)
timeout
red:set_timeout(time)
keepalive
red:set_keepalive(max_idle_timeout, pool_size)
close
ok, err = red:close()
pipeline
red:init_pipeline()results, err = red:commit_pipeline()
認證
    local res, err = red:auth("foobared")if not res thenngx.say("failed to authenticate: ", err)return
end
  local redis = require "resty.redis"local red = redis:new()red:set_timeouts(1000, 1000, 1000) -- 1 seclocal ok, err = red:connect("127.0.0.1", 6379)if not ok thenngx.say("failed to connect: ", err)returnendok, err = red:set("dog", "an animal")if not ok thenngx.say("failed to set dog: ", err)returnendngx.say("set result: ", ok)local res, err = red:get("dog")if not res thenngx.say("failed to get dog: ", err)returnendif res == ngx.null thenngx.say("dog not found.")returnendngx.say("dog: ", res)
redis-cluster支持

https://github.com/steve0511/resty-redis-cluster

redis2-nginx-module

redis2-nginx-module是一個支持 Redis 2.0 協議的 Nginx upstream 模塊,它可以讓 Nginx 以非阻塞方式直接防問遠方的 Redis 服務,同時支持 TCP 協議和 Unix Domain Socket 模式,并且可以啟用強大的 Redis 連接池功能。

test
location = /foo {default_type text/html;redis2_query auth 123123;set $value 'first';redis2_query set one $value;redis2_pass 192.168.199.161:6379;}
get
location = /get {default_type text/html;redis2_pass 192.168.199.161:6379;redis2_query auth 123123;set_unescape_uri $key $arg_key;  # this requires ngx_set_miscredis2_query get $key;}
set
# GET /set?key=one&val=first%20valuelocation = /set {default_type text/html;redis2_pass 192.168.199.161:6379;redis2_query auth 123123;set_unescape_uri $key $arg_key;  # this requires ngx_set_miscset_unescape_uri $val $arg_val;  # this requires ngx_set_miscredis2_query set $key $val;}
pipeline
     set $value 'first';redis2_query set one $value;redis2_query get one;redis2_query set one two;redis2_query get one;redis2_query del key1;
list
    redis2_query lpush key1 C;redis2_query lpush key1 B;redis2_query lpush key1 A;redis2_query lrange key1 0 -1;
集群
upstream redis_cluster {server 192.168.199.161:6379;server 192.168.199.161:6379;}location = /redis {default_type text/html;redis2_next_upstream error timeout invalid_response;redis2_query get foo;redis2_pass redis_cluster;}

lua-resty-mysql

https://github.com/openresty/lua-resty-mysql

local mysql = require "resty.mysql"local db, err = mysql:new()if not db thenngx.say("failed to instantiate mysql: ", err)returnenddb:set_timeout(1000) -- 1 seclocal ok, err, errcode, sqlstate = db:connect{host = "192.168.44.211",port = 3306,database = "zhangmen",user = "root",password = "111111",charset = "utf8",max_packet_size = 1024 * 1024,}ngx.say("connected to mysql.<br>")local res, err, errcode, sqlstate = db:query("drop table if exists cats")if not res thenngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".")returnendres, err, errcode, sqlstate =db:query("create table cats ".. "(id serial primary key, ".. "name varchar(5))")if not res thenngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".")returnendngx.say("table cats created.")res, err, errcode, sqlstate =db:query("select * from t_emp")if not res thenngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".")returnendlocal cjson = require "cjson"ngx.say("result: ", cjson.encode(res))local ok, err = db:set_keepalive(10000, 100)if not ok thenngx.say("failed to set keepalive: ", err)returnend

模板實時渲染 lua-resty-template

https://github.com/bungle/lua-resty-template

如果學習過JavaEE中的servlet和JSP的話,應該知道JSP模板最終會被翻譯成Servlet來執行;

而lua-resty-template模板引擎可以認為是JSP,其最終會被翻譯成Lua代碼,然后通過ngx.print輸出。

lua-resty-template大體內容有:

l 模板位置:從哪里查找模板;

l 變量輸出/轉義:變量值輸出;

l 代碼片段:執行代碼片段,完成如if/else、for等復雜邏輯,調用對象函數/方法;

l 注釋:解釋代碼片段含義;

l include:包含另一個模板片段;

l 其他:lua-resty-template還提供了不需要解析片段、簡單布局、可復用的代碼塊、宏指令等支持。

基礎語法

l {(include_file)}:包含另一個模板文件;

l {* var *}:變量輸出;

l {{ var }}:變量轉義輸出;

l {% code %}:代碼片段;

l {# comment #}:注釋;

l {-raw-}:中間的內容不會解析,作為純文本輸出;

lua代碼熱加載

在http模塊中加入

lua_code_cache off;

reload后Nginx會提示影響性能,記得在生產環境中關掉。

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

測試

一、初始化

-- Using template.new
local template = require "resty.template"
local view = template.new "view.html"
view.message = "Hello, World!"
view:render()-- Using template.render
-- template.render("view.html", { message = "Hel11lo, Worl1d!" })

二、執行函數,得到渲染之后的內容

local func = template.compile("view.html")  local content = func(context)  ngx.say("xx:",content) 
模板文件存放位置
nginx.conf中配置
set $template_root /usr/local/openresty/nginx/tmp;

resty.template.html

local template = require("resty.template")
local html = require "resty.template.html"template.render([[
<ul>
{% for _, person in ipairs(context) do %}{*html.li(person.name)*} --
{% end %}
</ul>
<table>
{% for _, person in ipairs(context) do %}<tr data-sort="{{(person.name or ""):lower()}}">{*html.td{ id = person.id }(person.name)*}</tr>
{% end %}
</table>]], {{ id = 1, name = "Emma"},{ id = 2, name = "James" },{ id = 3, name = "Nicholas" },{ id = 4 }
})

模板內容

<!DOCTYPE html>
<html>
<body><h1>{{message}}</h1>
</body>
</html>

多值傳入

template.caching(false)
local template = require("resty.template")
local context = {name = "lucy",age = 50,
}
template.render("view.html", context)

模板內容

<!DOCTYPE html>
<html>
<body><h1>name:{{name}}</h1><h1>age:{{age}}</h1>
</body>
</html>

模板管理與緩存

模板緩存:默認開啟,開發環境可以手動關閉

template.caching(true)

模板文件需要業務系統更新與維護,當模板文件更新后,可以通過模板版本號或消息通知Openresty清空緩存重載模板到內存中

template.cache = {}

完整頁面

local template = require("resty.template")
template.caching(false)
local context = {title = "測試",name = "lucy",description = "<script>alert(1);</script>",age = 40,hobby = {"電影", "音樂", "閱讀"},score = {語文 = 90, 數學 = 80, 英語 = 70},score2 = {{name = "語文", score = 90},{name = "數學", score = 80},{name = "英語", score = 70},}
}template.render("view.html", context)

模板

{(header.html)}  <body>  {# 不轉義變量輸出 #}  姓名:{* string.upper(name) *}<br/>  {# 轉義變量輸出 #}  簡介:{{description}}簡介:{* description *}<br/>  {# 可以做一些運算 #}  年齡: {* age + 10 *}<br/>  {# 循環輸出 #}  愛好:  {% for i, v in ipairs(hobby) do %}  {% if v == '電影' then  %} - xxoo{%else%}  - {* v *} 
{% end %}  {% end %}<br/>  成績:  {% local i = 1; %}  {% for k, v in pairs(score) do %}  {% if i > 1 then %},{% end %}  {* k *} = {* v *}  {% i = i + 1 %}  {% end %}<br/>  成績2:  {% for i = 1, #score2 do local t = score2[i] %}  {% if i > 1 then %},{% end %}  {* t.name *} = {* t.score *}  {% end %}<br/>  {# 中間內容不解析 #}  {-raw-}{(file)}{-raw-}  
{(footer.html)}  

layout 布局統一風格

使用模板內容嵌套可以實現全站風格同一布局

lua

local template = require "resty.template"

一、

local layout   = template.new "layout.html"layout.title   = "Testing lua-resty-template"layout.view    = template.compile "view.html" { message = "Hello, World!" }layout:render()

二、

template.render("layout.html", {title = "Testing lua-resty-template",msg = "type=2",view  = template.compile "view.html" { message = "Hello, World!" }})

三、

此方式重名變量值會被覆蓋

local view     = template.new("view.html", "layout.html")view.title     = "Testing lua-resty-template"view.msg = "type=3"view.message   = "Hello, World!"view:render()

四、

可以區分一下

local layout   = template.new "layout.html"layout.title   = "Testing lua-resty-template"layout.msg = "type=4"local view     = template.new("view.html", layout)view.message   = "Hello, World!"view:render()
layout.html
<!DOCTYPE html><html><head>?    <title>{{title}}</title></head><h1>layout</h1><body>?    {*view*}</body></html>
view.html·

msg:{{message}}

多級嵌套

lua

local view     = template.new("view.html", "layout.html")view.title     = "Testing lua-resty-template"view.message   = "Hello, World!"view:render()view.html{% layout="section.html" %}

msg:{{message}}

section.html

? {view} - sss

layout.html

? {?{title}}

layout {{msg}}

? {view}

Redis緩存+mysql+模板輸出

lua

  cjson = require "cjson"
sql="select * from t_emp"local redis = require "resty.redis"local red = redis:new()red:set_timeouts(1000, 1000, 1000) -- 1 seclocal ok, err = red:connect("127.0.0.1", 6379)if not ok thenngx.say("failed to connect: ", err)returnendlocal res, err = red:get(sql)if not res thenngx.say("failed to get sql: ", err)returnendif res == ngx.null thenngx.say("sql"..sql.." not found.")--mysql查詢
local mysql = require "resty.mysql"local db, err = mysql:new()if not db thenngx.say("failed to instantiate mysql: ", err)returnenddb:set_timeout(1000) -- 1 seclocal ok, err, errcode, sqlstate = db:connect{host = "192.168.44.211",port = 3306,database = "zhangmen",user = "root",password = "111111",charset = "utf8",max_packet_size = 1024 * 1024,}ngx.say("connected to mysql.<br>")res, err, errcode, sqlstate =db:query(sql)if not res thenngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".")returnend--ngx.say("result: ", cjson.encode(res))ok, err = red:set(sql, cjson.encode(res))if not ok thenngx.say("failed to set sql: ", err)returnendngx.say("set result: ", ok)returnendlocal template = require("resty.template")
template.caching(false)
local context = {title = "測試",name = "lucy",description = "<script>alert(1);</script>",age = 40,hobby = {"電影", "音樂", "閱讀"},score = {語文 = 90, 數學 = 80, 英語 = 70},score2 = {{name = "語文", score = 90},{name = "數學", score = 80},{name = "英語", score = 70},},zhangmen=cjson.decode(res)}template.render("view.html", context)

模板


{(header.html)}  <body>  {# 不轉義變量輸出 #}  姓名:{* string.upper(name) *}<br/>  {# 轉義變量輸出 #}  年齡: {* age + 10 *}<br/>  {# 循環輸出 #}  愛好:  {% for i, v in ipairs(hobby) do %}  {% if v == '電影' then  %} - xxoo{%else%}  - {* v *} 
{% end %}  {% end %}<br/>  成績:  {% local i = 1; %}  {% for k, v in pairs(score) do %}  {% if i > 1 then %},{% end %}  {* k *} = {* v *}  {% i = i + 1 %}  {% end %}<br/>  成績2:  {% for i = 1, #score2 do local t = score2[i] %}  {% if i > 1 then %},{% end %}  {* t.name *} = {* t.score *}  {% end %}<br/>  {# 中間內容不解析 #}  {-raw-}{(file)}{-raw-}  掌門:
{* zhangmen *}{% for i = 1, #zhangmen do local z = zhangmen[i] %}  {* z.deptId *},{* z.age *},{* z.name *},{* z.empno *},<br>{% end %}<br/>  {(footer.html)}  

Lua 開源項目

WAF

https://github.com/unixhot/waf

https://github.com/loveshell/ngx_lua_waf

l 防止 SQL 注入,本地包含,部分溢出,fuzzing 測試,XSS/SSRF 等 Web 攻擊

l 防止 Apache Bench 之類壓力測試工具的攻擊

l 屏蔽常見的掃描黑客工具,掃描器

l 屏蔽圖片附件類目錄執行權限、防止 webshell 上傳

l 支持 IP 白名單和黑名單功能,直接將黑名單的 IP 訪問拒絕

l 支持 URL 白名單,將不需要過濾的 URL 進行定義

l 支持 User-Agent 的過濾、支持 CC 攻擊防護、限制單個 URL 指定時間的訪問次數

l 支持支持 Cookie 過濾,URL 與 URL 參數過濾

l 支持日志記錄,將所有拒絕的操作,記錄到日志中去

Kong 基于Openresty的流量網關

https://konghq.com/

https://github.com/kong/kong

Kong 基于 OpenResty,是一個云原生、快速、可擴展、分布式的微服務抽象層(Microservice Abstraction Layer),也叫 API 網關(API Gateway),在 Service Mesh 里也叫 API 中間件(API Middleware)。

Kong 開源于 2015 年,核心價值在于高性能和擴展性。從全球 5000 強的組織統計數據來看,Kong 是現在依然在維護的,在生產環境使用最廣泛的 API 網關。

Kong 宣稱自己是世界上最流行的開源微服務 API 網關(The World’s Most Popular Open Source Microservice API Gateway)。

核心優勢:

l 可擴展:可以方便的通過添加節點水平擴展,這意味著可以在很低的延遲下支持很大的系統負載。

l 模塊化:可以通過添加新的插件來擴展 Kong 的能力,這些插件可以通過 RESTful Admin API 來安裝和配置。

l 在任何基礎架構上運行:Kong 可以在任何地方都能運行,比如在云或混合環境中部署 Kong,單個或全球的數據中心。

APISIX

ABTestingGateway

https://github.com/CNSRE/ABTestingGateway

ABTestingGateway 是一個可以動態設置分流策略的網關,關注與灰度發布相關領域,基于 Nginx 和 ngx-lua 開發,使用 Redis 作為分流策略數據庫,可以實現動態調度功能。

ABTestingGateway 是新浪微博內部的動態路由系統 dygateway 的一部分,目前已經開源。在以往的基于 Nginx 實現的灰度系統中,分流邏輯往往通過 rewrite 階段的 if 和 rewrite 指令等實現,優點是性能較高,缺點是功能受限、容易出錯,以及轉發規則固定,只能靜態分流。ABTestingGateway 則采用 ngx-lua,通過啟用 lua-shared-dict 和 lua-resty-lock 作為系統緩存和緩存鎖,系統獲得了較為接近原生 Nginx 轉發的性能。

l 支持多種分流方式,目前包括 iprange、uidrange、uid 尾數和指定uid分流

l 支持多級分流,動態設置分流策略,即時生效,無需重啟

l 可擴展性,提供了開發框架,開發者可以靈活添加新的分流方式,實現二次開發

l 高性能,壓測數據接近原生 Nginx 轉發

l 灰度系統配置寫在 Nginx 配置文件中,方便管理員配置

l 適用于多種場景:灰度發布、AB 測試和負載均衡等

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/161403.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/161403.shtml
英文地址,請注明出處:http://en.pswp.cn/news/161403.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何使用ArcGIS Pro進行坐標轉換

不同來源的數據坐標系可能是不同的&#xff0c;為了統一使用這些數據就需要進行坐標轉換&#xff0c;ArcGIS Pro作為專業的GIS軟件&#xff0c;坐標轉換功能肯定也是包含的&#xff0c;這里為大家介紹一下ArcGIS Pro如何進行坐標轉換&#xff0c;希望能對你有所幫助。 數據來源…

【Exception】npm ERR! code UNABLE_TO_GET_ISSUER_CERT_LOCALLY

Talk is cheap, show me the code. 環境 | Environment kversionOSwindows 11nodev18.14.2npm9.5.0 報錯日志 | Error log >npm create vitelatest Need to install the following packages:create-vite5.0.0 Ok to proceed? (y) y npm ERR! code UNABLE_TO_GET_ISSUER_…

2023亞太杯數學建模B題思路+模型+代碼+論文

2023亞太地區數學建模A題思路&#xff1a;開賽后第一時間更新&#xff0c;獲取見文末 名片 2023亞太地區數學建模B題思路&#xff1a;開賽后第一時間更新&#xff0c;獲取見文末 名片 2023亞太地區數學建模C題思路&#xff1a;開賽后第一時間更新&#xff0c;獲取見文末 名片…

idea修改行號顏色

前言 i當idea用了深色主題后&#xff0c;發現行號根本看不清&#xff0c;或者很模糊 例如下面這樣 修改行號顏色 在IntelliJ IDEA中&#xff0c;你可以根據自己的喜好和需求定制行號的顏色。下面是修改行號顏色的步驟&#xff1a; 打開 IntelliJ IDEA。 轉到 “File”&…

ChatGPT規模化服務的經驗與教訓

2022年11月30日&#xff0c;OpenAI發布ChatGPT&#xff0c;以很多人未曾預料的速度迅速走紅。與此同時&#xff0c;由于短時間內用戶量的暴漲&#xff0c;導致服務器過載&#xff0c;迫使OpenAI停止新用戶的注冊。 ChatGPT發布這一年&#xff0c;同樣的情景發生了好幾次。在最近…

opencv-圖像金字塔

圖像金字塔是一種圖像處理技術&#xff0c;它通過不斷降低圖像的分辨率&#xff0c;形成一系列圖像。金字塔分為兩種類型&#xff1a;高斯金字塔和拉普拉斯金字塔。 高斯金字塔&#xff08;Gaussian Pyramid&#xff09;&#xff1a; 高斯金字塔是通過使用高斯濾波和降采樣&a…

專業遠程控制如何塑造安全體系?向日葵“全流程安全閉環”解析

安全是遠程控制的重中之重&#xff0c;作為國民級遠程控制品牌&#xff0c;向日葵遠程控制就極為注重安全遠控服務的塑造。近期向日葵發布了以安全和核心的新版“向日葵15”以及同步發布《貝銳向日葵遠控安全標準白皮書》&#xff08;下簡稱《白皮書》&#xff09;&#xff0c;…

使用微信小程序openMapApp接口,報錯問題解決openMapApp:fail invaild coord

使用微信小程序的 openMapApp 接口時遇到了坐標無效的錯誤 (openMapApp:fail invalid coord)。這個錯誤通常是由于提供的地理坐標不符合預期的格式或范圍而引起的&#xff1a; 坐標格式&#xff1a; 確保提供的坐標符合正確的格式。常見的格式是 "緯度,經度"&#xf…

【11月比賽合集】5場可報名的「創新應用」、「數據分析」和「程序設計」大獎賽,任君挑選!

CompHub[1] 實時聚合多平臺的數據類(Kaggle、天池…)和OJ類(Leetcode、牛客…&#xff09;比賽。本賬號會推送最新的比賽消息&#xff0c;歡迎關注&#xff01; 以下信息僅供參考&#xff0c;以比賽官網為準 目錄 創新應用賽&#xff08;1場比賽&#xff09;數據分析賽&#…

逸學java【初級菜鳥篇】9.3 Stream流

hi&#xff0c;我是逸塵&#xff0c;一起學java吧 得益于Lambda所帶來的函數式編程&#xff0c;引入了一個全新的Stream流概念&#xff08;就是都基本使用lambda的形式&#xff09;。 流處理 我們首先理解什么是流處理&#xff0c;它類似于sql語句&#xff0c;可以執行非常復…

【開源】基于Vue和SpringBoot的智能教學資源庫系統

項目編號&#xff1a; S 050 &#xff0c;文末獲取源碼。 \color{red}{項目編號&#xff1a;S050&#xff0c;文末獲取源碼。} 項目編號&#xff1a;S050&#xff0c;文末獲取源碼。 目錄 一、摘要1.1 項目介紹1.2 項目錄屏 二、功能模塊2.1 數據中心模塊2.2 課程檔案模塊2.3 課…

原理Redis-SkipList

SkipList ZipList和QuickList的共同特點是節省內存。在遍歷元素時&#xff0c;只能從頭到尾或從尾到頭&#xff0c;所以在查找頭尾元素性能還是不錯的&#xff0c;但是中間元素查詢的性能就會差。 **SkipList&#xff08;跳表&#xff09;**首先是鏈表&#xff0c;但與傳統鏈表…

【算法】鏈表-20231123

這里寫目錄標題 一、19. 刪除鏈表的倒數第 N 個結點二、21. 合并兩個有序鏈表三、24. 兩兩交換鏈表中的節點 一、19. 刪除鏈表的倒數第 N 個結點 提示 中等 給你一個鏈表&#xff0c;刪除鏈表的倒數第 n 個結點&#xff0c;并且返回鏈表的頭結點。 輸入&#xff1a;head [1,…

第十二章 : Spring Boot 日志框架詳解

第十二章 : Spring Boot 日志框架詳解 前言 本章知識重點:介紹了日志誕生背景,4種日志框架:Logback、Log4j、Log4j2和Slf4j的優劣勢分析,以及重點介紹了log4j2的應用示例以及配置,以及日志框架應用中遇到常見的問題以及如何處理。 背景 Java日志框架的發展歷程可以追…

在PyCharm中正確設置Python項目

大家好&#xff0c;在Mac和Linux都支持Python&#xff0c;但許多開發者發現正確設置Python項目很困難。本文匯總了多平臺中運行Python的方法&#xff0c;提高編程的效率&#xff0c;如下所示&#xff1a; 使用命令行運行Python。 在PyCharm&#xff08;免費社區版&#xff09;…

【技巧】PDF文件如何編輯?

日常辦公中我們經常會用到PDF文件&#xff0c;PDF具備很好的兼容性、穩定性及安全性&#xff0c;但卻不容易編輯&#xff0c;那PDF要如何編輯呢&#xff1f; 如果打開PDF文件就只是只讀的性質&#xff0c;說明文件是在線打開&#xff0c;或者通過PDF閱讀器打開的&#xff0c;這…

Navmesh 尋路

用cocos2dx引擎簡單實現了一下navmesh的多邊形劃分&#xff0c;然后基于劃分多邊形的a*尋路。以及路徑拐點優化算法 用cocos主要是方便使用一些渲染接口和定時器。重點是實現的原理。 首先畫了一個帶有孔洞的多邊形 //多邊形的頂點數據Vec2(100, 100),Vec2(300, 200),Vec2(50…

高防服務器的工作原理

在當今互聯網時代&#xff0c;網絡安全問題日益突出&#xff0c;各種網絡攻擊層出不窮。為了保護企業的網絡安全&#xff0c;高防服務器應運而生。那么&#xff0c;你是否了解高防服務器的工作原理呢&#xff1f;下面就讓我們一起來探索一下。 高防服務器是一種能夠有效抵御各種…

語音識別入門——常用軟件及python運用

工具以及使用到的庫 ffmpegsoxaudacitypydubscipylibrosapyAudioAnalysisplotly 本文分為兩個部分&#xff1a; P1&#xff1a;如何使用ffmpeg和sox處理音頻文件 P2&#xff1a;如何編程處理音頻文件并執行基本處理 P1 處理語音數據——命令行方式 格式轉換 ffmpeg -i video…

shell 腳本循環語句

目錄 循環 echo 命令 for 循環次數 for 第二種格式 命令舉例 while 腳本舉例 雙重循環及跳出循環 腳本舉例 更改文件和目錄的后綴名的腳本 畫三角形的腳本 乘法口訣表的腳本 面試例題 補充命令 let 命令 循環 —— 一定要有跳出循環的條件 已知循環的次數 未知…