跳转至

Nginx心法

说明

  • 由原来的《认识Nginx》更名为《Nginx心法》
  • 本文对应的版本:Tengine Tag 3.0.0

Nginx是谁?它是反向代理服务器的代表之一,是高性能和模块化开发的典范,更是我的老师。

认识它,你将免费获得“最佳的代理体验”、“优雅的代码规范”、“极致的性能设计”和“优秀的模块思想”,还有无数个值得品味的编程细节。

现在比较常用的两个Nginx项目分别为:

考虑到 Tengine 相较于 Nginx 增加了不少特性,本文以 Tengine 为分析对象。


进程模型

进程模型
Nginx进程模型图

如上所示,Nginx设计上将程序分为4类,分别为:

  • Master:主进程,是其他3类程序的父进程,负责监控并管理其他进程的生命周期,同时也负责加载配置,其他程序通过继承的方式来获取配置信息。

    • 管理生命周期的方式有两种:
      • 一种是监控后触发,比如:在发现“Worker 1”异常退出后,主动拉起新的“Worker”取代“Worker 1”。
      • 一种是管理员控制,比如:管理员通过命令 nginx -s reload 告诉“Master”去重载配置,此时“Master”会生成新的“Worker”并将旧的“Worker”退出。
  • 管理进程:控制面程序,向上提供北向管理接口,向下负责控制“工作进程”如何处理业务。

    • 控制的传输方式是管道(PIPE),支持的命令需要由“工作进程”提供。
    • 虽然Nginx没有指出,但是我们应当清楚在控制上“管理进程”和“工作进程”是具有差异性的,具体差异体现在:
      1. “工作进程”应当是无状态的;
      2. “工作进程”在遇到“获取数据”类的消息时,应当只提供元数据,不进行任何渲染处理,渲染工作交由“管理进程”来做。
  • 工作进程:数据面程序,负责处理业务数据,以完成业务目标。

  • 独立进程:由Tengine设计,提供一种可以创建独立进程的机制。

    • 这样可以创建一些不受框架约束的独立进程,比如使用不支持异步的 IO库 完成一些 IO 工作,开发一个独立运行的 syslog 程序以对接 syslog 服务器。

这样就很清晰了,“系统管理员”通过“Master”来控制 Nginx 的启动、停止、重载配置、升级等,而“管理员”或“管理程序”则通过“管理进程”来控制业务行为。换言之,“Master”是老板,“管理进程”是管理层领导,而“工作进程”则是基层员工,最后的“独立进程”则有点像外包,具有一定的灵活性,方便扩展。

看到这里,相信会引发一定的争议。因为看的人可能会说:Nginx 没有 “管理进程”,更没有管控数三面分离这一说,有的只是 “Master-Worker” 模型。

其实,如果我们想真正的去看清一件事物,是不能仅留存于过去的状态,或者说停止了向前思考的过程,应当适当的带入未来的思想,否则只会人云亦云,永远寄于事物之下。换言之,Nginx也是经过发展后的产物,它也是有过程和变化的属性,如果不能充分认识其历史和局限并时刻思考其发展趋势,那么就永远也无法掌控它,将永远停留在使用中。

Nginx作为开源项目而到了不同人手上,应当有不同的发展方向,这也是开源项目的魅力所在。比如Tengine项目就是在Nginx的基础上进行二次开发,以满足阿里巴巴的业务需求,同时也将这些需求开源出来,让更多的人受益。

我们将管控数三面分离带入到Nginx中是顺滑的,完全可行的一步:

  1. ”工作进程“中的各个Worker是工作对等体(worker跟worker之间没有区别,一个worker所作的事情,在另一个worker都能完整复刻和承担),仅是根据配置或指令去处理数据。正因这个性质,worker在异常退出后的重启是无需特殊处理的,这也符合了”数据面“定义和要求。

    • 但也具有一定挑战性,包括但不限于:进程间的数据同步和状态共享。
  2. 我们只需要在”工作进程”与“管理进程”中引入IPC,并将控制过程移交给“管理进程“就可以实现”管理进程“控制”工作进程“的效果,也就是”控制面“与”数据面“的分离。

  3. 紧接着,”管理进程“只要提供北向API即可实现管控分离,从而达到管控数三面分离。这时我们便可以轻而易举的实现对Nginx进行实时控制,比如:获取统计信息、调整负载均衡算法等。

    • 这里会遇到一些挑战,包括但不限于:如何持久化动态修改后的配置

