Nginx使用入门及实例演示

一、3W问题

1.1 Nginx是什么

Nginx本质上是一个轻量级的Web服务器、高性能的HTTP服务器、反向代理服务器、同时也是一个IMAP/POP3/SMTP代理服务器。

1.2 为什么要使用Nginx

单体服务场景下可以不使用Nginx,但是集群环境为了让请求合理地分布到各个服务器上,就需要再加上一层负载层,负责对请求进行转发。(没有什么架构难题是加一层不能解决的,如果有,那就再加一层)

nginx工作示意图

Nginx最大的特点就是性能稳定,支持的并发数高,占用的资源较少。除此之外,还包含如下的功能:

  • HTTP服务器;可以作为一个HTTP服务器提供HTTP请求访问;
  • 反向代理;代理用户要访问的目标服务器;
  • 负载均衡;高并发场景下分流请求到多个服务器上;支持的方式有轮询、权重、ip哈希、url哈希、按响应时间等;
  • 动静分离;应用于前后端分离的项目,将前端和后端的请求分离;

1.3 如何使用

Windows系统下,下载zip包,解压缩后,进入文件夹,使用命令行执行命令./nginx.exe即可,此时访问localhost:80即可。

Linux系统下安装可以参考《Linux安装nginx》,本文强烈推荐使用Docker拉取nginx镜像进行学习。

docker pull nginx:latest
docer run -d -p 80:80 --name my-nginx nginx:latest

如下内容都是基于Linux下的nginx进行讲解。

如下是一些常用的命令来实现nginx的不同功能:

  • ./nginx./nginx -c path/nginx.conf,使用默认配置文件或者使用指定的配置文件启动;

  • nginx -tnginx -t -c path/nginx.conf,验证配置是否正确;

    # nginx -t
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful
    
  • nginx -v,查看版本号;

    # nginx -v
    nginx version: nginx/1.21.4
    
  • nginx -s quit,正常停止和关闭nginx;

  • nginx -s stop,强制快速停止和关闭nginx;

  • nginx -s reload,配置文件修改后重新装载启动;

    # nginx -s reload
    2021/11/23 01:14:51 [notice] 40#40: signal process started
    

二、文件夹分类

2.1 Windows

  • conf,用来存放配置的目录,其中最主要的就是nginx.conf文件;
  • html,默认的静态资源目录,index.html作为默认主页;
  • logs,默认的日志目录,主要有access.log和error.log;

2.2 Docker

  • /usr/share/nginx/html/,默认的静态资源目录,index.html作为默认主页;
  • /etc/nginx/,用来存放配置的目录,其中最主要的就是nginx.conf文件;
  • /var/log/nginx/,用来存放日志的目录,主要有access.log和error.log;

三、默认配置文件

配置文件内容可以有很多很复杂,但是总体来说分为如下几个模块:

  • 全局块,配置影响nginx全局的指令。一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等;
  • 事件块,配置影响nginx服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等;
  • HTTP块,可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type定义,日志自定义,是否使用sendfile传输文件,连接超时时间,单连接请求数等;
    • server块,配置虚拟主机的相关参数,一个http中可以有多个server。
      • location块,配置请求的路由,以及各种页面的处理情况。

如下是初始状态下nginx.conf配置的内容:

########### 每个指令必须有分号结束。#################

#####全局块#####
#配置的用户或者组
user  nginx;
#配置工作进程数量
worker_processes  auto;

#配置错误日志目录和级别
error_log  /var/log/nginx/error.log notice;
#主进程ID记录
pid        /var/run/nginx.pid;
#####全局块#####

#####事件块#####
events {
    #配置最大连接数
    worker_connections  1024;
}
#####事件块#####

