Nginx

  • Nginx 基础
  • Nginx 环境搭建
  • Nginx 基础概念
  • Nginx 配置文件结构
  • Nginx 配置文件通用语法
  • nginx.conf 常用配置
  • 虚拟主机配置 - server
  • 日志文件 - log_format、access_log
  • 日志文件切分
  • 资源路径匹配 - location
  • 全局变量
  • 反向代理配置 - proxy_pass
  • 负载均衡配置 - upstream
  • 动静分离
  • 对于静态资源的压缩处理 - gzip


Java 从 0 到架构师目录:【Java从0到架构师】学习记录

Nginx 基础

Nginx 官网:http://nginx.org/

网络请求分为动态请求和静态请求:

  • 动态请求:需要后台程序处理逻辑,比如查询数据库数据
  • 静态请求:请求一些觉得资源(html、css、js、png …)

Nginx:网络静态资源服务器,只能处理静态请求
Tomcat:可以处理动态请求 + 静态请求

Tomcat 默认最多支持 150 并发,Nginx 官网给出数据最多支持 5w 并发
Tengine,淘宝网基于 Nginx 开发的服务器

代理:

  • 正向代理:例如访问 YouTube,国内正常无法直接访问,需要通过一些代理软件,绕过国内的防火墙才能访问,这些软件就是正向代理(代理客户端)
  • 反向代理:YouTube 的后台有很多服务器,当用户访问的时候,YouTube 将用户的请求交给后台 N 台服务器中的一台来做,这就是反向代理(代理服务器)

更多网络相关的知识参考:网络协议从入门到底层原理 - 代理

常见的负载均衡策略

  • 使用硬件进行负载均衡,例如:F5、Array 等负载均衡器
  • 使用软件进行负载均衡,例如:Nginx,Tengine
  • 使用阿里云负载均衡 SLB
  • 使用 Nginx + Keepalived
  • 其他软件负载均衡,如:LVS (Linux Virtual Server)、HAProxy 等技术

Nginx 的优点:

  • Nginx 可以在大多数 Unix Linux OS 上编译运行,并有 Windows 移植版
  • Nginx 是开源免费的
  • Nginx 能够支持高达 50000 个并发连接数的响应

Tomcat 默认在 300 个左右

Nginx 环境搭建

Nginx 官网:http://nginx.org/,下载安装包

# 下载需要的依赖库文件
yum install pcre -y
yum install pcre-devel -y
yum install zlib -y
yum install zlib-devel -y

# 解压安装包
tar -zxvf nginx-1.18.0.tar.gz -C /usr/local/src

# 进行configure配置,查看是否报错
cd /usr/local/src/nginx-1.18.0
./configure --prefix=/usr/local/nginx

# 编译安装
make && make install

注意:如果出现这个错误 ./configure: error: C compiler cc is not found 执行这个命令:yum -y install gcc gcc-c++ autoconf automake make

Nginx 相关操作:

# 启动命令
/usr/local/nginx/sbin/nginx

# 关闭命令
/usr/local/nginx/sbin/nginx -s stop

# 重启命令
/usr/local/nginx/sbin/nginx -s reload

# 重新生成日志文件
/usr/local/nginx/sbin/nginx -s reopen

# 查看端口
netstat -ano | grep 80

访问浏览器:http://192.168.52.128


在 /usr/local/nginx 目录下,可以看到如下 4 个目录:

  • conf 配置文件
  • html 网页文件
  • logs 日志文件
  • sbin 主要二进制程序

Nginx 基础概念

在 Nginx 中会启动 Master 和 Worker 两种进程

Master 进程的主要工作:

  • 接收客户端的请求
  • 向 Worker 进程发送信号
  • 监控 Worker 进程的状态
  • 当 Worker 进程退出以后,会自动启动新的 Worker 进程

Worker 进程的主要工作:处理客户端的连接

在配置文件中可以配置:

# 配置 Worker 进程的数量与 CPU 内核有关系
worker_process 2;

Nginx 配置文件结构

java实现nginx类似的服务转发 java nginx_客户端


main:全局设置,设置的指令将影响其他所有设置

  • events
  • http
  • upstream:上游服务器设置,用于负载均衡,设置一系列的后端服务器
  • server:虚拟主机设置,主要用于指定特定的主机和端口
  • location:URL 匹配特定位置的设置,用于匹配网页的位置

