⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 sf.gg/a/1190000014893012 「海岛」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

基础篇

一、环境

服务器版本:CentOS 7.2

为了保证学习阶段不遇到奇怪的事情,请保证以下四点(大神选择性无视)

  1. 确认系统网络
  2. 确认yum可用
  3. 确认关闭iptables
  4. 确认停用selinux

#查看iptables状态
systemctl status firewalld.service
#关闭防火墙(临时关闭)
systemctl stop firewalld.service
#查看SELinux状态
getenforce
#临时关闭SELinux
setenforce 0

安装一些系统基本工具,正常情况系统都会自带(没有在装哦)

yum -y install gcc gcc-c++ autoconf pcre pcre-devel make automake
yum -y install wget httpd-tools vim

二、Nginx是什么?

Nginx是一个开源且高性能、可靠的HTTP中间件、代理服务 其他的HTTP服务:

  1. HTTPD-Apache基金会
  2. IIS-微软
  3. GWS-Google(不对外开放)

近几年,Nginx的市场占有率越来越高,一度飙升,为什么呢?接下来我们就知道了!

三、我们为什么选择Nginx?

1. IO多路复用epoll(IO复用)

如何理解呢?举个例子吧! 有A、B、C三个老师,他们都遇到一个难题,要帮助一个班级的学生解决课堂作业。 老师A采用从第一排开始一个学生一个学生轮流解答的方式去回答问题,老师A浪费了很多时间,并且有的学生作业还没有完成呢,老师就来了,反反复复效率极慢。 老师B是一个忍者,他发现老师A的方法行不通,于是他使用了影分身术,分身出好几个自己同一时间去帮好几个同学回答问题,最后还没回答完,老师B消耗光了能量累倒了。 老师C比较精明,他告诉学生,谁完成了作业举手,有举手的同学他才去指导问题,他让学生主动发声,分开了“并发”。 这个老师C就是Nginx。

2. 轻量级

  • 功能模块少 - Nginx仅保留了HTTP需要的模块,其他都用插件的方式,后天添加
  • 代码模块化 - 更适合二次开发,如阿里巴巴Tengine

3. CPU亲和

把CPU核心和Nginx工作进程绑定,把每个worker进程固定在一个CPU上执行,减少切换CPU的cache miss,从而提高性能。

三、安装与目录

本人使用了鸟哥的lnmp集成包 https://lnmp.org,简单方便-推荐!

#执行这句语句,根据指引,将安装 nginx php mysql 可进入lnmp官网查看更详细的过程
#默认安装目录/usr/local
wget -c http://soft.vpser.net/lnmp/lnmp1.4.tar.gz && tar zxf lnmp1.4.tar.gz && cd lnmp1.4 && ./install.sh lnmp

#默认安装目录
/usr/local

四、基本配置

#打开主配置文件,若你是用lnmp环境安装
vim /usr/local/nginx/conf/nginx.conf

----------------------------------------

user #设置nginx服务的系统使用用户
worker_processes #工作进程数 一般情况与CPU核数保持一致
error_log #nginx的错误日志
pid #nginx启动时的pid

events {
worker_connections #每个进程允许最大连接数
use #nginx使用的内核模型
}

我们使用 nginx 的 http 服务,在配置文件 nginx.conf 中的 http 区域,配置无数个 server ,每一个 server 对应这一个虚拟主机或者域名

http {
... ... #后面再详细介绍 http 配置项目

server {
listen 80 #监听端口;
server_name localhost #地址

location / { #访问首页路径
root /xxx/xxx/index.html #默认目录
index index.html index.htm #默认文件
}

error_page 500 504 /50x.html #当出现以上状态码时从新定义到50x.html
location = /50x.html { #当访问50x.html时
root /xxx/xxx/html #50x.html 页面所在位置
}
}

server {
... ...
}
}

一个 server 可以出现多个 location ,我们对不同的访问路径进行不同情况的配置 我们再来看看 http 的配置详情

http {
sendfile on #高效传输文件的模式 一定要开启
keepalive_timeout 65 #客户端服务端请求超时时间
log_format main XXX #定义日志格式 代号为main
access_log /usr/local/access.log main #日志保存地址 格式代码 main
}

五、模块

查看 nginx 已开启和编联进去的模块,模块太多了,就不在这长篇大论,有需要自行百度吧~

#大写V查看所有模块,小写v查看版本
nginx -V
# 查看此配置文件 是否存在语法错误
nginx -tc /usr/local/nginx/conf/nginx.conf

场景实现篇

一、静态资源WEB服务

1.静态资源类型