#####HTTP块#####
http {
    #包含其它配置文件,mime.types是MIME类型映射表,根据返回文件的后缀名在该配置中找到MIME类型放到"Content_Type"中
    include       /etc/nginx/mime.types;
    #默认文件类型,即MIME类型的"Content_Type"
    default_type  application/octet-stream;
    #配置日志格式
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    #配置访问日志目录和级别
    access_log  /var/log/nginx/access.log  main;
    #配置允许传输文件
    sendfile        on;
    #配置启用最小传输限制功能
    #tcp_nopush     on;
    #配置连接超时时间
    keepalive_timeout  65;
    #配置开启gizp压缩
    #gzip  on;
    #引入其它配置项内容
    include /etc/nginx/conf.d/*.conf;
}
#####HTTP块#####

引入的mime.types默认配置如下:

types {
    text/html                                        html htm shtml;
    text/css                                         css;
    text/xml                                         xml;
    image/gif                                        gif;
    image/jpeg                                       jpeg jpg;
    ......

    text/mathml                                      mml;
    text/plain                                       txt;
    ......

    image/avif                                       avif;
    image/png                                        png;
    ......

    application/java-archive                         jar war ear;
    application/json                                 json;
    application/mac-binhex40                         hqx;
    application/msword                               doc;
    application/pdf                                  pdf;
    ......
    application/xhtml+xml                            xhtml;
    application/xspf+xml                             xspf;
    application/zip                                  zip;
    ......
}

引入的conf.d文件夹下面就一个default.conf配置文件,内容如下:

#配置一个虚拟主机
server {
    #配置监听的端口
    listen       80;
    listen  [::]:80;
    #配置当前server的虚拟服务器识别路径,nginx根据请求头中的Host字段来匹配具体的server进行处理
    server_name  localhost;

    #access_log  /var/log/nginx/host.access.log  main;

    #配置匹配的路径
    location / {
        #寻找文件的目录
        root   /usr/share/nginx/html;
        #默认返回的文件资源
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

如此,一个默认的配置就是如此。我们启动nginx之后,访问localhost:80就能访问到index.html的内容了。

四、实例演示——反向代理

目标:使用Docker启动两个nginx服务:service1和service2,代表两个后端服务。然后再启动一个nginx服务作为反向代理服务器,根据请求路径的不同将请求分发给service1和service2。

创建文件夹nginx-demo,里面创建service1文件夹、service2文件夹、my-nginx文件夹;

service1下面文件内容分别如下:

  • index.html,代表service1服务的内容;

    <!DOCTYPE html>
    <head>
        <title>Service1</title>
    </head>
    <body>
        <h1>Message from Service1!</h1>
    </body>
    
  • default.conf,代表service1的nginx-server的配置;

    server {
        listen       80;
        server_name  localhost;
    
        #匹配/service1/路径
        location /service1 {
            # 去/usr/share/nginx/html/下面找文件
            alias /usr/share/nginx/html/;
            # 默认返回文件
            index index.html;
        }
    
        # redirect server error pages to the static page /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
    
  • Dockerfile,镜像打包文件;

    FROM nginx:latest
    COPY ./default.conf /etc/nginx/conf.d/
    COPY ./index.html /usr/share/nginx/html/
    EXPOSE 80
    

此时执行如下打包镜像和运行容器的命令:

docker build -t zhangxun/service1:1.0.1 .
docker run -d -p 8081:80 --name service1 zhangxun/service1:1.0.1

此时访问http://localhost:8081/service1/就能访问到service1的服务内容了。

service2文件夹下的内容和service1基本雷同,此处不再赘述,通过http://localhost:8082/service2/就能访问到service2的服务内容了。

最后,需要配置反向代理服务器,在my-nginx文件夹下创建如下文件内容:

  • default.conf,代表反向代理服务器的nginx-server的配置;

    server {
        listen       80;
        server_name  localhost;
    
        location /service1 {
            # 需要填写宿主机的地址 ,而不是localhost,不然就是my-nginx容器内部
            proxy_pass http://172.21.32.1:8081/service1/;
        }
    
        location /service2 {
            # 需要填写宿主机的地址 ,而不是localhost,不然就是my-nginx容器内部
            proxy_pass http://172.21.32.1:8082/service2/;
        }
    
        # redirect server error pages to the static page /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
    
  • Dockerfile,镜像打包文件;

    FROM nginx:latest
    COPY ./default.conf /etc/nginx/conf.d/
    EXPOSE 80
    

此时执行如下打包镜像和运行容器的命令:

docker build -t zhangxun/my-nginx:1.0.1 .
docker run -d -p 80:80 --name my-nginx zhangxun/my-nginx:1.0.1

此时访问http://localhost:80/service1就能得到service1的内容,访问http://localhost:80/service2就能得到service2的内容;如此反向代理的目标就实现了。大致实现原理图如下。

nginx实现反向代理

注意事项:

  • location有两种写法,location /path是模糊匹配,location = /path是精确匹配;

  • root /path会去 /path/location/目录下面去找文件;alias /path会在/path/下面去找文件;它们的区别就是alias不会带上location的路径;

  • my-nginx配置中代理的服务器地址不能写localhost,否则就还是访问自己容器内部的服务;

  • 反向代理服务器中只配置了一个server,两个location代表两个服务,也可以配置成两个server,如下配置所示,效果是一样的。

    server {
        listen       80;
        server_name  localhost;
    
        location /service1 {
            # 需要填写宿主机的地址 ,而不是localhost,不然就是my-nginx容器内部
            proxy_pass http://172.21.32.1:8081/service1/;
        }
    
        # redirect server error pages to the static page /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
    
    server {
        listen       80;
        server_name  localhost;
    
        location /service2 {
            # 需要填写宿主机的地址 ,而不是localhost,不然就是my-nginx容器内部
            proxy_pass http://172.21.32.1:8082/service2/;
        }
    
        # redirect server error pages to the static page /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
    

五、实例演示——动静分离

动静分离和反向代理的原理及配置几乎都是相同的,只不过静态资源并不算是一个服务,而是存放在nginx上的资源,通过请求路径来区分,究竟是把请求转发给服务,还是从nginx上的某个路径下去寻找资源,由于比较简单,不再重复演示。

六、实例演示——负载均衡

目标:使用Docker启动两个nginx服务:service1和service2,代表两个后端服务。然后再启动一个nginx服务作为负载均衡服务器,将请求分发给service1和service2。

4.1 准备工作

复用第四节的案例,但是service1和service2的default.conf更改如下:

server {
    listen       80;
    server_name  localhost;

    #匹配/路径
    location / {
        # 去/usr/share/nginx/html/下面找文件
        alias /usr/share/nginx/html/;
        # 默认返回文件
        index index.html;
    }

    # redirect server error pages to the static page /50x.html
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

其余内容保持不变,构建镜像并启动容器后测试下访问http://localhost:8081http://localhost:8082能否正常返回内容。

4.2 负载均衡服务搭建

不同于反向代理,需要根据不同的请求路径转发请求到不同的服务;负载均衡则是将相同的请求按照负载规则转发给某个服务集群中的一个服务。我们新建一个nginx.conf配置文件,用来替换etc/nginx/nginx.conf的默认配置文件。

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    #tcp_nopush     on;
    keepalive_timeout  65;
    #gzip  on;

    # my-service用于两个上游服务
    upstream my-service {
        server 172.21.32.1:8081;
        server 172.21.32.1:8082;
    }

    server {
        listen       80;
        server_name  localhost;

        location / {
            proxy_pass http://my-service;
        }

        # redirect server error pages to the static page /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
}

Dockerfile稍作改变:

FROM nginx:latest
COPY ./nginx.conf /etc/nginx/
EXPOSE 80

然后执行构建和启动容器的命令,我们访问localhost:80就会发现,service1和service2被轮询为我们提供服务,如此就实现了负载均衡的实例。

4.3 负载策略

Nginx负载均衡是通过upstream模块来实现的,内置实现了三种负载策略:

  • 轮询(默认策略),根据请求次数,将每个请求均匀地或者按照权重分配到每个服务上;

    upstream my-service {
        server 172.21.32.1:8081 weight=1;
        server 172.21.32.1:8082 weight=2;
    }
    

    weight表示权重,数值越大,被分到请求的次数越多。

  • 最少连接,将请求分配给连接数最少的服务,Nginx会统计哪些服务器的连接数最少;

    upstream my-service {
        least_conn;
        server 172.21.32.1:8081;
        server 172.21.32.1:8082;
    }
    
  • IP Hash,绑定处理请求的服务器;第一次请求时,根据该客户端的IP算出一个HASH值,将请求分配到集群中的某一台服务器上;后面该客户端的所有请求,都将通过HASH算法,找到之前处理这台客户端请求的服务器,然后将请求交给它来处理;

    upstream my-service {
        ip_hash;
        server 172.21.32.1:8081;
        server 172.21.32.1:8082;
    }
    

除了内置的负载策略,还有其它第三方插件提供的负载策略,这些策略需要在nginx服务器上单独安装:

  • Fair,按照后端服务器的响应时间来分配请求,响应时间短的会被分配更多请求;

    upstream my-service {
        fair;
        server 172.21.32.1:8081;
        server 172.21.32.1:8082;
    }
    
  • URL Hash,按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存服务器时比较有效;

    upstream my-service {
        hash $request_uri;
        hash_method crc32;
        server 172.21.32.1:8081;
        server 172.21.32.1:8082;
    }
    

4.4 负载状态

  • weight,默认为1,表示服务的权重,数值越大,被分到的请求越多;
  • max_fails,默认为1,该服务被允许请求失败的次数,一旦超过则会选择其它服务;
  • fail_timeout,默认10秒,服务在请求失败max_fails次数后,在fail_timeout时间内不会再给它分配请求;
  • backup,表示备份服务,所有服务都不可用是才分配请求给备份服务;
  • down,表示下线,该服务不参与负载;
  • max_conns,该服务允许的最大连接数量,超过时将不会再分配请求给它,默认为0表示不限制;
  • 等等其它状态可以参考官网文档;

七、参考文档

Using nginx as HTTP load balancer

Module ngx_http_upstream_module (nginx.org)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容