server 继承 main,location 继承 server,upstream 既不会继承其他设置也不会被继承

Nginx 配置文件通用语法

Nginx 通用语法:

  • 配置文件由“指令”(;结尾的语句)和“指令块”({})构成
  • 每条指令以分号;结尾,指令和参数之间用空格符号进行分割
  • 每条指令块以大括号{}将多条指令组织在一起
  • include 语句允许组合多个配置文件以提升可维护性
  • 使用 # 符号添加注释,提高可读性
  • 使用 $ 符号引用变量
  • 部分指令的参数支持正则表达式

nginx.conf 常用配置

虚拟主机配置 - server

# nginx虚拟主机配置
server {
	listen 80; # 监听端口
	server_name localhost; # 一般写域名(1个IP对应多个域名)
	
	# 寻找资源的路径
	location / {
		root html; # 相对路径,nginx目录下的html目录
		index index.html index.htm; # 默认访问的页面
	}
}

日志文件 - log_format、access_log

nginx 访问日志放在 logs/access.log 下,并且使用 main 格式(支持自定义格式)

# 对于main格式配置如下
# 日志文件输出格式这个位置相当于全局设置 
# 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  logs/host.access.log  main;
tail -n 100 -f access.log # 查看日志内容命令

日志文件切分

系统运行起来之后,我们需要对 nginx 日志进行分析,得到响应耗时的 url,请求时间,以及这段时间的请求量和并发量,通过分析找到优化系统的方案;所以需要运维人员对 nginx 的日志进行切割和分析处理,我们会通过定时器定时将日志按 天/小时 进行备份。

实现定时备份日志思路:

  • 第一步:分析如何去实现日志切分,编写 shell 脚本
  • 第二步:定时任务对脚本进行调度

1、日志备份脚本 backuplog.sh,放在 sbin 目录下

#!/bin/sh

BASE_DIR=/usr/local/nginx
BASE_FILE_NAME=access.log

CURRENT_PATH=$BASE_DIR/logs
BAK_PATH=$BASE_DIR/datalogs

CURRENT_FILE=$CURRENT_PATH/$BASE_FILE_NAME
BAK_TIME=`/bin/date -d yesterday +%Y%m%d%H%M`
BAK_FILE=$BAK_PATH/$BAK_TIME-$BASE_FILE_NAME
mv $CURRENT_FILE $BAK_FILE

$BASE_DIR/sbin/nginx -s reopen

2、执行定时任务调度:crontab -e */1 * * * * sh /usr/local/nginx/sbin/backuplog.sh

注意:将脚本从 Windows 拷贝到 Linux,执行脚本的时候可能会出现 $'\r': command not found,需要转换一下格式:yum install dos2unix -ydos2unix 脚本文件

资源路径匹配 - location

语法规则: location [=|~|~*|^~] /uri/ { … }
	= 开头表示精确匹配
	^~ 开头表示uri以某个常规字符串开头,理解为匹配url路径即可
		nginx不对url做编码,因此请求为/static/20%/aa,可以被规则^~ /static/ /aa匹配到(注意是空格)
	~ 开头表示区分大小写的正则匹配
	~*  开头表示不区分大小写的正则匹配
	/ 通用匹配,任何请求都会匹配到

多个location配置的情况下匹配顺序为:
=   --->   ^~   --->   ~/~*   --->   /

匹配案例:

  • 例如有以下匹配规则:
location = / {
   # 规则A
}
location = /login {
   # 规则B
}
location ^~ /static/ {
   # 规则C
}
location ~ \.(gif|jpg|png|js|css)$ {
   # 规则D
}
location ~* \.png$ {
   # 规则E
}
location / {
   # 规则H
}
  • 匹配效果:
http://localhost/ # 规则A
http://localhost/login # 规则B
http://localhost/register # 规则 H
http://localhost/static/a.html # 规则 C
http://localhost/a.gif # 规则 D
http://localhost/b.jpg # 规则 D
http://localhost/static/c.png # 规则 C
http://localhost/a.PNG # 规则 E
http://localhost/a.xhtml # 规则 H

实际应用配置:

# 直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理
# 这里是直接转发给后端应用服务器了,也可以是一个静态首页
# 第一个必选规则
location = / {
    proxy_pass http://192.168.52.128:9999/index;
}
 