非服务器动态运行生成的文件,换句话说,就是可以直接在服务器上找到对应文件的请求

  1. 浏览器端渲染:HTML,CSS,JS
  2. 图片:JPEG,GIF,PNG
  3. 视频:FLV,MPEG
  4. 文件:TXT,任意下载文件

2.静态资源服务场景-CDN

什么是CDN?例如一个北京用户要请求一个文件,而文件放在的新疆的资源存储中心,如果直接请求新疆距离太远,延迟久。使用nginx静态资源回源,分发给北京的资源存储中心,让用户请求的动态定位到北京的资源存储中心请求,实现传输延迟的最小化

2.nginx静态资源配置

配置域:http、server、location
#文件高速读取
http {
sendfile on;
}
#在 sendfile 开启的情况下,开启 tcp_nopush 提高网络包传输效率
#tcp_nopush 将文件一次性一起传输给客户端,就好像你有十个包裹,快递员一次送一个,来回十趟,开启后,快递员讲等待你十个包裹都派件,一趟一起送给你
http {
sendfile on;
tcp_nopush on;
}
#tcp_nodelay 开启实时传输,传输方式与 tcp_nopush 相反,追求实时性,但是它只有在长连接下才生效
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
}

#将访问的文件压缩传输 (减少文件资源大小,提高传输速度)
#当访问内容以gif或jpg结尾的资源时
location ~ .*\.(gif|jpg)$ {
gzip on; #开启
gzip_http_version 1.1; #服务器传输版本
gzip_comp_level 2; #压缩比,越高压缩越多,压缩越高可能会消耗服务器性能
gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml application/xml+rss image/jpeg image/gif image/png; #压缩文件类型
root /opt/app/code; #对应目录(去该目录下寻找对应文件)
}

#直接访问已压缩文件
#当访问路径以download开头时,如www.baidu.com/download/test.img
#去/opt/app/code目录下寻找test.img.gz文件,返回到前端时已是可以浏览的img文件
location ~ load^/download {
gzip_static on #开启;
tcp_nopush on;
root /opt/app/code;
}

二、浏览器缓存

HTTP协议定义的缓存机制(如:Expires; Cache-control等 ) 减少服务端的消耗,降低延迟

1.浏览器无缓存

浏览器请求 -> 无缓存 -> 请求WEB服务器 -> 请求相应 -> 呈现

呈现阶段会根据缓存的设置在浏览器中生成缓存

2.浏览器有缓存

浏览器请求 -> 有缓存 -> 校验本地缓存时间是否过期 -> 没有过期 -> 呈现

若过期从新请求WEB服务器

3.语法配置

location ~ .*\.(html|htm)$ {
expires 12h; #缓存12小时
}

服务器响应静态文件时,请求头信息会带上 etag 和 last_modified_since 2个标签值,浏览器下次去请求时,头信息发送这两个标签,服务器检测文件有没有发生变化,如无,直接头信息返 etag 和last_modified_since,状态码为 304 ,浏览器知道内容无改变,于是直接调用本地缓存,这个过程也请求了服务,但是传着的内容极少

三、跨站访问

开发nginx跨站访问设置

location ~ .*\.(html|htm)$ {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
#Access-Control-Allow-Credentials true #允许cookie跨域
}

在响应中指定 Access-Control-Allow-Credentials 为 true 时,Access-Control-Allow-Origin 不能指定为 *,需要指定到具体域名

相关跨域内容可参考 Laravel 跨域功能中间件 使用代码实现跨域,原理与nginx跨域配置相同

四、防盗链

防止服务器内的静态资源被其他网站所套用 此处介绍的 nginx 防盗链为基础方式,其它更加深入的方式将在之后的文章介绍

首先,需要理解一个nginx变量

$http_referer #表示当前请求上一次页面访问的地址,换句话说,访问 www.baidu.com 主页,这是第一次访问,所以 $http_referer 为空,但是 访问此页面的时候还需要获取一张首页图片,再请求这张图片的时候 $http_referer 就为 www.baidu.com

然后配置

location ~ .*\.(jpg|gif)$ {
#valid_referers 表示我们允许哪些 $http_referer 来访问
#none 表示没有带 $http_referer,如第一次访问时 $http_referer 为空
#blocked 表示 $http_referer 不是标准的地址,非正常域名等
#只允许此ip
valid_referers none blocked 127.xxx.xxx.xx
if ($invalid_referer) { #不满足情况下变量值为1
return 403;
}
}

五、HTTP代理服务

Nginx可以实现多种代理方式

  • HTTP
  • ICMPPOPIMAP
  • HTTPS
  • RTMP

1. 代理区别

区别在于代理的对象不一样

正向代理代理的对象是客户端 反向代理代理的对象是服务端

2. 反向代理

语法:proxy_pass URL
默认:——
位置:loaction

