Nginx
Nginx
小码同学介绍
Nginx是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的http://Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。 其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。2011年6月1日,nginx 1.0.4发布。 Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行。其特点是占有内存少,并发能力强。
以下的所有server块都是在nginx.conf
文件内的http
块内添加的,nginx.conf
文件位于/usr/local/nginx/nginx.conf
Nginx的安装
Nginx工作模式
nginx是一个多进程/多线程高性能web服务器,在linux系统中,nginx启动后会以后台守护进程(daemon)的方式去运行,后台进程包含一个master进程和多个worker进程。
master主进程主要用来管理worker进程
主要作用是
:读取并验正配置信息,管理真正提供服务的worker进程,向各worker进程发送信号,监控worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程。
master进程不会对用户请求提供服务,而用户的请求则是worker进程来响应的。
nginx是通过信号来控制,比如关闭,重启等去控制nginx进程。nginx信号是属于nginx进程间的通信的一种机制,比如master主进程控制多个worker子进程,也是通过信号控制的
配置文件
Nginx的配置文件位于/usr/local/nginx/conf/nginx.conf
,文件内容如下。
1 | worker_processes 1; |
简化一下Nginx.conf配置文件格式大概是下面这样的,http块包括里面的server块,其中server块是我们经常需要书写的,我们每配置一个服务(部署一个项目)都要配置一个server块。下面代码就是一个简单的服务,静态代理/www/main
文件夹下的静态资源,监听端口是80,使用本机环回地址127.0.0.1
。
1 | http{ |
Nginx子配置
由于我们经常对server块进行书写,我们也可以创建一个nginx子配置,用来专门书写server块。我们创建一个default.conf
文件,存放于/usr/local/nginx/conf/conf/
文件夹下。
创建default.conf文件
1 | touch /usr/local/nginx/conf/conf/default.conf |
引入default.conf文件
将以下的引入路径
存放于http块下。
1 | include /usr/local/nginx/conf/conf/conf.default; |
创建好文件后,然后nginx.conf
文件引入default.conf
,如下:
1 | worker_processes 1; |
引入default.conf文件
后,之后如果需要添加server块
直接在这个文件添加即可。文章后面有添加server块
的案例。
Nginx启动 & 停止 & 重启
启动Nginx
启动Nginx有两种方式,一个是直接启动执行nginx二进制文件,另一个方法是通过linux内置的server命令启动nginx。
直接启动
直接运行nginx
执行文件启动nginx
1 | /usr/local/nginx/sbin/nginx |
使用systemctl启动
1 | systemctl start nginx.service |
停止Nginx
停止Nginx也有几个方式,nginx内置命令和linux内置的命令。
立即停止Nginx
快速停止Nginx,无论进程是否在工作,都直接停止进程,该命令是nginx内置的命令。
1 | nginx -s stop |
-s
:表示要发送一个信号,而stop
表示要发送的信号是停止(停止服务)
从容停止Nginx
这种方法较stop相比就比较温和一些了,需要进程完成当前工作后再停止,该命令是nginx内置的命令。
1 | nginx -s quit |
使用Linux命令停止Nginx服务
systemctl属于Linux内置的命令,也可以停止nginx服务
1 | systemctl stop nginx.service |
使用Linux强制停止Nginx
systemctl属于Linux内置的命令,用kill
直接杀死进程,在上面无效的情况下使用,态度强硬,简单粗暴!
1 | killall nginx |
重启nginx
1 | systemctl restart nginx.service |
加载配置文件
一般在修改配置文件后,我们需要使它生效,加载配置前,我们一般先检测配置文件有没有写错,使用-t
可以检测是否有语法错误,比如漏写了;
这类似的语法就可以检查出来。当然,你不检查语法问题加载也没事,但如果有语法问题,加载也是不生效的,会提示有问题。
1 | nginx -t # 检测配置文件是否有语法错误 |
Nginx管理模块
安装模块(添加模块)
Nginx和Nginx模块的安装都要经过编译和安装两个步骤,你可以选择在安装Nginx的时候将你需要的模块一起编译安装,你也可以选择在安装完Nginx,什么时候需要新模块,再重新编译安装也是没有问题的。但需要记住
,如果是选择后者,记得在重新编译Nginx时备份一下Nginx目录,以防你重新编译安装时出问题。
需求:已经安装Nginx,但想添加新模块,又不想卸载旧的Nginx
备份Nginx
备份nginx防止在编译安装过程中出现问题,导致Nginx无法使用,备份一下以防出问题可以快速恢复原来的状态。
1. 停止Nginx
1 | /usr/local/nginx/sbin/nginx -s stop |
2. 备份整个Nginx文件夹
将nginx文件夹备份在/usr/local/
文件夹下,压缩包名为nginx.tar.gz
1 | tar -zcvf /usr/local/nginx.tar.gz /usr/local/nginx/ |
下载 & 解压
下载你要安装的新模块,并解压到/usr/local/nginx/plugins
中,其中plugins
文件夹需要你自己新建。
下面我以安装echo-nginx-module-0.61.tar.gz
模块为例:
echo-nginx-module下载:https://rookie1679.lanzoum.com/iLoEA0y6j81e
下载完后我放在/opt
下
1 | mkdir /usr/local/nginx/plugins |
创建plugins
文件夹–>解压echo-nginx-module
到/usr/local/nginx/plugins/
文件夹下–>将echo-nginx-module
改名
编译
编译我们需要用到Nginx的源码,也就是我们刚开始安装Nginx时下载的源码包,我们需要这个源码包重新编译,将新模块和nginx一起编译安装,其实相当于重装一次nginx,所以我们刚刚在前面备份了nginx的文件夹就是这个原因,如果出问题直接使用备份的恢复。
1. 没有源码包
如果你已经删除了原来旧的源码包,那也没关系,你可以根据你现在的nginx版本去nginx官网重新下载一个版本一样的就好了,官网在文章开头有。
1 | /usr/local/nginx/sbin/nginx -V # 查看nginx版本 |
2. 开始编译
将下载好的源码包解压后,进入源码包
1 | ./configure \ |
--prefix
:指定Nginx安装目录(你可以指定旧的nginx目录,也指定新的目录安装)--with-
:启用支持XXX模块,如启用支持SSL的HTTP模块(http_ssl_module)--add-module
:添加一个第三方模块
简单来说,就是--with-
所编译的都是Nginx内置的模块,是由Nginx开发者编写和维护的,而--add-module
是第三方模块,不是Nginx内置的。
安装成功
图中出现的不是错误,这个提示error.log日志文件不存在,你新建一个就不会报错了,不影响安装。
3. 安装
1 | make && make install |
添加新模块到这里就结束了。
URL匹配
当我们部署多个项目到Nginx下的时候,将多个项目部署在同一个域名下,我们可以设置用户访问不同的URI来反向代理到不同的服务,比如用户访问http://192.168.2.5/a
,我们可以反向代理到localhost:5050
,用户访问http://192.168.2.5/b
,我们反向代理到localhost:5051
端口,我们就可以通过匹配URI来做反向代理。
=、~、~*、^~、@
都是location
用于实现访问控制的前缀,用于匹配不同的URI
来返回不同的信息。
普通匹配
前缀 | 说明 |
---|---|
= | 精准匹配全路径, 命中它后直接返回, 不再进行后续匹配, 优先级最高. |
^~ | 精准匹配开头, 命中开头后直接返回, 不再进行后续匹配, 优先级第二 |
正则表达式匹配
前缀 | 说明 |
---|---|
~ | 使用正则表达式完成location的匹配,区分大小写 |
~* | 使用正则表达式完成location的匹配,不区分大小写 |
内部Nginx匹配
前缀 | 说明 |
---|---|
@ | 且该块不能被外部客户端所访问,只能被 Nginx 内部配置指令所访问 |
负载均衡
负载均衡(Load Balance)就是将负载分摊到多个操作单元上执行,从而提高服务的可用性和响应速度,带给用户更好的体验。
配置方式
配置方式 | 说明 |
---|---|
轮询方式 | 负载均衡默认设置方式,每个请求按照时间顺序逐一分配到不同的后端服务器进行处理,如果有服务器宕机,会自动剔除 |
权重方式 | 利用weight指定轮询的权重比率,与访问率成正比,用于后端服务器性能不均的情况 |
ip_hash方式 | 每个请求按访问IP的hash结果分配,这样可以使每个访客固定访问一个后端服务器,可以解决Session共享的问题 |
第三方模块 | 第三方模块采用fair时,按照每台服务器的响应时间来分配请求,响应时间短的优先分配;若第三方模块采用url_hash时,按照访问url的hash值来分配请求 |
跨域处理
什么是跨域?
跨域指的是在浏览器中,当前网页的资源请求了另一个域名下的资源。例如,网页A中包含了一个图片请求,这个图片位于另一个域名的服务器上,那么当网页A加载时,就会发生跨域行为。跨域是由于浏览器的同源策略所导致的,同源策略是为了保障用户在不同站点之间的安全,防止恶意网站获取用户的信息。因此,当跨域行为发生时,浏览器会限制请求和响应的行为。解决跨域问题可以通过使用 CORS、JSONP、代理等方式来实现。
待续
Nginx连接优化
该小节实验需要用到Apache2的ApacheBean工具来测试请求数量
实验环境:
A服务器:192.168.2.10(被测试的网站A)
B服务器:192.168.2.6(安装Apache测试网站A)
Apache请求测试
服务器A配置Nginx输出
1. 安装–with-http_stub_status_module模块
具体安装模块参考我另一篇文章;https://blog.hikki.site/85111a4c.html
2. 编辑Nginx配置
添加一个新的server
1 | server { |
服务器B开始测试
Apache2的bin
目录下有个ab
执行文件,该执行文件就是测试并发数量
1 | [root@localhost ~]# cd /usr/local/apache2/bin |
-
-n
:表示发送的请求总数; -
-c
:表示并发数,后面的网址是请求的服务器URL地址;
ApacheBench目前只能使用HTTP 1.0协议进行请求。
可能遇到的问题
错误一
接下来在测试时,将请求总数10000和并发数设置为200,会发现ApacheBench无法进行测试,出现如下提示。
1 | ./ab -n10000 -c200 http://192.168.2.10 |
服务器B测试时出现了错误,
1 | socket: Too many open files (24) |
该提示表示打开的文件超出了系统限制。按照Linux一切皆文件的理念,连接数也是文件,Linux系统默认限制了一个进程最多打开的文件数量。
提高系统打开文件数量
1. 查看系统打开文件限制
1 | [root@localhost ~]# ulimit -a | grep open |
从上述结果中可以看出,当前对于文件打开数量(open files)的限制为1024,使
2. 修改打开文件次数限制
用“ulimit -n”命令可以临时更改这个数量。
若要每次开机后自动修改,可以将命令写入到**/etc/profile**文件中。
1 | ulimit -n 65535 |
然后再次进行请求测试
1 | [root@localhost bin]# ./ab -n2000 -c10000 http://192.168.2.10/ |
出现以上的输出,Failed requests
的值为0,说明请求成功了。
错误二
如果你看到Failed requests
不为0,说明请求有部分没有请求成功,可能是对方网站(192.168.2.10)的问题。
编辑nginx的配置文件,/usr/local/nginx/conf/nginx.conf
配置,具体如下:
1 | worker_processes auto; |
-
worker_processes:指令用于指定工作进程的个数,设置为auto时Nginx将根据CPU的核心数来控制
-
worker_rlimit_nofile:用于设置最多打开的文件数量
-
worker_connections:用于设置每个工作进程可接收的连接数
-
multi_accept:表示是否允许一个工作进程响应多个请求
客户端请求限制
限制同一IP的并发数
通过limit_conn指令可以限制并发连接数
1 | http { |
-
limit_conn_zone
:指令用于开辟一个共享内存空间保存客户端IP,空间名称为perip,空间大小为10M -
limit_conn
:指令用于限制连接数量,该指令可以在server和location中使用,实现不同级别的控制,当超过允许连接数时返回503(服务暂时不可用) -
预定义变量$binary_remote_addr保存了用二进制表示的当前客户端IP地址
限制虚拟主机的并发数
(2)限制虚拟主机的并发数
在使用limit_conn_zone指令时,也可以用共享内存空间保存虚拟主机名($server_name),实现对虚拟主机的并发数进行限制,具体配置如下:
1 | http { |
限制响应的传输速率
Nginx的limit_rate指令用于限制服务器在响应时传输数据到客户端的速率,可以在http、server、location、location中的if块中使用。
1 | http { |
-
limit_rate
:用于限制每个连接的传输速率(每秒100KB); -
limit_rate_after
:用于在已经传输了指定大小的数据后再进行限速,从而实现只针对大文件限制下载速度; -
如果省略limit_rate_after指令,则无论文件大小是多少,都会进行限速。
浏览器缓存优化
介绍
服务器可以通过响应消息控制浏览器缓存
-
和缓存相关的响应头
:Expires、Cache-Control、ETag、Last-Modified等 -
Last-Modified和Etag
:由Nginx自动生成,前者表示最后修改的时间,后者用于浏览器判断内容有无改变 -
Cache-Control
:比较复杂,通常根据实际需要进行控制 -
Expires
:表示该资源的过期时间,如果没有过期则浏览器不会发起HTTP请求
浏览器缓存优化的一般对象:在优化浏览器缓存时,通常会对静态资源(如图片、CSS文件、JavaScript文件)进行优化。
理由:一个内容丰富的网页中会包含大量的静态资源,在没有浏览器缓存的情况下,每个资源都要请求服务器。
优化方法:服务器可以控制静态资源的过期时间,使浏览器对于未过期的资源直接从缓存中读取,以减少请求次数,使网页整体加载速度更快。
实操
接下来,在Nginx配置文件中通过expires指令为静态资源设置过期时间,将图片、swf(Flash动画)文件设置为30天后过期,将css、js文件设置为12小时后过期。
1 | server { |
即使服务器没有设置Expires,浏览器也会基于常见的静态资源扩展名自动缓存;但若设置Expires,可以使缓存具有更长的有效期。另外,当服务器需要更新静态资源时,可以修改HTML中引入的地址,利用URL参数让浏览器重新请求静态资源,如改为“style.css?ver=1.2”。
配置使用案例
案例一:直接部署静态资源
需求:将服务器下/www/hexo
路径下的博客文件部署到公网上,并且用域名hikki.site
和www.hikki.site
可以访问。
1 | server { |
listen
:指定端口访问server_name
:可一个或多个,以空格分开root
:指定部署的目录index
:指定访问第一个页面
server_name
如果不写,则默认是本机localhost
。
案例二:反向代理部署
需求:我的服务器上有一个Java项目,但Java项目需要使用tomcat,tomcat默认的端口是8080,我的Java项目放在/www/tomcat9/webapps/ROOT
下,并且使用域名java.hikki.site
域名访问该项目。
如果我们不使用指定的域名访问,我们一般是这样访问的:http://IP:8080
,在IP地址后面加端口号,这时你还需要开放防火墙8080
端口,将端口暴露出去。
但我们不想把这个端口暴露给别人看到怎么办?我们可以使用反向代理,指定一个三级域名java.hikki.site
,这时可以关闭防火墙的8080
端口了,当别人访问这个java.hikki.site
域名时,这个域名自动指向IP:8080
,这样就可以避免端口暴露出去。
1 | server{ |
案例三:加网站HTTPS
需求:给我的博客http://hikki.site
加SSL,使网站更加安全,在案例一的基础上修改
,首先需要申请SSL证书,如果你是在阿里云或者腾讯云购买的域名可以免费申请一年的SSL证书,如果你也没有申请到证书,那你也可以申请免费的SSL证书,可参考我另外的文章:https://blog.hikki.site/2e63f4a4.html
1 | server { |
案例四:给网站添加防盗链
博客使用的是twikoo评论系统
,使用docker部署的,怎么保证评论系统的安全呢?别人怎么获取不了这些数据呢?
我们可以给Nginx添加一些简单的防盗链,比如,设置该server块,只有当请求头hikki.site
时,才允许访问,或者是*.hikki.site
才允许访问,当然,这只是简单的过滤一些,如果别人模拟请求头盗取你数据,那还是没办法。
我使用的twikoo部署在docker容器中,对外映射的是5050端口,那我指定三级域名twikoo.hikki.site,在用户访问blog.hikki.site网站时,blog.hikki.site向服务器发起请求,获取评论区的数据,此时,请求头带有blog.hikki.site信息,则被通过,允许请求访问数据。
如果我们是在本地测试发起请求的话,这时候,对服务器发起的请求是被拒绝的,因为请求头不是带有hikki.site
或者是*.hikki.site
。
1 | # twikoo评论系统 |
常见的Nginx状态码、响应码
状态码分类
分类 | 分类描述 | 分类详细说明 |
---|---|---|
1** | 信息 | 服务器收到请求,需要请求者继续执行操作 |
2** | 成功 | 操作被成功接收并处理 |
3** | 重定向 | 需要进一步的操作以完成请求 |
4** | 客户端错误 | 请求包含语法错误或无法完成请求 |
5** | 服务器错误 | 服务器在处理请求的过程中发生了错误 |
1开头状态码
状态码 | 状态码英文名称 | 描述 |
---|---|---|
100 | Continue | 继续,客户端应继续请求 |
101 | Switching Protocols | 切换协议,服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议 |
2开头状态码
状态码 | 状态码英文名称 | 描述 |
---|---|---|
200 | OK | 请求成功,一般用于GET与POST请求 |
201 | Created | 已创建,成功请求创建了新的资源 |
202 | Accepted | 已接受,已经接受请求,但未处理完成 |
203 | Non-Authoritative Information | 非授权信息,请求成功,但返回的meta信息不在原始的服务器,而是一个副本 |
204 | No Content | 无内容,服务器成功处理,但未返回内容。在未更新的网页情况下,可确保浏览器继续显示当前文档 |
205 | Reset Content | 重置内容,服务器处理成功,用户终端(浏览器)应重置文档视图,可通过此返回码清除浏览器的表单域 |
206 | Partial Content | 部分内容,服务器成功处理了部分GET请求 |
3开头状态码
状态码 | 状态码英文名称 | 描述 |
---|---|---|
300 | Multiple Choices | 多种选择,请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端选择 |
301 | Moved Permanently | 永久跳转。请求的资源已被永久移动到新的URL,返回信息会包括新的URL,浏览器会自动定向到新URL,今后任何新的请求都应使用新的URL代替 |
302 | Found | 临时移动,与301类似,但资源只是临时被移动,客户端应继续使用原有的URL |
303 | See Other | 查看其它地址,与301类似,使用GET和POST请求查看 |
304 | Not Modified | 未修改,所请求的资源未修改,服务器返回此状态码时,不会返回任何资源,客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源 |
305 | Use Proxy | 使用代理,所请求的资源必须通过代理访问 |
306 | Unused | 已经被废弃的HTTP状态码 |
307 | Unused | 临时重定向,与302类似,使用GET请求重定向 |
4开头状态码
状态码 | 状态码英文名称 | 描述 |
---|---|---|
400 | Bad Request | 客户端请求的语法错误,服务器无法理解 |
401 | Unauthorized | 请求要求用户的身份认证 |
402 | Payment Required | 保留,将来使用 |
403 | Forbidden | 服务器拒绝执行此请求 |
404 | Not Found | 服务器无法根据客户端的请求找到资源(网页),通过此代码,网站设计人员可设置“你所请求的资源无法找到”的个性页面 |
405 | Method Not Allowed | 客户端请求中的访求被禁止 |
406 | Not Acceptable | 服务器无法根据客户端请求的内容性完成请求 |
407 | Proxy Authentication Required | 请求要求代理的身份证,与401类似,但请求者应当使用代理进行授权 |
408 | Request Time-out | 超时,服务器等待客户端发送的请求时间过长 |
409 | Conflict | 服务器完成客户端的PUT请求是可能返回此代码,服务器处理请求时发生了冲突 |
410 | Gone | 客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置 |
411 | Length Required | 服务器无法处理客户端发送的不带Content-Length的请求信息 |
412 | Precondition Failed | 客户端请求信息的先决条件错误 |
413 | Request Entity Too Large | 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息 414 Request-URI Too Large 请求的URI过长(URI通常为网址),服务器无法处理 |
414 | Request-URI Too Large | 请求的URI过长(URI通常为网址),服务器无法处理 |
415 | Unsupported Media Type | 服务器无法处理请求附带的媒体格式 |
416 | Requested range not satisfiable | 客户端请求的范围无效 |
417 | Expectation Failed | 服务器无法满足Expect的请求头信息 |
5开头状态码
状态码 | 状态码英文名称 | 描述 |
---|---|---|
500 | Internal Server Error | 服务器内部错误,无法完成请求 |
501 | Not Implemented | 服务器不支持请求的功能,无法完成请求 |
502 | Bad Gateway | 充当网关或代理的服务器,从远端服务器接收到了一个无效的请求 |
503 | Service Unavailable | 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中 |
504 | Gateway Time-out | 充当网关或代理的服务器,未及时从远端服务器获取请求 |
505 | HTTP Version not supported | 服务器不支持请求的HTTP协议的版本,无法完成处理 |