# 第二个必选规则是处理静态文件请求,这是nginx作为http服务器的强项
# 有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用
location ^~ /static/ {
    root /webroot/static/;
}
location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {
    root /webroot/res/;
}
 
# 第三个规则就是通用规则,用来转发动态请求到后端应用服务器
# 非静态文件请求就默认是动态请求,自己根据实际把握
# 毕竟目前的一些框架的流行,带.php,.jsp后缀的情况很少了
location / {
    proxy_pass http://tomcat:8080/
}

全局变量

官方配置:http://nginx.org/en/docs

$args						#请求中的参数值
$query_string				#同 $args
$arg_NAME				#GET请求中NAME的值
$is_args						#如果请求中有参数,值为"?",否则为空字符串
$uri							#请求中的当前URI(不带请求参数,参数位于$args),可以不同于浏览器传递的$request_uri的值,它可以通过内部重定向,或者使用index指令进行修改,$uri不包含主机名,如"/foo/bar.html"。
$document_uri			#同 $uri
$document_root			#当前请求的文档根目录或别名
$host						#优先级:HTTP请求行的主机名>"HOST"请求头字段>符合请求的服务器名
$hostname                #主机名
$https						#如果开启了SSL安全模式,值为"on",否则为空字符串。
$binary_remote_addr	#客户端地址的二进制形式,固定长度为4个字节
$body_bytes_sent		#传输给客户端的字节数,响应头不计算在内;这个变量和Apache的mod_log_config模块中的"%B"参数保持兼容
$bytes_sent				#传输给客户端的字节数
$connection				#TCP连接的序列号
$connection_requests	#TCP连接当前的请求数量
$content_length			#"Content-Length" 请求头字段
$content_type            #"Content-Type" 请求头字段
$cookie_name			#cookie名称
$limit_rate					#用于设置响应的速度限制
$msec						#当前的Unix时间戳
$nginx_version			#nginx版本
$pid                     		#工作进程的PID
$pipe                    	#如果请求来自管道通信,值为"p",否则为"."
$proxy_protocol_addr#获取代理访问服务器的客户端地址,如果是直接访问,该值为空字符串
$realpath_root           #当前请求的文档根目录或别名的真实路径,会将所有符号连接转换为真实路径
$remote_addr			#客户端地址
$remote_port      		#客户端端口
$remote_user     		#用于HTTP基础认证服务的用户名
$request                 	#代表客户端的请求地址
$request_body  			#客户端的请求主体:此变量可在location中使用,将请求主体通过proxy_pass,fastcgi_pass,uwsgi_pass和scgi_pass传递给下一级的代理服务器
$request_body_file    	#将客户端请求主体保存在临时文件中。文件处理结束后,此文件需删除。如果需要之一开启此功能,需要设置client_body_in_file_only。如果将次文件传递给后端的代理服务器,需要禁用request body,即设置proxy_pass_request_body off,fastcgi_pass_request_body off,uwsgi_pass_request_body off,or scgi_pass_request_body off
$request_completion 	#如果请求成功,值为"OK",如果请求未完成或者请求不是一个范围请求的最后一部分,则为空
$request_filename    	#当前连接请求的文件路径,由root或alias指令与URI请求生成
$request_length     	#请求的长度 (包括请求的地址,http请求头和请求主体)
$request_method    	#HTTP请求方法,通常为"GET"或"POST"
$request_time         	#处理客户端请求使用的时间; 从读取客户端的第一个字节开始计时
$request_uri          		#这个变量等于包含一些客户端请求参数的原始URI,它无法修改,请查看$uri更改或重写URI,不包含主机名,例如:"/cnphp/test.php?arg=freemouse"
$scheme              		#请求使用的Web协议,"http" 或 "https"
$server_addr             #服务器端地址,需要注意的是:为了避免访问linux系统内核,应将ip地址提前设置在配置文件中
$server_name  			#服务器名
$server_port      		#服务器端口
$server_protocol   		#服务器的HTTP版本,通常为 "HTTP/1.0" 或 "HTTP/1.1"
$status           			#HTTP响应代码
$time_iso8601    		#服务器时间的ISO 8610格式
$time_local       			#服务器时间(LOG Format 格式)
$cookie_NAME     		#客户端请求Header头中的cookie变量,前缀"$cookie_"加上cookie名称的变量,该变量的值即为cookie名称的值
$http_NAME        		#匹配任意请求头字段;变量名中的后半部分NAME可以替换成任意请求头字段,如在配置文件中需要获取http请求头:"Accept-Language",$http_accept_language即可
$http_cookie				#cookie 信息
$http_post
$http_referer				#引用地址
$http_user_agent		#客户端代理信息
$http_x_forwarded_for#相当于网络访问路径。
$sent_http_NAME     	#可以设置任意http响应头字段;变量名中的后半部分NAME可以替换成任意响应头字段,如需要设置响应头Content-length,$sent_http_content_length即可
$sent_http_cache_control
$sent_http_connection
$sent_http_content_type
$sent_http_keep_alive
$sent_http_last_modified
$sent_http_location
$sent_http_transfer_encoding