#代理端口
#场景:服务器80端口开放,8080端口对外关闭,客户端需要访问到8080
#在nginx中配置proxy_pass代理转发时,如果在proxy_pass后面的url加/,表示绝对根路径;如果没有/,表示相对路径,把匹配的路径部分也给代理走
server {
listen 80;
location / {
proxy_pass http://127.0.0.1:8080/;
proxy_redirect default;

proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr; #获取客户端真实IP

proxy_connect_timeout 30; #超时时间
proxy_send_timeout 60;
proxy_read_timeout 60;

proxy_buffer_size 32k;
proxy_buffering on; #开启缓冲区,减少磁盘io
proxy_buffers 4 128k;
proxy_busy_buffers_size 256k;
proxy_max_temp_file_size 256k; #当超过内存允许储蓄大小,存到文件
}
}

负载均衡和缓存服务

一、负载均衡

负载均衡的实现方法就是我们上章介绍的反向代理 。将客户的请求通过 nginx 分发(反向代理)到一组多台不同的服务器上

这一组服务器我们称为 服务池(upstream server),池内的每一个服务器称为一个 单元,服务池内将对每一个单元进行请求轮训,实现负载均衡

#配置
语法:upstream name ...
默认:——
位置:http

upstream #自定义组名 {
server x1.baidu.com; #可以是域名
server x2.baidu.com;
#server x3.baidu.com
#down 不参与负载均衡
#weight=5; 权重,越高分配越多
#backup; 预留的备份服务器
#max_fails 允许失败的次数
#fail_timeout 超过失败次数后,服务暂停时间
#max_coons 限制最大的接受的连接数
#根据服务器性能不同,配置适合的参数

#server 106.xx.xx.xxx; 可以是ip
#server 106.xx.xx.xxx:8080; 可以带端口号
#server unix:/tmp/xxx; 支出socket方式
}

假设我们有三台服务器,并且假设它们的IP地址,前端负载均衡服务器A(127.0.0.1),后台服务器B(127.0.0.2),后台服务器C(127.0.0.3)

新建文件 proxy.conf,内容如下,上一章介绍的反向代理配置

proxy_redirect default;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_connect_timeout 30;
proxy_send_timeout 60;
proxy_read_timeout 60;
proxy_buffer_size 32k;
proxy_buffering on;
proxy_buffers 4 128k;
proxy_busy_buffers_size 256k;
proxy_max_temp_file_size 256k;

#服务器A的配置
http {
...
upstream xxx {
server 127.0.0.2;
server 127.0.0.3;
}
server {
liseten 80;
server_name localhost;
location / {
proxy_pass http://xxx #upstream 对应自定义名称
include proxy.conf;
}
}
}

#服务器B、服务器C的配置
server {
liseten 80;
server_name localhost;
location / {
index index.html
}
}

调度算法

  • 轮训:按时间顺序逐一分配到不同的后端服务器
  • 加权轮训:weight值越大,分配到的几率越高
  • ip_hash:每个请求按访问IP的hash结果分配,这样来自同一个IP固定访问一个后端服务器
  • least_conn:最少链接数,哪个机器连接数少就分发给谁
  • url_hash:按照访问的URL的hash结果来分配请求,每一个URL定向到同一个后端服务器
  • hash关键数值:hash自定义key

ip_hash 配置

upstream xxx {
ip_hash;
server 127.0.0.2;
server 127.0.0.3;
}

ip_hash存在缺陷,当前端服务器再多一层时,将获取不到用户的正确IP,获取的将是前一个前端服务器的IP,因此 nginx1.7.2版本推出了 url_hash

url_hash 配置

upstream xxx {
hash $request_uri;
server 127.0.0.2;
server 127.0.0.3;
}

二、缓存服务

1. 缓存类型

  • 服务端缓存:缓存存储在后端服务器,如redis,memcache
  • 代理缓存:缓存存储在代理服务器或者中间件上,它的内容是从后端服务器获取的,但是保存在自己本地
  • 客户端缓存:缓存在浏览器内的

2. nginx 代理缓存 客户端请求nginx,nginx查看本地是否有缓存数据,若有直接返回给客户端,若没有再去后端服务器请求

http {
proxy_cache_path /var/www/cache #缓存地址
levels=1:2 #目录分级
keys_zone=test_cache:10m #开启的keys空间名字:空间大小(1m可以存放8000个key)
max_size=10g #目录最大大小(超过时,不常用的将被删除)
inactive=60m #60分钟内没有被访问的缓存将清理
use_temp_path=pff; #是否开启存放临时文件目录,关闭默认存储在缓存地址

server {
...
location / {
proxy_cache test_cache; #开启缓存对应的名称,在keys_zone命名好
proxy_cache_valid 200 304 12h; #状态码为200 304的缓存12小时
proxy_cache_valid any 10m; #其他状态缓存10小时
proxy_cache_key $host$uri$is_args$args; #设置key值
add_header Nginx-Cache "$upstream_cache_status";
}
}
}