系统架构

我第一次接触 Nginx 的时候是在 2018 年的时候,那时候刚毕业工作1年多,有幸被安排到服务器负载均衡组,开启了 Nginx 的使用和开发。

初接触 Nginx 时间是比较晚的,因此对 Nginx 的发展历程并不清楚,但那时候还没有Stream业务,因此 Nginx 的出身应该是 HTTP业务。HTTP业务是 Nginx 的最大特色,功能也最丰富和复杂,因此想要完全掌握也具有一定挑战性。为了更好的认识 Nginx,因此这里重点介绍 Nginx 的基础架构以及HTTp业务。

体系结构

首先从体系结构开始,Nginx可以大致可以分为4个部分,分别为“管理”、“业务”、“组件”和“基础库”,如下所示:

Nginx体系架构
Ngin体系结构图

Nginx中其实并没有区分这些,“组件”和“基础库”也是放到一起的,但是为了更好的理解,于是根据其API所负责的层面及粒度进行分层分类。“基础库”主要是一些算法和数据结构,“组件”则是能令业务更好的复用和开发的抽象,包括网络层面和系统层面。“组件”和“基础库”的实现一般都在src/core目录中。

同样的,Nginx也没有管理的概念,但这些是任何一个程序都应当包括或扩展的一部分,毕竟程序是需要可管理和可控制的,尤其是当出现问题时,需要有一些调试手段。

业务则集中在4部分,其中“HTTP代理”为核心业务,“Proc”则是阿里拓展出来的功能,用于满足一些无法跟Nginx框架对接的特殊业务需求,大大的增强了Nginx的业务扩展能力。

系统架构

Nginx的框架是由上下文限定的,即不同的上下文有不同的框架,框架大致可分为两类,一类是Nginx的外框架,支撑和指导业务框架的开发,提供可靠的事件、内存管理、定时器、配置和日志功能供业务使用,并且定义和限定了模块范围,这样可以保证扩展业务成为可能,且业务之间的能保持隔离;另一类则是业务框架,不同的业务可以定义自己的业务框架。具体如下图所示:

系统架构
系统架构

从上图不难看出,每种业务和Nginx外框架之间都存在一个“核心模块”,该模块是业务框架与Nginx外框架的桥梁,它负责定义业务的配置解析规则、模块以及框架,并将自身的业务注册到Nginx中,可以这么说,有了这一层设计后,既可以保证业务与框架的隔离,也能保证业务的扩展性。

但“核心模块”只负责定义和初始化业务框架,其本身并不驱动业务的运转,因此在每个业务中都会存在一个被称为“第0模块”的业务模块,它会具体化业务的配置和框架,驱动业务“核心模块”定义的各阶段的运行。因此各业务模块实际是运行在“第0模块”之上的。

因此我们可以得出的如下图所示的框架上下文:

框架上下文
框架上下文

“Nginx Context”在Nginx中的表现为ngx_cycle,该全局变量指向当前程序的上下文,每当重新加载配置或更新二进制程序后,都会创建一个新的上下文并由ngx_cycle指向。

因为Nginx是高度模块化的,有种“模块驱动”的感觉,因此它会把上述各业务上下文再细分为“模块上下文”,即每个模块都有自己的上下文,但与“Nginx Context”有点不同,这里的 Context 指的只是“配置上下文”,而“Nginx Context”则包括了“配置上下文”和“程序上下文”。同时为了保证访问的速度,用数组实现该设计:

框架上下文-模块细分
框架上下文-模块细分