反向代理配置 - proxy_pass

# 配置反向代理
# proxy_pass url;
proxy_pass http://192.168.52.128:8080;

# 注意: 反向代理之后,获取客户端ip地址为nginx服务器地址
# 这里需要nginx进行forward,设置真实的ip地址:
# 设置客户端真实ip地址 
proxy_set_header x-real-ip $remote_addr;

负载均衡配置 - upstream

# 设定负载均衡的服务器列表在Server模块配置
# upstream myproject {
	# weigth 参数表示权值,权值越高被分配到的几率越大
	# max_fails 当有max_fails个请求失败,就表示后端的服务器不可用,默认为1,将其设置为0可以关闭检查
	# fail_timeout 在以后的fail_timeout时间内nginx不会再把请求发往已检查出标记为不可用的服务器
# }
upstream p2p {   
	ip_hash;
	server 192.168.11.130:8888 weight=1 max_fails=2 fail_timeout=30s;
	server 192.168.11.194:8888 weight=1 max_fails=2 fail_timeout=30s;
	server 192.168.11.225:8888 weight=1 max_fails=2 fail_timeout=30s;
}

location / {
    proxy_pass http://p2p;
}

负载均衡的配置:

  • Round Robin 轮询,默认值
  • Least Connections 最少连接数
  • IP Hash 将客户端请求的 IP 进行 Hash 运算
  • Generic Hash 自定义 Hash 的一个取值
  • Random 随机发送

负载均衡可能会引起 session 丢失的问题:nginx 在处理转发到不同的后台服务器的时候,session 可能存储在 A 服务器,但是用户发送的请求却发到 B 服务器,从而没有权限进行操作

java实现nginx类似的服务转发 java nginx_客户端_02


以上问题的解决方案是:

  • 方案一:对于同一个客户端的请求,直接由指定的服务器处理
    例如使用 IP_Hash 规则,对客户端的 ip 进行处理,保证同一个 ip 由同一个服务器处理
  • 方案二:使用 Redis + Token 作为分布式 Session 解决方案

动静分离

第一步:把静态资源存放到 nginx 可以访问的目录
第二部:配置静态资源的目录

# 静态资源访问地址, \.是转义
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|css|js)$ {
    root /datas/crm/static/;
}
# 动态请求访问地址
location / {
    proxy_pass http://localhost:8080;
}

对于静态资源的压缩处理 - gzip

满足的条件:

  1. 客户端发送的 HTTP 报头必须含有 Accept-Encoding 字段,且其值包含 gzip 这个压缩类型
    一般浏览器都会发 Accept-Encoding:gzip, deflate, sdch 这样的报头
  2. 服务器启用了 gzip 压缩,那么响应头会包含 Content-Encoding:gzip,客户端根据这个来判断服务器返回的内容是否为 gzip 压缩过的内容
# 开启gzip压缩    
gzip on;
# 文件达到多大开始压缩
gzip_min_length 1k;
# 压缩级别(1-9),级别越高,越消耗cpu,默认值为1
gzip_comp_level 2;
# 压缩文件的类型, 对于jpg,png图片压缩效率不高
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript;
# 根据客户端的HTTP头来判断,是否需要解压缩
gzip_vary on;

效果:开启压缩后,原本 70k 的 js 资源被压缩为 18k

gzip 对文本压缩效果很好,对图片压缩效果差一点