nginx的配置粗记

小白nginx的配置随笔(随便记记)

前言

我们都知道nginx有很多用途,比如:负载均衡,反向代理,网关路由,解决跨域等问题。我这次开发项目,用到的一些功能也涉及到了对nginx的配置,且听我后文慢慢讲解~

功能

1.定义网关路由转发规则

我这次开发的项目是基于调用大模型的接口api的web应用,因此我把我的接口大致分为两类,一类是我自主开发的api接口,另一种则是采用sse流式输出的接口(即调用大模型的api接口)

因此,我们需要将这两类api接口分出两个大类(提取公共的前缀)
/api/sse,请看我的nginx配置

nginx 复制代码
    location /api {  
        # 保留请求前缀的/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.更改请求的输出形式

通过调用大模型接口的不断深入,我发现,得需要流式输出 ,不单单是模型需要流式输出,我们后端返回给前端,也是需要流式响应。因此我们这里就引生出来了sse 概念(有时间我自己单独写一篇doge),这里我就放上一篇博客链接吧sse在springboot工程实战应用

后面调用接口发现如果在本地直接调用后端接口(端口是8102),是没有问题的;但是如果是调用部署在服务器的后端接口(端口是nginx监听的88),则没有出现分批数据分发给前端,导致前端无法显示大模型返回来的数据(分批的概念就跟sse分不开的)

我在想:本地和线上之间差别在于哪里呢 ?然后灵机一动,不就是有一个中间件nginx吗?然后我去问了gpt,才知道,对于sse流式输出,nginx有单独的一套规定,默认常规api接口转发是开启了缓冲的(会出现全量返回),而流式输出是需要关闭的 。这里我还是贴一张gpt的原话吧~

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

在使用Server-Sent Events(SSE)时,Nginx充当反向代理将消息从服务器转发到客户端。出现消息合并的问题通常是由于Nginx的缓冲机制或者配置问题导致的。

SSE消息应该是连续发送到前端的,而Nginx可能会缓存这些消息并在满足一定条件后一次性发送到前端。为了解决这个问题,你需要调整Nginx的配置以确保它不会缓存这些消息,而是尽可能实时地转发它们。

以下是一些常见的配置调整方法:

  1. 关闭缓冲 :设置proxy_bufferingoff,这将禁用Nginx的缓冲功能。
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
}
  1. 增加时间限制 :确保proxy_read_timeoutproxy_send_timeout设置的时间足够长,以保持连接稳定。
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
    proxy_read_timeout 3600s;  # 读超时时间
    proxy_send_timeout 3600s;  # 写超时时间
}
  1. 确保后端配置正确:检查你的后端服务器配置,确保它支持并正确实现SSE。

例如,如果你使用的是Node.js,可以使用类似以下的代码来发送SSE消息:

javascript 复制代码
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);

通过这些配置调整,Nginx应该能够正确地转发SSE消息到前端而不会将多段消息合并。如果问题依然存在,请确保所有相关的代理和缓存配置都已经正确调整,并检查是否有其他的网络组件可能影响消息的传输。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

3.溯源发起的请求

有这么需求,需要获取发起请求的ip地址,那么我的实现的思路如下图

关键点:

  • 免费的获取天气api接口(我用的免费api大全网站
  • 获取客户端发起请求的ip地址
    ps:这里忽略代理导致客户端的ip地址的问题
    问题:我当时就是一直获取不到客户端真实的ip地址,获取的内网地址???这让我很疑惑,我都没有开代理呀,只有一个nginx进行接口转发。因此当时我提出一个大胆的猜想,是nginx的问题。
    毕竟nginx是前后端的中间件,相当一堵墙。nginx代替前端把请求发给后端,那么nginx理应是知道前端的发起请求是从哪里来的。可是拿到的是内网地址???说明就是配置有问题!!
    没改配置前:
nginx 复制代码
    location /api {  
        # 保留请求前缀的/v1
        proxy_pass http://localhost:8102/api;  
    }

然后我跟着网上的说法,需要开启对前端发起请求携带的真实的ip地址

更改配置后:

nginx 复制代码
    location /api {  
        # 保留请求前缀的/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地址的工具代码吧(本质就是request头获取ip信息,考虑多种情况)

java 复制代码
    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")) {
                    // 根据网卡取本机配置的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";
            }
            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress = "";
        }
        return ipAddress;
    }

结语

好啦,小白对于nginx的理解又浅浅进了一小步。(看来得好好补课nginx啦~)

ps:我使用宝塔对nginx进行配置的!有一说一,真的好用!降低我对服务器操作难度哈哈~

相关推荐
勤奋的凯尔森同学2 小时前
webmin配置终端显示样式,模仿UbuntuDesktop终端
linux·运维·服务器·ubuntu·webmin
技术小齐6 小时前
网络运维学习笔记 016网工初级(HCIA-Datacom与CCNA-EI)PPP点对点协议和PPPoE以太网上的点对点协议(此处只讲华为)
运维·网络·学习
ITPUB-微风6 小时前
Service Mesh在爱奇艺的落地实践:架构、运维与扩展
运维·架构·service_mesh
落幕6 小时前
C语言-进程
linux·运维·服务器
chenbin5207 小时前
Jenkins 自动构建Job
运维·jenkins
java 凯7 小时前
Jenkins插件管理切换国内源地址
运维·jenkins
AI服务老曹7 小时前
运用先进的智能算法和优化模型,进行科学合理调度的智慧园区开源了
运维·人工智能·安全·开源·音视频
sszdzq8 小时前
Docker
运维·docker·容器
book01218 小时前
MySql数据库运维学习笔记
运维·数据库·mysql
bugtraq20219 小时前
XiaoMi Mi5(gemini) 刷入Ubuntu Touch 16.04——安卓手机刷入Linux
linux·运维·ubuntu