如上图所示,我们变更了名字以更符合其定义,即将“xxx框架 Context”改为“xxx配置 Context”。“Nginx Context”的第K位置指向的是“HTTP配置 Context”,M指向“Stream配置 Context”,N指向“Mail配置 Context”,I指向“Proc配置 Context”。最终每个业务的上下文还会有自己的“Context 数组”去存储各自业务模块的配置上下文。而ngx_cycle除此之外还有很多全局的属性存在,因此在程序运行的任何时候,都可以通过它去获取想要的数据。

该上下文是静态的,即在程序从启动进入到就绪状态后就确定了,它主要由配置决定。Nginx对启动过程有明确的定义,分为以下几个过程:

启动过程
启动过程

图中的create_confconf_parseinit_confinit_module是发生在“初始化 Nginx Context期间”,其中把“解析命令行参量”和“解析配置文件”放到了一起,用conf_parse表示,其中前三个过程是完成加载配置的过程。在解释上面流程之前,必须要先清楚一件事,那就是任何类型模块都首先是“Nginx 模块”,然后才有自己的类型,这里就有点“派生”的意思在里面了,因为它必须属于某个基类,而“Nginx 模块”就是基类。

那对于“Nginx模块”来说,一定有如下属性和方法:

类-Nginx模块
类-Nginx模块

如上图所示,“Nginx模块”的属性有如下:

  • name:模块名称,用于标识模块。该属性实际上并没有用。
  • index:模块索引,用于索引“Nginx Context”。比如 A 模块,想获得在“Nginx Context”中自己的位置,就可以通过该索引来获取。
  • ctx_index:业务上下文的索引,如果一个模块是某个业务的子模块,那么就可以通过该索引来获取自己在业务上下文中的位置。
  • type:模块类型,用来标识该模块是核心模块还是属于某个业务的子模块。注意,Nginx外框架只定义了“核心模块”,其他类型由业务定义。
  • ctx:顾名思义,Context 的意思,与type相对应,用于完成模块的初始化工作,换句话说,用于完成模块的上下文初始化。ctx是一组方法,可以抽样的理解为继承“Nginx 模块”的子类后可以重写的方法,它会在“核心模块”定义的“启动/初始化/加载配置”流程中被调用,因此不同的模块类型其ctx是不同的。
  • commandsNginx将配置的指令称为“command”,因此这里引用模块自己定义的命令。后文讲解“配置”时再展开讨论。

它有如下几个方法,对应着“启动过程”中的几个关键点:

  • init_module:初始化模块,这里可以做一些程序级别的一次性加载工作,并且是在配置加载之前。
  • init_process:初始化工作进程,这里可以对每个worker进行一次性初始化工作,比如当配置成功加载后,你需要根据配置来为每个工作进程初始化一些数据结构,那么就可以在这里完成。
  • exit_process:退出工作进程时的清理工作,比如释放一下在init_process中创建的资源。
  • exit_master:退出主进程时的清理工作,比如释放一下在init_module中创建的资源。

那么由“核心模块”以及4大业务组成的各模块,就有如下关系:

类-继承
类-继承

配置框架

对于Nginx开发者来说,配置是一个非常重要概念。在Nginx中,开发者在不了解全貌的情况下完成命令的开发和修改,这完全得益于Nginx优秀的配置框架。

接着看一下Nginx的配置解析流程:

配置解析流程
配置解析流程

为了更好的理解配置解析框架,这里贴出一段配置文件的内容:

worker_processes 1;

user root;

error_log logs/info.log info;

events {
    use epoll;
    worker_connections 1024;
}

