Skip to content

Nginx 学习


Nginx 介绍

Nginx是一个高性能的HTTP、反向代理服务器;主要功能:①反向代理 ②实现集群和负载均衡③ 静态资源虚拟化。

什么是代理

正向代理

正向代理可以理解为「客户端」的代理

alt text

反向代理

反向代理可以理解为「服务器」的代理

alt text

Nginx 下载与安装

1.下载源代码手动编译安装(推荐),下载Nginx包,官网下载地址:http://nginx.org/en/download.html

alt text

  • 使用FTP工具将文件上传到虚拟机中
  • 解压Nginx包,并安装
java
tar -zxvf  nginx-1.21.6.tar.gz #解压到当前目录

cd nginx-1.21.6 #进入解压后的文件夹
ls 
#文件夹中的文件:auto     CHANGES.ru  configure  html     man     src CHANGES  conf        contrib    LICENSE  README
  • 安装依赖库
java
#安装C编译器
yum install -y gcc

#安装pcre库PVEL
yum install -y pcre pcre-devel

#安装zlib
yum install -y zlib zlib-devel
  • 编译安装
java
./configure --prefix=/usr/local/nginx # 指定编译选项,--prefix选项指定安装的目录
make # 编译
make install # 安装编译结果
  • 创建软连接(可以直接使用nginx命令,不会出现-bash: nginx: command not found错误)
ln -s /usr/local/nginx/sbin/nginx /usr/local/bin  #实际路径根据自己的情况确定
  • 放开防火墙80端口
java
firewall-cmd --zone=public --add-port=80/tcp --permanent 
firewall-cmd --reload # 开放或者关闭端口需要重启防火墙

Nginx 目录

Nginx一般安装在/usr/local/nginx目录下(安装时--prefix可指定安装目录)

alt text

java
conf #配置文件
-nginx.conf # 主配置文件
-其他配置文件 # 可通过那个include关键字,引入到了nginx.conf生效
	
html #静态页面

logs(默认不是放在这里,可以在配置文件中修改为这里)
-access.log #访问日志(每次访问都会记录)
-error.log #错误日志
-nginx.pid #进程号
	
sbin
-nginx #主进程文件
	
*_temp #运行时,生成临时文件

Nginx进程模型

Master 进程和Work 进程

alt text

Master 进程的作用

  • 启动和关闭服务: master 进程负责启动和关闭 Nginx 服务。它读取并解析配置文件,启动 worker 进程。

  • 管理 Worker 进程: master 进程管理所有的 worker 进程,监控它们的运行状态。它会在 worker 进程异常退出或终止时,自动重启新的 worker 进程,以保持服务的正常运行。

  • 信号处理: master 进程处理外部信号,如重载配置、平滑升级和停止服务等信号。根据接收到的信号,master 进程会执行相应的操作,如重新加载配置文件或优雅地关闭服务。

Work 进程的作用

  • 处理请求: worker 进程负责处理客户端的请求。每个 worker 进程是等价的,能够独立处理请求。通过这种方式,Nginx 能够并行处理大量的客户端请求,提高了服务的并发处理能力。

  • 异步非阻塞模式: worker 进程采用异步非阻塞的方式处理请求,通过事件驱动机制,高效地 处理大量的并发连接。

  • 共享内存: worker 进程之间通过共享内存机制共享部分数据,例如缓存、限速等数据,保证各 worker 进程之间的数据一致性。

Work 进程数量设置

要设置 Nginx 的 worker 进程数量,可以在 Nginx 的配置文件中进行配置。通常情况下,worker 进程的数量由服务器的 CPU 核心数来确定,以充分利用服务器的硬件资源,提高性能。

打开 Nginx 的配置文件,通常位于 /etc/nginx/nginx.conf。在配置文件中找到 worker_processes 指令。这是设置 worker 进程数量的地方。

java
worker_processes auto;
//#auto 表示让 Nginx 自动根据系统的 CPU 核心数来确定 worker 进程数量。

Master关系和Work和协作

