柚子快報(bào)激活碼778899分享:運(yùn)維 nginx的配置粗記
小白nginx的配置隨筆(隨便記記)
前言
我們都知道nginx有很多用途,比如:負(fù)載均衡,反向代理,網(wǎng)關(guān)路由,解決跨域等問題。我這次開發(fā)項(xiàng)目,用到的一些功能也涉及到了對(duì)nginx的配置,且聽我后文慢慢講解~
功能
1.定義網(wǎng)關(guān)路由轉(zhuǎn)發(fā)規(guī)則
我這次開發(fā)的項(xiàng)目是基于調(diào)用大模型的接口api的web應(yīng)用,因此我把我的接口大致分為兩類,一類是我自主開發(fā)的api接口,另一種則是采用sse流式輸出的接口(即調(diào)用大模型的api接口) 因此,我們需要將這兩類api接口分出兩個(gè)大類(提取公共的前綴) /api和/sse,請(qǐng)看我的nginx配置
location /api {
# 保留請(qǐng)求前綴的/v1
proxy_pass http://localhost:8102/api;
# # 其他可能的配置,如proxy_set_header等
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /sse {
proxy_pass http://localhost:8102/sse;
proxy_buffering off; # 禁用緩沖
chunked_transfer_encoding off; # 禁用分塊傳輸編碼
proxy_cache off; # 禁用緩存
proxy_set_header Connection ''; # 保持連接
proxy_http_version 1.1; # 使用HTTP 1.1
}
2.更改請(qǐng)求的輸出形式
通過調(diào)用大模型接口的不斷深入,我發(fā)現(xiàn),得需要流式輸出,不單單是模型需要流式輸出,我們后端返回給前端,也是需要流式響應(yīng)。因此我們這里就引生出來了sse概念(有時(shí)間我自己單獨(dú)寫一篇doge),這里我就放上一篇博客鏈接吧sse在springboot工程實(shí)戰(zhàn)應(yīng)用。 后面調(diào)用接口發(fā)現(xiàn)如果在本地直接調(diào)用后端接口(端口是8102),是沒有問題的;但是如果是調(diào)用部署在服務(wù)器的后端接口(端口是nginx監(jiān)聽的88),則沒有出現(xiàn)分批數(shù)據(jù)分發(fā)給前端,導(dǎo)致前端無法顯示大模型返回來的數(shù)據(jù)(分批的概念就跟sse分不開的) 我在想:本地和線上之間差別在于哪里呢?然后靈機(jī)一動(dòng),不就是有一個(gè)中間件nginx嗎?然后我去問了gpt,才知道,對(duì)于sse流式輸出,nginx有單獨(dú)的一套規(guī)定,默認(rèn)常規(guī)api接口轉(zhuǎn)發(fā)是開啟了緩沖的(會(huì)出現(xiàn)全量返回),而流式輸出是需要關(guān)閉的。這里我還是貼一張gpt的原話吧~ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
在使用Server-Sent Events(SSE)時(shí),Nginx充當(dāng)反向代理將消息從服務(wù)器轉(zhuǎn)發(fā)到客戶端。出現(xiàn)消息合并的問題通常是由于Nginx的緩沖機(jī)制或者配置問題導(dǎo)致的。
SSE消息應(yīng)該是連續(xù)發(fā)送到前端的,而Nginx可能會(huì)緩存這些消息并在滿足一定條件后一次性發(fā)送到前端。為了解決這個(gè)問題,你需要調(diào)整Nginx的配置以確保它不會(huì)緩存這些消息,而是盡可能實(shí)時(shí)地轉(zhuǎn)發(fā)它們。
以下是一些常見的配置調(diào)整方法:
關(guān)閉緩沖:設(shè)置proxy_buffering為off,這將禁用Nginx的緩沖功能。
location /sse {
proxy_pass http://your_backend;
proxy_buffering off; # 禁用緩沖
chunked_transfer_encoding off; # 禁用分塊傳輸編碼
proxy_cache off; # 禁用緩存
proxy_set_header Connection ''; # 保持連接
proxy_http_version 1.1; # 使用HTTP 1.1
}
增加時(shí)間限制:確保proxy_read_timeout和proxy_send_timeout設(shè)置的時(shí)間足夠長,以保持連接穩(wěn)定。
location /sse {
proxy_pass http://your_backend;
proxy_buffering off; # 禁用緩沖
chunked_transfer_encoding off; # 禁用分塊傳輸編碼
proxy_cache off; # 禁用緩存
proxy_set_header Connection ''; # 保持連接
proxy_http_version 1.1; # 使用HTTP 1.1
proxy_read_timeout 3600s; # 讀超時(shí)時(shí)間
proxy_send_timeout 3600s; # 寫超時(shí)時(shí)間
}
確保后端配置正確:檢查你的后端服務(wù)器配置,確保它支持并正確實(shí)現(xiàn)SSE。
例如,如果你使用的是Node.js,可以使用類似以下的代碼來發(fā)送SSE消息:
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
setInterval(() => {
res.write(`data: ${new Date().toISOString()}\n\n`);
}, 1000);
}).listen(8000);
通過這些配置調(diào)整,Nginx應(yīng)該能夠正確地轉(zhuǎn)發(fā)SSE消息到前端而不會(huì)將多段消息合并。如果問題依然存在,請(qǐng)確保所有相關(guān)的代理和緩存配置都已經(jīng)正確調(diào)整,并檢查是否有其他的網(wǎng)絡(luò)組件可能影響消息的傳輸。 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3.溯源發(fā)起的請(qǐng)求
有這么需求,需要獲取發(fā)起請(qǐng)求的ip地址,那么我的實(shí)現(xiàn)的思路如下圖 關(guān)鍵點(diǎn):
免費(fèi)的獲取天氣api接口(我用的免費(fèi)api大全網(wǎng)站)獲取客戶端發(fā)起請(qǐng)求的ip地址 ps:這里忽略代理導(dǎo)致客戶端的ip地址的問題 問題:我當(dāng)時(shí)就是一直獲取不到客戶端真實(shí)的ip地址,獲取的內(nèi)網(wǎng)地址???這讓我很疑惑,我都沒有開代理呀,只有一個(gè)nginx進(jìn)行接口轉(zhuǎn)發(fā)。因此當(dāng)時(shí)我提出一個(gè)大膽的猜想,是nginx的問題。 畢竟nginx是前后端的中間件,相當(dāng)一堵墻。nginx代替前端把請(qǐng)求發(fā)給后端,那么nginx理應(yīng)是知道前端的發(fā)起請(qǐng)求是從哪里來的??墒悄玫降氖莾?nèi)網(wǎng)地址???說明就是配置有問題?。?沒改配置前:
location /api {
# 保留請(qǐng)求前綴的/v1
proxy_pass http://localhost:8102/api;
}
然后我跟著網(wǎng)上的說法,需要開啟對(duì)前端發(fā)起請(qǐng)求攜帶的真實(shí)的ip地址 更改配置后:
location /api {
# 保留請(qǐng)求前綴的/v1
proxy_pass http://localhost:8102/api;
# # 其他可能的配置,如proxy_set_header等
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
這里貼一下獲取ip地址的工具代碼吧(本質(zhì)就是request頭獲取ip信息,考慮多種情況)
public static String getIpAddr(HttpServletRequest request) {
String ipAddress = null;
try {
ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1")) {
// 根據(jù)網(wǎng)卡取本機(jī)配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress = inet.getHostAddress();
}
}
if(request.getLocalAddr().toString().contains("0:0:0:0:0:0:0:1")) {
ipAddress="127.0.0.1";
}
// 對(duì)于通過多個(gè)代理的情況,第一個(gè)IP為客戶端真實(shí)IP,多個(gè)IP按照','分割
if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
} catch (Exception e) {
ipAddress = "";
}
return ipAddress;
}
結(jié)語
好啦,小白對(duì)于nginx的理解又淺淺進(jìn)了一小步。(看來得好好補(bǔ)課nginx啦~) ps:我使用寶塔對(duì)nginx進(jìn)行配置的!有一說一,真的好用!降低我對(duì)服務(wù)器操作難度哈哈~
柚子快報(bào)激活碼778899分享:運(yùn)維 nginx的配置粗記
相關(guān)鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。