http {

    #
    # default config
    #
    include         mime.types;
    default_type    application/octet-stream;

    sendfile on;

    log_format default  '$remote_addr - $remote_user [$time_local] "$request" $status';

    ssl_protocols               TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers   on;

    proxy_set_header    Upgrade $http_upgrade;
    proxy_http_version  1.1;

    types {
        text/html  html;
        image/gif  gif;
        image/jpeg jpg;
    }

    #
    # HTTPS代理
    #
    server {
        listen      443 ssl;
        server_name singapore.proxy.homqyy.cn;

        ssl_certificate         certs/singapore.example.cn/fullchain.pem;
        ssl_certificate_key     certs/singapore.example.cn/privkey.pem;
        ssl_verify_client       on;
        ssl_client_certificate  proxy_ca_chain.pem;
        ssl_verify_depth        3;

        # 启用WEB代理,即代理CONNECT请求
        proxy_connect;
        proxy_connect_allow             all;
        proxy_connect_connect_timeout   120;
        proxy_connect_read_timeout      5m;
        proxy_connect_send_timeout      5m;

        location / {
            proxy_http_version    1.1;
            proxy_set_header      Host $host;
            proxy_pass            $scheme://$host:$server_port;
        }
    }
}
  1. Nginx以“核心模块(NGX_CORE_MODULE)”、“主配置(NGX_MAIN_CONF)”作为起始开始执行配置解析;
  2. 在解析配置时,首先会读取到worker_processes 1;,并将其拆分成worker_processes1两个部分,然后放入到cf->args中;
  3. 由于是;结尾,因此是解析结果为OK,接着会“执行配置handler”

    1. 遍历所有模块的commands,并找到ngx_core_commands中同名配置:

      static ngx_command_t  ngx_core_commands[] = {
          /* 省略... */
          { ngx_string("worker_processes"),               /* name */
            NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, /* type */
            ngx_set_worker_processes,                     /* set */
            0,                                            /* conf_offset */
            0,                                            /* variable offset */
            NULL },                                       /* post_handler */
          /* 省略... */
          ngx_null_command
      }
      
      ngx_module_t  ngx_core_module = {
          NGX_MODULE_V1,
          &ngx_core_module_ctx,                  /* module context */
          ngx_core_commands,                     /* module directives */
          NGX_CORE_MODULE,                       /* module type */
          NULL,                                  /* init master */
          NULL,                                  /* init module */
          NULL,                                  /* init process */
          NULL,                                  /* init thread */
          NULL,                                  /* exit thread */
          NULL,                                  /* exit process */
          NULL,                                  /* exit master */
          NGX_MODULE_V1_PADDING
      };
      
    2. 检查命令位置:有效,支持NGX_MAIN_CONF

    3. 检查参数个数:有效,是NGX_CONF_TAKE1,即只支持一个参数;
    4. 由于是DIRECT_CONF,因此获取槽位的配置;
    5. 执行命令:调用ngx_set_worker_processes
    6. 返回执行结果

