目录
什么是Nginx?
什么是反向代理?
正向代理
反向代理
Nginx进程模型
进程模型的好处
worker抢占机制
修改Nginx工作进程数
Nginx事件模型
在Nginx的角度思考如何选择IO模型
IO复用模型 -> epoll
CPU亲和
零拷贝sendFile
动静分离
Nginx文件夹结构
Nginx配置文件结构
特殊配置介绍
关于location
Nginx控制浏览器缓存时间 => expires(浏览器缓存)
Nginx设置跨域
Nginx valid_referer 防盗链
Nginx设置Tomcat集群
反向代理的静态资源缓存在nginx服务器(也称代理缓存)
Nginx负载均衡
前置知识upstream
轮询
权重
IP Hash
URL Hash
最少连接数
Nginx日志
变量列表
Nginx和Lua
Lua
Nginx是一个高性能的HTTP服务器,它是一个高性能的Web和反向代理服务器由俄罗斯的程序设计师Igor Sysoev开发。Nginx在反向代理、Rewrite规则、稳定性、内存消耗等方面表现出很强的优势,选用Nginx代替传统的Apache服务器会获得很多方面的性能提升。(参考网上资料加以整理)。简而言之,Nginx是一款轻量级高性能且好用的HTTP服务器,在近几年被广泛使用,在使用量上有超越Apache的趋势。
Nginx是常用的反向代理服务器,那么什么是反向代理,反向代理和正向代理有什么区别,可能各位大哥都了解这个东西,但是如何清楚地描述它嘞?
正向代理就是沿着你请求的方向进行代理,比如说你的目标网站是CSDN,直接去访问它访问不通,那么就可以找一个代理服务器帮助我们,通过代理服务器请求到CSDN的网站,这个代理服务器有可能是移动、电信或者联通的服务器。
正向代理是代理服务器帮助你访问到你想要访问的目标网站上,但是不考虑细节,比如说具体访问到那个IP地址的服务器提供的服务,而这个部分就是由反向代理来实现,可能文字描述比较迷,可以通过下图理解:
上图就是使用反向代理来实现路由的一个例子,请求来到Nginx服务器,通过Nginx服务器来决定访问到那个IP地址的服务器上,这就是反向代理。通过反向代理还可以实现负载均衡等功能,比如根据权重分发请求到不同的集群服务器上。
Nginx在启动后会有一个master进程和一个或多个worker进程。
管理员发出stop等命令,master会传递给worker,若worker连接了客户端会等待其释放连接后执行停止。
采用进程模型而不是线程,其好处是进程间相互独立,互不影响,如果一个worker除了一些问题不会影响到其他的进程。
worker的具体抢占机制如下:
修改下图中红框的配置即可,比如说我们想将其改为1个,就设置为worker_processes 1;即可
了解了Nginx进程模型后,Nginx采用进程而不是线程,相互独立的同时,可能就出现这样的疑问=>进程模型去处理的话并发性不就很差?
对于这个问题就不得不提到Nginx事件模型。
Linux下Nginx采用的是epoll多路复用IO模型
相比大家都了解,如果我们使用阻塞的IO模型,也就是一个进程处理一个请求,当请求发生阻塞,进程会在那里等待,再多来一个请求,我们的系统就需要多fork一个进程,这样当请求量一上来,我们需要的进程数量是一个非常大的数字,这样系统吃不消。
所以从Nginx的角度看,阻塞的IO模型是不可取的,因为它的目标是一个能够支持高并发的服务器。那么我们就要思考如何让一个进程能够处理多个请求,这就需要说到IO复用模型。
什么是IO复用模型,关于五种IO模型本人在这篇博客有介绍过:https://blog.csdn.net/qq_36652619/article/details/102996683
这里再介绍一次IO复用模型
用本狗子以前介绍Socket与IO模型的图来讲解一下大致的结构:
其大致的思路就是一开始会创建一个监听,循环监听所有的IO是否有可读的数据,如果有的话,就将数据读取,这个思路的伪代码如下:
while true{
for i in steam[]{
if i has data{
read until unavailable
}
}
}
这种模式的话就是IO复用模型,会不断地轮询查看是否有数据,这样的话其实有一定的缺陷:
当所有的IO流都没有数据的话,我们的CPU就一直在白干,一直轮询
所以就引出了IO复用的几种模型比如说select模型:
while true{
//只有有数据的时候才会轮询
select(stream[]){
for i in steam[]{
if i has data{
read until unavailable
}
}
}
}
select模型解决了上面的白干的缺陷,但是select模型有一个文件句柄的限制,而epoll模型解决了这个限制,同时epoll模型最大的优势体现在callback事件回调的机制优化,也就是当流可读会事件回调,那么这样较大的提高了效率。
所以Nginx中采用的就是这样的epoll模型,一个进程可以处理大量请求。
所以通过epoll的模式让Nginx能够高并发。
将CPU核心和Nginx工作进程绑定,让每个worker进程在一个cpu上执行,减少cpu的缓存损失加强性能。
Nginx进程和核心的匹配(除了下面那种方法还可以手动配置):
worker_cpu_affinity auto;
传统的HTTP服务器传输文件的过程如下:
文件会经过内核空间切换到用户空间再到内核空间传递到socket,而nginx的零拷贝的过程如下:
减少了内核空间和用户空间之间的切换,提高了文件传输的效率。
所以有了这个优点之后很多用Nginx做动静分离的场景。
动静分离比较常见,所以这里就放一个流程图,其实就是将静态资源和静态资源分离开。
nginx
-------------------- CHANGES //版本更新的内容,跟王者荣耀的更新通知差不多,记录每个版本修改了什么
-------------------- conf //配置文件的文件夹
-------------------- configure //用来编译和配置
-------------------- contrib //一些工具文件夹
-------------------- html //默认存放的静态文件,安装完会拷到指定目录
-------------------- LICENSE //说明
---------------------Makefile //通过makefile文件来描述源程序之间的相互关系并自动维护编译工作,本质上makefile文件是个文本文件,用于配置编译过程。
---------------------man //一些手则
---------------------objs //里面有很多模块,也包含第三方的模块
---------------------README
---------------------src //nginx的源码
Nginx的配置文件nginx.conf的内部结构是这样的:
1. tcp_nopush
开启了sendfile之后可以开启tcp_nopush,指当数据表累积一定大小后才发送,提高效率。
sendfile on;
tcp_nopush on;
2. 设置客户端与服务端请求的超时时间,减少资源损耗。
keepalive_timeout 65;
3. 开启压缩,让html/js/css压缩后快速传输
gzip on;
# 小于1024字节的文件不会压缩
gzip_min_length 1024;
# 压缩比,范围:1-9,压缩比越大占用CPU越大
gzip_comp_level 5;
# 要压缩的类型
gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css;
4. worker进程工作数设置,设置为cpu的数目,设置方法上文介绍了
location是路径匹配规则,可以看成是路由映射
# 默认模式可以访问指定文件夹下的文件
location / {
}
# 精准匹配,精准的匹配到某个文件
location / = {
}
# ~^表示以某个路径作为前缀
location ~^ /pre/img/ {
root /home;
}
# 正则表达式
# *表示不区分大小写
location ~* \.(JPG|png) {
root /home;
index index.html index.htm
}
通过设置expires可以控制静态资源的缓存时间。但是静态资源发生变动的话,会重新去请求
location / {
root /home/static;
expires 30s;
}
#允许跨域请求的域,*代表所有
add_header 'Access-Control-Allow-Origin' *;
#允许带上cookie请求
add_header 'Access-Control-Allow-Credentials' 'true';
#允许请求的方法,比如 GET/POST/PUT/DELETE
add_header 'Access-Control-Allow-Methods' *;
#允许请求的header
add_header 'Access-Control-Allow-Headers' *;
这里注意一个问题,就是Nginx和SpringMVC本身都配置了跨域会报错,具体看本人这篇:https://blog.csdn.net/qq_36652619/article/details/89377196
location ~* \.(JPG|png) {
# 如果来自*.domain.com会继续往下走,如果不是的话会返回403
valid_referers *.domain.com;
if ($invalid_referer) {
return 403;
}
root /home;
}
其实这个很简单,大家可能都玩过,设置两台Tomcat集群,其结构图如下:
nginx.conf:
upstream www.mydomain.com {
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
server {
listen 80;
server_name www.mydomain.com
location / {
proxy_pass http://www.mydomain.com;
}
}
upstream www.mydomain.com {
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
# 路径代表缓存的路径
# keys_zone 设置共享内存以及占用空间大小
# inactive 超过此时间则被清理
# max_size 设置硬盘中最多可以缓存多少数据,当到达该数值时,nginx会删除最少访问的数据。
proxy_cache_path /test/cache/ keys_zone=my_zone:10m inactive=12h max_size=5g;
server {
listen 80;
server_name www.mydomain.com
location / {
proxy_pass http://www.mydomain.com;
# 使用缓存
proxy_cache my_zone;
}
}
ps:三种缓存=>
有集群必有负载均衡,Nginx有几种负载均衡的模式。
文档:http://nginx.org/en/docs/stream/ngx_stream_upstream_module.html#upstream
max_conns
每台服务器的最大连接数,设置可以避免连接数过大
upstream www.mydomain.com {
server 192.168.1.11:8080 max_conns=10000;
server 192.168.1.12:8080 max_conns=1000;
}
server {
listen 80;
server_name www.mydomain.com
location / {
proxy_pass http://www.mydomain.com;
}
}
down
这个参数来标记服务器不可用
upstream www.mydomain.com {
server 192.168.1.11:8080 down;
server 192.168.1.12:8080;
}
server {
listen 80;
server_name www.mydomain.com
location / {
proxy_pass http://www.mydomain.com;
}
}
backup
将服务器标记为备份服务器。当其他服务器宕机的时候会加入集群
upstream www.mydomain.com {
server 192.168.1.11:8080 backup;
server 192.168.1.12:8080;
server 192.168.1.12:8080;
}
server {
listen 80;
server_name www.mydomain.com
location / {
proxy_pass http://www.mydomain.com;
}
}
max_fails和fail_timeout
上面这段说明,在fail_timeout设置的时间内,出现了max_fails次请求失败,则在这段时间内视为服务器不可用,请求会打到正常的服务器。默认是十秒。
upstream www.mydomain.com {
server 192.168.1.11:8080 max_fails=3 fail_timeout=10s;
server 192.168.1.12:8080;
}
server {
listen 80;
server_name www.mydomain.com
location / {
proxy_pass http://www.mydomain.com;
}
}
keepalive
将一些连接保持为长连接,减少新增和销毁的开销,多个连接公用一个长连接,提高性能,
关于长连接:
upstream www.mydomain.com {
server 192.168.1.11:8080 max_fails=3 fail_timeout=10s;
server 192.168.1.12:8080;
keepalive 50;
}
server {
listen 80;
server_name www.mydomain.com
location / {
proxy_pass http://www.mydomain.com;
proxy_http_version 1.1; # 设置http版本为1.1
proxy_set_header Connection "";
}
}
轮询的思想其实特别简单,就口头描述了,比如说我们上面做了两个Tomcat集群,第一个请求给第一个Tomcat,第二个请求给第二个Tomcat,第三个请求给第一个Tomcat,第四个请求给第二个Tomcat,以此类推。
这种就是Nginx默认的形式,这种形式一般适用于,所有的集群服务器都是同样配置的,因为每个Tomcat分配到的请求是一样的。
权重的方式其实也比较好理解,字面意思,两台服务器,根据每台服务器的配置高低分配不同的权重,比如说现在有三个请求,一台2核4G的服务器一台4核8G的服务器,那么让其中两个请求打到4核8G的服务器上,另外一个请求打到2核4G的服务器,这样的形式就是权重的方式。
按照上述例子的配置方法:(其中weight默认是1)
upstream www.mydomain.com {
server 192.168.1.11:8080 weight=2;
server 192.168.1.12:8080 weight=1;
}
server {
listen 80;
server_name www.mydomain.com
location / {
proxy_pass http://www.mydomain.com;
}
}
这是通过IP进行哈希的方式来负载均衡,用户请求过来的IP的网络号去进行一个哈希算法然后达到对应的服务器上面,如果请求的网络号一样,那么会打到同一台服务器上,也就是会访问到同样的服务器,访问到同样的缓存。弊端的话其实它有可能不均匀,某些网络号的请求量大,某些小。
配置方法如下:
upstream www.mydomain.com {
ip_hash;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
server {
listen 80;
server_name www.mydomain.com
location / {
proxy_pass http://www.mydomain.com;
}
}
通过用户请求的url进行哈希,同样的url会哈希到同一台服务器,这种可以做不同服务访问到不同的服务器上。
配置方法:
upstream www.mydomain.com {
hash $request_uri;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
server {
listen 80;
server_name www.mydomain.com
location / {
proxy_pass http://www.mydomain.com;
}
}
看那台服务器的连接数少,就请求到哪台服务器上面。
upstream www.mydomain.com {
least_conn;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
server {
listen 80;
server_name www.mydomain.com
location / {
proxy_pass http://www.mydomain.com;
}
}
nginx的日志如下:
Nginx通过log_format将需要的信息输出到日志中去,
nginx.conf:
$remote_addr:客户端(用户)IP地址
$time_local:访问时间
$request:get请求的url地址(目标url地址)的host
'$status:http状态码
$body_bytes_sent:请求页面大小
$http_referer: 来源页面,即从哪个页面转到本页
$http_user_agent:用户浏览器其他信息,浏览器版本、浏览器类型
$http_x_forwarded_for:请求中的X-Forwarded-For信息
另外,http的头信息可以通过$http_加上参数名字,其中大写改小写,横杠改成下划线例如:$http_user_agent
还有很多nginx的变量可以去官方文档查找。
这里仅做科普,不详细讲解Lua的开发与应用
一个轻量级的可扩展脚本语言。一些功能可以通过几行代码实现,常用于埋点统计比如用户IP、访问等信息,运行上也很轻量。结合Nginx和Lua可以提高并发,因为Nginx能够接受大量的请求,恰好和Lua的轻量搭配。在秒杀系统中也有使用Lua作限流的一种方式。