美文网首页
从零手写实现 nginx-30-upstream+proxy_p

从零手写实现 nginx-30-upstream+proxy_p

作者: 老马啸西风2020 | 来源:发表于2024-07-15 23:37 被阅读0次

    前言

    大家好,我是老马。很高兴遇到你。

    我们为 java 开发者实现了 java 版本的 nginx

    https://github.com/houbb/nginx4j

    如果你想知道 servlet 如何处理的,可以参考我的另一个项目:

    手写从零实现简易版 tomcat minicat

    手写 nginx 系列

    如果你对 nginx 原理感兴趣,可以阅读:

    从零手写实现 nginx-01-为什么不能有 java 版本的 nginx?

    从零手写实现 nginx-02-nginx 的核心能力

    从零手写实现 nginx-03-nginx 基于 Netty 实现

    从零手写实现 nginx-04-基于 netty http 出入参优化处理

    从零手写实现 nginx-05-MIME类型(Multipurpose Internet Mail Extensions,多用途互联网邮件扩展类型)

    从零手写实现 nginx-06-文件夹自动索引

    从零手写实现 nginx-07-大文件下载

    从零手写实现 nginx-08-范围查询

    从零手写实现 nginx-09-文件压缩

    从零手写实现 nginx-10-sendfile 零拷贝

    从零手写实现 nginx-11-file+range 合并

    从零手写实现 nginx-12-keep-alive 连接复用

    从零手写实现 nginx-13-nginx.conf 配置文件介绍

    从零手写实现 nginx-14-nginx.conf 和 hocon 格式有关系吗?

    从零手写实现 nginx-15-nginx.conf 如何通过 java 解析处理?

    从零手写实现 nginx-16-nginx 支持配置多个 server

    从零手写实现 nginx-17-nginx 默认配置优化

    从零手写实现 nginx-18-nginx 请求头+响应头操作

    从零手写实现 nginx-19-nginx cors

    从零手写实现 nginx-20-nginx 占位符 placeholder

    从零手写实现 nginx-21-nginx modules 模块信息概览

    从零手写实现 nginx-22-nginx modules 分模块加载优化

    从零手写实现 nginx-23-nginx cookie 的操作处理

    从零手写实现 nginx-24-nginx IF 指令

    从零手写实现 nginx-25-nginx map 指令

    从零手写实现 nginx-26-nginx rewrite 指令

    从零手写实现 nginx-27-nginx return 指令

    从零手写实现 nginx-28-nginx error_pages 指令

    从零手写实现 nginx-29-nginx try_files 指令

    从零手写实现 nginx-30-nginx proxy_pass upstream 指令

    从零手写实现 nginx-31-nginx load-balance 负载均衡

    nginx 的 upstream+proxy_pass 指令有什么用?

    Nginx 的 upstreamproxy_pass 指令主要用于反向代理和负载均衡。

    我们可以用一个简单的例子来说明它们的作用。

    场景

    假设你有一个网站,用户访问的是 www.example.com,但你的服务器有三台机器来处理请求,这三台机器的 IP 分别是 192.168.0.1192.168.0.2192.168.0.3

    你希望 Nginx 能够根据负载情况,将用户的请求均匀地分发到这三台机器上。

    配置文件示例

    以下是一个 Nginx 配置文件的简化示例,展示了如何使用 upstreamproxy_pass 指令:

    http {
        upstream my_backend {
            server 192.168.0.1;
            server 192.168.0.2;
            server 192.168.0.3;
        }
    
        server {
            listen 80;
            server_name www.example.com;
    
            location / {
                proxy_pass http://my_backend;
            }
        }
    }
    

    解读配置

    1. 定义后端服务器组(upstream)

      upstream my_backend {
          server 192.168.0.1;
          server 192.168.0.2;
          server 192.168.0.3;
      }
      

      在这个部分,我们定义了一个名为 my_backend 的服务器组,包含三台服务器。这就是 upstream 指令的作用:定义一组可以负载均衡的后端服务器。

    2. 配置代理请求(proxy_pass)

      server {
          listen 80;
          server_name www.example.com;
      
          location / {
              proxy_pass http://my_backend;
          }
      }
      

      在这个部分,我们配置了一个监听在 80 端口的服务器,并指定当用户访问 www.example.com 时,Nginx 会将请求代理到 my_backend 定义的服务器组中。proxy_pass http://my_backend; 这条指令告诉 Nginx,将匹配到的请求(这里是所有路径 /)转发到 my_backend

    简单总结

    • upstream:用来定义一组后端服务器,这些服务器会被 Nginx 用于负载均衡。
    • proxy_pass:用来指定请求的目标地址,可以是单个服务器,也可以是一个 upstream 组。

    通过这样的配置,Nginx 就可以将用户的请求分发到不同的后端服务器上,实现负载均衡。

    这种配置方式灵活且易于管理,让你可以根据需要轻松扩展或修改服务器组的配置。

    nginx 为什么选择 upstream+proxy_pass 这两个名字?

    Nginx 选择 upstreamproxy_pass 这两个名字,是为了形象地描述它们在请求处理过程中的角色。

    让我们用一个通俗易懂的方式来解释它们的名字选择。

    Upstream

    “Upstream” 的意思是“上游”。想象你正在组织一个农产品的分销网络。

    • 上游:就是农民和农场,他们是最初生产产品的人。
    • 下游:就是超市或市场,他们是最终销售产品给消费者的地方。

    在 Nginx 的世界里,“上游”服务器就是那些实际处理和响应请求的后端服务器。

    所以,“upstream”这个名字表示这些服务器在请求处理链条中是“上游”的部分,它们是数据和响应的源头。

    Proxy Pass

    “Proxy” 就是代理。想象你是一个物流公司,负责把农产品从农场运到市场。

    • 代理(proxy):你作为中介,负责把产品从农场转运到市场。
    • 传递(pass):你把农产品从农场运到市场。

    在 Nginx 中,“proxy_pass”指令就是告诉 Nginx 要把请求传递给谁,就像你告诉物流公司要把货物送到哪个市场一样。

    “proxy_pass”表示“代理传递”的意思,它明确了 Nginx 需要将客户端的请求传递到哪个上游服务器。

    为什么是 proxy_pass http://my_backend; 而不干脆是 proxy_pass my_backend;?

    Nginx 的 proxy_pass 需要指定协议和地址,以确保配置的明确性和灵活性。下面是对这个问题的详细解释。

    为什么需要指定协议?

    1. 明确协议类型http://my_backend 指明了请求应该使用 HTTP 协议进行代理。如果省略协议,如 proxy_pass my_backend;,Nginx 就不知道应该使用 HTTP 还是 HTTPS,或者其他可能的协议。

    2. 一致性和兼容性:Nginx 的 proxy_pass 支持多种协议,比如 HTTP、HTTPS、FastCGI 等。通过显式指定协议,配置文件的可读性和可维护性更好,避免混淆和潜在的配置错误。

    如何判断是 upstream 组还是具体 URL?

    Nginx 通过以下规则来判断 proxy_pass 是指向 upstream 组还是具体的 URL:

    1. 如果 proxy_pass 后面包含协议(如 http://https://),且不在 upstream 组中Nginx 会将其视为具体的 URL。

    2. 否则以 http:// or https:// 开头,则认为是 URL

    配置示例

    假设有以下配置:

    http {
        upstream my_backend {
            server 192.168.0.1;
            server 192.168.0.2;
        }
    
        server {
            listen 80;
            server_name www.example.com;
    
            location / {
                proxy_pass http://my_backend;
            }
    
            location /specific {
                proxy_pass http://192.168.0.3;
            }
    
            location /another {
                proxy_pass my_backend;  # 这种写法会出错
            }
        }
    }
    

    解释

    • proxy_pass http://my_backend;:明确地告诉 Nginx 使用 HTTP 协议,将请求代理到名为 my_backendupstream 组。
    • proxy_pass http://192.168.0.3;:明确地告诉 Nginx 使用 HTTP 协议,将请求代理到具体的 URL 192.168.0.3
    • proxy_pass my_backend;:这种写法是错误的,因为省略了协议,Nginx 不知道如何处理这个指令。

    灵活性和明确性

    这种设计保证了配置的灵活性和明确性:

    • 通过显式指定协议,避免了误解和错误配置。
    • 保证了配置文件的清晰度,便于管理和维护。

    如何判断是否为 upstream 组?

    问题

    如果有一个 proxy_pass xxx,又改如何判断到底是到 upstream 组,还是具体的 url 呢?

    实现

    具体的 java 实现:

    import java.util.Set;
    
    public class ProxyResolver {
    
        public static void main(String[] args) {
            Set<String> upstream = Set.of("upstream1", "upstream2", "upstream3");
            String proxy = "http://upstream1"; // 示例输入
    
            String result = resolveProxy(proxy, upstream);
            System.out.println("Resolved to: " + result);
        }
    
        public static String resolveProxy(String proxy, Set<String> upstream) {
            // 先检查是否是具体的 URL
            if (proxy.startsWith("http://") || proxy.startsWith("https://")) {
                // 如果是具体的 URL,判断是否在 upstream 中
                String potentialUpstream = proxy.substring(proxy.indexOf("//") + 2);
                if (upstream.contains(potentialUpstream)) {
                    return "Upstream: " + potentialUpstream;
                } else {
                    return "URL: " + proxy;
                }
            } else if (upstream.contains(proxy)) {
                // 如果不是具体的 URL,直接判断是否在 upstream 中
                return "Upstream: " + proxy;
            } else {
                // 默认情况,无法判断的情况
                return "Unknown: " + proxy;
            }
        }
    }
    

    小结

    Nginx 的 upstreamproxy_pass 指令用于实现反向代理和负载均衡,简单总结如下:

    1. Upstream:定义了一组后端服务器,每个服务器可以处理请求。这些服务器可以是物理服务器的地址或者其他可访问的网络位置。Upstream 为 Nginx 提供了一个管理和分发请求的目标列表。

    2. Proxy Pass:指示 Nginx 将客户端的请求代理到指定的后端服务器组(upstream)或者具体的服务器地址。它告诉 Nginx 如何转发请求,并且可以指定不同的路径和条件来匹配不同的后端处理逻辑。

    通过这两个指令,Nginx 可以实现高效的负载均衡和反向代理,从而提升网站或应用程序的性能、可靠性和扩展性。

    相关文章

      网友评论

          本文标题:从零手写实现 nginx-30-upstream+proxy_p

          本文链接:https://www.haomeiwen.com/subject/yetkhjtx.html