接着快进到events {...},讲解一下 Block 的解析过程:

  1. 在解析配置时,会读取到events {;,并只获取到一个字的event,将其放入到cf->args中;
  2. 由于是{结尾,因此是解析结果为block start,接着会“执行配置handler”:

    1. 遍历所有模块的commands,并找到ngx_events_commands中同名配置:

      static ngx_command_t  ngx_events_commands[] = {
      
          { ngx_string("events"),                         /* name */
            NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, /* type */
            ngx_events_block,                             /* set */
            0,                                            /* conf_offset */
            0,                                            /* variable_offset */
            NULL },                                       /* post handler */
      
            ngx_null_command
      };
      
      ngx_module_t  ngx_events_module = {
          NGX_MODULE_V1,
          &ngx_events_module_ctx,                /* module context */
          ngx_events_commands,                   /* module directives */
          NGX_CORE_MODULE,                       /* module type */
          NULL,                                  /* init master */
          NULL,                                  /* init module */
          NULL,                                  /* init process */
          NULL,                                  /* init thread */
          NULL,                                  /* exit thread */
          NULL,                                  /* exit process */
          NULL,                                  /* exit master */
          NGX_MODULE_V1_PADDING
      };
      
    2. 检查命令位置:有效,支持NGX_MAIN_CONF

    3. 检查参数个数:有效,是NGX_CONF_NOARGS,即不支持参数;
    4. 由于是“块配置”(!DIRECT_CONF && MAIN_CONF),因此获取槽位的配置指针;
    5. 执行命令:调用ngx_events_block

      static char *
      ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
      {
          void               ***ctx;
          // 省略...
      
          // 1. 创建 events 的配置上下文
          ctx = ngx_pcalloc(cf->pool, sizeof(void *));
          if (ctx == NULL) {
              return NGX_CONF_ERROR;
          }
      
          *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
          if (*ctx == NULL) {
              return NGX_CONF_ERROR;
          }
      
          *(void **) conf = ctx; // 2. 将上下文更新到 配置指针 中
      
          // 省略...
      
          // 调用配置解析
          pcf = *cf;                          // 3. 保存原有的配置上下文
      
          cf->ctx = ctx;                      // 4. 设置上下文
          cf->module_type = NGX_EVENT_MODULE; // 5. 设置模块类型
          cf->cmd_type = NGX_EVENT_CONF;      // 6. 设置命令位置
      
          rv = ngx_conf_parse(cf, NULL);      // 7. 调用配置解析
      
          *cf = pcf;                          // 8. 回复原有的配置上下文
      }
      
      • 在第7步的时候,会嵌套调用ngx_conf_parse,此时区别在于ctxmodule_typecmd_type都改成 events 自己定义的了,然后继续解析子模块的配置。
    6. 在解析配置时,会读取到use epoll;,并将其拆分成useepoll两个部分,然后放入到cf->args中;

    7. 由于是;结尾,因此解析结果为OK,接着会“执行配置handler”

      1. 遍历所有模块的commands,并找到ngx_core_commnads中同名配置:

        static ngx_command_t  ngx_event_core_commands[] = {
            // 省略...
        
            { ngx_string("use"),                /* name */
              NGX_EVENT_CONF|NGX_CONF_TAKE1,    /* type */
              ngx_event_use,                    /* set */
              0,                                /* conf_offset */
              0,                                /* variable_offset */
              NULL },                           /* post handler */
        
            // 省略...
              ngx_null_command
        };
        
    8. 检查命令位置:有效,支持NGX_EVENT_CONF

    9. 检查参数个数:有效,是NGX_CONF_TAKE1,即只支持一个参数;
    10. 由于是“自定义配置上下文”(!NGX_DIRECT_CONF && !NGX_MAIN_CONF),因此获取指定上下文配置:

      # 因为 conf_offset 为 0,因此就省略了
      
      conf => cf->ctx[module.ctx_index]
      
      • 前文提到,自己定义的上下文用 ctx_index 索引。
      • 关于 ctx_index 很好理解,因为对于自身的配置上下文来说,并非所有的“Nginx 模块”都需要,因此如果直接使用 index 则会浪费空间,因为需要为所有“Nginx 模块”都创建一个槽位。但是,如果使用ctx_index来从0开始索引自身上下文的位置,则只需要给同类型的“Nginx 模块”创建槽位即可。
    11. 执行命令:调用ngx_event_use

  3. 返回执行结果

events 的嵌套配置解析的效果如下:

嵌套配置解析
嵌套配置解析

从上文的“配置解析流程”中可以看到,还有一个特殊的处理分支是“自定义解析函数”,即模块可以定义自己的配置解析函数,也是配置框架的另一个关键功能。从流程中我们知道,想要“自定义解析函数”只需要在Block的处理函数中去定义cf->handler即可,为了更好的展示,我们直接查看http{}中的types指令的处理函数:

static char *
ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_core_loc_conf_t *clcf = conf;

    char        *rv;
    ngx_conf_t   save;

    if (clcf->types == NULL) {
        clcf->types = ngx_array_create(cf->pool, 64, sizeof(ngx_hash_key_t));
        if (clcf->types == NULL) {
            return NGX_CONF_ERROR;
        }
    }

    save = *cf;
    cf->handler = ngx_http_core_type;
    cf->handler_conf = conf;

    rv = ngx_conf_parse(cf, NULL);

    *cf = save;

    return rv;
}

从上面代码可以看出,自定义解析函数只需要设置cf->handlercf->handler_conf即可。

启动流程(0%)

核心模块(0%)

HTTP业务(0%)

Stream业务(5%)

Stream业务,即流代理。其代理应用层以下的业务流量,是应用代理能力的补充。最直接就是传输层的TCP和UDP代理,同时也支持“Proxy protocol”(v1和v2)。如何理解流代理,最简约的表达就是“传输层”代理,不考虑数据的应用特征,只进行数据代理/转发,同时在需要时提供“会话层”和“表示层”的支撑,比如TLS加密,这也是源生Nginx的“Stream代理”中携带的能力,基于此思想,我们也能在需要的时候去扩展其“传输能力”、“会话能力”和“表示能力”,完成自身的定制化开发。其协议栈如下所示:

stream代理
Stream代理协议栈图

从图中可以直接看出“Stream代理”的完整协议栈,正如前面所说,我们可以根据需要去开阔能力,比如在“传输协议”中加入“SOCK5协议”即可丰富Stream的协议代理能力;在UDP之上中加入“KCP(会话协议框架)”即可增强UDP的代理能力,提供重视时效性的传输需求的流代理业务。

Mail业务(0%)


安装和部署

也可可参阅:“Nginx官网-安装Nginx

1. 安装依赖

$ sudo yum install yum-utils

2. 添加nginx源到仓库

创建文件 /etc/yum.repos.d/nginx.repo 并写入以下内容:

[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

3. 安装Nginx

$ sudo yum install nginx

1. 安装依赖

$ sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring

2. 添加nginx源到仓库

  1. 导入Nginx的签名密钥,用于核验包来源是否可靠:

    $ curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
        | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
    
  2. 核实下载的内容是否正确:

    $ gpg --dry-run --quiet --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg
    
    • 这时候应该会输出指纹 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 ,如下所示:

      pub   rsa2048 2011-08-19 [SC] [expires: 2024-06-14]
          573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62
      uid                      nginx signing key <signing-key@nginx.com>
      
    • 如果指纹值不对,说明来源有问题,立刻删除文件

  3. 设置nginx源:

    echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
    http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
        | sudo tee /etc/apt/sources.list.d/nginx.list
    

3. 安装Nginx

$ sudo apt update
$ sudo apt install nginx

基本使用

也可参阅:“Nginx官网-入门指导

默认配置文件路径:conf/nginx.conf(相当于/usr/local/nginx/conf/nginx.conf)

默认日志路径:logs/(相当于/usr/local/nginx/logs/)

默认页面资源路径:html/(相当于/usr/local/nginx/html)

启动、停止和重载配置

通过发送信号来控制Nginx的启动、停止和重载。发送信号的方法是执行以下命令$ nginx -s <signal>,这里的signal可以有以下三种:

  • start:启动Nginx
  • stop:停止Nginx
  • reload:重载配置。在修改Nginx的配置文件后,可以通过此命令来加载新的配置。届时旧的连接会继续在旧的worker上运行(相当于运行旧的配置),但新的连接将会由新worker处理(此worker运行新配置)

配置HTTP服务器

假设有如下需求:搭建一个HTTP服务器,监听默认端口80,资源路径在/var/www,首页在/var/www/index.html

修改Nginx配置文件nginx.conf为以下内容:

http {
    server {
        listen 80; # HTTP服务器,监听端口为80

        localtion / {
            root /var/www/; # 设置资源的默认路径“/var/www/”
        }
    }
}

接着重载Nginx配置即可:$ nginx -s reload

配置HTTPS服务器

假设有如下需求:搭建一个HTTPS服务器,监听默认端口443,资源路径在/var/www,首页在/var/www/index.html

修改Nginx配置文件nginx.conf为以下内容:

http {
    server {
        listen 443 ssl; # HTTPS服务器,监听端口为443

        localtion / {
            root /var/www/; # 设置资源的默认路径“/var/www/”
        }
    }
}

接着重载Nginx配置即可:$ nginx -s reload

!!! warn “注意” HTTPS和HTTP服务器的区别就是在listen指令中,是否有配置ssl参数。

配置HTTP代理

假设有如下需求:搭建一个HTTP代理,监听默认端口80,代理到 192.168.1.1:8080 服务器。

修改Nginx配置文件nginx.conf为以下内容:

http {
    server {
        listen 80; # 监听端口为80

        localtion / {
            proxy_pass http://192.168.1.1:8080; # 代理到 192.168.1.1:8080 服务器
        }
    }
}

评论