alt text

  • 独立处理请求: 每个 worker 进程独立于其他 worker 进程,处理自己的客户端请求。这种设计避免了进程间的锁争用,提高了性能和处理效率。
  • Master-Worker 模式: master 进程负责全局管理和控制,worker 进程负责实际的请求处理工作。这种模式有效地分离了控制和工作职责,使得 Nginx 既能高效地处理请求,又能灵活地管理进程。
  • 负载均衡: master 进程在启动时创建多个 worker 进程,操作系统负责将客户端请求均匀分配给这些 worker 进程,实现了负载均衡。

Worker的抢占机制

多个worker进程争抢一个锁,获取锁的进程进行响应

alt text

Worker事件处理机制

传统HTTP服务器是同步处理,当多个客户端请求时,如果Client1的请求被阻塞,Master会fork新的worker进程处理

但是Nginx采用的是异步非阻塞方式,如果Client1的请求被阻塞,worker会取处理下一个请求,不会阻塞当前worker进程。所以Nginx的一个worker进程可以并发处理大量请求

alt text

Nginx 配置

Nginx 配置文件详情

java
//# master进程会启动worker进程,该选项设置在系统中显示启动该进程的用户名(一般不改动,默认nobody)
//# user nobody


//# 启动的worker进程数
worker_processes  1; 



//# 错误日志放置的路径 notice、info是错误日志的级别,比如:info就是日志级别大于info才生成日志
//# 默认地址为/var/log/nginx/error.log ,可通过nginx -V返回的--eror-log-path字段获取实际值
//#error_log  logs/error.log;
//#error_log  logs/error.log  notice;
//#error_log  logs/error.log  info;


//# pid文件存放路径,默认:/var/run/nginx/nginx.pid,可通过nginx -V返回的--pid-path字段获取实际值
//#pid        logs/nginx.pid;



//# 配置事件处理方式、worker最大连接数
events {
		use epoll; //# 使用epoll事件处理机制(默认值)
    worker_connections  1024; //# 每个worker进程处理的最大连接数
}



//# http模块配置
http {
    include       mime.types; //#include是引入关键字,这里引入了mime.types这个配置文件的内容(同在conf目录下,mime.types是用来定义,请求返回的content-type)
    default_type  application/octet-stream; //#mime.types未定义的,使用默认格式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"';

		//# 访问日志地址,默认:/var/log/nginx/access.log,可通过nginx -V返回的--http-log-path字段获取实际值
    //#access_log  logs/access.log  main;

		//# 详见下文
    sendfile        on; 
    // #tcp_nopush     on;
    
    keepalive_timeout  65; //# TCP链接超时时间,单位秒
	
		//# 压缩相关,详见下文
		gzip on; //# 开启压缩,压缩后发送给客户端
		
		//# 详见下文 
    server {
        //xxx
    }
}

sendfile配置

打开sendfile,用户请求的数据不用再加载到nginx的内存中,而是直接发送

高负载的场景下,使用 sendfile 功能可以降低 CPU 和内存的占用,提升服务器性能

java
http{
	sendfile:on # off
}

//# 或者指定某个server开启
server {
    location / {
        sendfile off;
        ...
    }
}

gzip配置

