文章目錄
- 概念
- 安裝
- 本文環境
- 反向代理
- 問題
- 解決方案:
- 負載均衡:
- 基本配置
- 失敗重試配置
- 限流
- limit_req_zone
- 實戰
- 其他引數
- 錯誤碼 limit_req_status
- limit_conn_zone
- 實戰
- 黑名單:
- 配置錯誤頁面
概念
Nginx 是一個高性能的HTTP和反向代理web服務器,Nginx是一款輕量級的Web 服務器/反向代理服務器及電子郵件(IMAP/POP3)代理服務器,其特點是占有記憶體少,并發能力強
安裝
因為要測驗nginx的各種特性,在Linux上不太方便,所以這里直接下載Windows的nginx , 下載地址

下載解壓即安裝完成
本文環境
我們先創建一個Spring Boot專案,然后創建如下的Controller:
@RestController
public class MyController {
@GetMapping("/hello")
public String hello(){
return "hello";
}
}
我們去nginx配置反向代理:
location / {
proxy_pass http://127.0.0.1:8080;
}
然后啟動nginx
反向代理
問題
我們使用nginx一般都是用于反向代理我們的服務器,那么此時會出現一些問題,
我們將 controller修改如下:
@GetMapping("/hello")
public String hello(HttpServletRequest request){
String remoteHost = request.getRemoteHost();
int remotePort = request.getRemotePort();
return "Host的值是"+remoteHost+",port的值是"+remotePort;
}
然后分別測驗直接訪問和通過nginx訪問該介面:
使用瀏覽器直接訪問:

通過nginx訪問:

可以發現,HOST和IP的值均不同,我們這里知道瀏覽器就是當前訪問的客戶端,客戶端身處65235發送請求,經過nginx轉發到服務器,服務器得到的是經過nginx修改過后的值,
總結:
域名、協議、埠都是Nginx訪問Web應用時的域名、協議、埠,而非客戶端瀏覽器地址欄上的真實域名、協議、埠,
解決方案:
由于Nginx是代理服務器,所有客戶端請求都從Nginx轉發到Tomcat,如果Nginx不把客戶端真實IP、域名、協議、埠告訴Tomcat,那Tomcat應用永遠不會知道這些資訊,所以Nginx需要配置HTTP Header來將這些資訊告訴被代理的Tomcat,而Tomcat則需要從header中獲取
將nginx組態檔修改如下:
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
}
然后我們就可以從
request.getRemoteHost()中獲取到客戶端的實際IP地址了
負載均衡:
基本配置
負載均衡的東西比較簡單,這里就放幾個配置:
http {
upstream real_server {
server localhost:8080;
server localhost:8081;
}
server {
listen 80;
location / {
proxy_pass http://real_server;
}
}
}
不過負載均衡的演算法可以配置不同的,像上面這樣就是輪詢,先8080服務器,再8081,再8080一直回圈
帶權重則像下面這樣:
server localhost:8080 weight=1;
server localhost:8081 weight=2;
權重越高被分配的客戶端越多
ip_hash: 每個請求按照訪問IP(客戶端IP)的hash結果分配,這樣每個訪客固定一個服務器,可以解決服務器集群的session問題,配置如下
upstream real_server {
server localhost:8080;
server localhost:8081;
ip_hash;
}
Fair公平,根據后端服務器的回應時間來分配請求,越快的服務器得到的請求越多
upstream real_server {
server localhost:8080;
server localhost:8081;
fair;
}
失敗重試配置
配置:
upstream real_server {
server localhost:8080 max_fails=2 fail_timeout=60s;
server localhost:8081 max_fails=2 fail_timeout=60s;
}
是在fail_timeout時間內失敗了max_fails次請求后,則認為該上游服務器不可用,然后將該服務地址洗掉,fail_timeout時間后會再次將該服務器加入存活串列,進行重試,
限流
在開發高并發系統時有三把利器用來保護系統:快取、降級和限流,
限流可以認為服務降級的一種,限流就是限制系統的輸入和輸出流量已達到保護系統的目的,一般來說系統的吞吐量是可以被測算的,為了保證系統的穩定運行,一旦達到的需要限制的閾值,就需要限制流量并采取一些措施以完成限制流量的目的,比如:延遲處理,拒絕處理,或者部分拒絕處理等等,
nginx給我們提供了限流的配置:
limit_req_zone
limit_req_zone 限制單位時間內的請求數,采用的是 “漏桶演算法”
需要配置的資訊如下:
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
- 第一個引數:$binary_remote_addr 表示通過remote_addr 標識(即IP地址)來做限制,是限制同一客戶端ip地址,
binary_表示保存客戶端IP地址的二進制形式, - 第二個引數:zone=one:10m表示生成一個大小為10M,名字為one的記憶體區域,用來存儲訪問的頻次資訊,
- 第三個引數:rate=1r/s表示允許相同標識(即相同IP)的客戶端的訪問頻次,這里限制的是每秒1次,還可以有比如30r/m(每分鐘三十次)的,
還要配置limit_req:
limit_req zone=one burst=5 nodelay;
- 第一個引數:zone=one 設定使用哪個配置區域來做限制,與上面limit_req_zone 里的name對應,
- 第二個引數:burst=5,burst爆發的意思,這個配置的意思是設定一個大小為5的
緩沖區,當有大量請求(爆發)過來時,超過了訪問頻次限制的請求可以先放到這個緩沖區內 - 第三個引數:nodelay,如果設定,超過訪問頻次而且緩沖區也滿了的時候就會直接回傳503,如果沒有設定,則所有請求會等待排隊,不會有503報錯,
burst的作用是讓多余的請求可以先放到佇列里,慢慢處理,如果不加nodelay引數,佇列里的請求不會立即處理,而是按照rate設定的速度,以毫秒級精確的速度慢慢處理
實戰
接下來我們就配置一下我們的nginx:
http {
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
limit_req zone=one burst=1 nodelay;
}
}
}
我們配置的burst是1,是為了讓限流的效果更明顯,當請求數超出緩沖區的時候,就會進行限流,
當我們瘋狂發送請求到介面時:

發現nginx直接回傳報錯了,所以限流成功了
其他引數
錯誤碼 limit_req_status
這里回傳的是503報錯,我們可以自定義回傳的錯誤碼:
http {
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
limit_req_status 555;
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
limit_req zone=one burst=1 nodelay;
}
}
}
這樣回傳的就會是555錯誤碼:

limit_conn_zone
這個用來限制單個IP的請求數,并非所有的連接都被計數,只有在服務器處理了請求并且已經讀取了整個請求頭時,連接才被計數,
配置如下:
limit_conn_zone $binary_remote_addr zone=addr:10m;
- 第一個引數也是用IP地址進行標識
- 第二個引數也是用于記錄計數的記憶體區域
和limit_conn配置,
limit_conn addr 1;
- 第二個引數是指 允許多少連接數,這里是 1
實戰
因為要讓連接保持著,所以我們修改一下 controller:
@RestController
public class MyController {
@GetMapping("/hello")
public String hello(HttpServletRequest request) {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "ok";
}
}
當我們第一個請求正在服務器處理的時候,我們用另一個瀏覽器發送請求,發現會報錯:

黑名單:
配置使用 deny ip,如果是白名單則是 allow ip,如果我們要將所有IP錄入黑名單或者白名單,不是使用 *,而是 all ,即 allow all和 deny all
如果規則之間有沖突,會以最前面匹配的規則為準,
現在我們修改controller如下:
@RestController
public class MyController {
@GetMapping("/hello")
public String hello(HttpServletRequest request) {
return "ok";
}
}
此時我們配置黑名單:
location / {
proxy_pass http://127.0.0.1:8080;
deny 127.0.0.1
}
此時我們再去訪問這個介面時:

會得到403錯誤,也就是被禁止訪問
配置錯誤頁面
可以看到上面的403報錯是沒有html頁面的,如果我們要自定義我們的403頁面,可以仿照nginx已經寫好的配置:

如上,這是nginx的默認配置,是出現500這些錯誤的時候,就去訪問 html 檔案夾下的50x.html 檔案,
所以我們先去html檔案夾下創建我們的403.html:

403.html的內容:
<html>
<body>
<h1>這是自定義的403頁面</h1></body>
</html>
然后在組態檔中寫入:
error_page 403 /403.html;
location = /403.html {
root html;
}
此時自定義錯誤頁面的效果已經出來了:

但是出現了亂碼,這是編碼問題,增加 <meta charset="UTF-8" />即可
`
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/317736.html
標籤:其他
上一篇:controller(Deployment)概述和應用場景
下一篇:016 CentOS7新增硬碟
