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 小时前
GIT使用SSH 多账户配置
运维·git·ssh
程序猿小三4 小时前
Linux下基于关键词文件搜索
linux·运维·服务器
虚拟指尖5 小时前
Ubuntu编译安装COLMAP【实测编译成功】
linux·运维·ubuntu
椎4956 小时前
苍穹外卖前端nginx错误之一解决
运维·前端·nginx
刘某的Cloud6 小时前
parted磁盘管理
linux·运维·系统·parted
极验6 小时前
iPhone17实体卡槽消失?eSIM 普及下的安全挑战与应对
大数据·运维·安全
爱倒腾的老唐6 小时前
24、Linux 路由管理
linux·运维·网络
yannan201903136 小时前
Docker容器
运维·docker·容器
_清浅6 小时前
计算机网络【第六章-应用层】
运维·服务器·计算机网络
正在努力的小河7 小时前
Linux 自带的 LED 灯驱动实验
linux·运维·服务器