java
http{
	  gzip on; //# 开启压缩,压缩后发送给客户端
		gzip_min_length 1;//# 设置最小压缩下限。1就是小于1字节的文件不压缩
		gzip_comp_level 3 //# 压缩级别0-9,值越大文件就压缩的越小,相应的会损耗更多性能
		gzip_type text/plain application/javascript image/* # 指定哪些 MIME 类型,开启压缩(不写默认全部),可以使用通配符 image/* 就是所有图片。具体哪些类型可以看conf/mime.types文件
}

nginx 中的 gzip 压缩分为动态压缩静态压缩

  • 动态压缩:

    • 服务器给客户端返回响应时,消耗自身的资源进行实时压缩,保证客户端拿到 gzip 格式的文件
    • gzip on开启的就是动态压缩,gzip_comp_level设置的级别高,可能会造成CPU占用过高
  • 静态压缩:

    • 直接将预先压缩过的 .gz 文件返回给客户端,不再实时压缩文件,如果找不到 .gz 文件,会使用对应的原始文件
    • 该功能需要模块: ngx_http_gzip_static_module(默认不会被构建);通过下面命令查看,当前安装的是否包含该模块
    java
    nginx -V

    alt text

    如果不包含,需要重新编译Nginx

    java
    ./configure --with-http_gzip_static_module //# 指定编译配置,这个参数安装模块 ngx_http_gzip_static_module
     make # 编译
     make install # 安装

    包含该模块,则可以启用下面配置

    java
    http{
    gzip_static  on;
    gzip_proxied expired no-cache no-store private auth;
    }

server配置

虚拟主机配置(可以启用多个),多个server字段,会根据请求的域名+端口从前向后匹配

用户访问bk.zuoyou.space(注意:需要解析到当前Nginx的这台机器),请求会匹配到下面的配置中

java
http {
 		//#....其他属性
 		server {//# server 规则可以有多个,每个对应 一个域名+端口的组合
 				
 		    access_log logs/bk.zuoyou.space-access.log //# 访问日志字段也可以设置在Server中,命中该Server的请求日志都在这里
 		    
        listen       80; //# 端口
        server_name  bk.zuoyou.space; //# 域名,可以有多个值,用空格分隔。支持通配符,例如:*.example.org 。
        //# server_name  bk.zuoyou.space default_server; #default_server表示当Nginx接收的请求没有能匹配上的server,就命中设置了default_server

				//# 域名+端口 匹配上了,就会走这里的映射(映射规则可以有多个)
				
				//# 【类型1】
				//# / 路径的映射配置,这个映射比较特殊,无论用户请求的是什么路径,只要没有被更具体的 location 块匹配到,就都会命中 location /。即 bk.zuoyou.space:80 会命中这里
        location / {  
        	//# root指定当前这个server的根路径为 html/pro(注意root字段如果是相对路径很特殊:是相对于nginx的安装目录,即这里html与conf目录同级)
            root   html/pro;  
            //# 当命中/路径时的首页,nginx会在root目录下找index.html、index.htm并返回
            index  index.html index.htm; 
        }
        
        //# 【类型2】
        //# 一般请求用root
        //# bk.zuoyou.space:80/abc、bk.zuoyou.space:80/abc/1 都会命中这条规则
        //# 例如: bk.zuoyou.space:80/abc/ 会返回首页,即index字段(root目录下查找index指定的文件)
        //# 例如: bk.zuoyou.space:80/abc/1/index.html 返回的是机器上的home/abc/1/index.html (实际机器路径为:root+【请求命中路径-全部路径结尾,例如:/abc/1/index.html】)
        location /abc {  
            root   /home; 
            index  index.html index.htm; 
        }
        
        //# 【类型3】
        //# 一般静态文件会用alias
        //# /images/ 路径的映射配置,即 bk.zuoyou.space:80/images/xx会命中这里,实际请求路径为:/var/www/images/xxx。对比root字段,如果下面的alias替换为root字段,则实际请求路径为/var/www/images/images/xxx,这显然是错误的
        //# 注意尾斜杠,bk.zuoyou.space:80/images不能命中
        location /images/ {
            //# alias设置请求的别名,用于替换文件系统路径。
        	alias /var/www/images/;
    		}


			//# 反向代理,proxy_pass将请求代理到指定的后端服务器。
    		location /api/ {
        	proxy_pass http://backend_server;
    		}

		//# 服务器错误码为500 502 503 504,转到"域名/50x.html"
        error_page   500 502 503 504  /50x.html;
        
        //# 【类型4】使用=代表精确匹配
        //#  只有访问 bk.zuoyou.space.ltd:80/50x.html才能命中这条规则
        location = /50x.html {
            root   html;
        }
        
        //# 【类型5】使用正则匹配
        //#  '~',例子:location ~ { xxx },表示匹配所有路径
        //#  '~(空格)正则' ,例子:~ \.(png|gif) 匹配png、gif的路径
        //#  '~*(空格)正则',不区分大小写,例子:~ *\.(png|gif) 匹配png、PNG、gif、GIF等路径
        //#  '^~(空格)字符串' ,表示匹配以字符串开头的请求,例子:^~ /test,即 bk.zuoyou.space:80/test/xxx可以命中
        location ~*\.(png|gif) { //#因为忽略大小写,所以 bk.zuoyou.space:80/a.PNG 可以命中规则
            root   /home;
        }
		}

 		server {
        //# xxxx
		}
}

server配置在单独文件维护

新建配置文件zuoyou.conf,在server配置放在一个独立文件维护

java
server {
    //# xxxx
}

server {
    //# xxxx
}

在http中引入server配置

java
http{
	include zuoyou.conf
}

通过server_name,实现不同域名,映射到同一静态页面

  • 可以写多个,用空格分开
  • 使用通配符(*)
  • 使用正则表达式
java
http{ 		
 	server {
        listen       80;
        server_name  *.zuoyou.space.ltd  ~^[0-9]+\.zuoyou\.ltd$; # "\."是转译"."

        location / { 
            root   html/test; 
            index  index.html index.htm;
        } 
	}
}

日志切割

Nginx长期运行会产生大量日志,如果不开启日志切割,出现问题很难通过日志排查,需要借助crontabs设置定时任务

shell
默认的日志路径
# 错误日志地址:--eror-log-path=/var/log/nginx/error.log
# 请求日志地址:--http-log-path=/var/log/nginx/access.log(每一个请求就记录一个日志)
  • 安装crontabs
java
yum install crontabs
  • crontabs 常用命令
java
service crond start         //启动服务
service crond stop          //关闭服务
service crond restart       //重启服务
service crond reload        //重新载入配置
crontab -e                  // 编辑任务
crontab -l                  // 查看任务列表
service crond status		//查看服务状态
  • 常见的表达式
shell
*/1 * * * *   #每分钟执行一次
59 23 * * *   #每天23:59分执行一次
0 1 * * *     #每天凌晨一点执行一次
* 23,00-07/1 * * * #当天23点,第二天0点到凌晨7点 每隔1分钟执行一次
  • 设置定时任务以及添加任务
shell
crontabs -l # 查看全部定时任务
crontabs -e # 添加定时任务(打开一个文件)
* */1 * * * *  /usr/local/nginx/sbin/cut_log.sh # 每隔1分钟执行一次,其中,

cut_log.sh自己写的切割脚本

shell
#!/bin/bash
LOG_FILE="/var/log/nginx/access.log"  
DATE=$(date +"%Y-%m")  
BACKUP_FILE="/var/log/nginx/access.${DATE}.log"  
  
# 检查当前月份是否已更改,如果已更改,则备份日志文件并创建一个新的日志文件  
if [[ ! -e "$BACKUP_FILE" ]]; then  
    mv "$LOG_FILE" "$BACKUP_FILE"  
    touch "$LOG_FILE"  
    chown nginx:nginx "$LOG_FILE"  # 确保 Nginx 用户有权写入新的日志文件  
    chmod 640 "$LOG_FILE"          # 设置适当的文件权限  
    /etc/init.d/nginx reload       # 重新加载 Nginx 配置,确保日志文件句柄更新  
fi

重启crontabs配置才会生效

java
service cornd restart

反向代理与负载均衡概念

反向代理:这种代理方式叫做,隧道代理。有性能瓶颈,因为所有的数据都经过Nginx,所以Nginx服务器的性能至关重要

alt text

负载均衡:把请求,按照一定算法规则,分配给多台业务服务器(即使其中一个坏了/维护升级,还有其他服务器可以继续提供服务)

alt text

Nginx实现反向代理

负载均衡配置

单台主机代理

使用proxy pass关键字实现负载均衡代理,使用该关键字之后root和index字段就会失效

  • 当请求命中了这条规则,会被转发到目标服务器地址
shell
http{ 		
 		server {
        listen       80;
        server_name  localhost;

        location / { 
        		proxy_pass http://xxx; # 参数是 http://server_name
        }
    }
}
  • 路径 + 参数会被一起转发
shell
假设Nginx机器的地址为:http://www.xxx.com

请求 http://www.xxx.com/test?a=1
会被 proxy_pass地址/test?a=1
  • 重写需求: 例如只有api前缀的才转发到后台服务器,但是接口并不包含api路径
shell
location / { 
	rewrite ^/api/(.*)  $1 
    proxy_pass http://xxx; # 参数是 http://server_name
}

反向代理集群

Nginx实现负载均衡集群,例如将请求代理到192.168.174.133:80和192.168.174.134:80这两个服务

shell
http{
	upstream test_server{ # 参数是server_name
		server 192.168.174.133:80; #如果是80端口,可以省略不写
		server 192.168.174.134:80;
	}
	server {
        listen       80;
        server_name  localhost;

        location / { 
        		proxy_pass http://test_server;
        }
    }
}
  • 设置权重,多个请求,Nginx会根据权重分配
shell
upstream test_server{
		server 192.168.174.133:80 weight=10;
		server 192.168.174.134:80 weight=80;
}
  • 关闭down,server设置成down,如果server只剩下这一台,必须使用down的server
shell
upstream test_server{
		server 192.168.174.133:80 down;
		server 192.168.174.134:80;
}
  • 慢启动(商业版本才用这个配置)
shell
upstream test_server{
		server 192.168.174.133:80 slow_start=60s; # Nginx会在60的时间内,慢慢分配流量从0到正常值
		server 192.168.174.134:80;
}
  • 备用机,backup的这个机器正常是不会被访问到,如果192.168.174.133:80出现故障,无法提供服务,才会自动启用
shell
 upstream test_server{
		server 192.168.174.133:80 ;
		server 192.168.174.134:80 backup;
}
  • 失败重试
shell
upstream test_server{
    # 失败次数超过max_fail后,Nginx在fail_timeout时间内将不会转发任何请求给这个服务,超过fail_timeout后会在尝试一次。成功则恢复转发,否则仍然fail_timeout时间,再次尝试
	server 192.168.174.133:80 max_fail=2 fail_timeout=10s;
	server 192.168.174.134:80 ;
}

负载均衡算法

IP_hash

对用户的ip进行计算:Hash(IP)%upstream_node_count,返回要使用机器的索引;每一个用户会固定分配到一台机器,防止在A机器上创建Session的用户,后续被分配到其他机器,导致Session失效。

开启ip_hash后,如果想要移除一台server,必须使用down配置。如果直接删除,会导致upstream_node_count变化,使得所有用户访问的访问的机器发生变化

缺点

  • 增加服务节点会导致upstream_node_count变化,进而导致所有用户访问的机器变化
  • 某个用户短时间发起大量请求,会打到一台固定的机器,导致这台机器性能大幅下降,而其他机器可能还是空闲
shell
upstream test_server{
    # 开启ip_hash,
    ip_hash;
		server 192.168.174.133:80;
		server 192.168.174.134:80;
}

一致性哈希算法(Consistent Hashing)

一致性哈希算法解决了使用ip_hash,在新增、删除服务节点时,所有用户访问节点发生变化的问题;hash函数返回值的范围是[0,2^32-1] 节点IP计算哈希,用户IP计算哈希,用户分配的节点是其右侧的第一个节点,增加、减少服务节点,只会影响一小部分用户,而不是全部. alt text

shell
# consistent不加使用的就是普通hash算法
upstream test_server{
    hash $request_uri consistent; # 基于请求的uri做一致性哈希
    hash $remote_addr consistent; # 基于请求的全部32为IPv4地址做一致性哈希(ip_hash只对IPv4的前24位做哈希)
    hash $cookie_xxx consistent; # 基于请求cookie中的xxx字段做一致性哈希
		server 192.168.174.133:80;
		server 192.168.174.134:80;
}

最少连接数(least_conn)

尽可能将请求转发到当前连接数最少的后端服务器

shell
upstream test_server{
    least_conn;
	    server 192.168.174.133:80;
	    server 192.168.174.134:80;
}

下面例子,开启least_conn,Nginx会优先转发到Tomcat3 alt text

动静分离

动静分离是将网站静态资源(HTML,JavaScript,CSS,img等文件)与后台应用分开部署,之所以要进行动静分离,其一为了提高前端的响应速度,其二就是为了将动态请求和静态请求进行分别部署,后端各个服务各司其职,提高处理性能。适用于中小型项目(将静态资源放在nginx服务器上)

静 :前端项目(静态资源) 动:接口服务

shell
location ~ .*\.(gif|jpg|pdf|jpeg|png)$ {
    expires 8h;
    root /nginx/data/image;
}
location~ .*\.(html)$ {
    root /nginx/data/html;
}
#拦截后台请求,正则匹配 api 路径
location ~* ^/(lb){
    # 配置代理地址
    proxy_pass http://myserver;
}

URL重写

rewrite需要写正则比较复杂,所以目前使用return的比较多

return

把Http重定向为Https

shell
server {
        listen       80;
        server_name  bk.zuoyou.space;
			location / { 
				return 302 https://bk.zuoyou.space$request_uri 
				# 302是状态码
				# $request_uri是路径和参数 ,例如:/xxx/xx?xx=xx
        }
}

rewrite

shell
rewrite是URL重写的关键指令,根据regex(正则表达式)部分内容,重定向到replacement,结尾是flag标记。
rewrite    <regex>   <replacement>  [flag];
关键字				正则				替代内容     flagt标记
正则:per1森容正则表达式语句进行规则匹配
替代内容:将正则匹配的内容替换成replacement
flag标记说明:
last  #本条规则匹配完成后,继续向下匹配新的1ocation URI规则
break #本条规则匹配完成即终止,不再匹配后面的任何规则
redirect #返回302临重定向,游览器地址会显示跳转后的URL地址
permanent #返回301永久重定向,测览器地址栏会显示跳转后的URL地址

把Http重定向为Https

shell
server {
        listen       80;
        server_name  bk.zuoyou.space;
			location / { 
				rewrite ^/(.*) https://bk.zuoyou.space/$1 redirect;
				# 匹配到uri的/后的内容,并放到$1中,执行重定向
        		proxy_pass http://xxx;
        }
}

配置Https证书

  • 申请域名证书

alt text

  • ssl证书下载 alt text

下载后,解压压缩包,可以看到两个文件,一个是 xxx.key(私钥)和xxx.pem(证书)

注:我自己解压的文件中一共有4个文件,分别是 xxx.key(私钥)和xxx.pem(证书),还有xxx.crt和xxx.csr

  • 安装ssl模块 ssl配置依赖于ngx_http_ssl_module模块
shell
#查看是否有 --with-http_ssl_module 表示
./nginx -V
#没有需要重新编译安装
./configure --prefix=/usr/local/nginx --with-http_ssl_module # 指定编译选项
make # 编译
make install # 安装编译结果--with-http_ssl_module
  • 修改配置文件

将两个文件上传到Nginx目录中,记得放置的位置。建议直接放在nginx.conf配置文件所在的目录(/etc/nginx/conf),所以写的都是相对路径

注意:ssl配置可以放在http模块(所有server都开启ssl)、server模块(当前server开启)

shell
http{
	server {
		listen 443 ssl;
		server_name zuolong.space;
	
	  # 这里是证书路径
		ssl_certificate  xxx.pem; 
		# 这里是私钥路径
		ssl_certificate_key  xxx.key  
		
		location / {
		   # xxx
		}
	}
	
	# 如果通过http访问,重定向到https
	server {
		listen 80;
		server_name zuolong.space;
		
		location / {
		   return https://www.zuolong.space$request_uri #$request_uri是路径+参数,例如:/xxx/xx?xx=xx
		}
	}
}

Last updated: