Nginx流量同时转发多后端(流量镜像分发)

 更新时间:2024年10月24日 09:21:56   作者:GEEK JUMP  
在需要同时将请求转发至多个后端服务的场景中,Nginx的mirror模块提供了流量镜像分发的功能,本文就来介绍一下Nginx流量同时转发多后端(流量镜像分发),感兴趣的可以了解一下

一、背景

请注意,我这里标题提到的是一个请求流量被同时转发到2个或者多个后端,而非负载均衡的场景!!!

负载均衡的场景我想就不用赘述了,定义一个upstrem, upstrem定了一组提供相同服务的server地址, 最后通过proxy_pass转发到这个upstrem。  但是,这个是负载均衡的场景,那就意味着,无论你的算法如何,最终只会转发到一个server目标上。

那文章标题提到的流量镜像是什么场景呢?  那就是一次HTTP请求,被同时转发到多个后端。 例如一次请求,既代理到A服务、也代理到B服务,发送了2次或者n次的情况。

为什么有这个流量镜像分发需求呢?  我们有这么一个场景, 由于新版服务和旧版服务的代码变更, 出于某种原因,既想保留旧版、也想保留新版做过渡, 所以想把前端的流量分发2次,这样新旧系统的数据都能保存下来,不影响业务使用。 等过段时间,过渡期好了之后,再把旧版server下掉。

寻找了一些资料,openresty+lua也许能实现,但是我测试不行。 最终发现了Nginx本身就有这个功能模块。 这个就是mirror模块。

二、Nginx mirror模块

mirror模块从Nginx1.13开始就是内置了,所以只要是1.13版本以后就可以直接使用,不需要重新编译nginx。

官方文档地址: Module ngx_http_mirror_module

从配置样例来看,很简单。 首先代理的location / 还是先做主代理站点, proxy_pass到$backend,  之后再加一个mirror  /mirror;  流量镜像分发到/mirror的URL,  再看下面定了了/mirror的location定义, 这里面再次定义proxy_pass 到test_backend, 从而完成流量镜像分发的目的。

三、实际配置测试

1、准备2个flask服务,8081、8082端口

1、flask-8081.py

from flask import Flask, request, jsonify

app = Flask(__name__)


@app.route('/api/data', methods=['GET', 'POST'])
def save_request_data():
    headers = dict(request.headers)
    if request.is_json:
        # 如果请求体是 JSON 格式
        body = request.get_json(silent=True)
    elif request.content_type and 'form' in request.content_type.lower():
        # 如果请求体是表单数据
        body = request.form.to_dict()
    else:
        # 其他类型的请求体(例如纯文本)
        body = request.data.decode('utf-8')
    args = request.args.to_dict()
    data = {
        "headers": headers,
        "body": body,
        "args": args
    }
    with open('request_data.txt', 'a') as file:
        file.write(str(data))
        file.write('\n')
    return jsonify({
        "status": "success",
        "message": "Data has been saved."
    })


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8081, debug=True)

2、flask-8082.py

from flask import Flask, request, jsonify

app = Flask(__name__)


@app.route('/api/data', methods=['GET', 'POST'])
def save_request_data():
    headers = dict(request.headers)
    if request.is_json:
        # 如果请求体是 JSON 格式
        body = request.get_json(silent=True)
    elif request.content_type and 'form' in request.content_type.lower():
        # 如果请求体是表单数据
        body = request.form.to_dict()
    else:
        # 其他类型的请求体(例如纯文本)
        body = request.data.decode('utf-8')
    args = request.args.to_dict()
    data = {
        "headers": headers,
        "body": body,
        "args": args
    }
    with open('request_data.txt', 'a') as file:
        file.write(str(data))
        file.write('\n')
    return jsonify({
        "status": "success",
        "message": "Data has been saved."
    })


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8082, debug=True)

2、修改nginx的配置文件

    location /api/data/ {
       #优先转发172.16.0.3:8081
       proxy_pass http://172.16.0.3:8081/api/data;
       #同时, 流量镜像转发到/mirror/, 172.16.0.3:8082
       mirror /mirror/;
    }
    location /mirror/ {
       internal;
       proxy_pass http://172.16.0.3:8082/api/data;
    }

3、测试结果

CURL请求代理的nginx端口,然后分别查看8081、8082端口是否同时接收到相同的HTTP请求: 

观察8081端口程序日志输出:

观察8082端口程序日志 输出:

由此发现,我们在nginx发生的2次请求, 2个后端8081、8082同时收到了2次请求。 由此证明,我们的流量镜像分发是符合预期的。

 4、最终响应内容是8081还是8082的内容?

结论:   一切都以第一个proxy_pass的站点的实际响应结果为准,不管第二个被mirror流量分发站点的响应情况是否正常

服务运行情况以及响应信息表格如下:

8081 正常8082正常以8081内容为准, 正常, 2台都会转发
8081 正常8082异常以8081内容为准, 正常,  8082不会在后台转发
8081 异常8082正常以8081内容为准, 异常, 8082会在后台转发
8081 异常8082异常以8081内容为准, 异常, 8082不会在后台转发

四、总结

mirror流量镜像分发的场景还是有实际存在意义的,要不然官方也不会把它纳入到内置模块当中。具体的需求情况需要自己判定。

流量转发毕竟是2次转发过程,对于nginx的压力、以及性能应该是会有损耗的,但是具体损耗没测试过, 这个读者朋友可以自行测试。

到此这篇关于Nginx流量同时转发多后端(流量镜像分发)的文章就介绍到这了,更多相关Nginx 流量镜像分发内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

最新评论