当有个特定请求我们不需要缓存的时候,在上面配置的内容中加入以下配置

server {
...
if ($request_uri ~ ^/(login|register) ) { #当请求地址有login或register时
set $nocache = 1; #设置一个自定义变量为true
}
location / {
proxy_no_cache $nocache $arg_nocache $arg_comment;
proxy_no_cache $http_pragma $http_authoriztion;
}
}

3. 分片请求

早期版本 nginx 对大文件的分片请求不支持缓存,1.9版本后slice模块实现了这个功能 前端发起请求,nginx去获取这个请求文件的大小,若超过我们的定义slice的大小,会进行切片,分割成多个小的请求去请求后端,到前端就成为一个一个独立的缓存文件

优势:每个子请求收到的数据都会形成独立文件,一个请求中断了,其他请求不受影响,原本情况请求中断,再次请求文件将从头开始,而开启分片请求,就接下去获取未请求的小文件

劣势:当文件很大或者slice很小时,可能会导致文件描述符耗尽等情况

语法:   slice size;    #当大文件请求时,设置size为每个小文件的大小
默认: slice 0;
位置: http/server/location

常见问题

一、相同 server_name 多个虚拟主机优先级

#当出现虚拟主机域名相同的情况,重启nginx时,会出现警告⚠️处理,但是并不不会阻止nginx继续使用

server {
listen 80;
server_name www.baidu.com
...
}

server {
listen 80;
server_name www.baidu.com
...
}

...

优先选择最新读取到的配置文件,当多个文件是通过include时,文件排序越靠前,越早被读取

二、location 匹配优先级

 =        #进行普通字符精确匹配,完全匹配
^~ #进行普通字符匹配,当前表示前缀匹配
~\~* #表示执行一个正则匹配()

#当程序使用精确匹配时,一但匹配成功,将停止其他匹配
#当正则匹配成功时,会继续接下来的匹配,寻找是否还有更精准的匹配

三、try_files的使用

按顺序检查文件是否存在

location / {
try_files $uri $uri/ /index.php;
}

#先查找$uri下是否有文件存在,若存在直接返回给用户
#若$url下没有文件存在,再次访问$uri/的路径是否有文件存在
#还是没有文件存在,交给index.php处理

例:
location / {
root /test/index.html
try_files $uri @test
}

location @test {
proxy_pass http://127.0.0.1:9090;
}

#访问 / 时,查看 /test/index.html 文件是否存在
#若不存在,让9090端口的程序去处理这个请求

四、alias和root的区别

location /request_path/image/ {
root /local_path/image/;
}

#当我们访问 http://xxx.com/request_path/image/cat.png时
#将访问 http://xxx.com/request_path/image/local_path/image/cat.png 下的文件

location /request_path/image/ {
alias /local_path/image/;
}

#当我们访问 http://xxx.com/request_path/image/cat.png时
#将访问 http://xxx.com/local_path/image/cat.png 下的文件

五、如果用户真实IP

当一个请求通过多个代理服务器时,用户的IP将会被代理服务器IP覆盖

#在第一个代理服务器中设置
set x_real_ip=$remote_addr
#最后一个代理服务器中获取
$x_real_ip=IP1

六、Nginx 常见错误码

413 Request Entity Too Large    #上传文件过大,设置 client_max_body_size
503 bad gateway #后端服务无响应
504 Gateway Time-out #后端服务执行超时

文章目录
  1. 1. 基础篇
    1. 1.1. 一、环境
    2. 1.2. 二、Nginx是什么?
    3. 1.3. 三、我们为什么选择Nginx?
    4. 1.4. 三、安装与目录
    5. 1.5. 四、基本配置
    6. 1.6. 五、模块
  2. 2. 场景实现篇
    1. 2.1. 一、静态资源WEB服务
    2. 2.2. 二、浏览器缓存
    3. 2.3. 三、跨站访问
    4. 2.4. 四、防盗链
    5. 2.5. 五、HTTP代理服务
  3. 3. 负载均衡和缓存服务
    1. 3.1. 一、负载均衡
    2. 3.2. 二、缓存服务
  4. 4. 常见问题
    1. 4.1. 一、相同 server_name 多个虚拟主机优先级
    2. 4.2. 二、location 匹配优先级
    3. 4.3. 三、try_files的使用
    4. 4.4. 四、alias和root的区别
    5. 4.5. 五、如果用户真实IP
    6. 4.6. 六、Nginx 常见错误码