Nginx#Nginx-Typora笔记

  • Post author:
  • Post category:其他




一、HTTP 介绍

HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。

HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。



1、HTTP 工作原理

HTTP协议工作于客户端-服务端架构上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。

Web服务器有:Nginx,Apache服务器,IIS服务器(Internet Information Services)等。

Web服务器根据接收到的请求后,向客户端发送响应信息。

HTTP默认端口号为80,但是你也可以改为8080或者其他端口。


HTTP三点注意事项:

  • HTTP是无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  • HTTP是媒体独立的:这意味着,只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的MIME-type内容类型。
  • HTTP是无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

以下图表展示了HTTP协议通信流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LwIHXNE6-1602213757497)(assets/1561893004169.png)]



2、HTTP 消息结构

HTTP是基于客户端/服务端(C/S)的架构模型,通过一个可靠的链接来交换信息,是一个无状态的请求/响应协议。

一个HTTP”客户端”是一个应用程序(Web浏览器或其他任何客户端),通过连接到服务器达到向服务器发送一个或多个HTTP的请求的目的。

一个HTTP”服务器”同样也是一个应用程序(通常是一个Web服务,如Apache Web服务器或IIS服务器等),通过接收客户端的请求并向客户端发送HTTP响应数据。

HTTP使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和建立连接。

一旦建立连接后,数据消息就通过类似Internet邮件所使用的格式[RFC5322]和多用途Internet邮件扩展(MIME)[RFC2045]来传送。



3、客户端请求消息

客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。

在这里插入图片描述



4、服务器响应消息

HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TwJqV4vx-1602213757504)(assets/1561893177737.png)]


实例

下面实例是典型的使用GET来传递数据的实例:

客户端请求:

Connected to www.testpm.cn (47.244.247.240) port 80 (#0)
> GET /hello.txt HTTP/1.1   # 请求方式与版本协议。
> User-Agent: curl/7.29.0   #用什么客户端访问
> Host: www.testpm.cn  #主机名,域名。主机和端口号,
> Accept: */*  #匹配什么文件类型,“*” 是通用匹配。匹配所有类型

服务端响应:

< HTTP/1.1 200 OK       #请求返回的状态码
< Server: nginx/1.16.0  #请求的服务和版本号
< Date: Thu, 04 Jul 2019 08:19:40 GMT
< Content-Type: text/plain #文本类型,有html,plain:普通文本
< Content-Length: 12
< Last-Modified: Thu, 04 Jul 2019 08:13:25 GMT
< Connection: keep-alive  #是否支持长连接
< ETag: "5d1db525-c"  #标识,每次访问如果与最开始的一样返回304否则校验不一致返回200
< Accept-Ranges: bytes

输出结果:

hello world



5、HTTP 请求方法

根据HTTP标准,HTTP请求可以使用多种请求方法。

HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。

HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JrW8DBqy-1602213757507)(assets/1561896279402.png)]

get:正常请求资源

post:登录界面表单



6、HTTP 响应头信息

HTTP请求头提供了关于请求,响应或者其他的发送实体的信息。

在本章节中我们将具体来介绍HTTP响应头信息。

Allow

Content-Encoding

Content-Type

应答头 说明
Allow 服务器支持哪些请求方法(如GET、POST等)。
Content-Encoding 文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader(“Accept-Encoding”))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面。
Content-Length 表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入 ByteArrayOutputStream,完成后查看其大小,然后把该值放入Content-Length头,最后通过byteArrayStream.writeTo(response.getOutputStream()发送内容。
Content-Type 表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置Content-Type,因此HttpServletResponse提供了一个专用的方法setContentType。
Date 当前的GMT(格林威治时间)时间GMT+8。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦。
Expires 应该在什么时候认为文档已经过期,从而不再缓存它?
Last-Modified 文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档才会返回,否则返回一个304(Not Modified)状态。Last-Modified也可用setDateHeader方法来设置。
Location 表示客户应当到哪里去提取文档。Location通常不是直接设置的,而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302。
Refresh 表示浏览器应该在多少时间之后刷新文档,以秒计。除了刷新当前文档之外,你还可以通过setHeader(“Refresh”, “5; URL=http://host/path”)让浏览器读取指定的页面。 注意这种功能通常是通过设置HTML页面HEAD区的<META HTTP-EQUIV=“Refresh” CONTENT=“5;URL=http://host/path”>实现,这是因为,自动刷新或重定向对于那些不能使用CGI或Servlet的HTML编写者十分重要。但是,对于Servlet来说,直接设置Refresh头更加方便。 注意Refresh的意义是”N秒之后刷新本页面或访问指定页面”,而不是”每隔N秒刷新本页面或访问指定页面”。因此,连续刷新要求每次都发送一个Refresh头,而发送204状态代码则可以阻止浏览器继续刷新,不管是使用Refresh头还是<META HTTP-EQUIV=“Refresh” …>。 注意Refresh头不属于HTTP 1.1正式规范的一部分,而是一个扩展,但Netscape和IE都支持它。
Server 服务器名字。Servlet一般不设置这个值,而是由Web服务器自己设置。
Set-Cookie 设置和页面关联的Cookie。Servlet不应使用response.setHeader(“Set-Cookie”, …),而是应使用HttpServletResponse提供的专用方法addCookie。参见下文有关Cookie设置的讨论。
WWW-Authenticate 客户应该在Authorization头中提供什么类型的授权信息?在包含401(Unauthorized)状态行的应答中这个头是必需的。例如,response.setHeader(“WWW-Authenticate”, “BASIC realm=\“executives\””)。 注意Servlet一般不进行这方面的处理,而是让Web服务器的专门机制来控制受密码保护页面的访问(例如.htaccess)。



7、HTTP 状态码

当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头(server header)用以响应浏览器的请求。

HTTP状态码的英文为HTTP Status Code。

下面是常见的HTTP状态码:

  • 200 – 请求成功
  • 301 – 资源(网页等)被永久转移到其它URL
  • 404 – 请求的资源(网页等)不存在
  • 500 – 内部服务器错误


HTTP协议状态码含义

​ 200  OK,服务器成功返回网页

301  永久跳转,请求的网页已永久跳转到新位置

403  禁止访问,服务器拒绝请求

404  服务器找不到请求的页面

500  内部服务器错误

502  坏的网关,一般是网关服务器请求后端服务时,后端服务没有按照http协议正确返回结果

503  服务当前不可用,可能因为超载或停机维护

504  网关超时,一般是网关服务器请求后端服务时,后端服务没有在特定的时间内完成服务


HTTP状态码分类

HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用。HTTP状态码共分为5种类型:

200、301、302、304、400、401、403、404、500、501、502、

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QACpudWc-1602213757509)(assets/1561896413177.png)]

HTTP状态码列表:

状态码 状态码英文名称 中文描述
100 Continue 继续。客户端应继续其请求
101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议
200 OK 请求成功。一般用于GET与POST请求
201 Created 已创建。成功请求并创建了新的资源
202 Accepted 已接受。已经接受请求,但未处理完成
203 Non-Authoritative Information 非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本
204 No Content 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
205 Reset Content 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
206 Partial Content 部分内容。服务器成功处理了部分GET请求
300 Multiple Choices 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
301 Moved Permanently 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
302 Found 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
303 See Other 查看其它地址。与301类似。使用GET和POST请求查看
304 Not Modified 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
305 Use Proxy 使用代理。所请求的资源必须通过代理访问
306 Unused 已经被废弃的HTTP状态码
307 Temporary Redirect 临时重定向。与302类似。使用GET请求重定向
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通常为网址),服务器无法处理
415 Unsupported Media Type 服务器无法处理请求附带的媒体格式
416 Requested range not satisfiable 客户端请求的范围无效
417 Expectation Failed 服务器无法满足Expect的请求头信息
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协议的版本,无法完成处理



二、nginx 服务



1、nginx 介绍

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SgX0xyEd-1602213757511)(assets/1561897043854.png)]


Nginx

(engine x) 是一个高性能的 HTTP 和 反向代理 服务,也是一个IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。2011年6月1日,nginx 1.0.4发布。

Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、

京东



新浪



网易



腾讯



淘宝

等。

在高连接并发的情况下,Nginx是Apache服务器不错的替代品。


创始人伊戈尔·赛索耶夫

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QEnmK4UH-1602213757513)(assets/1561897072438.png)]



2、为什么选择 nginx

Nginx 是一个高性能的 Web 和反向代理服务器, 它具有有很多非常优越的特性:

单机环境下参考服务器配置。 并发连接数在7000+ -8000左右。 集群模式20000+


作为 Web 服务器

:相比 Apache,Nginx 使用更少的资源,支持更多的并发连接,体现更高的效率,这点使 Nginx 尤其受到虚拟主机提供商的欢迎。能够支持高达 50,000 个并发连接数的响应,感谢 Nginx 为我们选择了 epoll and kqueue 作为开发模型.


作为负载均衡服务器

:Nginx 既可以在内部直接支持 Rails 和 PHP,也可以支持作为 HTTP代理服务器 对外进行服务。Nginx 用 C 编写, 不论是系统资源开销还是 CPU 使用效率都比 Perlbal 要好的多。


作为邮件代理服务器

: Nginx 同时也是一个非常优秀的邮件代理服务器(最早开发这个产品的目的之一也是作为邮件代理服务器),Last.fm 描述了成功并且美妙的使用经验。


Nginx 安装非常的简单,配置文件 非常简洁(还能够支持perl语法),Bugs非常少的服务器

: Nginx 启动特别容易,并且几乎可以做到7*24不间断运行,即使运行数个月也不需要重新启动。你还能够在 不间断服务的情况下进行软件版本的升级。



3、IO多路复用



1、I/O multiplexing【多并发】

第一种方法就是最传统的多进程并发模型 (每进来一个新的I/O流会分配一个新的进程管理。)

第二种方法就是I/O多路复用 (单个线程,通过记录跟踪每个I/O流(sock)的状态,来同时管理多个I/O流 。)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fAvOqQpP-1602213757514)(assets/1561897144109.png)]

I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态来同时管理多个I/O流。发明它的原因,是尽量多的提高服务器的吞吐能力。

在同一个线程里面, 通过拨开关的方式,来同时传输多个I/O流

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4kcCQvCk-1602213757516)(assets/1561897166658.png)]



2、一个请求到来了,nginx使用epoll接收请求的过程是怎样的?

ngnix会有很多连接进来, epoll会把他们都监视起来,然后像拨开关一样,谁有数据就拨向谁,然后调用相

应的代码处理。


  • select, poll, epoll

    都是I/O多路复用的具体的实现,其实是他们出现是有先后顺序的。

I/O多路复用这个概念被提出来以后, 相继出现了多个方案


  • select

    是第一个实现 (1983 左右在BSD里面实现的)。

select 被实现以后,很快就暴露出了很多问题。

• select 会修改传入的参数数组,这个对于一个需要调用很多次的函数,是非常不友好的。

• select 如果任何一个sock(I/O stream)出现了数据,select 仅仅会返回,但是并不会告诉你是那个sock上有数据,于是你只能自己一个一个的找,10几个sock可能还好,要是几万的sock每次都找一遍…

• select 只能监视1024个链接。

• select 不是线程安全的,如果你把一个sock加入到select, 然后突然另外一个线程发现,这个sock不用,要收

回,这个select 不支持的,如果你丧心病狂的竟然关掉这个sock, select的标准行为是不可预测的

  • 于是14年以后(1997年)一帮人又实现了

    poll,

    poll 修复了select的很多问题,比如

• poll 去掉了1024个链接的限制,于是要多少链接呢, 主人你开心就好。

• poll 从设计上来说,不再修改传入数组,不过这个要看你的平台了,所以行走江湖,还是小心为妙。

其实拖14年那么久也不是效率问题, 而是那个时代的硬件实在太弱,一台服务器处理1千多个链接简直就是神一样的存在了,select很长段时间已经满足需求。

但是poll仍然不是线程安全的, 这就意味着,不管服务器有多强悍,你也只能在一个线程里面处理一组I/O流。

你当然可以那多进程来配合了,不过然后你就有了多进程的各种问题。

  • 于是5年以后, 在2002, 大神 Davide Libenzi 实现了

    epoll

    .

epoll 可以说是I/O 多路复用最新的一个实现,epoll 修复了poll 和select绝大部分问题, 比如:

• epoll 现在是线程安全的。

• epoll 现在不仅告诉你sock组里面数据,还会告诉你具体哪个sock有数据,你不用自己去找了。



3、异步,非阻塞

$ pstree |grep nginx

|-+= 81666 root nginx: master process nginx

| |— 82500 nobody nginx: worker process

| — 82501 nobody nginx: worker process

1个master进程,2个work进程

​ 每进来一个request,会有一个worker进程去处理。但不是全程的处理,处理到什么程度呢?处理到可能发生阻塞的地方,比如向上游(后端)服务器转发request,并等待请求返回。那么,这个处理的worker不会这么一直等着,他会在发送完请求后,注册一个事件:“如果upstream返回了,告诉我一声,我再接着干”。于是他就休息去了。这就是异步。此时,如果再有request 进来,他就可以很快再按这种

方式处理。这就是非阻塞和IO多路复用。而一旦上游服务器返回了,就会触发这个事件,worker才会来

接手,这个request才会接着往下走。这就是异步回调。



4、nginx 的内部技术架构

Nginx服务器,以其处理网络请求的高并发、高性能及高效率,获得了行业界的广泛认可,近年已稳居web服务器部署排名第二的位置,并被广泛用于反向代理和负载均衡。

Nginx是如何实现这些目标的呢?答案就是其独特的内部技术架构设计。看懂下面这张图,就明白了Nginx的内部技术架构。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HUNDe9IZ-1602213757517)(assets/1561897235155.png)]

简要说明几点:

1)nginx启动时,会生成两种类型的进程,一个是主进程(Master),一个(windows版本的目前只有一个)或多个工作进程(Worker)。主进程并不处理网络请求,主要负责调度工作进程,也就是图示的三项:加载配置、启动工作进程及非停升级。所以,nginx启动以后,查看操作系统的进程列表,我们就能看到至少有两个nginx进程。

2)服务器实际处理网络请求及响应的是工作进程(worker),在类unix系统上,nginx可以配置多个worker,而每个worker进程都可以同时处理数以千计的网络请求。

3)模块化设计。nginx的worker,包括核心和功能性模块,核心模块负责维持一个运行循环(run-loop),执行网络请求处理的不同阶段的模块功能,如网络读写、存储读写、内容传输、外出过滤,以及将请求发往上游服务器等。而其代码的模块化设计,也使得我们可以根据需要对功能模块进行适当的选择和修改,编译成具有特定功能的服务器。

4)事件驱动、异步及非阻塞,可以说是nginx得以获得高并发、高性能的关键因素,同时也得益于对Linux、Solaris及类BSD等操作系统内核中事件通知及I/O性能增强功能的采用,如kqueue、epoll及event ports。

5)代理(proxy)设计,可以说是nginx深入骨髓的设计,无论是对于HTTP,还是对于FastCGI、memcache、Redis等的网络请求或响应,本质上都采用了代理机制。所以,nginx天生就是高性能的代理服务器



5、nginx安装部署和配置管理


1、nginx部署-Yum安装

访问nginx的官方网站:

http://www.nginx.org/

Nginx版本类型

Mainline version: 主线版,即开发版

Stable version: 最新稳定版,生产环境上建议使用的版本

Legacy versions: 遗留的老版本的稳定版

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gBiqKe3L-1602213757519)(assets/1561531095060.png)]


Yum安装nginx

配置Yum源的官网:

http://nginx.org/en/linux_packages.html


1、配置nginx的Yum源

Installation instructions

Before you install nginx for the first time on a new machine, you need to set up the nginx packages repository. Afterward, you can install and update nginx from the repository.

安装说明

在新计算机上首次安装nginx之前,需要设置nginx软件包存储库。 之后,您可以从存储库安装和更新nginx。


RHEL/CENTOS

Install the prerequisites:

sudo yum install yum-utils -y

To set up the yum repository, create the file named

/etc/yum.repos.d/nginx.repo

with the following contents:

[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key

By default, the repository for stable nginx packages is used. If you would like to use mainline nginx packages, run the following command:

sudo yum-config-manager --enable nginx-mainline

To install nginx, run the following command:

sudo yum install nginx -y

When prompted to accept the GPG key, verify that the fingerprint matches

573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62

, and if so, accept it.

当提示您接受GPG密钥时,请验证指纹是否匹配

573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62

,如果匹配 ,请接受它。


这里我们用稳定版本

[root@nginx-server yum.repos.d]# yum install -y nginx

[root@nginx-server yum.repos.d]# nginx -V    //格式化打印
nginx version: nginx/1.16.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) 
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'

[root@nginx-server yum.repos.d]# nginx -v
nginx version: nginx/1.16.0


关闭防火墙和selinux:

[root@nginx-server ~]# getenforce 
Enforcing

[root@nginx-server ~]# sed -i '/SELINUX/s/enforcing/disabled/' /etc/selinux/config

[root@nginx-server ~]# systemctl stop firewalld
[root@nginx-server ~]# systemctl disable firewalld


启动并设置开机启动

[root@nginx-server ~]# systemctl start nginx 
[root@nginx-server ~]# systemctl enable nginx 

浏览器输入ip访问:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xBI6wUeo-1602213757521)(assets/1561536791743.png)]



6、nginx 编译安装与配置使用



1、安装编译环境


yum -y install gcc gcc-c++



2、安装pcre软件包(使nginx支持http rewrite模块)


yum install -y pcre pcre-devel



3、安装openssl-devel(使nginx支持ssl)


yum install -y openssl openssl-devel



4、安装zlib


yum install -y zlib zlib-devel



5、创建用户nginx


useradd nginx


passwd nginx


6、安装nginx

[root@localhost ~]# wget http://nginx.org/download/nginx-1.16.0.tar.gz
[root@localhost ~]# tar xzf nginx-1.16.0.tar.gz -C /usr/local/
[root@localhost ~]# cd nginx-1.16.0
[root@localhost ~]# wget https://github.com/openresty/echo-nginx-module/archive/v0.61.tar.gz
[root@localhost ~]# tar xf v0.61.tar.gz
[root@localhost nginx-1.16.0]# ./configure --prefix=/usr/local/nginx --group=nginx --user=nginx --sbin-path=/usr/local/nginx/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/tmp/nginx/client_body --http-proxy-temp-path=/tmp/nginx/proxy --http-fastcgi-temp-path=/tmp/nginx/fastcgi --pid-path=/var/run/nginx.pid --lock-path=/var/lock/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module --with-pcre --with-http_realip_module --with-stream # --add-module=./echo-nginx-module-0.61 --add-module=./ngx_req_status-master
[root@localhost nginx-1.16.0]# m	ake && make install


7、Nginx 编译参数

# 查看 nginx 安装的模块
[root@localhost ~]#/usr/local/nginx/sbin/nginx -V
# 模块参数具体功能 
--with-cc-opt='-g -O2 -fPIE -fstack-protector    //设置额外的参数将被添加到CFLAGS变量。(FreeBSD或者ubuntu使用)
--param=ssp-buffer-size=4 -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2' 
--with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -Wl,-z,relro -Wl,-z,now' 

--prefix=/usr/local/nginx                        //指向安装目录
--conf-path=/etc/nginx/nginx.conf                //指定配置文件
--http-log-path=/var/log/nginx/access.log        //指定访问日志
--error-log-path=/var/log/nginx/error.log        //指定错误日志
--lock-path=/var/lock/nginx.lock                 //指定lock文件
--pid-path=/run/nginx.pid                        //指定pid文件

--http-client-body-temp-path=/var/lib/nginx/body    //设定http客户端请求临时文件路径
--http-fastcgi-temp-path=/var/lib/nginx/fastcgi     //设定http fastcgi临时文件路径
--http-proxy-temp-path=/var/lib/nginx/proxy         //设定http代理临时文件路径
--http-scgi-temp-path=/var/lib/nginx/scgi           //设定http scgi临时文件路径
--http-uwsgi-temp-path=/var/lib/nginx/uwsgi         //设定http uwsgi临时文件路径

--with-debug                                        //启用debug日志
--with-pcre-jit                                     //编译PCRE包含“just-in-time compilation”
--with-ipv6                                         //启用ipv6支持
--with-http_ssl_module                              //启用ssl支持
--with-http_stub_status_module                      //获取nginx自上次启动以来的状态
--with-http_realip_module                 //允许从请求标头更改客户端的IP地址值,默认为关
--with-http_auth_request_module           //实现基于一个子请求的结果的客户端授权。如果该子请求返回的2xx响应代码,所述接入是允许的。如果它返回401或403中,访问被拒绝与相应的错误代码。由子请求返回的任何其他响应代码被认为是一个错误。
--with-http_addition_module               //作为一个输出过滤器,支持不完全缓冲,分部分响应请求
--with-http_dav_module                    //增加PUT,DELETE,MKCOL:创建集合,COPY和MOVE方法 默认关闭,需编译开启
--with-http_geoip_module                  //使用预编译的MaxMind数据库解析客户端IP地址,得到变量值
--with-http_gunzip_module                 //它为不支持“gzip”编码方法的客户端解压具有“Content-Encoding: gzip”头的响应。
--with-http_gzip_static_module            //在线实时压缩输出数据流
--with-http_image_filter_module           //传输JPEG/GIF/PNG 图片的一个过滤器)(默认为不启用。gd库要用到)
--with-http_spdy_module                   //SPDY可以缩短网页的加载时间
--with-http_sub_module                    //允许用一些其他文本替换nginx响应中的一些文本
--with-http_xslt_module                   //过滤转换XML请求
--with-mail                               //启用POP3/IMAP4/SMTP代理模块支持
--with-mail_ssl_module                    //启用ngx_mail_ssl_module支持启用外部模块支持


8、修改配置文件/etc/nginx/nginx.conf

# 全局参数设置 
worker_processes  4;          #设置nginx启动进程的数量,一般设置成与逻辑cpu数量相同 
error_log  logs/error.log;    #指定错误日志 
worker_rlimit_nofile 102400;  #设置一个nginx进程能打开的最大文件数 
pid        /var/run/nginx.pid; 
events { 
    worker_connections  1024; #设置一个进程的最大并发连接数 
}
# http 服务相关设置 
http { 
    include      mime.types; 
    default_type  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"'; 
    access_log  /var/log/nginx/access.log  main;    #设置访问日志的位置和格式 
    sendfile          on; #是否调用sendfile函数输出文件,一般设置为on,若nginx是用来进行磁盘IO负载应用时,可以设置为off,降低系统负载 
    gzip              on;      #是否开启gzip压缩,将注释去掉开启 
    keepalive_timeout  65;     #设置长连接的超时时间
# 虚拟服务器的相关设置 
    server { 
        listen      80;        #设置监听的端口 
        server_name  localhost;        #设置绑定的主机名、域名或ip地址 
        charset koi8-r;        # 设置编码字符 
        location / { 
            root  /var/www/nginx;           #设置服务器默认网站的根目录位置,需要手动创建
            index  index.html index.htm;    #设置默认打开的文档 
            } 
        error_page  500 502 503 504  /50x.html; #设置错误信息返回页面 
        location = /50x.html { 
            root  html;        #这里的绝对位置是/usr/local/nginx/html
        } 
    } 
 }
nginx.conf的组成:nginx.conf一共由三部分组成,分别为:全局块、events块、http块。在http块中又包含http全局块、多个server块。每个server块中又包含server全局块以及多个location块。在统一配置块中嵌套的配置快,各个之间不存在次序关系。


检测nginx配置文件是否正确

[root@localhost ~]# /usr/local/nginx/sbin/nginx -t
[root@localhost ~]# mkdir -p /tmp/nginx


10、启动nginx服务

[root@localhost ~]# /usr/local/nginx/sbin/nginx


11、通过 nginx 命令控制 nginx 服务

nginx -c /path/nginx.conf  	     # 以特定目录下的配置文件启动nginx:
nginx -s reload            	 	 # 修改配置后重新加载生效
nginx -s reopen   			 	 # 重新打开日志文件
nginx -s stop  				 	 # 快速停止nginx
nginx -s quit  				  	 # 完整有序的停止nginx
nginx -t    					 # 测试当前配置文件是否正确
nginx -t -c /path/to/nginx.conf  # 测试特定的nginx配置文件是否正确

注意:
nginx -s reload 命令加载修改后的配置文件,命令下达后发生如下事件
1. Nginx的master进程检查配置文件的正确性,若是错误则返回错误信息,nginx继续采用原配置文件进行工作(因为worker未受到影响)
2. Nginx启动新的worker进程,采用新的配置文件
3. Nginx将新的请求分配新的worker进程
4. Nginx等待以前的worker进程的全部请求已经都返回后,关闭相关worker进程
5. 重复上面过程,知道全部旧的worker进程都被关闭掉


12、实现nginx开机自启

[root@localhost ~]# vim /etc/init.d/nginx
#!/bin/sh 
# 
# nginx - this script starts and stops the nginx daemon 
# 
# chkconfig:  - 85 15  
# description:  Nginx is an HTTP(S) server, HTTP(S) reverse \ 
#              proxy and IMAP/POP3 proxy server 
# processname: nginx 
# config:      /etc/nginx/nginx.conf 
# config:      /etc/sysconfig/nginx 
# pidfile:    /var/run/nginx.pid 
  
# Source function library. 
. /etc/rc.d/init.d/functions
  
# Source networking configuration. 
. /etc/sysconfig/network
  
# Check that networking is up. 
[ "$NETWORKING" = "no" ] && exit 0 
  
nginx="/usr/local/nginx/sbin/nginx"
prog=$(basename $nginx) 
  
NGINX_CONF_FILE="/etc/nginx/nginx.conf"
  
[ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx
  
lockfile=/var/lock/nginx
  
make_dirs() { 
  # make required directories 
  user=`nginx -V 2>&1 | grep "configure arguments:" | sed 's/[^*]*--user=\([^ ]*\).*/\1/g' -` 
  options=`$nginx -V 2>&1 | grep 'configure arguments:'` 
  for opt in $options; do
      if [ `echo $opt | grep '.*-temp-path'` ]; then
          value=`echo $opt | cut -d "=" -f 2` 
          if [ ! -d "$value" ]; then
              # echo "creating" $value 
              mkdir -p $value && chown -R $user $value 
          fi
      fi
  done
} 
  
start() { 
    [ -x $nginx ] || exit 5 
    [ -f $NGINX_CONF_FILE ] || exit 6 
    make_dirs 
    echo -n $"Starting $prog: "
    daemon $nginx -c $NGINX_CONF_FILE 
    retval=$? 
    echo
    [ $retval -eq 0 ] && touch $lockfile 
    return $retval 
} 
  
stop() { 
    echo -n $"Stopping $prog: "
    killproc $prog -QUIT 
    retval=$? 
    echo
    [ $retval -eq 0 ] && rm -f $lockfile 
    return $retval 
} 
  
restart() { 
    configtest || return $? 
    stop 
    sleep 1 
    start 
} 
  
reload() { 
    configtest || return $? 
    echo -n $"Reloading $prog: "
    killproc $nginx -HUP 
    RETVAL=$? 
    echo
} 
  
force_reload() { 
    restart 
} 
  
configtest() { 
  $nginx -t -c $NGINX_CONF_FILE 
} 
  
rh_status() { 
    status $prog 
} 
  
rh_status_q() { 
    rh_status >/dev/null 2>&1 
} 
  
case "$1" in
    start) 
        rh_status_q && exit 0 
        $1 
        ;; 
    stop) 
        rh_status_q || exit 0 
        $1 
        ;; 
    restart|configtest) 
        $1 
        ;; 
    reload) 
        rh_status_q || exit 7 
        $1 
        ;; 
    force-reload) 
        force_reload 
        ;; 
    status) 
        rh_status 
        ;; 
    condrestart|try-restart) 
        rh_status_q || exit 0 
            ;; 
    *) 
        echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
        exit 2 
esac

b、添加权限

chmod +x /etc/init.d/nginx

c、重新加载系统启动文件

systemctl daemon-reload

d、启动并设置开机自启

systemctl start nginx
[root@localhost ~]# /sbin/chkconfig nginx on ---开机启动


10、nginx 日志文件详解

​ nginx 日志文件分为

log_format



access_log

两部分

​ log_format 定义记录的格式,其语法格式为

​ log_format 样式名称 样式详情

​ 配置文件中默认有

log_format  main  'remote_addr - remote_user [time_local] "request" '
                  'status body_bytes_sent "$http_referer" '
                  '"http_user_agent" "http_x_forwarded_for"';

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XYu6hXjl-1602213757522)(assets/1561599608585.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6U2dJnE4-1602213757524)(assets/1561599718230.png)]


6、nginx 高级应用

1.使用alias实现虚拟目录;

location /test {
            alias   /var/www/qianfeng/;
            index  index.html; #访问http://ip/test时实际上访问是/var/www/qianfeng/index.html
        }


6、通过 stub_status 模块监控 nginx 的工作状态

​ 1、通过 nginx -V 命令查看是否已安装 stub_status 模块

​ 2、编辑 /etc/nginx/nginx.conf 配置文件

#添加以下内容~~ 
location /nginx-status { 
      stub_status on; 
      access_log    /var/log/nginx/nginxstatus.log;    #设置日志文件的位置 
      auth_basic    "nginx-status";    #指定认证机制(与location后面的内容相同即可) 
      auth_basic_user_file    /etc/nginx/htpasswd;     #指定认证的密码文件 
      }

3.创建认证口令文件并添加用户qianfeng和zdgg,密码用md5加密

[root@localhost ~]# yum install -y httpd-tools  #htpasswd 是开源 http 服务器 apache httpd 的一个命令工具,用于生成 http 基本认证的密码文件
htpasswd -c -m /etc/nginx/htpasswd qianfeng             # -c 创建解密文件,-m MD5加密
htpasswd -m /etc/nginx/htpasswd zsgg

4、重启服务

5、客户端访问 http://ip/nginx-status 即可



7、使用 limit_rate 限制客户端传输数据的速度

1、编辑/etc/nginx/nginx.conf

location / {
            root   /var/www/nginx/;
            index  index.html index.htm;
            limit_rate  2k;  #对每个连接的限速为2k/s
        }

重启服务


注意要点:

  • ​ 配置文件中的每个语句要以 ; 结尾

  • ​ 使用 htpasswd 命令需要先安装 httpd-tools



7、nginx 虚拟主机配置


什么是虚拟主机?


虚拟主机是一种特殊的软硬件技术,它可以将网络上的每一台计算机分成多个虚拟主机,每个虚拟主机可以独立对外提供www服务,这样就可以实现一台主机对外提供多个web服务,每个虚拟主机之间是独立的,互不影响。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x0rBjLwt-1602213757525)(assets/1561605672295.png)]

nginx可以实现虚拟主机的配置,nginx支持三种类型的虚拟主机配置。

1、基于域名的虚拟主机 (server_name来区分虚拟主机——应用:外部网站)

2、基于ip的虚拟主机, (一台主机绑定多个ip地址)

3、基于端口的虚拟主机 (端口来区分虚拟主机——应用:公司内部网站,外部网站的管理后台)


1、 基于域名的虚拟主机

1、配置通过域名区分的虚拟机

[root@localhost ~]# cat /etc/nginx/nginx.conf
worker_processes  4;

#error_log  logs/error.log;
worker_rlimit_nofile 102400;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;
    
    server {
        listen       80;
        server_name  web.testpm.com;
        location / {
            root   /var/www/nginx/;
            index  index.html index.htm;
            limit_rate	2k;
        	}
        }
    
    server {
        listen       80;
        server_name  web.1000phone.com;
        location / {
            root   /1000phone/html;
            index  index.html index.htm;
        	}
        }
}

2、 为 域名为 web.1000phone.com 的虚拟机,创建 index 文件

[root@localhost ~]# mkdir -p /1000phone/html
[root@localhost ~]# vim /1000phone/html/index.html
<html>
<p>
this is my 1000phone
</p>
</html>

3、重新加载配置文件

# 如果编译安装的执行
[root@nginx]# /usr/local/nginx/sbin/nginx -s reload
# 如果 yum 安装的执行
[root@nginx]# nginx -s reload

4、客户端配置路由映射

在 C:\Windows\System32\drivers\etc\hosts 文件中添加两行(linux:/etc/hosts)

10.0.105.199 web.testpm.com
10.0.105.199 web.1000phone.com

5、 测试访问

浏览器输入:http://web.testpm.com/

浏览器输入:http://web.1000phone.com/

6、补充:如果配置不能正常访问,

问题描述: 配置完 nginx 两个虚拟机后,客户端能够访问原始的server ,新增加的 server 虚拟机 不能够访问,报错如下页面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ltXJu7wI-1602213757526)(assets/1561607242127.png)]

解决过程:

  1. 查看报错日志(找到错误日志)

    [root@localhost ~]# cat /var/log/nginx/error.log
    2017/06/15 04:00:57 [error] 6702#0: *14 "/root/html/index.html" is forbidden (13: Permission denied), client: 10.219.24.1, server: web.1000phone.com, request: "GET / HTTP/1.1", host: "web.1000phone.com"
    

    2.检查权限

    [root@localhost html]# ll
    drwxr-xr-x. 2 root root 4096 Jun 15 03:59 html
    [root@localhost nginx]# ll
    total 8
    -rw-r--r--. 1 root root 537 Jun 15 03:59 50x.html
    -rw-r--r--. 1 root root 616 Jun 15 03:51 index.html
    说明:发现目录权限没有问题
    

    3.检查nginx启动进程

    [root@localhost nginx]# ps -ef | grep nginx
    root       2079      1  0 12:16 ?        00:00:00 nginx: master process /usr/local/nginx/sbin/nginx -c /etc/nginx/nginx.conf
    nobody     2080   2079  0 12:16 ?        00:00:00 nginx: worker process
    nobody     2081   2079  0 12:16 ?        00:00:00 nginx: worker process
    nobody     2082   2079  0 12:16 ?        00:00:00 nginx: worker process
    说明:发现nginx的work process是 nobody 的
    

    4.修改 nginx.conf 文件

    打开nginx.conf文件所在的目录,查看文件的属性 (root root)
    [root@nginx]# ll
    drwxr-xr-x. 2 root root 4096 Jun 15 04:08 conf
    在nginx.conf文件的第一行加上 user root root;
    [root@nginx]# cat conf/nginx.conf
    user root;
    

    重新 reload nginx进程


2、 基于ip的虚拟主机

[root@localhost ~]# ip a 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:29:17:f1:af brd ff:ff:ff:ff:ff:ff
    inet 10.0.105.199/24 brd 10.0.105.255 scope global dynamic ens33
       valid_lft 81438sec preferred_lft 81438sec
    inet6 fe80::9d26:f3f0:db9c:c9be/64 scope link 
       valid_lft forever preferred_lft forever
[root@localhost ~]# ifconfig ens33:1 10.0.105.201/24
[root@localhost ~]# ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.105.199  netmask 255.255.255.0  broadcast 10.0.105.255
        inet6 fe80::9d26:f3f0:db9c:c9be  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:17:f1:af  txqueuelen 1000  (Ethernet)
        RX packets 9844  bytes 1052722 (1.0 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 5567  bytes 886269 (865.4 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens33:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.105.201  netmask 255.255.255.0  broadcast 10.0.105.255
        ether 00:0c:29:17:f1:af  txqueuelen 1000  (Ethernet)

2、配置通过ip区分的虚拟机
[root@localhost ~]# cat /etc/nginx/nginx.conf
user  root;
worker_processes  4;

#error_log  logs/error.log;
worker_rlimit_nofile 102400;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  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"';

    server {
        listen       10.0.105.199:80;
        server_name  web.testpm.com;
        location / {
            root   /var/www/nginx/;
            index  index.html index.htm;
            limit_rate	2k;
        }
        
     server {
        listen       10.0.105.201:80;
        server_name  web.testpm.com;
        location / {
            root   /1000phone/html/;
            index  index.html index.htm;
        	}
        }
}
3、重新加载配置文件
[root@localhost ~]# /usr/local/nginx/sbin/nginx -s reload
4、 测试访问
浏览器输入:http://10.0.105.199
浏览器输入:http://10.0.105.201
5、补充
-- 删除绑定的vip
[root@localhost ~]# ifconfig ens33:1 10.0.105.201/24 down
重启一下nginx
[root@localhost ~]# systemctl restart nginx


3、 基于端口的虚拟主机

[root@localhost ~]# cat /etc/nginx/nginx.conf
user  root;
worker_processes  4;

worker_rlimit_nofile 102400;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  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"';


    sendfile        on;

    keepalive_timeout  65;


    server {
        listen       80;
        server_name  web.testpm.com;
        location / {
            root   /var/www/nginx/;
            index  index.html index.htm;
            limit_rate	2k;
        }
        
    
     server {
        listen       8080;
        server_name  web.1000phone.com;
        location / {
            root   /1000phone/html/;
            index  index.html index.htm;
        	}
        }
}
重新加载配置文件:
[root@localhost ~]# /usr/local/nginx/sbin/nginx -s reload
测试访问:
浏览器输入:http://web.testpm.com/
浏览器输入:http://web.1000phone.com:8080



8、nginx Proxy 代理



1、代理原理
  • 反向代理产生的背景:

    在计算机世界里,由于单个服务器的处理客户端(用户)请求能力有一个极限,当用户的接入请求蜂拥而入时,会造成服务器忙不过来的局面,可以使用多个服务器来共同分担成千上万的用户请求,这些服务器提供相同的服务,对于用户来说,根本感觉不到任何差别。

  • 反向代理服务的实现:

    需要有一个负载均衡设备(即反向代理服务器)来分发用户请求,将用户请求分发到空闲的服务器上。

    服务器返回自己的服务到负载均衡设备。

    负载均衡设备将服务器的服务返回用户。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nSoNwIJl-1602213757527)(assets/1561616834649.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5NUDxDgj-1602213757529)(assets/1561616855038.png)]



2、正/反向代理的区别

那么问题来了,很多人这时会问什么是反向代理?为什么叫反向代理?什么是正向代理?我们来举例说明

  • 正向代理:

    举例:贷款

    正向代理的过程隐藏了真实的请求客户端,服务器不知道真实的客户端是谁,客户端请求的服务都被代理服务器代替请求。我们常说的代理也就是正向代理,正向代理代理的是请求方,也就是客户端;比如我们要访问youtube,可是不能访问,只能先安装个FQ软件代你去访问,通过FQ软件才能访问,FQ软件就叫作正向代理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZbqHP6cZ-1602213757530)(assets/1561616889383.png)]

FQ软件就是正向代理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5przYVVl-1602213757531)(assets/1561617051925.png)]

正向代理中,proxy和client同属一个LAN

反向代理:

反向代理的过程隐藏了真实的服务器,客户不知道真正提供服务的人是谁,客户端请求的服务都被代理服务器处理。反向代理代理的是响应方,也就是服务端;我们请求www.baidu.com时这www.baidu.com就是反向代理服务器,真实提供服务的服务器有很多台,反向代理服务器会把我们的请求分转发到真实提供服务的各台服务器。Nginx就是性能非常好的反向代理服务器,用来做负载均衡。

访问www.baidu.com是正向代理的过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BQOgQF8w-1602213757532)(assets/1561617154985.png)]

反向代理中,proxy和server同属一个LAN

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qx1D7p1p-1602213757533)(assets/1561617180382.png)]

正向代理和反向代理对比示意图

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

正向代理中代理的对象是客户端,proxy和client同属一个LAN,对server透明;

反向代理中代理的对象是服务端,proxy和server同属一个LAN,对client透明。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zy9w6sjb-1602213757534)(assets/1561618001304.png)]



3、知识扩展1
  1. 没有使用LVS时,客户端请求直接到反向代理Nginx,Nginx分发到各个服务器,服务端响应再由Ngnix返回给客户端,这样请求和响应都经过Ngnix的模式使其性能降低,这时用LVS+Nginx解决。

  2. LVS+Nginx,客户端请求先由LVS接收,分发给Nginx,再由Nginx转发给服务器,LVS有三种方式:NAT模式(Network Address Translation)网络地址转换,DR模式(直接路由模式),IP隧道模式,路由方式使服务器响应不经过LVS,由Nginx直接返回给客户端。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WReqpSK7-1602213757535)(assets/1561618131640.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uoz5GkmK-1602213757537)(assets/1561618191407.png)]



    4、知识扩展2
    1. HTTP Server和Application Server的区别和联系

      Apache/nignx是静态服务器(HTTP Server):

      Nginx优点:负载均衡、反向代理、处理静态文件优势。nginx处理静态请求的速度高于apache;

      Apache优点:相对于Tomcat服务器来说处理静态文件是它的优势,速度快。Apache是静态解析,适合静态HTML、图片等。

      HTTP Server 关心的是 HTTP 协议层面的传输和访问控制,所以在 Apache/Nginx 上你可以看到代理、负载均衡等功能

      HTTP Server(Nginx/Apache)常用做静态内容服务和代理服务器,将外来请求转发给后面的应用服务(tomcat,jboss,jetty等)。

      应用服务器(tomcat/jboss/jetty)是动态服务器(Application Server):

      应用服务器Application Server,则是一个应用执行的容器。它首先需要支持开发语言的 Runtime(对于 Tomcat 来说,就是 Java,若是Ruby/Python 等其他语言开发的应用也无法直接运行在 Tomcat 上)。

    2. 但是事无绝对,为了方便,应用服务器(如tomcat)往往也会集成 HTTP Server 的功能,nginx也可以通过模块开发来提供应用功能,只是不如专业的 HTTP Server 那么强大,所以应用服务器往往是运行在 HTTP Server 的背后,执行应用,将动态的内容转化为静态的内容之后,通过 HTTP Server 分发到客户端。

    3. 常用开源集群软件有:lvs,keepalived,haproxy,nginx,apache,heartbeat

      常用商业集群硬件有:F5, Netscaler,Radware,A10等


5、nginx Proxy 配置

1、代理模块

ngx_http_proxy_module

2、代理配置

代理
Syntax: 	proxy_pass URL;				   #代理的后端服务器URL
Default: 	—
Context: 	location, if in location, limit_except

缓冲区
Syntax:     proxy_buffering on | off;
Default:    proxy_buffering on;			   #缓冲开关
Context: 	http, server, location
proxy_buffering开启的情况下,nignx会把后端返回的内容先放到缓冲区当中,然后再返回给客户端
(边收边传,不是全部接收完再传给客户端)。
Nginx 全局配置中的 tcp_nopush 的作用就是 数据包会累计到一定大小之后才会发送 。而 tcp_nodelay 是尽快发送数据,所以若你启用了 buffer,建议关闭 tcp_nodelay。

Syntax:   	proxy_buffer_size size;
Default: 	proxy_buffer_size 4k|8k;	   #缓冲区大小
Context: 	http, server, location

Syntax: 	proxy_buffers number size;
Default: 	proxy_buffers 8 4k|8k;		   #缓冲区数量
Context: 	http, server, location

Syntax:    	proxy_busy_buffers_size size;
Default: 	proxy_busy_buffers_size 8k|16k;#忙碌的缓冲区大小控制同时传递给客户端的buffer数量
Context: 	http, server, location

头信息
Syntax: 	proxy_set_header field value;
Default: 	proxy_set_header Host $proxy_host;		#设置真实客户端地址
            proxy_set_header Connection close;
Context: 	http, server, location

超时
Syntax: 	proxy_connect_timeout time;
Default: 	proxy_connect_timeout 60s;				#链接超时
Context: 	http, server, location

Syntax: 	proxy_read_timeout time;
Default: 	proxy_read_timeout 60s;
Context: 	http, server, location

Syntax: 	proxy_send_timeout time; #nginx进程向fastcgi进程发送request的整个过程的超时时间
Default: 	proxy_send_timeout 60s;
Context: 	http, server, location

#buffer 工作原理
1. 所有的proxy buffer参数是作用到每一个请求的。每一个请求会安按照参数的配置获得自己的buffer。proxy buffer不是global而是 request的。

2. proxy_buffering 是为了开启response buffering of the proxied server,开启后proxy_buffers和proxy_busy_buffers_size参数才会起作用。

3. 无论proxy_buffering是否开启,proxy_buffer_size(main buffer)都是工作的,proxy_buffer_size所设置的buffer_size的作用是用来存储upstream端response的header。

4. 在proxy_buffering 开启的情况下,Nginx将会尽可能的读取所有的upstream端传输的数据到buffer,直到proxy_buffers设置的所有buffer们 被写满或者数据被读取完(EOF)。此时nginx开始向客户端传输数据,会同时传输这一整串buffer们。同时如果response的内容很大的话,Nginx会接收并把他们写入到temp_file里去。大小由proxy_max_temp_file_size控制。如果busy的buffer 传输完了会从temp_file里面接着读数据,直到传输完毕。

5. 一旦proxy_buffers设置的buffer被写入,直到buffer里面的数据被完整的传输完(传输到客户端),这个buffer将会一直处 在busy状态,我们不能对这个buffer进行任何别的操作。所有处在busy状态的buffer size加起来不能超过proxy_busy_buffers_size,所以proxy_busy_buffers_size是用来控制同时传输到客户端的buffer数量的。


3、启用 nginx proxy 代理

环境两台nginx真实服务器

a、nginx-1 启动网站(内容)(作为网站服务器)

nginx-1的ip:10.0.105.199
已经编译安装好,检查nginx是否启动是否可以访问

b、nginx-2 启动代理程序

nginx-2的ip:10.0.105.202
配置nginx的yum源直接yum安装
启动
编辑nginx的配置文件:
[root@nginx-server ~]# vim /etc/nginx/conf.d/default.conf
server {
    server {
    listen       80;
    server_name  localhost;

    location / {
    proxy_pass http://10.0.105.199:80;
    proxy_redirect default;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    #proxy_set_header REMOTE-HOST $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_connect_timeout 30;
    proxy_send_timeout 60;
    proxy_read_timeout 60;

    proxy_buffering on;
    proxy_buffer_size 32k;
    proxy_buffers 4 128k;
    proxy_busy_buffers_size 256k;
    proxy_max_temp_file_size 256k;
    }
}
重新加载nginx配置文件
[root@nginx-server ~]# nginx -s reload

c、nginx proxy 具体配置详解

proxy_pass :真实服务器的地址,可以是ip也可以是域名和url地址
proxy_redirect :如果真实服务器使用的是的真实IP:非默认端口。则改成IP:默认端口。
proxy_set_header:重新定义或者添加发往后端服务器的请求头
proxy_set_header X-Real-IP :启用客户端真实地址(否则日志中显示的是代理在访问网站)
proxy_set_header X-Forwarded-For:记录代理地址

proxy_connect_timeout::后端服务器连接的超时时间发起三次握手等候响应超时时间
proxy_send_timeout:后端服务器数据回传时间就是在规定时间之内后端服务器必须传完所有的数据
proxy_read_timeout :nginx接收upstream(上游/真实) server数据超时, 默认60s, 如果连续的60s内没有收到1个字节, 连接关闭。像长连接



proxy_buffering on;开启缓存
proxy_buffer_size:proxy_buffer_size只是响应头的缓冲区
proxy_buffers 4 128k; 内容缓冲区域大小
proxy_busy_buffers_size 256k; 从proxy_buffers划出一部分缓冲区来专门向客户端传送数据的地方
proxy_max_temp_file_size 256k;超大的响应头存储成文件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sAQns57z-1602213757538)(assets/1561619960959.png)]

proxy_set_header X-Real-IP 
未配置
Nginxbackend 的日志:记录只有192.168.107.112
配置
Nginxbackend 的日志,记录的有192.168.107.16 192.168.107.107 192.168.107.112

proxy_buffers 的缓冲区大小一般会设置的比较大,以应付大网页。 proxy_buffers当中单个缓冲区的大小是由系统的内存页面大小决定的,Linux系统中一般为4k。 proxy_buffers由缓冲区数量和缓冲区大小组成的。总的大小为number*size。
若某些请求的响应过大,则超过_buffers的部分将被缓冲到硬盘(缓冲目录由_temp_path指令指定), 当然这将会使读取响应的速度减慢, 影响用户体验. 可以使用proxy_max_temp_file_size指令关闭磁盘缓冲.


注意

:proxy_pass http:// 填写nginx-1服务器的地址。

d、 使用PC客户端访问nginx-2服务器地址

浏览器中输入http://10.0.105.202 (也可以是nginx-2服务器的域名)

成功访问nginx-1服务器页面

e、 观察nginx-1服务器的日志 (记得打开下面的日志路径,默认为/var/log)

10.0.105.202 - - [27/Jun/2019:15:54:17 +0800] "GET / HTTP/1.0" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36" "10.0.105.207"

10.0.105.202 代理服务器地址

10.0.105.207 客户机地址。

访问成功。 记录了客户机的IP和代理服务器的IP


6、Nginx负载均衡



1、负载均衡的作用

如果你的nginx服务器给2台web服务器做代理,负载均衡算法采用轮询,那么当你的一台机器web程序关闭造成web不能访问,那么nginx服务器分发请求还是会给这台不能访问的web服务器,如果这里的响应连接时间过长,就会导致客户端的页面一直在等待响应,对用户来说体验就打打折扣,这里我们怎么避免这样的情况发生呢。这里我配张图来说明下问题。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-56bBrXZs-1602213757540)(assets/1561622346506.png)]

如果负载均衡中其中web2发生这样的情况,nginx首先会去web1请求,但是nginx在配置不当的情况下会继续分发请求道web2,然后等待web2响应,直到我们的响应时间超时,才会把请求重新分发给web1,这里的响应时间如果过长,用户等待的时间就会越长。

下面的配置是解决方案之一。

proxy_connect_timeout 1;   #nginx服务器与被代理的服务器建立连接的超时时间,默认60秒
proxy_read_timeout 1; #nginx服务器想被代理服务器组发出read请求后,等待响应的超时间,默认为60秒。
proxy_send_timeout 1; #nginx服务器想被代理服务器组发出write请求后,等待响应的超时间,默认为60秒。
proxy_ignore_client_abort on;  #客户端断网时,nginx服务器是否中断对被代理服务器的请求。默认为off。

使用upstream指令配置一组服务器作为被代理服务器,服务器中的访问算法遵循配置的负载均衡规则,同时可以使用该指令配置在发生哪些异常情况时,将请求顺次交由下一组服务器处理.

proxy_next_upstream timeout;  #反向代理upstream中设置的服务器组,出现故障时,被代理服务器返回的状态值。error|timeout|invalid_header|http_500|http_502|http_503|http_504|http_404|off

error:建立连接或向被代理的服务器发送请求或读取响应信息时服务器发生错误。

timeout:建立连接,想被代理服务器发送请求或读取响应信息时服务器发生超时。

invalid_header:被代理服务器返回的响应头异常。

off:无法将请求分发给被代理的服务器。

http_400,…:被代理服务器返回的状态码为400,500,502,等

2、upstream配置

首先给大家说下 upstream 这个配置的,这个配置是写一组被代理的服务器地址,然后配置负载均衡的算法。这里的被代理服务器地址有2中写法。

upstream testapp { 
      server 10.0.105.199:8081;
      server 10.0.105.202:8081;
    }
 server {
        ....
        location / {         
           proxy_pass  http://testapp;  #请求转向 testapp 定义的服务器列表         
        } 
upstream mysvr { 
      server  http://10.0.105.199:8081;
      server  http://10.0.105.202:8081;
    }
 server {
        ....
        location  / {         
           proxy_pass  http://mysvr;  #请求转向mysvr 定义的服务器列表         
        } 


1、负载均衡算法

upstream 支持4种负载均衡调度算法:

A、

轮询(默认)

:每个请求按时间顺序逐一分配到不同的后端服务器;

B、

ip_hash

:每个请求按访问IP的hash结果分配,同一个IP客户端固定访问一个后端服务器。可以保证来自同一ip的请求被打到固定的机器上,可以解决session问题。

C、

url_hash

:按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器。后台服务器为缓存的时候效率。

D、

fair

:这是比上面两个更加智能的负载均衡算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。

Nginx

本身是不支持

fair

的,如果需要使用这种调度算法,必须下载Nginx的

upstream_fair

模块。


2、配置实例

1、热备:如果你有2台服务器,当一台服务器发生事故时,才启用第二台服务器给提供服务。服务器处理请求的顺序:AAAAAA突然A挂啦,BBBBBBBBBBBBBB…

upstream myweb { 
      server 172.17.14.2:8080; 
      server 172.17.14.3:8080 backup;  #热备     
    }

2、轮询:nginx默认就是轮询其权重都默认为1,服务器处理请求的顺序:ABABABABAB…

upstream myweb { 
      server 172.17.14.2:8080;
      server 172.17.14.3:8080;
    }

3、加权轮询:跟据配置的权重的大小而分发给不同服务器不同数量的请求。如果不设置,则默认为1。下面服务器的请求顺序为:ABBABBABBABBABB…

upstream myweb { 
      server 172.17.14.2:8080 weight=1;
      server 172.17.14.3:8080 weight=2;
}

4、ip_hash:nginx会让相同的客户端ip请求相同的服务器。

upstream myweb { 
      server 172.17.14.2:8080; 
      server 172.17.14.3:8080;
      ip_hash;
    }

5、nginx负载均衡配置状态参数

  • down,表示当前的server暂时不参与负载均衡。
  • backup,预留的备份机器。当其他所有的非backup机器出现故障或者忙的时候,才会请求backup机器,因此这台机器的压力最轻。
  • max_fails,允许请求失败的次数,默认为1。当超过最大次数时,返回proxy_next_upstream 模块定义的错误。
  • fail_timeout,在经历了max_fails次失败后,暂停服务的时间单位秒。max_fails可以和fail_timeout一起使用。
 upstream myweb { 
      server 172.17.14.2:8080 weight=2 max_fails=2 fail_timeout=2;
      server 172.17.14.3:8080 weight=1 max_fails=2 fail_timeout=1;    
    }

如果你像跟多更深入的了解 nginx 的负载均衡算法,nginx官方提供一些插件大家可以了解下。



3、nginx配置7层协议及4层协议方法(扩展)

举例讲解下什么是7层协议,什么是4层协议。

(1)7层协议

OSI(Open System Interconnection)是一个开放性的通行系统互连参考模型,他是一个定义的非常好的协议规范,共包含七层协议。直接上图,这样更直观些:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tKLnLaAu-1602213757542)(assets/1561624521541.png)]

好,详情不进行仔细讲解,可以自行

百度

(2)4层协议

TCP/IP协议

之所以说TCP/IP是一个协议族,是因为TCP/IP协议包括TCP、IP、UDP、ICMP、RIP、TELNETFTP、SMTP、ARP、TFTP等许多协议,这些协议一起称为TCP/IP协议。

从协议分层模型方面来讲,TCP/IP由四个层次组成:网络接口层、网络层、传输层、应用层。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQQ5eJWY-1602213757543)(assets/1561624556568.png)]

(3)协议配置

这里我们举例,在nginx做负载均衡,负载多个服务,部分服务是需要7层的,部分服务是需要4层的,也就是说7层和4层配置在同一个配置文件中。

准备三台机器:

代理服务IP:10.0.105. –配置本地host解析域名;

后端服务器IP:nginx-a :10.0.105.199/nginx-b:10.0.105.202(yum安装)后端服务器将nginx服务启动

配置代理服务器的nginx配置文件

worker_processes  4;

worker_rlimit_nofile 102400;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  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"';

    #access_log  logs/access.log  main;

    sendfile        on;
    keepalive_timeout  65;
    gzip  on;

    upstream testweb {
	ip_hash;
	server 10.0.105.199:80 weight=2 max_fails=2 fail_timeout=2s;
	server 10.0.105.202:80 weight=2 max_fails=2 fail_timeout=2s;
     }
    server {
        listen       80;
        server_name  www.test.com;
        charset utf-8;
        #access_log  logs/host.access.log  main;
        location / {
	    proxy_pass http://testweb;
            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        	}

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        	}
	}

     upstream testapp {
	 server 10.0.105.202:8081 weight=2 max_fails=2 fail_timeout=2s;
      server 10.0.105.199:8081 weight=2 max_fails=2 fail_timeout=2s;               
	}

     server {
	listen	    81;
	server_name	www.app.com;
	charset utf-8;
	#access_log  logs/host.access.log  main;
	location / {
	    proxy_pass http://testapp;
            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

		}

	}
}

浏览器测试访问:


http://www.test.com/


http://www.app.com:81/

202服务器yum安装的创建新的配置文件:

[root@nginx-server ~]# cd /etc/nginx/conf.d/
[root@nginx-server conf.d]# cp default.conf test.conf
[root@nginx-server conf.d]# cat test.conf 
server {
    listen       80;
    server_name  localhost;

    location / {
         root   /usr/share/nginx/html;
         index  index.html index.htm;
    }
}

server {
    listen       8081;
    server_name  localhost;

    location / {
         root   /var/www/nginx/html;
         index  index.html index.htm;
    }
}
[root@nginx-server ~]# nginx -s reload

nginx在1.9.0的时候,增加了一个 stream 模块,用来实现四层协议(网络层和传输层)的转发、代理、负载均衡等。stream模块的用法跟http的用法类似,允许我们配置一组TCP或者UDP等协议的监听,然后通过proxy_pass来转发我们的请求,通过upstream添加多个后端服务,实现负载均衡。

#4层tcp负载 
stream {
			upstream myweb {
                hash $remote_addr consistent;
                server 172.17.14.2:8080;
                server 172.17.14.3:8080;
        }
        server {
            listen 82;
            proxy_connect_timeout 10s;
            proxy_timeout 30s;
            proxy_pass myweb;
        }
}


9、nginx 会话保持

nginx会话保持主要有以下几种实现方式。




1、ip_hash

ip_hash使用源地址哈希算法,将同一客户端的请求总是发往同一个后端服务器,除非该服务器不可用。

ip_hash语法:

upstream backend {
    ip_hash;
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com down;
}

ip_hash简单易用,但有如下问题:

当后端服务器宕机后,session会丢失;

来自同一局域网的客户端会被转发到同一个后端服务器,可能导致负载失衡;

不适用于CDN网络,不适用于前段还有代理的情况。



2、sticky_cookie_insert

使用sticky_cookie_insert启用会话亲缘关系,这会导致来自同一客户端的请求被传递到一组服务器的同一台服务器。与ip_hash不同之处在于,它不是基于IP来判断客户端的,而是基于cookie来判断。因此可以避免上述ip_hash中来自同一局域网的客户端和前段代理导致负载失衡的情况。(需要引入第三方模块才能实现)

语法:

upstream backend {
    server backend1.example.com;
    server backend2.example.com;
    sticky_cookie_insert srv_id expires=1h domain=3evip.cn path=/;
}

说明:

expires:设置浏览器中保持cookie的时间

domain:定义cookie的域

path:为cookie定义路径




3、jvm_route方式

jvm_route是通过session_cookie这种方式来实现session粘性。将特定会话附属到特定tomcat上,从而解决session不同步问题,但是无法解决宕机后会话转移问题。如果在cookie和url中并没有session,则这只是个简单的round-robin负载均衡。

jvm_route的原理

  • 一开始请求过来,没有带session的信息,jvm_route就根据round robin的方法,发到一台Tomcat上面
  • Tomcat添加上session信息,并返回给客户
  • 用户再次请求,jvm_route看到session中有后端服务器的名称,他就把请求转到对应的服务器上

暂时jvm_route模块还不支持fair的模式。jvm_route的工作模式和fair是冲突的。对于某个特定用户,当一直为他服务的Tomcat宕机后,默认情况下它会重试max_fails的次数,如果还是失败,就重新启用round robin的方式,而这种情况下就会导致用户的session丢失。


4、使用后端服务器自身通过相关机制保持session同步,如:使用数据库、redis、memcached 等做session复制



9、nginx 实现动静分离

为了加快网站的解析速度,可以把动态页面和静态页面由不同的服务器来解析,加快解析速度。降低原来单个服务器的压力。 在动静分离的tomcat的时候比较明显,因为tomcat解析静态很慢,其实这些原理的话都很好理解,简单来说,就是使用正则表达式匹配过滤,然后交个不同的服务器。


1、准备环境

准备一个nginx代理 两个http 分别处理动态和静态。

1.配置nginx反向代理upstream;
upstream static {
        server 10.0.105.196:80 weight=1 max_fails=1 fail_timeout=60s;
        }
upstream php {
        server 10.0.105.200:80 weight=1 max_fails=1 fail_timeout=60s;
        }
     server {
        listen      80;
        server_name     localhost;
        #动态资源加载
        location ~ \.(php|jsp)$ {
            proxy_pass http://php;
            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                }
        #静态资源加载
        location ~ .*\.(html|gif|jpg|png|bmp|swf|css|js)$ {
            proxy_pass http://static;
            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                }
        }
静态资源配置
server {
        listen 80;
        server_name     localhost;

        location ~ \.(html|jpg|png|js|css|gif|bmp|jpeg) {
        root /home/www/nginx;
        }
}
动态资源配置:
yum 安装php7.1
[root@nginx-server ~]#rpm -Uvh https://mirror.webtatic.com/yum/el7/epel-release.rpm
[root@nginx-server ~]#rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm
[root@nginx-server ~]#yum install php71w-xsl php71w php71w-ldap php71w-cli php71w-common php71w-devel php71w-gd php71w-pdo php71w-mysql php71w-mbstring php71w-bcmath php71w-mcrypt -y
[root@nginx-server ~]#yum install -y php71w-fpm
[root@nginx-server ~]#systemctl start php-fpm
[root@nginx-server ~]#systemctl enable php-fpm
编辑nginx的配置文件:
server {
        listen      80;
        server_name     localhost;
        location ~ \.php$ {
            root           /home/nginx/html;  #指定网站目录
            fastcgi_pass   127.0.0.1:9000;    #指定访问地址
            fastcgi_index  index.php;		#指定默认文件
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name; #站点根目录,取决于root配置项
            include        fastcgi_params;  #包含nginx常量定义
        		}
        }

当访问静态页面的时候location 匹配到 (html|jpg|png|js|css|gif|bmp|jpeg) 通过转发到静态服务器,静态服务通过location的正则匹配来处理请求。

当访问动态页面时location匹配到 .\php 结尾的文件转发到后端php服务处理请求。


11、nginx 防盗链问题

两个网站 A 和 B, A网站引用了B网站上的图片,这种行为就叫做盗链。 防盗链,就是要防止A引用B的图片。

1、nginx 防止网站资源被盗用模块

ngx_http_referer_module


如何区分哪些是不正常的用户?

HTTP Referer是Header的一部分,当浏览器向Web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器借此可以获得一些信息用于处理,例如防止未经允许的网站盗链图片、文件等。因此HTTP Referer头信息是可以通过程序来伪装生成的,所以通过Referer信息防盗链并非100%可靠,但是,它能够限制大部分的盗链情况.

比如在www.google.com 里有一个

www.baidu.com

链接,那么点击这个

www.baidu.com

,它的

header

信息里就有:Referer=http://www.google.com



10、防盗链配置


配置要点:

[root@nginx-server ~]# vim /etc/nginx/nginx.conf
# 日志格式添加"$http_referer"
log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                         '$status $body_bytes_sent "$http_referer" '
                         '"$http_user_agent" "$http_x_forwarded_for"';
# valid_referers 使用方式                         
Syntax: 	valid_referers none | blocked | server_names | string ...;
Default: 	—
Context: server, location
  • none : 允许没有http_refer的请求访问资源;

  • blocked : 允许不是http://开头的,不带协议的请求访问资源;

  • server_names : 只允许指定ip/域名来的请求访问资源(白名单);

    准备两台机器,两张图片(缓存问题)

    配置nginx配置文件,并上传图片
    [root@nginx-server html]# vim /etc/nginx/conf.d/nginx.conf
    server {
        listen       80;
        server_name  localhost;
    
        location / {
             root   /usr/share/nginx/html;
             index  index.html index.htm;
    
             valid_referers none blocked *.qf.com 10.0.105.202;
                    if ($invalid_referer) {
                       return 403;
                    }
            }
        location ~  .*\.(gif|jpg|png|jpeg)$ {
             root  /usr/share/nginx/html;
    
             valid_referers  qf.com 10.0.105.202;
                    if ($invalid_referer) {
                       return 403;
                    }
            }
    }
    
    重载nginx服务
    [root@nginx-server ~]# nginx -s reload -c /etc/nginx/nginx.conf
    
    第二台机器客户端
    配置nginx访问页面
    创建页面
    [root@nginx-server nginx]# vim index.html
    <html>
    <head>
        <meta charset="utf-8">
        <title>qf.com</title>
    </head>
    <body style="background-color:red;">
        <img src="http://10.0.105.202/test.jpg"/>
    </body>
    </html>
    
    测试不带http_refer:
    [root@nginx-server nginx]# curl -I "http://10.0.105.202/test1.png"
    HTTP/1.1 200 OK
    Server: nginx/1.16.0
    Date: Thu, 27 Jun 2019 16:21:13 GMT
    Content-Type: image/png
    Content-Length: 235283
    Last-Modified: Thu, 27 Jun 2019 11:27:11 GMT
    Connection: keep-alive
    ETag: "5d14a80f-39713"
    Accept-Ranges: bytes
    
    测试带非法http_refer:
    [root@nginx-server nginx]# curl -e http://www.baidu.com -I "http://10.0.105.202/test.jpg"
    HTTP/1.1 403 Forbidden
    Server: nginx/1.16.0
    Date: Thu, 27 Jun 2019 16:22:32 GMT
    Content-Type: text/html
    Content-Length: 153
    Connection: keep-alive
    
    测试带合法的http_refer:
    [root@nginx-server nginx]# curl -e http://10.0.105.202 -I "http://10.0.105.202/test.jpg"
    HTTP/1.1 200 OK
    Server: nginx/1.16.0
    Date: Thu, 27 Jun 2019 16:23:21 GMT
    Content-Type: image/jpeg
    Content-Length: 27961
    Last-Modified: Thu, 27 Jun 2019 12:28:51 GMT
    Connection: keep-alive
    ETag: "5d14b683-6d39"
    Accept-Ranges: bytes
    


    5、其他配置


    5.1、匹配域名
#    location ~  .*\.(gif|jpg|png|jpeg)$ {
#        root  /usr/share/nginx/html;

#         valid_referers none blocked qf.com 10.0.105.202;
#               if ($invalid_referer) {
#                  return 403;
#               }
#       }
    location ~ .*\.(gif|jpg|png|jpeg)$ {
        root  /usr/share/nginx/html;

        valid_referers 10.0.105.202 *.baidu.com *.google.com;
                if ($invalid_referer) {
                rewrite ^/ http://10.0.105.202/test.jpg;
                #return 403;
                }
        }

以上所有来自 qf.com 和域名中google和baidu的站点都可以访问到当前站点的图片,如果来源域名不在这个列表中,那么$invalid_referer等于1,在if语句中返回一个403给用户,这样用户便会看到一个403的页面,如果使用下面的rewrite,那么盗链的图片都会显示403.jpg。如果用户直接在浏览器输入你的图片地址,那么图片显示正常,因为它符合none这个规则。



11、nginx 地址重写 rewrite



1、什么是Rewrite

​ Rewrite对称URL Rewrite,即URL重写,就是把传入Web的请求重定向到其他URL的过程。

  • URL Rewrite最常见的应用是URL伪静态化,是将动态页面显示为静态页面方式的一种技术。比如

    http://www.123.com/news/index.php?id=123 使用URLRewrite 转换后可以显示为 http://www.123

    .com/news/123.html对于追求完美主义的网站设计师,就算是网页的地址也希望看起来尽量简洁明快。

    理论上,搜索引擎更喜欢静态页面形式的网页,搜索引擎对静态页面的评分一般要高于动态页面。所以,UrlRewrite可以让我们网站的网页更容易被搜索引擎所收录。
  • 从安全角度上讲,如果在URL中暴露太多的参数,无疑会造成一定量的信息泄漏,可能会被一些黑客

    利用,对你的系统造成一定的破坏,所以静态化的URL地址可以给我们带来更高的安全性。
  • 实现网站地址跳转,例如用户访问360buy.com,将其跳转到jd.com。例如当用户访问tianyun.com的

    80端口时,将其跳转到443端口。


2、Rewrite 相关指令

  • Nginx Rewrite 相关指令有 if、rewrite、set、return


2.1、if 语句
  • 应用环境

    server,location
    

    语法:

    if (condition) {}
    if 可以支持如下条件判断匹配符号
    ~ 					正则匹配 (区分大小写)
    ~* 				    正则匹配 (不区分大小写)
    !~                  正则不匹配 (区分大小写)
    !~*		            正则不匹配  (不区分大小写)
    -f 和!-f 		    用来判断是否存在文件
    -d 和!-d 		    用来判断是否存在目录
    -e 和!-e 		    用来判断是否存在文件或目录
    -x 和!-x 		    用来判断文件是否可执行
    
    在匹配过程中可以引用一些Nginx的全局变量
    $args				请求中的参数;
    $document_root	    针对当前请求的根路径设置值;
    $host				请求信息中的"Host",如果请求中没有Host行,则等于设置的服务器名;
    $limit_rate			对连接速率的限制;
    $request_method		请求的方法,比如"GET""POST";
    $remote_addr		客户端地址;
    $remote_port		客户端端口号;
    $remote_user		客户端用户名,认证用;
    $request_filename   当前请求的文件路径名(带网站的主目录/usr/local/nginx/html/images /a.jpg)
    $request_uri		当前请求的文件路径名(不带网站的主目录/images/a.jpg)
    $query_string$args相同;
    $scheme				用的协议,比如http或者是https
    $server_protocol	请求的协议版本,"HTTP/1.0""HTTP/1.1";
    $server_addr 		服务器地址,如果没有用listen指明服务器地址,使用这个变量将发起一次系统调用以取得地址(造成资源浪费);
    $server_name		请求到达的服务器名;
    $document_uri$uri一样,URI地址;
    $server_port 		请求到达的服务器端口号;
    


    2.2、Rewrite flag


    rewrite

    指令根据表达式来重定向URI,或者修改字符串。可以应用于

    server,location, if

    环境下每行rewrite指令最后跟一个flag标记,支持的flag标记有:

    last 			    相当于Apache里的[L]标记,表示完成rewrite。默认为last。
    break 				本条规则匹配完成后,终止匹配,不再匹配后面的规则
    redirect 			返回302临时重定向,浏览器地址会显示跳转后的URL地址
    permanent 		    返回301永久重定向,浏览器地址会显示跳转后URL地址
    

    redirect 和 permanent区别则是返回的不同方式的重定向,对于客户端来说一般状态下是没有区别的。而对于搜索引擎,相对来说301的重定向更加友好,如果我们把一个地址采用301跳转方式跳转的话,搜索引擎会把老地址的相关信息带到新地址,同时在搜索引擎索引库中彻底废弃掉原先的老地址。使用302重定向时,搜索引擎(特别是google)有时会查看跳转前后哪个网址更直观,然后决定显示哪个,如果它觉的跳转前的URL更好的话,也许地址栏不会更改,那么很有可能出现URL劫持的现像。在做URI重写时,有时会发现URI中含有相关参数,如果需要将这些参数保存下来,并且在重写过程中重新引用,可以用到 () 和 $N 的方式来解决。

2.3、Rewrite匹配参考示例

本地解析host文件
# http://www.testpm.com/a/1.html ==> http://www.testpm.com/b/2.html

    location /a {
        root    /html;
        index   1.html index.htm;
        rewrite .* /b/2.html permanent;
        }
    location /b {
        root    /html;
        index   2.html index.htm;
        }
例2:
# http://www.testpm.com/2019/a/1.html ==> http://www.testpm.com/2018/a/1.html

     location /2019/a {
        root    /var/www/html;
        index   1.html index.hml;
        rewrite ^/2019/(.*)$ /2018/$1 permanent;
        }
     location /2018/a {
        root    /var/www/html;
        index   1.html index.htl;
        }

例3:
# http://www.qf.com/a/1.html ==> http://jd.com

location /a {
        root    /html;
        if ($host ~* www.qf.com ) {
        rewrite .* http://jd.com permanent;
        }
        }

例4:
# http://www.qf.com/a/1.html ==> http://jd.com/a/1.html
location /a {
        root /html;
        if ( $host ~* testpm.com ){
        rewrite .* http://jd.com$request_uri permanent;
        }
        }

例5: 在访问目录后添加/  (如果目录后已有/,则不加/)
# http://www.tianyun.com/a/b/c
# $1: /a/b
# $2: c
# http://$host$1$2$3/
location /a/b/c {
        root    /usr/share/nginx/html;
        index   index.html index.hml;
        if (-d $request_filename) {
        rewrite ^(.*)([^/])$ http://$host$1$2/ permanent;
        }
        }
	
例6:
# http://www.tianyun.com/login/tianyun.html ==>  http://www.tianyun.com/reg/login.html?user=tianyun

	location /login {
        root   /usr/share/nginx/html;
        rewrite ^/login/(.*)\.html$ http://$host/reg/login.html?user=$1;
        }
    location /reg {
        root /usr/share/nginx/html;
        index login.html;

        }

例7:
#http://www.tianyun.com/qf/11-22-33/1.html  ==>  http://www.tianyun.com/qf/11/22/33/1.html
location /qf {
            rewrite ^/qf/([0-9]+)-([0-9]+)-([0-9]+)(.*)$ /qf/$1/$2/$3$4 permanent;
        }

        location /qf/11/22/33 {
                root /html;
                index   1.html;
        }



2.4、set 指令

set 指令是用于定义一个变量,并且赋值

应用环境:

server,location,if

应用示例

例8:
#http://alice.testpm.com ==> http://www.testpm.com/alice
#http://jack.testpm.com ==> http://www.testpm.com/jack

[root@nginx-server conf.d]# cd /usr/share/nginx/html/
[root@nginx-server html]# mkdir jack alice
[root@nginx-server html]# echo "jack.." >> jack/index.html
[root@nginx-server html]# echo "alice.." >> alice/index.html

a. DNS实现泛解析
*   		IN      A			    网站IP
或者本地解析域名host文件
10.0.105.202 www.testpm.com
10.0.105.202 alice.testpm.com
10.0.105.202 jack.testpm.com
编辑配置文件:
server {
    listen       80;
    server_name  www.testpm.com;

    location / {
         root   /usr/share/nginx/html;
         index  index.html index.htm;
         if ( $host ~* ^www.testpm.com$) {
                break;
                }
         if ( $host ~* "^(.*)\.testpm\.com$" ) {
                set $user $1;
                rewrite .* http://www.testpm.com/$user permanent;
                }
        }
    location /jack {
         root /usr/share/nginx/html;
         index  index.html index.hml;
        }
    location /alice {
         root /usr/share/nginx/html;
         index index.html index.hml;
        }
}


2.5、return 指令

return 指令用于返回状态码给客户端

server,location,if

应用示例:

例9:如果访问的.sh结尾的文件则返回403操作拒绝错误
server {
    listen       80;
    server_name  www.testpm.cn;
    #access_log  /var/log/nginx/http_access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        }

    location ~* \.sh$ {
        return 403;
        }
}

例10:80 ======> 443 :80转443端口
server {
    listen       80;
    server_name  www.testpm.cn;
    access_log  /var/log/nginx/http_access.log  main;
    return 301 https://www.testpm.cn$request_uri;
}

server {
    listen 443 ssl;
    server_name www.testpm.cn;
    access_log  /var/log/nginx/https_access.log  main;

    #ssl on;
    ssl_certificate   /etc/nginx/cert/2447549_www.testpm.cn.pem;
    ssl_certificate_key  /etc/nginx/cert/2447549_www.testpm.cn.key;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
    ssl_prefer_server_ciphers on;

    location / {
        root  /usr/share/nginx/html;
        index index.html index.htm;
    }
}

[root@nginx-server ~]# curl -I http://www.testpm.cn
HTTP/1.1 301 Moved Permanently
Server: nginx/1.16.0
Date: Wed, 03 Jul 2019 13:52:30 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://www.testpm.cn/


3、last,break详解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OUsakwd0-1602213757545)(assets/1561898537620.png)]

[root@localhost test]# cat /etc/nginx/conf.d/last_break.conf 
server {
    listen       80;
    server_name  localhost;
    access_log  /var/log/nginx/last.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
    location /break/ {
        root /usr/share/nginx/html;
        rewrite .* /test/break.html break;
    }
    location /last/ {
        root /usr/share/nginx/html;
        rewrite .* /test/break/last.html last;
    }
    location /test/ {
        root /usr/share/nginx/html;
        rewrite .* /test/test.html break;
    }

}
[root@localhost conf.d]# cd /usr/share/nginx/html/
[root@localhost html]# mkdir test
[root@localhost html]# echo "last" > test/last.html
[root@localhost html]# echo "break" > test/break.html
[root@localhost html]# echo "test" > test/test.html

http://10.0.105.196/break/break.html
http://10.0.105.196/last/last.html


注意:

  • last 标记在本条 rewrite 规则执行完后,会对其所在的 server { … } 标签重新发起请求;

  • break 标记则在本条规则匹配完成后,停止匹配,不再做后续的匹配;

  • 使用 alias 指令时,必须使用 last;

  • 使用 proxy_pass 指令时,则必须使用break。


    4、Nginx 的 https ( rewrite )

    server {
            listen       80;
            server_name  *.vip9999.top vip9999.top;
    
            if ($host ~* "^www.vip9999.top$|^vip9999.top$" ) {
                    return 301 https://www.vip9999.top$request_uri;
            }
    
            if ($host ~* "^(.*).vip9999.top$" ) {
                    set $user $1;
                    return 301 https://www.vip9999.top/$user;
            }
    
        }
    
        # Settings for a TLS enabled server.
        server {
            listen       443 ssl;
            server_name  www.vip9999.top;
    
            location / {
                    root      /usr/share/nginx/html;
                    index     index.php index.html;
            }
    
            #pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
            location ~ \.php$ {
                root           /usr/share/nginx/html;
                fastcgi_pass   127.0.0.1:9000;
                fastcgi_index  index.php;
                fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                include        fastcgi_params;
            }
            ssl on;
            ssl_certificate cert/214025315060640.pem;
            ssl_certificate_key cert/214025315060640.key;
            ssl_session_cache shared:SSL:1m;
            ssl_session_timeout  10m;
            ssl_ciphers HIGH:!aNULL:!MD5;
            ssl_prefer_server_ciphers on;
            }
    


    5、Apache 的 https ( rewrite )
    [root@localhost ~]# yum -y install httpd mod_ssl
    [root@localhost ~]# vim /etc/httpd/conf.d/vip9999.conf
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ykvMDafY-1602213757546)(assets/1561898727918.png)]



12、nginx的localtion指令详解

Nginx 的 HTTP 配置主要包括三个区块,结构如下:

http { 						# 这个是协议级别
  include mime.types;
  default_type application/octet-stream;
  keepalive_timeout 65;
  gzip on;
    server {			 # 这个是服务器级别
      listen 80;
      server_name localhost;
        location / {  # 这个是请求级别
          root html;
          index index.html index.htm;
        }
      }
}


1、location 区段
  • location 是在 server 块中配置,根据不同的 URI 使用不同的配置,来处理不同的请求。

  • location 是有顺序的,会被第一个匹配的location 处理。

  • 基本语法如下:

    location [=|~|~*|^~|@] pattern{……}
    


2、

location 前缀含义

=    表示精确匹配,优先级也是最高的 
^~   表示uri以某个常规字符串开头,理解为匹配url路径即可 
~    表示区分大小写的正则匹配  
~*   表示不区分大小写的正则匹配
!~   表示区分大小写不匹配的正则
!~*  表示不区分大小写不匹配的正则
/    通用匹配,任何请求都会匹配到
@    内部服务跳转


3、location 配置示例

本地解析域名host

1、没有修饰符 表示:必须以指定模式开始

server {
    listen       80;
    server_name  qf.com;

    location  /abc {
        root    /home/www/nginx;
        index   2.html;
        }

那么,如下是对的:
http://qf.com/abc
http://qf.com/abc?p1
http://qf.com/abc/
如下是错的
http://qf.com/abcde 

2、=表示:必须与指定的模式精确匹配

server {
    listen       80;
    server_name  www.testpm.cn;
    access_log  /var/log/nginx/http_access.log  main;

    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
    }
    location = /abc {
        root /usr/share/nginx/html;
        index index.html index.htm;
    }
}

那么,如下是对的:
http://qf.com/abc
http://qf.com/abc?p1
http://qf.com/abc/

http://qf.com/abcde

3、~ 表示:指定的正则表达式要区分大小写

server {
server_name qf.com;
  location ~ ^/abc$ {
    ……
  }
}
那么,如下是对的:
http://qf.com/abc
http://qf.com/abc?p1=11&p2=22
如下是错的
http://qf.com/ABC
http://qf.com/abc/
http://qf.com/abcde

4、~* 表示:指定的正则表达式不区分大小写

server {
server_name qf.com;
location ~* ^/abc$ {
    ……
  }
}
那么,如下是对的:
http://qf.com/abc
http://qf.com/ABC
http://qf.com/abc?p1=11&p2=22
如下是错的:
http://qf.com/abc/
http://qf.com/abcde

5、^~ :类似于无修饰符的行为,也是以指定模式开始,不同的是,如果模式匹配,那么就停止搜索其他模式了。

6、@ :定义命名 location 区段,这些区段客户段不能访问,只可以由内部产生的请求来访问,如try_files或error_page等


查找顺序和优先级


1:带有“=“的精确匹配优先


2:没有修饰符的精确匹配


3:正则表达式按照他们在配置文件中定义的顺序


4:带有“^~”修饰符的,开头匹配


5:带有“~” 或“~*” 修饰符的,如果正则表达式与URI匹配


6:没有修饰符的,如果指定字符串与URI开头匹配

= 大于 ^~  大于 ~|~*|!~|!~* 大于 /
多个location配置的情况下匹配顺序为:首先匹配 =,其次匹配^~, 其次是按正则匹配,最后是交给 / 通用匹配。当有匹配成功时候,停止匹配,按当前匹配规则处理请求。
================================================
(1) =:表示完全匹配;
(2) ^~:匹配URI的前缀,并且后面的正则表达式不再匹配,如果一个URI同时满足两个规则的话,匹配最长的规则;
(3) ~:匹配正则表达式,大小写敏感;
(4) ~*:匹配正则表达式,大小写不敏感;
优先级:(1)> (2) > (3) = (4)
location 区段匹配示例

location = / {
  # 只匹配 / 的查询.
  [ configuration A ]
}
location / {
  # 匹配任何以 / 开始的查询,但是正则表达式与一些较长的字符串将被首先匹配。
  [ configuration B ]
}
location ^~ /images/ {
  # 匹配任何以 /images/ 开始的查询并且停止搜索,不检查正则表达式。
  [ configuration C ]
}
location ~* \.(gif|jpg|jpeg)$ {
  # 匹配任何以gif, jpg, or jpeg结尾的文件,但是所有 /images/ 目录的请求将在Configuration C中处理。
  [ configuration D ]
} 
各请求的处理如下例:
	/ → configuration A
	/documents/document.html → configuration B
	/images/1.gif → configuration C
	/documents/1.jpg → configuration D


4、root 、alias 指令区别

location /img/ {
    alias /var/www/image/;
}
#若按照上述配置的话,则访问/img/目录里面的文件时,ningx会自动去/var/www/image/目录找文件
location /img/ {
    root /var/www/image;
}
#若按照这种配置的话,则访问/img/目录下的文件时,nginx会去/var/www/image/img/目录下找文件。]
  • alias 是一个目录别名的定义,
  • root 则是最上层目录的定义。
  • 还有一个重要的区别是alias后面必须要用“/”结束,否则会找不到文件的,而root则可有可无



13、nginx 日志配置



1、nginx 日志介绍


nginx

有一个非常灵活的日志记录模式,每个级别的配置可以有各自独立的访问日志, 所需日志模块

ngx_http_log_module

的支持,日志格式通过

log_format

命令来定义,日志对于统计和排错是非常有利的,下面总结了

nginx

日志相关的配置 包括

access_log



log_format



open_log_file_cache



log_not_found



log_subrequest



rewrite_log



error_log

# 设置访问日志
access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
# 关闭访问日志
access_log off; 

  • path

    指定日志的存放位置。

  • format

    指定日志的格式。默认使用预定义的

    combined


  • buffer

    用来指定日志写入时的缓存大小。默认是64k。

  • gzip

    日志写入前先进行压缩。压缩率可以指定,从1到9数值越大压缩比越高,同时压缩的速度也越慢。默认是1。

  • flush

    设置缓存的有效时间。如果超过flush指定的时间,缓存中的内容将被清空。

  • if

    条件判断。如果指定的条件计算为0或空字符串,那么该请求不会写入日志。


作用域:

可以应用

access_log

指令的作用域分别有

http



server



location



limit_except

。也就是说,在这几个作用域外使用该指令,Nginx会报错。

access_log /var/logs/nginx-access.log

该例子指定日志的写入路径为

/var/logs/nginx-access.log

,日志格式使用默认的

combined

access_log /var/logs/nginx-access.log buffer=32k gzip flush=1m

该例子指定日志的写入路径为

/var/logs/nginx-access.log

,日志格式使用默认的

combined

,指定日志的缓存大小为 32k,日志写入前启用 gzip 进行压缩,压缩比使用默认值 1,缓存数据有效时间为1分钟。



2、log_format 指令

Nginx 预定义了名为

combined

日志格式,如果没有明确指定日志格式默认使用该格式:

log_format combined '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

如果不想使用Nginx预定义的格式,可以通过

log_format

指令来自定义。

语法

log_format name [escape=default|json] string ...;

  • name

    格式名称。在 access_log 指令中引用。

  • escape

    设置变量中的字符编码方式是

    json

    还是

    default

    ,默认是

    default


  • string

    要定义的日志格式内容。该参数可以有多个。参数中可以使用Nginx变量。


log_format

指令中常用的一些变量:

$remote_addr, $http_x_forwarded_for   #记录客户端IP地址
$remote_user                    #记录客户端用户名称
$request                   #记录请求的URL和HTTP协议
$status                     #记录请求状态
$body_bytes_sent      #发送给客户端的字节数,不包括响应头的大小
$bytes_sent               #发送给客户端的总字节数
$http_referer            #记录从哪个页面链接访问过来的,可以根据该参数进行防盗链设置
$http_user_agent      #记录客户端浏览器相关信息
$time_local               #通用日志格式下的本地时间。

自定义日志格式的使用:

access_log /var/logs/nginx-access.log main

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';

使用

log_format

指令定义了一个

main

的格式,并在

access_log

指令中引用了它。假如客户端有发起请求:

https://qf.com/

,我们看一下我截取的一个请求的日志记录:

10.0.105.207 - - [01/Jul/2019:10:44:36 +0800] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36" "-"

我们看到最终的日志记录中

$remote_user



$http_referer



$http_x_forwarded_for

都对应了一个

-

,这是因为这几个变量为空。


面试时:注意日志里面的ip地址一定要在第一列。



3、error_log 指令

错误日志在Nginx中是通过

error_log

指令实现的。该指令记录服务器和请求处理过程中的错误信息。


语法

配置错误日志文件的路径和日志级别。

error_log file [level];
Default:	
error_log logs/error.log error;


file

参数指定日志的写入位置。


level

参数指定日志的级别。level可以是

debug

,

info

,

notice

,

warn

,

error

,

crit

,

alert

,

emerg

中的任意值。可以看到其取值范围是按紧急程度从低到高排列的。只有日志的错误级别等于或高于level指定的值才会写入错误日志中。默认值是

error


基本用法

error_log /var/logs/nginx/nginx-error.log

配置段:

main



http

,

mail

,

stream

,

server

,

location

作用域。

例子中指定了错误日志的路径为:

/var/logs/nginx/nginx-error.log

,日志级别使用默认的

error



4、open_log_file_cache 指令

每一条日志记录的写入都是先打开文件再写入记录,然后关闭日志文件。如果你的日志文件路径中使用了变量,如

access_log /var/logs/$host/nginx-access.log

,为提高性能,可以使用

open_log_file_cache

指令设置日志文件描述符的缓存。


语法

open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];

默认值: 
open_log_file_cache off;

  • max

    设置缓存中最多容纳的文件描述符数量,如果被占满,采用LRU算法将描述符关闭。


  • inactive

    设置缓存存活时间,默认是10s。


  • min_uses



    inactive

    时间段内,日志文件最少使用几次,该日志文件描述符记入缓存,默认是1次。


  • valid

    :设置多久对日志文件名进行检查,看是否发生变化,默认是60s。


  • off

    :不使用缓存。默认为off。


    基本用法

    open_log_file_cache max=1000 inactive=20s valid=1m min_uses=2;
    

    配置段:

    http



    server



    location

    作用域中。

    例子中,设置缓存最多缓存1000个日志文件描述符,20s内如果缓存中的日志文件描述符至少被被访问2次,才不会被缓存关闭。每隔1分钟检查缓存中的文件描述符的文件名是否还存在。



5、log_not_found 指令

是否在

error_log

中记录不存在的错误。默认是


基本语法:

log_not_found on | off;
默认值: 
log_not_found on;

配置段:

http

,

server

,

location

作用域。



6、log_subrequest 指令

是否在

access_log

中记录子请求的访问日志。默认不记录


基本语法

log_subrequest on | off;

默认值: 
log_subrequest off;

配置段:

http

,

server

,

location

作用域。



7、rewrite_log 指令



ngx_http_rewrite_module

模块提供的。用来记录重写日志的。对于调试重写规则建议开启,启用时将在

error log

中记录

notice

级别的重写日志。


基本语法:

rewrite_log on | off;

默认值: 
rewrite_log off;

配置段:

http

,

server

,

location

,

if

作用域。



8、nginx 日志配置总结

Nginx中通过

access_log



error_log

指令配置访问日志和错误日志,通过

log_format

我们可以自定义日志格式。如果日志文件路径中使用了变量,我们可以通过

open_log_file_cache

指令来设置缓存,提升性能。其他的根据自己的使用场景定义。

详细的日志配置信息可以参考

Nginx官方文档



14、nginx 的平滑升级(了解)



1、为什么要对 nginx 平滑升级

随着

nginx

越来越流行,并且

nginx

的优势也越来越明显,

nginx

的版本迭代也来时加速模式,1.9.0版本的nginx更新了许多新功能,例如

stream

四层代理功能,伴随着

nginx

的广泛应用,版本升级必然越来越快,线上业务不能停,此时

nginx

的升级就是运维的工作了

nginx 方便地帮助我们实现了平滑升级。其原理简单概括,就是:

(1)在不停掉老进程的情况下,启动新进程。

(2)老进程负责处理仍然没有处理完的请求,但不再接受处理请求。

(3)新进程接受新请求。

(4)老进程处理完所有请求,关闭所有连接后,停止。

这样就很方便地实现了平滑升级。一般有两种情况下需要升级 nginx,一种是确实要升级 nginx 的版本,另一种是要为 nginx 添加新的模块。



2、nginx 平滑升级原理


多进程模式下的请求分配方式

nginx 默认工作在多进程模式下,即主进程(master process)启动后完成配置加载和端口绑定等动作,

fork

出指定数量的工作进程(worker process),这些子进程会持有监听端口的文件描述符(fd),并通过在该描述符上添加监听事件来接受连接(accept)。


信号的接收和处理

nginx 主进程在启动完成后会进入等待状态,负责响应各类系统消息,如SIGCHLD、SIGHUP、SIGUSR2等。


Nginx信号简介


主进程支持的信号


  • TERM

    ,

    INT

    : 立刻退出

  • QUIT

    : 等待工作进程结束后再退出

  • KILL

    : 强制终止进程

  • HUP

    : 重新加载配置文件,使用新的配置启动工作进程,并逐步关闭旧进程。

  • USR1

    : 重新打开日志文件

  • USR2

    : 启动新的主进程,实现热升级

  • WINCH

    : 逐步关闭工作进程


工作进程支持的信号


  • TERM

    ,

    INT

    : 立刻退出

  • QUIT

    : 等待请求处理结束后再退出

  • USR1

    : 重新打开日志文件


3、nginx 平滑升级实战

1、查看现有的 nginx 编译参数

[root@nginx-server ~]# cd /usr/local/nginx/sbin/nginx -V

2、编译

按照原来的编译参数安装 nginx 的方法进行安装,

只需要到 make,千万不要 make install

。如果make install 会将原来的配置文件覆盖

[root@nginx-server ~]# cd /usr/local/nginx-1.16.0/
[root@nginx-server nginx-1.16.0]# ./configure --prefix=/usr/local/nginx --group=nginx --user=nginx --sbin-path=/usr/local/nginx/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/tmp/nginx/client_body --http-proxy-temp-path=/tmp/nginx/proxy --http-fastcgi-temp-path=/tmp/nginx/fastcgi --pid-path=/var/run/nginx.pid --lock-path=/var/lock/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module --with-pcre --with-http_realip_module --with-stream --with-http_image_filter_module
[root@nginx-server nginx-1.16.0]# make

3、备份原 nginx 二进制文件

备份二进制文件和 nginx 的配置文件(期间nginx不会停止服务)

[root@nginx-server nginx-1.16.0]# mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx_$(date +%F)

4、复制新的nginx二进制文件,进入新的nginx源码包

[root@nginx-server nginx-1.16.0]# cp /usr/local/nginx-1.16.0/objs/nginx /usr/local/nginx/sbin/

5、测试新版本的nginx是否正常

[root@nginx-server nginx-1.16.0]# /usr/local/nginx/sbin/nginx -t

6、给nginx发送平滑迁移信号(若不清楚pid路径,请查看nginx配置文件)

[root@nginx-server ~]# kill -USR2 `cat /var/run/nginx.pid`

7、查看nginx pid,会出现一个nginx.pid.oldbin

[root@nginx-server ~]# ll /var/run/nginx.pid*
-rw-r--r-- 1 root root 5 Jul  1 11:29 /var/run/nginx.pid
-rw-r--r-- 1 root root 5 Jul  1 09:54 /var/run/nginx.pid.oldbin

8、从容关闭旧的Nginx进程

[root@nginx-server ~]# kill -WINCH `cat /var/run/nginx.pid.oldbin`

9、此时不重载配置启动旧的工作进程

[root@nginx-server ~]# kill -HUP `cat /var/run/nginx.pid.oldbin`

10、结束工作进程,完成此次升级

[root@nginx-server ~]# kill -QUIT `cat /var/run/nginx.pid.oldbin`

11、验证Nginx是否升级成功

[root@nginx-server ~]# /usr/local/nginx/sbin/nginx -V


4、升级实验


1、安装配置1.6版本的 nginx

[root@localhost ~]# yum install -y gcc gcc-c++ pcre-devel openssl-devel zlib-devel
[root@localhost ~]# tar xzf nginx-1.6.3.tar.gz -C /usr/local/
[root@localhost ~]# cd /usr/local/nginx-1.6.3
[root@localhost nginx-1.6.3]# ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_stub_status_module
[root@localhost nginx-1.6.3]# make && make install
[root@localhost nginx-1.6.3]# useradd -M -s /sbin/nologin nginx
[root@localhost nginx-1.6.3]# /usr/local/nginx/sbin/nginx -t 
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@localhost nginx-1.6.3]# /usr/local/nginx/sbin/nginx 
[root@localhost nginx-1.6.3]# netstat -lntp
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      13989/nginx: master


2、查看版本和模块

[root@localhost nginx-1.6.3]# /usr/local/nginx/sbin/nginx -V 
nginx version: nginx/1.6.3
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) 
configure arguments: --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_stub_status_module
[root@localhost nginx-1.6.3]# echo "nginx1.6" > /usr/local/nginx/html/index.html
[root@localhost nginx-1.6.3]# yum install -y elinks


4、访问验证

[root@localhost nginx-1.6.3]# elinks 10.0.105.189


5、升级nginx

将 nginx 版本进行升级 并在不影响业务的情况下添加 SSL 和 pcre 模块

[root@localhost ~]# tar xzf nginx-1.12.2.tar.gz -C /usr/local/
[root@localhost ~]# cd /usr/local/nginx-1.12.2/
[root@localhost nginx-1.12.2]# ./configure --prefix=/usr/local/nginx --user=nginx --group=ngiinx --with-http_stub_status_module --with-http_ssl_module --with-pcre
[root@localhost nginx-1.12.2]# make
[root@localhost nginx-1.12.2]# cd
[root@localhost ~]# mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx_lod
[root@localhost ~]# cp /usr/local/nginx-1.12.2/objs/nginx /usr/local/nginx/sbin/
[root@localhost ~]# mv /usr/local/nginx/conf/nginx.conf /usr/local/nginx/conf/nginx.conf.bak
[root@localhost ~]# kill -USR2 `cat /usr/local/nginx/logs/nginx.pid`
[root@localhost ~]# ls /usr/local/nginx/logs/
access.log  error.log  nginx.pid
[root@localhost ~]# ps aux | grep nginx 
root      13989  0.0  0.0  24860   952 ?        Ss   13:55   0:00 nginx: master process /usr/local/nginx/sbin/nginx
nginx     13990  0.0  0.1  25284  1720 ?        S    13:55   0:00 nginx: worker process
root      16525  0.0  0.0 112708   976 pts/2    S+   14:09   0:00 grep --color=auto nginx



15、nginx 错误页面配置

nginx错误页面包括404 403 500 502 503 504等页面,只需要在server中增加以下配置即可:

#error_page  404 403 500 502 503 504  /404.html;
                location = /404.html {
                        root   /usr/local/nginx/html;
                }


注意:

/usr/local/nginx/html/ 路径下必须有404.html这个文件!!!

404.html上如果引用其他文件的png或css就会有问题,显示不出来,因为其他文件的访问也要做配置;

为了简单,可以将css嵌入文件中,图片用base编码嵌入;如下:

[root@localhost html]# vim 404.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
        <title>404</title>
        <style>
            .layout-table{display:table;height:100%;width:100%;vertical-align: middle;margin-top:150px}
            .layout-table-cell{display: table-cell;vertical-align: middle;text-align:center}
            .layout-tip{font-size:28px;color:#373737;margin: 0 auto;margin-top:16px;border-bottom: 1px solid #eee;padding-bottom: 20px;width: 360px;}
            #tips{font-size:18px;color:#666666;margin-top:16px;}
        </style>
    </head>
    <body class="layui-layout-body">
        <div class="layui-layout layui-layout-admin">
            
            <div class="layui-body">
                <div class="layout-table">
                    <div class="layout-table-cell">
                        <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAV4AAACMCAYAAAA0qsGKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkIwRDQ0ODRFMzMyODExRThBQ0Q5Q0UyNkNCMkE4MDk0IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkIwRDQ0ODRGMzMyODExRThBQ0Q5Q0UyNkNCMkE4MDk0Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QjBENDQ4NEMzMzI4MTFFOEFDRDlDRTI2Q0IyQTgwOTQiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QjBENDQ4NEQzMzI4MTFFOEFDRDlDRTI2Q0IyQTgwOTQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4Qz6opAAAU5klEQVR42uxd3XXjthKe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" class="layout-img">
                        <p class="layout-tip">哎呀,找不到该页面啦!</p>
                        <p id="tips">请检查您的网络连接是否正常或者输入的网址是否正确</p>
                    </div>
                </div>
            </div>
        </div>
        
    </body>
</html>

展示效果;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XFjg8cyv-1602213757548)(assets/1561964133748.png)]



16、nginx 流量控制


流量限制

(rate-limiting),是Nginx中一个非常实用,却经常被错误理解和错误配置的功能。我们可以用来限制用户在给定时间内HTTP请求的数量。请求,可以是一个简单网站首页的GET请求,也可以是登录表单的 POST 请求。流量限制可以用作安全目的,比如可以减慢暴力密码破解的速率。通过将传入请求的速率限制为真实用户的典型值,并标识目标URL地址(通过日志),还可以用来抵御 DDOS 攻击。更常见的情况,该功能被用来保护上游应用服务器不被同时太多用户请求所压垮。

以下将会介绍Nginx的

流量限制

的基础知识和高级配置,”流量限制”在Nginx Plus中也适用。



1、Nginx如何限流

Nginx的”流量限制”使用漏桶算法(leaky bucket algorithm),该算法在通讯和分组交换计算机网络中广泛使用,用以处理带宽有限时的突发情况。就好比,一个桶口在倒水,桶底在漏水的水桶。如果桶口倒水的速率大于桶底的漏水速率,桶里面的水将会溢出;同样,在请求处理方面,水代表来自客户端的请求,水桶代表根据”先进先出调度算法”(FIFO)等待被处理的请求队列,桶底漏出的水代表离开缓冲区被服务器处理的请求,桶口溢出的水代表被丢弃和不被处理的请求。



2、配置基本的限流

“流量限制”配置两个主要的指令,

limit_req_zone



limit_req

,如下所示:

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
        upstream myweb {
                server 10.0.105.196:80 weight=1 max_fails=1 fail_timeout=1;
                }
        server {
                listen 80;
                server_name localhost;

                location /login {
                        limit_req zone=mylimit;
                        proxy_pass http://myweb;
                        proxy_set_header Host $host:$server_port;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        }
        }
10.0.105.196配置:
server {
        listen 80;
        server_name localhost;
        location /login {
                root    /usr/share/nginx/html;
                index   index.html index.html;
                }
}


limit_req_zone

指令定义了流量限制相关的参数,而

limit_req

指令在出现的上下文中启用流量限制(示例中,对于”/login/”的所有请求)。


limit_req_zone

指令通常在HTTP块中定义,使其可在多个上下文中使用,它需要以下三个参数:


  • Key

    – 定义应用限制的请求特性。示例中的 Nginx 变量

    $binary_remote_addr

    ,保存客户端IP地址的二进制形式。这意味着,我们可以将每个不同的IP地址限制到,通过第三个参数设置的请求速率。(使用该变量是因为比字符串形式的客户端IP地址

    $remote_addr

    ,占用更少的空间)

  • Zone

    – 定义用于存储每个IP地址状态以及被限制请求URL访问频率的共享内存区域。保存在内存共享区域的信息,意味着可以在Nginx的worker进程之间共享。定义分为两个部分:通过

    zone=keyword

    标识区域的名字,以及冒号后面跟区域大小。16000个IP地址的状态信息,大约需要1MB,所以示例中区域可以存储160000个IP地址。

  • Rate

    – 定义最大请求速率。在示例中,速率不能超过每秒10个请求。Nginx实际上以毫秒的粒度来跟踪请求,所以速率限制相当于每100毫秒1个请求。因为不允许”突发情况”(见下一章节),这意味着在前一个请求100毫秒内到达的请求将被拒绝。

当Nginx需要添加新条目时存储空间不足,将会删除旧条目。如果释放的空间仍不够容纳新记录,Nginx将会返回

503状态码

(Service Temporarily Unavailable)。另外,为了防止内存被耗尽,Nginx每次创建新条目时,最多删除两条60秒内未使用的条目。


limit_req_zone

指令设置流量限制和共享内存区域的参数,但实际上并不限制请求速率。所以需要通过添加


limit_req

指令,将流量限制应用在特定的

location

或者

server

块。在上面示例中,我们对

/login/

请求进行流量限制。

现在每个IP地址被限制为每秒只能请求10次

/login/

,更准确地说,在前一个请求的100毫秒内不能请求该URL。



3、处理突发

如果我们在100毫秒内接收到2个请求,怎么办?对于第二个请求,Nginx将给客户端返回状态码503。这可能并不是我们想要的结果,因为应用本质上趋向于突发性。相反地,我们希望缓冲任何超额的请求,然后及时地处理它们。我们更新下配置,在

limit_req

中使用

burst

参数:

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
        upstream myweb {
                server 10.0.105.196:80 weight=1 max_fails=1 fail_timeout=1;
                }
        server {
                listen 80;
                server_name localhost;

                location /login {
                        limit_req zone=mylimit burst=20;
                        proxy_pass http://myweb;
                        proxy_set_header Host $host:$server_port;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        }
        }


burst

参数定义了超出zone指定速率的情况下(示例中的

mylimit

区域,速率限制在每秒10个请求,或每100毫秒一个请求),客户端还能发起多少请求。上一个请求100毫秒内到达的请求将会被放入队列,我们将队列大小设置为20。

这意味着,如果从一个给定IP地址发送21个请求,Nginx会立即将第一个请求发送到上游服务器群,然后将余下20个请求放在队列中。然后每100毫秒转发一个排队的请求,只有当传入请求使队列中排队的请求数超过20时,Nginx才会向客户端返回503。



4、无延迟的排队

配置

burst

参数将会使通讯更流畅,但是可能会不太实用,因为该配置会使站点看起来很慢。在上面的示例中,队列中的第20个包需要等待2秒才能被转发,此时返回给客户端的响应可能不再有用。要解决这个情况,可以在

burst

参数后添加

nodelay

参数:

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
        upstream myweb {
                server 10.0.105.196:80 weight=1 max_fails=1 fail_timeout=1;
                }
        server {
                listen 80;
                server_name localhost;

                location /login {
                        limit_req zone=mylimit burst=20 nodelay;
                        proxy_pass http://myweb;
                        proxy_set_header Host $host:$server_port;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        }
        }

使用

nodelay

参数,Nginx仍将根据

burst

参数分配队列中的位置,并应用已配置的速率限制,而不是清理队列中等待转发的请求。相反地,当一个请求到达“太早”时,只要在队列中能分配位置,Nginx将立即转发这个请求。将队列中的该位置标记为”taken”(占据),并且不会被释放以供另一个请求使用,直到一段时间后才会被释放(在这个示例中是,100毫秒后)。

假设如前所述,队列中有20个空位,从给定的IP地址发出的21个请求同时到达。Nginx会立即转发这个21个请求,并且标记队列中占据的20个位置,然后每100毫秒释放一个位置。如果是25个请求同时到达,Nginx将会立即转发其中的21个请求,标记队列中占据的20个位置,并且返回503状态码来拒绝剩下的4个请求。

现在假设,第一组请求被转发后101毫秒,另20个请求同时到达。队列中只会有一个位置被释放,所以Nginx转发一个请求并返回503状态码来拒绝其他19个请求。如果在20个新请求到达之前已经过去了501毫秒,5个位置被释放,所以Nginx立即转发5个请求并拒绝另外15个。

效果相当于每秒10个请求的“流量限制”。如果希望不限制两个请求间允许间隔的情况下实施“流量限制”,

nodelay

参数是很实用的。


注意:

对于大部分部署,我们建议使用

burst



nodelay

参数来配置

limit_req

指令。



5、高级配置示例

通过将基本的“流量限制”与其他Nginx功能配合使用,我们可以实现更细粒度的流量限制。



1、白名单

下面这个例子将展示,如何对任何不在白名单内的请求强制执行“流量限制”:

http {
    include       /etc/nginx/mime.types;
    default_type  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"';

    access_log  /var/log/nginx/access.log  main;
    geo $limit {
	default 		1;
	10.0.0.0/24 		0;
	192.168.0.0/24 		0;
	}
    map $limit $limit_key {
	0 "";
	1 $binary_remote_addr;
	}
    limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;

	server {
		listen 80;
		server_name localhost;
		location / {
		limit_req zone=req_zone burst=10 nodelay;
		root /usr/share/nginx/html;
		index index.html index.hml;
		}
}
    include /etc/nginx/conf.d/*.conf;
}

这个例子同时使用了

geo



map

指令。

geo

块将给在白名单中的IP地址对应的

$limit

变量分配一个值0,给其它不在白名单中的分配一个值1。然后我们使用一个映射将这些值转为key,如下:

  • 如果

    $limit

    变量的值是0,

    $limit_key

    变量将被赋值为空字符串
  • 如果

    $limit

    变量的值是1,

    $limit_key

    变量将被赋值为客户端二进制形式的IP地址

两个指令配合使用,白名单内IP地址的

$limit_key

变量被赋值为空字符串,不在白名单内的被赋值为客户端的IP地址。当

limit_req_zone

后的第一个参数是空字符串时,不会应用“流量限制”,所以白名单内的IP地址(10.0.0.0/24和192.168.0.0/24 网段内)不会被限制。其它所有IP地址都会被限制到每秒5个请求。


limit_req

指令将限制应用到**/**的location块,允许在配置的限制上最多超过10个数据包的突发,并且不会延迟转发。



2、location 包含多

limit_req

指令

我们可以在一个location块中配置多个

limit_req

指令。符合给定请求的所有限制都被应用时,意味着将采用最严格的那个限制。例如,多个指令都制定了延迟,将采用最长的那个延迟。同样,请求受部分指令影响被拒绝,即使其他指令允许通过也无济于事。

扩展前面将“流量限制”应用到白名单内IP地址的例子:

http {
	# ...

	limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;
	limit_req_zone $binary_remote_addr zone=req_zone_wl:10m rate=15r/s;

	server {
		# ...
		location / {
			limit_req zone=req_zone burst=10 nodelay;
			limit_req zone=req_zone_wl burst=20 nodelay;
			# ...
		}
	}
}

白名单内的IP地址不会匹配到第一个“流量限制”,而是会匹配到第二个

req_zone_wl

,并且被限制到每秒15个请求。不在白名单内的IP地址两个限制能匹配到,所以应用限制更强的那个:每秒5个请求。



6、配置流量控制相关功能


1、配置日志记录

默认情况下,Nginx会在日志中记录由于流量限制而延迟或丢弃的请求,如下所示:

2019/02/13 04:20:00 [error] 120315#0: *32086 limiting requests, excess: 1.000 by zone "mylimit", client: 192.168.1.2, server: nginx.com, request: "GET / HTTP/1.0", host: "nginx.com"

日志条目中包含的字段:

  • limiting requests – 表明日志条目记录的是被“流量限制”请求
  • excess – 每毫秒超过对应“流量限制”配置的请求数量
  • zone – 定义实施“流量限制”的区域
  • client – 发起请求的客户端IP地址
  • server – 服务器IP地址或主机名
  • request – 客户端发起的实际HTTP请求
  • host – HTTP报头中host的值

默认情况下,Nginx以

error

级别来记录被拒绝的请求,如上面示例中的

[error]

所示(Ngin以较低级别记录延时请求,一般是

info

级别)。如要更改Nginx的日志记录级别,需要使用

limit_req_log_level

指令。这里,我们将被拒绝请求的日志记录级别设置为

warn

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
        upstream myweb {
                server 10.0.105.196:80 weight=1 max_fails=1 fail_timeout=1;
                }
        server {
                listen 80;
                server_name localhost;

                location /login {
                        limit_req zone=mylimit burst=20 nodelay;
                        limit_req_log_level warn;
                        proxy_pass http://myweb;
                        proxy_set_header Host $host:$server_port;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        }
        }


2、发送到客户端的错误代码

一般情况下,客户端超过配置的流量限制时,Nginx响应状态码为

503(Service Temporarily Unavailable)

。可以使用

limit_req_status

指令来设置为其它状态码(例如下面的

444

状态码):

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
	upstream myweb {
        	server 10.0.105.196:80 weight=1 max_fails=1 fail_timeout=1;
		}
	server {
        	listen 80;
        	server_name localhost;
		
        	location /login {
			limit_req zone=mylimit burst=20 nodelay;
			limit_req_log_level warn;
			limit_req_status 444;
                	proxy_pass http://myweb;
                        proxy_set_header Host $host:$server_port;
	            	proxy_set_header X-Real-IP $remote_addr;
            		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                	}
	}


7、nginx 流量控制总结

以上已经涵盖了Nginx和Nginx Plus提供的“流量限制”的很多功能,包括为HTTP请求的不同loation设置请求速率,给“流量限制”配置

burst



nodelay

参数。还涵盖了针对客户端IP地址的白名单和黑名单应用不同“流量限制”的高级配置,阐述了如何去日志记录被拒绝和延时的请求。



17、nginx 访问控制



1、nginx 访问控制模块

(1)基于IP的访问控制:http_access_module

(2)基于用户的信任登录:http_auth_basic_module



2、基于IP的访问控制


1、配置语法
Syntax:allow address | CIDR | unix: | all;
default:默认无
Context:http,server,location,limit_except

Syntax:deny address | CIDR | unix: | all;
default:默认无
Context:http,server,location,limit_except


2、配置测试

修改

/etc/nginx/conf.d/access_mod.conf

内容如下:

server {
        listen 80;
        server_name localhost;
        location ~ ^/admin {
                root /home/www/html;
                index index.html index.hml;
                deny 192.168.1.8;
                allow all;
                #deny 192.168.1.8;
                }
}
#需要注意:
如果先允许访问,在定义拒绝访问。那么拒绝访问不生效。

虚拟机宿主机IP为

192.168.1.8

,虚拟机IP为

192.168.1.11

,故这里禁止宿主机访问,允许其他所有IP访问。

宿主机访问

http://192.168.1.11/admin

,显示

403 Forbidden



当然也可以反向配置,同时也可以使用IP网段的配置方式,如

allow 192.168.1.0/24;

,表示满足此网段的IP都可以访问。



3、指定

location

拒绝所有请求

如果你想拒绝某个指定URL地址的所有请求,而不是仅仅对其限速,只需要在

location

块中配置

deny


all

指令:

server {
        listen 80;
        server_name localhost;
        location /foo.html {
                root /home/www/html;
                deny all;
                }
}


4、局限性


remote_addr

只能记录上一层与服务器直接建立连接的IP地址,若中间有代理,则记录的是代理的IP地址。


http_x_forwarded_for

可以记录每一层级的IP。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kmSgk23O-1602213757549)(assets/1561994965370.png)]



5、解决方法

(1)采用别的HTTP头信息控制访问,如HTTP_X_FORWARD_FOR(无法避免被改写)

(2)结合geo模块

(3)通过HTTP自定义变量传递



3、基于用户的信任登录


1、配置语法
Syntax:auth_basic string | off;
default:auth_basic off;
Context:http,server,location,limit_except

Syntax:auth_basic_user_file file;
default:默认无
Context:http,server,location,limit_except
file:存储用户名密码信息的文件。


2、配置示例

改名

access_mod.conf



auth_mod.conf

,内容如下:

server {
	listen 80;
	server_name localhost;
	location ~ ^/admin {
		root /home/www/html;
		index index.html index.hml;
		auth_basic "Auth access test!";
		auth_basic_user_file /etc/nginx/auth.conf;
		}
}


auth_basic

不为

off

,开启登录验证功能,

auth_basic_user_file

加载账号密码文件。



3、建立口令文件
[root@192 ~]# yum install -y httpd-tools #htpasswd 是开源 http 服务器 apache httpd 的一个命令工具,用于生成 http 基本认证的密码文件
[root@192 ~]# htpasswd -cm /etc/nginx/auth_conf user10
[root@192 ~]# htpasswd -m /etc/nginx/auth_conf user20
[root@192 ~]# cat /etc/nginx/auth_conf 
user10:$apr1$MOa9UVqF$RlYRMk7eprViEpNtDV0n40
user20:$apr1$biHJhW03$xboNUJgHME6yDd17gkQNb0


4、访问测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ykhK3kra-1602213757551)(assets/1561996355328.png)]

]



5、局限性

(1)用户信息依赖文件方式

(2)操作管理机械,效率低下



6、解决方法

(1)Nginx结合LUA实现高效验证

(2)Nginx和LDAP打通,利用nginx-auth-ldap模块

(3)Nginx只做中间代理,具体认证交给应用。



18、nginx 变量(了解)

Nginx 同 Apache 和 Lighttpd 等其他 Web 服务器的配置方法不太相同,Nginx的配置文件使用语法的就是一门微型的编程语言。可以类似写程序一般编写配置文件,可操作性很大。既然是编程语言,一般也就少不了“变量”这种东西。



1、nginx变量简介
  • 所有的 Nginx变量在 Nginx 配置文件中引用时都须带上 $ 前缀

  • 在 Nginx 配置中,变量只能存放一种类型的值,有且也只存在一种类型,那就是字符串类型

  • nginx可以使用变量简化配置与提高配置的灵活性,所有的变量值都可以通过这种方式引用:

    $变量名
    


2、nginx 变量的定义和使用

nginx中的变量分为两种,自定义变量与内置预定义变量



1、自定义变量


1、声明变量


可以在sever,http,location等标签中使用set命令(非唯一)声明变量,语法如下

set $变量名 变量值


注意:

  • nginx 中的变量必须都以$开头
  • nginx 的配置文件中所有使用的变量都必须是声明过的,否则 nginx 会无法启动并打印相关异常日志


2、变量的可见性

nginx 变量的一个有趣的特性就是nginx中每一个变量都是全局可见的,而他们又不是全局变量。如下例子

location a/ {
  return 200 $a
}

location b/ {
 set $a hello nginx
 return 200 $a
}

由于变量是全局可见的所以nginx启动不会报错,而第一个location中并不知道$a的具体值因此返回的响应结果为一个空字符串。

在不同层级的标签中声明的变量性的可见性规则如下:

  • location标签中声明的变量中对这个location块可见
  • server标签中声明的变量对server块以及server块中的所有子块可见
  • http标签中声明的变量对http块以及http块中的所有子块可见


3、配置 $foo=hello

server {
    listen 8080;
    server_name  localhost;
    
    location /test {
            set $foo hello;
            echo "foo: $foo";
    }
}

输出

[root@localhost html]# nginx -s reload
[root@localhost html]# curl localhost/test-dollar
This is a dollar sign: \$

这里用到了标准模块 ngx_geo 提供的配置指令 geo 来为变量



d

o

l

l

a

r

dollar 赋予字符串 ”






d


o


l


l


a


r























“,这样我们在下面需要使用美元符的地方,就直接引用我们的 $dollar 变量就可以了。


4、 使用大括号插值

在“变量插值”的上下文中,还有一种特殊情况,即当引用的变量名之后紧跟着变量名的构成字符时(比如后跟字母、数字以及下划线),我们就需要使用特别的记法来消除歧义,例如:

server {
    ...
    location /test-brace {
        set $first "hello ";
        echo "${first}world";
    }
}

输出

[root@localhost html]# nginx -s reload
[root@localhost html]# curl localhost/test-brace
hello world

这里,我们在 echo 配置指令的参数值中引用变量 first 的时候,后面紧跟着 world 这个单词,所以如果直接写作 “firstworld” 则 Nginx “变量插值”计算引擎会将之识别为引用了变量 firstworld. 为了解决这个难题,Nginx 的字符串记法支持使用花括号在 之后把变量名围起来,比如这里的 ${first}。


5、变量作用域

set 指令(以及前面提到的 geo 指令)不仅有赋值的功能,它还有创建 Nginx 变量的副作用,即当作为赋值对象的变量尚不存在时,它会自动创建该变量。比如在上面这个例子中,如果 $a 这个变量尚未创建,则 set 指令会自动创建 $a 这个用户变量。如果我们不创建就直接使用它的值,则会报错。

例如

server {
    ...
    location /bad {
        echo $foo;
    }
}

此时 Nginx 服务器会拒绝加载配置:

[emerg] unknown "foo" variable

Nginx 变量的创建和赋值操作发生在全然不同的时间阶段,Nginx 变量的创建只能发生在 Nginx 配置加载的时候,或者说 Nginx 启动的时候,而赋值操作则只会发生在请求实际处理的时候。

这意味着不创建而直接使用变量会导致启动失败,同时也意味着我们无法在请求处理时动态地创建新的 Nginx 变量。

Nginx 变量一旦创建,其变量名的可见范围就是整个 Nginx 配置,甚至可以跨越不同虚拟主机的 server 配置块。我们来看一个例子:

server {
    listen 8080;
    
    location /foo {
        echo "foo = [$foo]";
    }
    
    location /bar {
        set $foo 32;
        echo "foo = [$foo]";
    }
}

输出

[root@localhost html]# curl 'http://localhost/foo'
foo = []

[root@localhost html]# curl 'http://localhost/bar'
foo = [32]

[root@localhost html]# curl 'http://localhost/foo'
foo = []

这里我们在 location /bar 中用 set 指令创建了变量 foo,于是在整个配置文件中这个变量都是可见的,因此我们可以在 location /foo 中直接引用这个变量而不用担心 Nginx 会报错。

从这个例子我们可以看到,set 指令因为是在 location /bar 中使用的,所以赋值操作只会在访问 /bar 的请求中执行。而请求 /foo 接口时,我们总是得到空的 foo值,因为用户变量未赋值就输出的话,得到的便是空字符串。

从这个例子我们可以窥见的另一个重要特性是,Nginx 变量名的可见范围虽然是整个配置,但每个请求都有所有变量的独立副本,或者说都有各变量用来存放值的容器的独立副本,彼此互不干扰。比如前面我们请求了 /bar 接口后,foo 变量被赋予了值 32,但它丝毫不会影响后续对 /foo 接口的请求所对应的 foo 值(它仍然是空的!),因为各个请求都有自己独立的 $foo 变量的副本。

对于 Nginx 新手来说,最常见的错误之一,就是将 Nginx 变量理解成某种在请求之间全局共享的东西,或者说“全局变量”。而事实上,Nginx 变量的生命期是不可能跨越请求边界的。

关于 Nginx 变量的另一个常见误区是认为变量容器的生命期,是与 location 配置块绑定的。其实不然。我们来看一个涉及“内部跳转”的例子:

server {
    listen 8080;

    location /foo {
        set $a hello;
        echo_exec /bar;
    }

    location /bar {
        echo "a = [$a]";
    }
}

输出

[root@localhost html]# curl localhost/foo
a = [hello]

这 里我们在 location /foo 中,使用第三方模块 ngx_echo 提供的 echo_exec 配置指令,发起到 location /bar 的“内部跳转”。所谓“内部跳转”,就是在处理请求的过程中,于服务器内部,从一个 location 跳转到另一个 location 的过程。这不同于利用 HTTP 状态码 301 和 302 所进行的“外部跳转”,因为后者是由 HTTP 客户端配合进行跳转的,而且在客户端,用户可以通过浏览器地址栏这样的界面,看到请求的 URL 地址发生了变化。内部跳转和 Bourne Shell(或 Bash)中的 exec 命令很像,都是“有去无回”。另一个相近的例子是 C 语言中的 goto 语句。

既然是内部跳转,当前正在处理的请求就还是原来那个,只是当前的 location 发生了变化,所以还是原来的那一套 Nginx 变量的容器副本。对应到上例,如果我们请求的是 /foo 这个接口,那么整个工作流程是这样的:先在 location /foo 中通过 set 指令将 a 变量的值赋为字符串 hello,然后通过 echo_exec 指令发起内部跳转,又进入到 location /bar 中,再输出 a 变量的值。因为 a 还是原来的 a,所以我们可以期望得到 hello 这行输出。测试证实了这一点:

但如果我们从客户端直接访问 /bar 接口,就会得到空的 a 变量的值,因为它依赖于 location /foo 来对 a 进行初始化。

从上面这个例子我们看到,一个请求在其处理过程中,即使经历多个不同的 location 配置块,它使用的还是同一套 Nginx 变量的副本。这里,我们也首次涉及到了“内部跳转”这个概念。值得一提的是,标准 ngx_rewrite 模块的 rewrite 配置指令其实也可以发起“内部跳转”,例如上面那个例子用 rewrite 配置指令可以改写成下面这样的形式:

server {

    listen 8080;

    location /foo {
        set $a hello;
        rewrite ^ /bar;
    }

    location /bar {
        echo "a = [$a]";
    }
}

从上面这个例子我们看到,Nginx 变量值容器的生命期是与当前正在处理的请求绑定的,而与 location 无关。



2、内置预定义变量

内置预定义变量即无需声明就可以使用的变量,通常包括一个http请求或响应中一部分内容的值,以下为一些常用的内置预定义变量


变量名

定义
$arg_PARAMETER GET请求中变量名PARAMETER参数的值。
$args 这个变量等于GET请求中的参数。例如,foo=123&bar=blahblah;这个变量只可以被修改
$binary_remote_addr 二进制码形式的客户端地址。
$body_bytes_sent 传送页面的字节数
$content_length 请求头中的Content-length字段。
$content_type 请求头中的Content-Type字段。
$cookie_COOKIE cookie COOKIE的值。
$document_root 当前请求在root指令中指定的值。
$document_uri 与$uri相同。
$host 请求中的主机头(Host)字段,如果请求中的主机头不可用或者空,则为处理请求的server名称(处理请求的server的server_name指令的值)。值为小写,不包含端口。
$hostname 机器名使用 gethostname系统调用的值
$http_HEADER HTTP请求头中的内容,HEADER为HTTP请求中的内容转为小写,-变为_(破折号变为下划线),例如:$http_user_agent(Uaer-Agent的值);
$sent_http_HEADER HTTP响应头中的内容,HEADER为HTTP响应中的内容转为小写,-变为_(破折号变为下划线),例如: $sent_http_cache_control, $sent_http_content_type…;
$is_args 如果$args设置,值为”?”,否则为””。
$limit_rate 这个变量可以限制连接速率。
$nginx_version 当前运行的nginx版本号。
$query_string 与$args相同。
$remote_addr 客户端的IP地址。
$remote_port 客户端的端口。
$remote_user 已经经过Auth Basic Module验证的用户名。
$request_filename 当前连接请求的文件路径,由root或alias指令与URI请求生成。
$request_body 这个变量(0.7.58+)包含请求的主要信息。在使用proxy_pass或fastcgi_pass指令的location中比较有意义。
$request_body_file 客户端请求主体信息的临时文件名。
$request_completion 如果请求成功,设为”OK”;如果请求未完成或者不是一系列请求中最后一部分则设为空。
$request_method 这个变量是客户端请求的动作,通常为GET或POST。包括0.8.20及之前的版本中,这个变量总为main request中的动作,如果当前请求是一个子请求,并不使用这个当前请求的动作。
$request_uri 这个变量等于包含一些客户端请求参数的原始URI,它无法修改,请查看$uri更改或重写URI。
$scheme 所用的协议,比如http或者是https,比如rewrite ^(.+)$ $scheme://example.com$1 redirect;
$server_addr 服务器地址,在完成一次系统调用后可以确定这个值,如果要绕开系统调用,则必须在listen中指定地址并且使用bind参数。
$server_name 服务器名称。
$server_port 请求到达服务器的端口号。
$server_protocol 请求使用的协议,通常是HTTP/1.0或HTTP/1.1。
$uri 请求中的当前URI(不带请求参数,参数位于args),不同于浏览器传递的args),不同于浏览器传递的args),不同于浏览器传递的request_uri的值,它可以通过内部重定向,或者使用index指令进行修改。不包括协议和主机名,例如/foo/bar.html

Nginx 内建变量最常见的用途就是获取关于请求或响应的各种信息。


1、uri vs request_uri

由 ngx_http_core 模块提供的内建变量 uri,可以用来获取当前请求的 URI(经过解码,并且不含请求参数),

而 request_uri 则用来获取请求最原始的 URI (未经解码,并且包含请求参数)。

location /test-uri {
    echo "uri = $uri";
    echo "request_uri = $request_uri";
}

输出

[root@localhost html]# nginx -s reload
[root@localhost html]# curl localhost/test-uri
uri = /test-uri
request_uri = /test-uri

[root@localhost html]# curl "localhost/test-uri?a=3&b=4"
uri = /test-uri
request_uri = /test-uri?a=3&b=4

[root@localhost html]# curl "localhost/test-uri/hello%20world?a=3&b=4"
uri = /test-uri/hello world
request_uri = /test-uri/hello%20world?a=3&b=4


2、$arg_XXX

另一个特别常用的内建变量其实并不是单独一个变量,而是有无限多变种的一群变量,即名字以 arg_ 开头的所有变量,我们估且称之为 arg_XXX 变量群。

一个例子是 arg_name,这个变量的值是当前请求中名为 name 的参数的值,而且还是未解码的原始形式的值。

location /test-arg {
    echo "name: $arg_name";
    echo "class: $arg_class";
}

输出

[root@localhost html]# nginx -s reload
[root@localhost html]# curl localhost/test-arg
name: 
class:

[root@localhost html]# curl "localhost/test-arg?name=Tom&class=3"
name: Tom
class: 3

[root@localhost html]# curl "localhost/test-arg?name=hello%20world&class=9"
name: hello%20world
class: 9


3、$arg_XXX 不区分大小写

其实 $arg_name 不仅可以匹配 name 参数,也可以匹配 NAME 参数,抑或是 Name,Nginx 会在匹配参数名之前,自动把原始请求中的参数名调整为全部小写的形式。

[root@localhost html]# curl "localhost/test-arg?NAME=Marry"
name: Marry
class:

[root@localhost html]# curl "localhost/test-arg?Name=Jimmy"
name: Jimmy
class:


4、对 uri 解码

如果你想对 URI 参数值中的 %XX 这样的编码序列进行解码,可以使用第三方 ngx_set_misc 模块提供的

location /test-unescape-uri {
    set_unescape_uri $name $arg_name;
    set_unescape_uri $class $arg_class;
    echo "name: $name";
    echo "class: $class";
}

现在我们再看一下效果:

[root@localhost html]# curl "localhost/test-arg?name=hello%20world&class=9"
name: hello world
class: 9

从这个例子我们同时可以看到,这个 set_unescape_uri 指令也像 set 指令那样,拥有自动创建 Nginx 变量的功能。后面我们还会专门介绍到 ngx_set_misc 模块。

像 $arg_XXX 这种类型的变量拥有无穷无尽种可能的名字,所以它们并不对应任何存放值的容器。而且这种变量在 Nginx 核心中是经过特别处理的,第三方 Nginx 模块是不能提供这样充满魔法的内建变量的。

类 似 arg_XXX 的内建变量还有不少,比如用来取 cookie 值的 cookie_XXX 变量群,用来取请求头的 http_XXX 变量群,以及用来取响应头的 sent_http_XXX 变量群。这里就不一一介绍了,感兴趣的读者可以参考 ngx_http_core 模块的官方文档。



19、nginx 监控



1、nginx的基础监控
  • 进程监控
  • 端口监控

注意: 这两个是必须要加在zabbix监控,加触发器有问题及时告警。

web 服务器 nginx 以其高性能与抗并发能力越来越多的被用户使用

作为一款服务器产品,其运行状态是运维密切关注的,因此,对 nginx 的实时监控就必须要关注的了

nginx 提供了 ngx_http_stub_status_module,ngx_http_reqstat_module模块,这个模块提供了基本的监控功能

作为官方企业版的 nginx plus 通过 ngx_http_status_module 提供了更加完善的监控功能: http://demo.nginx.com/status.html



2、监控的主要指标

我们需要对以下主要的指标进行监控:



1、基本活跃指标

Accepts(接受)、Handled(已处理)、Requests(请求数)是一直在增加的计数器。Active(活跃)、Waiting(等待)、Reading(读)、Writing(写)随着请求量而增减。

名称 描述 指标类型
Accepts(接受) NGINX 所接受的客户端连接数 资源: 功能
Handled(已处理) 成功的客户端连接数 资源: 功能
Dropped(已丢弃,计算得出) 丢弃的连接数(接受 – 已处理) 工作:错误*
Requests(请求数) 客户端请求数 工作:吞吐量

NGINX worker 进程接受 OS 的连接请求时 Accepts 计数器增加,而Handled 是当实际的请求得到连接时(通过建立一个新的连接或重新使用一个空闲的)。这两个计数器的值通常都是相同的,如果它们有差别则表明连接被Dropped,往往这是由于资源限制,比如已经达到 NGINX 的worker_connections的限制。



2、每秒请求数 – QPS

按照固定时间间隔采样请求数据,计算出单位时间的请求量可以看到你的 web 服务器的请求情况

通过持续的 QPS 监控,可以立刻发现是否被恶意攻击或对服务的可用性进行评估

虽然当问题发生时,通过 QPS 不能定位到确切问题的位置,但是他却可以在第一时间提醒你环境可能出问题了



3、服务器错误率

通过监控固定时间间隔内的错误代码(4XX代码表示客户端错误,5XX代码表示服务器端错误),可以了解到客户端收到的结果是否是正确的错误率突然的飙升很可能是你的网站漏洞发出的信号

如果你希望通过 access log 分析错误率,那么你需要配置 nginx 的日志模块,让 nginx 将响应码写入访问日志



4、请求处理时间

请求处理时间也可以被记录在 access log 中,通过分析 access log,统计请求的平均响应时间,通过持续观察,可以发现上游服务器的问题



3、指标的收集

介绍了这么多的监控指标,事实上,上面介绍的仅仅是基本的监控指标,针对实际的情况,还有很多指标十分具有监控的必要

那么,怎么去收集这些指标进行监控呢?

通过在编译时加入

nginx



ngx_http_stub_status_module

模块我们可以实时监控以下基本的指标:



1、nginx Stub Status 监控模块安装

先使用命令查看是否已经安装这个模块:

# -V大写会显示版本号和模块等信息、v小写仅显示版本信息
[root@localhost ~]# nginx -V

如果没有此模块,需要重新安装,编译命令如下:

./configure –with-http_stub_status_module

具体的使用方法是在执行 ./configure 时,指定 –with-http_stub_status_module,然后通过配置:

server {
        listen 80;
        server_name localhost;
        location /nginx-status {
                stub_status     on;
                access_log      on;
                allow   10.0.105.207; #允许本地电脑访问
                deny    all;
                }
}

此处默认只有本地访问,如果远程查看需要加相关的IP或者干脆去掉Deny all即可。加密文件可以使用#htpasswd -c /usr/nginx/conf hxb 命令来创建。配置完成后需要重启Nginx服务。状态配置只能是针对某个Nginx服务。目前Nginx还无法做到针对单个站点进行监控。



2、nginx 状态查看

配置完成后在浏览器中输入http://10.0.105.207/nginx-status 查看(或者用

curl localhost/nginx_status

),显示信息如下:

Active connections: 2 
server accepts handled requests
 26 26 48 
Reading: 0 Writing: 1 Waiting: 1 

Accepts(接受)、Handled(已处理)、Requests(请求数)是一直在增加的计数器。Active(活跃)、Waiting(等待)、Reading(读)、Writing(写)随着请求量而增减

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2YCsNzYB-1602213757552)(assets/1562035406107.png)]



3、Stub Status 参数说明

active connections – 活跃的连接数量

server accepts handled requests — 总共处理了1075个连接 , 成功创建1064次握手, 总共处理了6253个请求

每个连接有三种状态waiting、reading、writing

reading —读取客户端的Header信息数.这个操作只是读取头部信息,读取完后马上进入writing状态,因此时间很短。

writing — 响应数据到客户端的Header信息数.这个操作不仅读取头部,还要等待服务响应,因此时间比较长。

waiting — 开启keep-alive后等候下一次请求指令的驻留连接.

正常情况下waiting数量是比较多的,并不能说明性能差。反而如果reading+writing数量比较多说明服务并发有问题。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mfn2VHGd-1602213757553)(assets/1562035574221.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zr7ObEYM-1602213757554)(assets/1562035977477.png)]

当用户请求连接Nginx服务器时,accepts计数器会加一。且当服务器处理该连接请求时,handled计数器同样会加一。一般而言,两者的值是相等的,除非达到了某些资源极限(如worker_connection的限制)。

用户连接请求被处理,就会进入 active 状态。如果该连接没有其他 request,则进入 waiting 的子状态;如果有 request,nginx 会读取 request 的 header,计数器 request 加一,进入 reading 的子状态。 reading 状态持续时间非常短,header 被读取后就会进入 writing 状态。事实上,直到服务器将响应结果返回给用户之前,该连接会一直保持 writing 状态。所以说,writing 状态一般会被长时间占用。

一旦 NGINX 成功处理一个连接时,连接会移动到Active状态,在这里对客户端请求进行处理:


Active状态


Waiting

: 活跃的连接也可以处于 Waiting 子状态,如果有在此刻没有活跃请求的话。新连接可以绕过这个状态并直接变为到 Reading 状态,最常见的是在使用“accept filter(接受过滤器)” 和 “deferred accept(延迟接受)”时,在这种情况下,NGINX 不会接收 worker 进程的通知,直到它具有足够的数据才开始响应。如果连接设置为 keep-alive ,那么它在发送响应后将处于等待状态。


Reading

: 当接收到请求时,连接离开 Waiting 状态,并且该请求本身使 Reading 状态计数增加。在这种状态下 NGINX 会读取客户端请求首部。请求首部是比较小的,因此这通常是一个快速的操作。


Writing

: 请求被读取之后,其使 Writing 状态计数增加,并保持在该状态,直到响应返回给客户端。这意味着,该请求在 Writing 状态时, 一方面 NGINX 等待来自上游系统的结果(系统放在 NGINX “后面”),另外一方面,NGINX 也在同时响应。请求往往会在 Writing 状态花费大量的时间。

通常,一个连接在同一时间只接受一个请求。在这种情况下,Active 连接的数目 == Waiting 的连接 + Reading 请求 + Writing 。


怎么利用这些参数?

开源的 Nginx 提供的原始参数中,实时性的会比较有用,如 Active connections、Reading、Writing 以及 Waiting。这些数据能够反映当前 Nginx 的负载情况,方便在服务器出现问题时及时发现问题。而另一些数据由于不是状态量,Nginx 无法计算当前的量值而改做其统计数,如 accepts、handled 和 requests。

对于维护网站人员,accepts、handled 和 requests 的统计值用处是不大的,值得参考的是短时间内这三者数值的增量。这个短时间可以是一秒,如 accepts_per_second、handled_per_second 和 requests_per_second。一个简单的做法就是每秒都去读取这些参数,返回一个和上一秒的差值就行。当然,handled_per_second 替换成 dropped_per_second=accepts_per_second-handled_per_second 就更完美了。

通过这七个参数,就可以从连接到请求全方位的监控起 Nginx 的运行状态。为了方便检测,对每次获取的参数保留下来,然后按时间展现出来。下图展示了 Nginx 在运行时的参考数据。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N1dCsJMn-1602213757556)(assets/1562036059274.png)]



4、Reqstat 模块监控


描述

  • ngx_http_reqstat_module 模块
  • 这个模块计算定义的变量,根据变量值分别统计 nginx 的运行状况。
  • 可以监视的运行状况有:连接数、请求数、各种响应码范围的请求数、输入输出流量、rt、upstream访问等。
  • 可以指定获取所有监控结果或者一部分监控结果。
  • 利用变量添加自定义监控状态。总的监控状态最大个数为50个。
  • 回收过期的监控数据。
  • 设置输出格式
  • 跟踪请求,不受内部跳转的影响
  • 不要使用与响应相关的变量作为条件,比如”$status”


编译

默认编入Tengine,可通过 –without-http_reqstat_module 编译此模块,或通过–with-http_reqstat_module=shared 编译为so模块。

使用so模块加载的话,请确保其顺序在”ngx_http_lua_module”之后。可以借助”nginx -m”来确认。



Syntax

:

req_status zone_name1 [zone_name2 [zone_name3 […]]]



Default

:

none



Context

:

http、srv、loc

开启统计,可以指定同时统计多个目标,每一个zone_name对应一个目标。



Syntax

:

req_status_show [zone_name1 [zone_name2 […]]]



Default

:

所有建立的共享内存目标



Context

:

loc

按格式返回统计结果。可指定返回部分目标的统计结果。



Syntax

:

req_status_show_field field_name1 [field_name2 [field_name3 […]]]



Default

:

all the fields, including user defined fields



Context

:

loc

定义输出格式。可以使用的字段:内置字段,以上面的名字来表示;自定义字段,用变量表示。

‘kv’总是每行的第一个字段。



Syntax

:

req_status_zone_add_indecator zone_name



v

a

r

1

[

var1 [






v


a


r


1


[





var2 […]]



Default

:

none



Context

:

http

通过变量增加自定义字段,新增加的字段目前会展现在每行的末尾。



Syntax

:

req_status_zone_key_length zone_name length



Default

:

none



Context

:

http

定义某个共享内存块中key的最大长度,默认值104。key中超出的部分会被截断。



Syntax

:

req_status_zone_recycle zone_name times seconds



Default

:

none



Context

:

http

定义某个共享内存块过期数据的回收。回收在共享内存耗尽时自动开启。只会回收访问频率低于设置值的监控数据。

频率定义为 times / seconds,默认值为10r/min,即

req_status_zone_recycle demo_zone 10 60;

安装模块:

tengine官方说req-statu模块默认安装。但是并没有。而且tengine的req-status模块不能分upstream监控,从github引入第三方模块解决该问题
yum与编译安装的nginx扩展模块安装:
[root@nginx-server ~]# yum install -y unzip
1. 安装,先查看一下当前编译安装nginx的版本
[root@localhost nginx-1.16.0]# nginx -V  
下载或者上传一个和当前的nginx版本一样的nginx的tar包。
[root@nginx-server ~]# tar xzf nginx-1.16.0.tar.gz -C /usr/local/
2.下载ngx_req_status_module 模块, 这是第三方模块需要添加
[root@nginx-server ~]# wget https://github.com/zls0424/ngx_req_status/archive/master.zip -O ngx_req_status.zip
[root@nginx-server ~]# unzip ngx_req_status.zip
[root@nginx-server ~]# cp -r ngx_req_status-master/ /usr/local/ #与解压的nginx在同一级目录下
[root@nginx-server ~]# cd /usr/local/nginx-1.16.0/
[root@nginx-server nginx-1.16.0]# yum -y install pcre pcre-devel openssl openssl-devel gcc gcc-c++   zlib zlib-devel
[root@nginx-server nginx-1.16.0]# yum -y install patch.x86_64
[root@nginx-server nginx-1.16.0]# patch -p1 < ../ngx_req_status-master/write_filter-1.7.11.patch
[root@localhost nginx-1.16.0]# ./configure 添加上原来的参数 --add-module=/usr/local/ngx_req_status-master
[root@localhost nginx-1.16.0]# make -j2
由于原先已有nginx,所以不能执行make install,否则会覆盖掉以前的配置文件及内容
[root@localhost nginx-1.16.0]# mv /usr/sbin/nginx /usr/sbin/nginx_bak
[root@localhost nginx-1.16.0]# cp objs/nginx /usr/sbin/
[root@localhost nginx-1.16.0]# systemctl restart nginx 
[root@localhost nginx-1.16.0]# nginx -V   
如果发现编译的配置文件有变化就成功了!
配置如下: 需要在http里面配置。
!!注意,添加了此配置,只有重启nginx才能生效。
[root@localhost ~]# vim /etc/nginx/nginx.conf
	req_status_zone server_name $server_name 256k;
    req_status_zone server_addr $server_addr 256k;
    req_status_zone server_url  $server_name$uri 256k;
    req_status server_name server_addr server_url;
    server {
        server_name localhost;
        location /req-status {
        req_status_show on;
        }
        }

指令介绍
 req_status_zone
语法: req_status_zone name string size
默认值: None
配置块: http
定义请求状态ZONE,请求按照string分组来排列,例如:
req_status_zone server_url  $server_name$uri 256k;
域名+uri将会形成一条数据,可以看到所有url的带宽,流量,访问数

req_status
语法: req_status zone1[ zone2]
默认值: None
配置块: http, server, location
在location中启用请求状态,你可以指定更多zones。

req_status_show
语法: req_status_show on
默认值: None
配置块: location
在当前位置启用请求状态处理程序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ytRk6hnq-1602213757557)(assets/1562055908162.png)]

请求状态信息包括以下字段:

  • zone_name – 利用req_status_zone定义的分组标准。例如,按照服务器名称对请求进行分组后;
  • key – 请求按分组标准分组后的分组标识(即组名)。例如按服务器名称分组时,组名可能是10.0.105.196;
  • max_active – 该组的最大并发连接数;
  • max_bw – 该组的最大带宽;
  • traffic – 该组的总流量;
  • requests – 该组的总请求数;
  • active – 该组当前的并发连接数;
  • bandwidth – 该组当前带宽。


5、补充:

查看Nginx并发进程数:ps -ef | grep nginx | wc -l

查看Web服务器TCP连接状态:netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’

LISTEN:侦听来自远方的TCPport的连接请求

SYN-SENT:再发送连接请求后等待匹配的连接请求

SYN-RECEIVED:再收到和发送一个连接请求后等待对方对连接请求的确认

ESTABLISHED:代表一个打开的连接

FIN-WAIT-1:等待远程TCP连接中断请求,或先前的连接中断请求的确认

FIN-WAIT-2:从远程TCP等待连接中断请求

CLOSE-WAIT:等待从本地用户发来的连接中断请求

CLOSING:等待远程TCP对连接中断的确认

LAST-ACK:等待原来的发向远程TCP的连接中断请求的确认

TIME-WAIT:等待足够的时间以确保远程TCP接收到连接中断请求的确认

CLOSED:没有不论什么连接状态

商业版的 nginx plus 通过他的 ngx_http_status_module 提供了比 nginx 更多的监控指标,可以参看 http://demo.nginx.com/status.html



4、nginx access log 分析

nginx 的 access log 中可以记录很多有价值的信息,通过分析 access log,可以收集到很多指标

python 编写的 linux 工具 ngxtop 就实现了对 access log 的分析功能

1.制作nginx的日志切割,每天凌晨切割并压缩。

PV:PV(

访问量

): 即Page View, 即页面浏览量或

点击量

,用户每次刷新即被计算一次。

UV:UV(独立访客):即Unique Visitor,访问您网站的一台

电脑客户端

为一个访客。00:00-24:00内相同的客户端只被计算一次。


面试:

1.根据访问IP统计UV

awk ‘{print $1}’ access.log|sort | uniq -c |wc -l

2.统计访问URL统计PV

awk ‘{print $7}’ access.log|wc -l

3.查询访问最频繁的URL

awk ‘{print $7}’ access.log|sort | uniq -c |sort -n -k 1 -r|more

4.查询访问最频繁的IP

awk ‘{print $1}’ access.log|sort | uniq -c |sort -n -k 1 -r|more

5.查询访问最频繁的前10的IP

awk '{print $1}' access.log|sort | uniq -c |sort -n -k 1 -r|head -n 10

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hF1P8h9t-1602213757559)(assets/1562230171957.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9DWoKa41-1602213757561)(assets/1562230190957.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U813dDvX-1602213757562)(assets/1562230212061.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yy28h7eD-1602213757564)(assets/1562230247706.png)]



三、HTTPS 基本原理



1、https 介绍

HTTPS(全称:HyperText Transfer Protocol over Secure Socket Layer),其实 HTTPS 并不是一个新鲜协议,Google 很早就开始启用了,初衷是为了保证数据安全。 近些年,Google、Baidu、Facebook 等这样的互联网巨头,不谋而合地开始大力推行 HTTPS, 国内外的大型互联网公司很多也都已经启用了全站 HTTPS,这也是未来互联网发展的趋势。

为鼓励全球网站的 HTTPS 实现,一些互联网公司都提出了自己的要求:

  1. Google 已调整搜索引擎算法,让采用 HTTPS 的网站在搜索中排名更靠前;
  2. 从 2017 年开始,Chrome 浏览器已把采用 HTTP 协议的网站标记为不安全网站;
  3. 苹果要求 2017 年App Store 中的所有应用都必须使用 HTTPS 加密连接;
  4. 当前国内炒的很火热的微信小程序也要求必须使用 HTTPS 协议;
  5. 新一代的 HTTP/2 协议的支持需以 HTTPS 为基础。

等等,因此想必在不久的将来,全网 HTTPS 势在必行。



1、HTTPS 协议介绍

  • HTTP 协议(HyperText Transfer Protocol,超文本传输协议):是客户端浏览器或其他程序与Web服务器之间的应用层通信协议 。
  • HTTPS 协议(HyperText Transfer Protocol over Secure Socket Layer):可以理解为HTTP+SSL/TLS, 即 HTTP 下加入 SSL 层,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL,用于安全的 HTTP 数据传输。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hUjjXEjU-1602213757568)(assets/1562050089966.png)]

  • 如上图所示 HTTPS 相比 HTTP 多了一层 SSL/TLS

    • SSL(Secure Socket Layer,安全套接字层):1994年为 Netscape 所研发,SSL 协议位于 TCP/IP 协议与各种应用层协议之间,为数据通讯提供安全支持。
    • TLS(Transport Layer Security,传输层安全):其前身是 SSL,它最初的几个版本(SSL 1.0、SSL 2.0、SSL 3.0)由网景公司开发,1999年从 3.1 开始被 IETF 标准化并改名,发展至今已经有 TLS 1.0、TLS 1.1、TLS 1.2 三个版本。SSL3.0和TLS1.0由于存在安全漏洞,已经很少被使用到。TLS 1.3 改动会比较大,目前还在草案阶段,目前使用最广泛的是TLS 1.1、TLS 1.2。



2、加密算法

据记载,公元前400年,古希腊人就发明了置换密码;在第二次世界大战期间,德国军方启用了“恩尼格玛”密码机,所以密码学在社会发展中有着广泛的用途。

  1. 对称加密

    有流式、分组两种,加密和解密都是使用的同一个密钥。

    例如:DES、AES-GCM、ChaCha20-Poly1305等

  2. 非对称加密

    加密使用的密钥和解密使用的密钥是不相同的,分别称为:公钥、私钥,公钥和算法都是公开的,私钥是保密的。非对称加密算法性能较低,但是安全性超强,由于其加密特性,非对称加密算法能加密的数据长度也是有限的。

    例如:RSA、DSA、ECDSA、 DH、ECDHE

  3. 哈希算法

    将任意长度的信息转换为较短的固定长度的值,通常其长度要比信息小得多,且算法不可逆。

    例如:MD5、SHA-1、SHA-2、SHA-256 等

  4. 数字签名

    签名就是在信息的后面再加上一段内容(信息经过hash后的值),可以证明信息没有被修改过。hash值一般都会加密后(也就是签名)再和信息一起发送,以保证这个hash值不被修改。



3、HTTPS 原理



1、HTTP 访问过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UoYM2uwR-1602213757569)(assets/1562050123147.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U0ulkyhV-1602213757571)(assets/1562050201266.png)]

如上图所示,HTTP请求过程中,客户端与服务器之间没有任何身份确认的过程,数据全部明文传输,“裸奔”在互联网上,所以很容易遭到黑客的攻击,如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VJwzEWs2-1602213757572)(assets/1562050246969.png)]

可以看到,客户端发出的请求很容易被黑客截获,如果此时黑客冒充服务器,则其可返回任意信息给客户端,而不被客户端察觉,所以我们经常会听到一词“劫持”,现象如下:

下面两图中,浏览器中填入的是相同的URL,左边是正确响应,而右边则是被劫持后的响应(从貌美如花变成如花。。。)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IADreBrm-1602213757573)(assets/1562050281277.png)]

所以 HTTP 传输面临的风险有:

  • 窃听风险:黑客可以获知通信内容。
  • 篡改风险:黑客可以修改通信内容。
  • 冒充风险:黑客可以冒充他人身份参与通信。



2、HTTP 向 HTTPS 演化的过程

第一步:为了防止上述现象的发生,人们想到一个办法:对传输的信息加密(即使黑客截获,也无法破解)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2pbVCqs2-1602213757575)(assets/1562050310151.png)]

如上图所示,此种方式属于对称加密,双方拥有相同的密钥,信息得到安全传输,但此种方式的缺点是:

(1)不同的客户端、服务器数量庞大,所以双方都需要维护大量的密钥,维护成本很高

(2)因每个客户端、服务器的安全级别不同,密钥极易泄露

第二步:既然使用对称加密时,密钥维护这么繁琐,那我们就用非对称加密试试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ei1Q5VN-1602213757576)(assets/1562050336264.png)]

如上图所示,客户端用公钥对请求内容加密,服务器使用私钥对内容解密,反之亦然,但上述过程也存在缺点:

(1)公钥是公开的(也就是黑客也会有公钥),所以第 ④ 步私钥加密的信息,如果被黑客截获,其可以使用公钥进行解密,获取其中的内容

第三步:非对称加密既然也有缺陷,那我们就将对称加密,非对称加密两者结合起来,取其精华、去其糟粕,发挥两者的各自的优势

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HL9aodA9-1602213757577)(assets/1562050389994.png)]

如上图所示

(1)第 ③ 步时,客户端说:(咱们后续回话采用对称加密吧,这是对称加密的算法和对称密钥)这段话用公钥进行加密,然后传给服务器

(2)服务器收到信息后,用私钥解密,提取出对称加密算法和对称密钥后,服务器说:(好的)对称密钥加密

(3)后续两者之间信息的传输就可以使用对称加密的方式了

遇到的问题:

(1)客户端如何获得公钥

(2)如何确认服务器是真实的而不是黑客

第四步:获取公钥与确认服务器身份

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bmetnPyB-1602213757579)(assets/1562050412953.png)]

1、获取公钥

(1)提供一个下载公钥的地址,回话前让客户端去下载。(缺点:下载地址有可能是假的;客户端每次在回话前都先去下载公钥也很麻烦)

(2)回话开始时,服务器把公钥发给客户端(缺点:黑客冒充服务器,发送给客户端假的公钥)

2、那有木有一种方式既可以安全的获取公钥,又能防止黑客冒充呢? 那就需要用到终极武器了:SSL 证书(申购)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uXiPyZA3-1602213757580)(assets/1562050492512.png)]

如上图所示,在第 ② 步时服务器发送了一个SSL证书给客户端,SSL 证书中包含的具体内容有:

(1)证书的发布机构CA

(2)证书的有效期

(3)公钥

(4)证书所有者

(5)签名

………

3、客户端在接受到服务端发来的SSL证书时,会对证书的真伪进行校验,以浏览器为例说明如下:

(1)首先浏览器读取证书中的证书所有者、有效期等信息进行一一校验

(2)浏览器开始查找操作系统中已内置的受信任的证书发布机构CA,与服务器发来的证书中的颁发者CA比对,用于校验证书是否为合法机构颁发

(3)如果找不到,浏览器就会报错,说明服务器发来的证书是不可信任的。

(4)如果找到,那么浏览器就会从操作系统中取出 颁发者CA 的公钥,然后对服务器发来的证书里面的签名进行解密

(5)浏览器使用相同的hash算法计算出服务器发来的证书的hash值,将这个计算的hash值与证书中签名做对比

(6)对比结果一致,则证明服务器发来的证书合法,没有被冒充

(7)此时浏览器就可以读取证书中的公钥,用于后续加密了

4、所以通过发送SSL证书的形式,既解决了公钥获取问题,又解决了黑客冒充问题,一箭双雕,HTTPS加密过程也就此形成

所以相比HTTP,HTTPS 传输更加安全

(1) 所有信息都是加密传播,黑客无法窃听。

(2) 具有校验机制,一旦被篡改,通信双方会立刻发现。

(3) 配备身份证书,防止身份被冒充。



3、HTTPS 总结

综上所述,相比 HTTP 协议,HTTPS 协议增加了很多握手、加密解密等流程,虽然过程很复杂,但其可以保证数据传输的安全。所以在这个互联网膨胀的时代,其中隐藏着各种看不见的危机,为了保证数据的安全,维护网络稳定,建议大家多多推广HTTPS。

HTTPS 缺点:

  1. SSL 证书费用很高,以及其在服务器上的部署、更新维护非常繁琐
  2. HTTPS 降低用户访问速度(多次握手)
  3. 网站改用HTTPS 以后,由HTTP 跳转到 HTTPS 的方式增加了用户访问耗时(多数网站采用302跳转)
  4. HTTPS 涉及到的安全算法会消耗 CPU 资源,需要增加大量机器(https访问过程需要加解密)



4、构建私有的 CA 机构(了解)



1、CA 介绍

CA(Certificate Authority)证书颁发机构主要负责证书的颁发、管理以及归档和吊销。证书内包含了拥有证书者的姓名、地址、电子邮件帐号、公钥、证书有效期、发放证书的CA、CA的数字签名等信息。证书主要有三大功能:加密、签名、身份验证。



2、构建私有 CA



1、检查安装 openssl
[root@https-ca ~]# rpm -qa openssl

如果未安装

[root@https-ca ~]# yum install openssl openssl-devel


2、查看配置文件

openssl 配置

/etc/pki/tls/openssl.cnf

有关CA的配置。如果服务器为证书签署者的身份那么就会用到此配置文件,此配置文件对于证书申请者是无作用的。

[root@https-ca ~]# vim /etc/pki/tls/openssl.cnf
####################################################################
[ ca ]
default_ca      = CA_default            # 默认的CA配置;CA_default指向下面配置块

####################################################################
[ CA_default ]

dir             = /etc/pki/CA           # CA的默认工作目录
certs           = $dir/certs            # 认证证书的目录
crl_dir         = $dir/crl              # 证书吊销列表的路径
database        = $dir/index.txt        # 数据库的索引文件


new_certs_dir   = $dir/newcerts         # 新颁发证书的默认路径

certificate     = $dir/cacert.pem       # 此服务认证证书,如果此服务器为根CA那么这里为自颁发证书
serial          = $dir/serial           # 下一个证书的证书编号
crlnumber       = $dir/crlnumber        # 下一个吊销的证书编号
                                        
crl             = $dir/crl.pem          # The current CRL
private_key     = $dir/private/cakey.pem# CA的私钥
RANDFILE        = $dir/private/.rand    # 随机数文件

x509_extensions = usr_cert              # The extentions to add to the cert

name_opt        = ca_default            # 命名方式,以ca_default定义为准
cert_opt        = ca_default            # 证书参数,以ca_default定义为准


default_days    = 365                   # 证书默认有效期
default_crl_days= 30                    # CRl的有效期
default_md      = sha256                # 加密算法
preserve        = no                    # keep passed DN ordering


policy          = policy_match          #policy_match策略生效

# For the CA policy
[ policy_match ]
countryName             = match         #国家;match表示申请者的申请信息必须与此一致
stateOrProvinceName     = match         #州、省
organizationName        = match         #组织名、公司名
organizationalUnitName  = optional      #部门名称;optional表示申请者可以的信息与此可以不一致
commonName              = supplied
emailAddress            = optional

# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]                     #由于定义了policy_match策略生效,所以此策略暂未生效
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional


3、根证书服务器目录

根CA服务器:因为只有 CA 服务器的角色,所以用到的目录只有/etc/pki/CA

网站服务器:只是证书申请者的角色,所以用到的目录只有

/etc/pki/tls


4、创建所需要的文件

[root@https-ca ~]# cd /etc/pki/CA/
[root@https-ca CA]# ls
certs  crl  newcerts  private
[root@https-ca CA]# touch index.txt   #创建生成证书索引数据库文件
[root@https-ca CA]# ls
certs  crl  index.txt  newcerts  private
[root@https-ca CA]# echo 01 > serial   #指定第一个颁发证书的序列号
[root@https-ca CA]# ls
certs  crl  index.txt  newcerts  private  serial
[root@https-ca CA]# 


5、创建密钥

在根CA服务器上创建密钥,密钥的位置必须为

/etc/pki/CA/private/cakey.pem

,这个是openssl.cnf中中指定的路径,只要与配置文件中指定的匹配即可。

[root@https-ca CA]# (umask 066; openssl genrsa -out private/cakey.pem 2048)
Generating RSA private key, 2048 bit long modulus
...........+++
...............+++
e is 65537 (0x10001)


6、生成自签名证书

根CA自签名证书,根CA是最顶级的认证机构,没有人能够认证他,所以只能自己认证自己生成自签名证书。

[root@https-ca CA]# openssl req -new -x509 -key /etc/pki/CA/private/cakey.pem -days 7300 -out /etc/pki/CA/cacert.pem -days 7300
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:BEIJING
Locality Name (eg, city) [Default City]:BEIJING
Organization Name (eg, company) [Default Company Ltd]:CA
Organizational Unit Name (eg, section) []:OPT
Common Name (eg, your name or your server's hostname) []:ca.qf.com
Email Address []:
[root@https-ca CA]# ls
cacert.pem  certs  crl  index.txt  newcerts  private  serial
-new: 	生成新证书签署请求
-x509: 	专用于CA生成自签证书
-key: 	生成请求时用到的私钥文件
-days n:	证书的有效期限
-out /PATH/TO/SOMECERTFILE: 	证书的保存路径


7、下载安装证书


/etc/pki/CA/cacert.pem

就是生成的自签名证书文件,使用

SZ/xftp

工具将他导出到窗口机器中。然后双击安装此证书到受信任的根证书颁发机构

[root@https-ca CA]# yum install -y lrzsz
[root@https-ca CA]# sz cacert.pem

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gNGfOzMW-1602213757582)(assets/1562117752276.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-maF3KJNj-1602213757583)(assets/1562117778176.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4aSl3e6f-1602213757584)(assets/1562118358230.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5EBOO0vO-1602213757586)(assets/1562118410144.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LTm1tUYS-1602213757587)(assets/1562118451555.png)]




3、客户端CA 证书申请及签名


1、检查安装 openssl

[root@nginx-server ~]# rpm -qa openssl

如果未安装,安装 openssl

[root@nginx-server ~]# yum install openssl openssl-devel


2、客户端生成私钥文件

[root@nginx-server ~]# (umask 066; openssl genrsa -out /etc/pki/tls/private/www.qf.com.key 2048)
Generating RSA private key, 2048 bit long modulus
..............................+++
..........+++
e is 65537 (0x10001)
[root@nginx-server ~]# cd /etc/pki/tls/private/
[root@nginx-server private]# ls
www.qf.com.key
[root@nginx-server private]#


3、客户端用私钥加密生成证书请求

[root@nginx-server private]# ls ../
cert.pem  certs  misc  openssl.cnf  private
[root@nginx-server private]# openssl req -new -key /etc/pki/tls/private/www.qf.com.key -days 365 -out /etc/pki/tls/www.qf.com.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:BEIJING
Locality Name (eg, city) [Default City]:BEIJING
Organization Name (eg, company) [Default Company Ltd]:QF
Organizational Unit Name (eg, section) []:OPT
Common Name (eg, your name or your server's hostname) []:www.qf.com
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
[root@nginx-server private]# ls ../
cert.pem  certs  misc  openssl.cnf  private  www.qf.com.csr
[root@nginx-server private]#

CSR(Certificate Signing Request)包含了公钥和名字信息。通常以.csr为后缀,是网站向CA发起认证请求的文件,是中间文件。

在这一命令执行的过程中,系统会要求填写如下信息:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-faJ8gSrT-1602213757588)(assets/1562059278073.png)]

最后把生成的请求文件(

/etc/pki/tls/www.qf.com.csr

)传输给CA ,这里我使用scp命令,通过ssh协议,将该文件传输到CA下的

/etc/pki/CA/private/

目录

[root@nginx-server private]# cd ../
[root@nginx-server tls]# scp www.qf.com.csr 10.0.105.181:/etc/pki/CA/private
root@10.0.105.181's password: 
www.qf.com.csr                                                           100%  997   331.9KB/s   00:00 


4、CA 签署证书

[root@https-ca ~]# openssl ca -in /etc/pki/CA/private/www.qf.com.csr -out /etc/pki/CA/certs/www.qf.com.ctr -days 365
Using configuration from /etc/pki/tls/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Jul  3 10:12:23 2019 GMT
            Not After : Jul  2 10:12:23 2020 GMT
        Subject:
            countryName               = CN
            stateOrProvinceName       = BEIJING
            organizationName          = QF
            organizationalUnitName    = OPT
            commonName                = www.qf.com
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            Netscape Comment: 
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier: 
                E3:AC:1A:55:2B:28:B9:80:DC:9C:C2:13:70:53:27:AD:3D:44:8F:D3
            X509v3 Authority Key Identifier: 
                keyid:5D:2A:81:B2:E7:8D:D8:88:E5:7B:94:CA:75:65:9C:82:2B:A9:B2:3C

Certificate is to be certified until Jul  2 10:12:23 2020 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

证书通常以.crt为后缀,表示证书文件


1、可能遇到的问题

[root@https-ca private]# cd
[root@https-ca ~]# openssl ca -in /etc/pki/CA/private/www.qf.com.csr -out /etc/pki/CA/certs/www.qf.com.ctr -days 365
Using configuration from /etc/pki/tls/openssl.cnf
Check that the request matches the signature
Signature ok
The organizationName field needed to be the same in the
CA certificate (CA) and the request (QF)

因为默认使用/etc/pki/tls/openssl.cnf,里面要求其一致,修改organizationName=supplied

修改 /etc/pki/tls/openssl.cnf

[root@https-ca ~]# vim /etc/pki/tls/openssl.cnf
policy          = policy_match
 82 
 83 # For the CA policy
 84 [ policy_match ]
 85 countryName             = match
 86 stateOrProvinceName     = match
 87 organizationName        = supplied
 88 organizationalUnitName  = optional
 89 commonName              = supplied
 90 emailAddress            = optional


2、查看生成的证书的信息

[root@https-ca ~]# openssl x509 -in /etc/pki/CA/certs/www.qf.com.ctr -noout -subject
subject= /C=CN/ST=BEIJING/O=QF/OU=OPT/CN=www.qf.com


3、将生成的证书发放给请求客户端

[root@https-ca ~]# cd /etc/pki/CA/certs/
[root@https-ca certs]# scp www.qf.com.ctr 10.0.105.199:/etc/pki/CA/certs/
root@10.0.105.199's password: 
www.qf.com.ctr                                                           100% 4422   998.3KB/s   00:00 

测试:

nginx-client(充当服务端)[root@localhost ~]# cd /etc/pki/
[root@localhost pki]# ls
CA  ca-trust  java  nssdb  nss-legacy  rpm-gpg  rsyslog  tls
[root@localhost pki]# cd CA/
[root@localhost CA]# ls
certs  crl  newcerts  private
[root@localhost CA]# cd certs/
[root@localhost certs]# ls
www.qf.com.ctr
[root@localhost certs]# pwd
/etc/pki/CA/certs
[root@localhost certs]# ls
www.qf.com.ctr
[root@localhost certs]# mv www.qf.com.ctr www.qf.com.crt
[root@localhost certs]# ls
www.qf.com.crt
[root@localhost certs]# pwd
/etc/pki/CA/certs
[root@localhost certs]# find / -name *.key
/etc/pki/tls/private/www.qf.com.key
/usr/share/doc/openssh-7.4p1/PROTOCOL.key
[root@localhost certs]# find / -name *.ctr

还是在这台机器安装nginx并且配置证书:
root@localhost conf.d]# pwd
/etc/nginx/conf.d
[root@localhost conf.d]# vim nginx.conf
server {
        listen       443 ssl;
        server_name  localhost;

        ssl_certificate         /etc/pki/CA/certs/www.qf.com.crt;
        ssl_certificate_key  /etc/pki/tls/private/www.qf.com.key;
        ssl_session_timeout  5m;
        ssl_protocols  SSLv2 SSLv3 TLSv1;
        ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
        ssl_prefer_server_ciphers   on;

        location / {
                root /usr/share/nginx/html;
                index index.html index.htm;
                }
}
保存重启
[root@localhost conf.d]# nginx -t 
[root@localhost conf.d]# nginx -s reload

浏览器测试访问:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PsN0hxiy-1602213757590)(assets/1563288178855.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2CJ6spm2-1602213757591)(assets/1563288198981.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yQskaLCC-1602213757592)(assets/1563288213916.png)]

自己生成的证书成功!


4、CA吊销证书


1、知道客户端吊销的证书的serial

[root@https-ca ~]# openssl x509 -in /etc/pki/tls/cert.pem  -noout -serial -subject
serial=5EC3B7A6437FA4E0
subject= /CN=ACCVRAIZ1/OU=PKIACCV/O=ACCV/C=ES


2、吊销证书

先根据客户提交的serial与subject信息,对比检验是否与index.txt文件中的信息一致;然后

[root@https-ca ~]# openssl ca -revoke /etc/pki/CA/newcerts/01.pem


3、生成吊销证书的编号

第一次吊销一个证书时才需要执行

[root@https-ca ~]# echo 01 > /etc/pki/CA/crlnumber


4、更新证书吊销列表

[root@https-ca ~]# openssl ca -gencrl -out thisca.crl


5、查看证书吊销列表

[root@https-ca ~]# openssl crl -in /root/thisca.crl -noout -text



5、nginx HTTPS 部署实战

  1. 申请证书与认证
  2. 证书下载与配置
  3. 问题分析与总结



1、申请证书与认证

要搭建https服务首先需有SSL证书,证书通常是在第三方申请,在阿里云的安全服务中有SSL证书这一项,可以在里面申请免费的证书;

也可以在自己电脑中生成,虽然也能完成加密,但是浏览器是不认可的,因此最好还是去第三方申请



1、证书申请

阿里云提供免费的证书,不需要人工审核,用来做测试是非常不错的选择,申请地址如下URL。

https://common-buy.aliyun.com/?spm=5176.2020520163.cas.1.1aa12b7aWWn20O&commodityCode=cas#/buy

免费型的证书隐藏的比较深,想要申请免费证书需要先选择 1个域名->Symantec->免费型 ,所以读者这里需要注意一下,如下图参考。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zR34LBD8-1602213757594)(assets/1562123381388.png)]

选择之后,一直点击下一步,便可购买完成,免费购买证书之后笔者需要回到证书控制台,在控制台有一个补全信息的链接地址,需要通过此地址补充申请人的联系信息,参考下图填写

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WmyFuFpN-1602213757595)(assets/1562123418824.png)]



2、域名验证

补全个人信息之后,还需要给阿里云验证当前域名是属于本人的,验证方式有两种,第一种是通过dns解析认证,第二种是通过上传验证文件认证,这里采用的是验证文件认证,首先需要下载文件,如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VX3XJRwD-1602213757597)(assets/1562129262162.png)]

在下载验证文件完成之后,笔者需要把文件放到服务器中去,这里提供一条复制命令

[root@web ~]#scp ~/Downloads/fileauth.txt  root@192.168.43.34:~/

将验证文件复制到服务器之后,还需要将验证文件放到站点对应目录,参考命令如下:

[root@nginx ~]# cd /usr/share/nginx/html/
[root@nginx html]#mkdir -p /website/.well-known/pki-validation  &&  cp  fileauth.txt  /website/.well-known/pki-validation/
[root@nginx html]# cd && vim /etc/nginx/nginx.conf
server {
        listen       80;
        server_name  localhost;
        location / {
             root /usr/share/nginx/html/website;
        }


1、手动验证

手动验证的目的是首先确保文件位置放置是否正确,可以通过访问站点的url是否成功进行判断,比如笔者可以访问如下URL,如果返回如果页面能够正常打开,并且可以看到某些值,则代表配置成功。

http://www.qf.com/.well-known/pki-validation/fileauth.txt


2、通过阿里云来验证

在确保文件放置正确之后,关键的是能让阿里云能访问到,阿里云这里提供了一个检查配置的功能,在下载验证文件页面,有一个检测配置的链接,单击之后便可进行检查,如下图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hse3FTNR-1602213757598)(assets/1562133394941.png)]

当点击 检查配置 之后,如果阿里云能够正常访问,则会在左侧给出提示,现在可以返回证书列表,在列表中可以看到当前状态为审核中,如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kW9oUGEj-1602213757600)(assets/1562133415788.png)]

审核因为不需要人为干预,所以很快就能下发证书,下发证书的时间大约是2分钟左右。



2、证书下载与配置



1、证书下载

证书签发之后,可以在列表中可以看到状态栏中为 已签发 ,同时操作栏可以下载以及查看详情等,如下图所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LwEwSBDL-1602213757601)(assets/1562133543812.png)]

点击下载后,会跳转到下载详情页面,在下载详情页可以选择自己相对应的web服务,比如使用nginx,当选择nginx之后,下方还会很贴心的提示如何配置,下载nginx配置文件。

下载配置文件之后,需要将其解压,解压之后可以看见里面包含了两个证书文件

xxx.key

xxx.pem

接着需要把这两个证书文件给复制到服务器当中去,首先需要在服务器创建对应的文件夹,参考命令如下

[root@nginx ~]# cd /etc/nginx/ && mkdir cert

在服务器创建完成对应文件夹之后,将证书文件复制到服务器中

[root@nginx ~]# ls
2447549_www.testpm.cn_nginx.zip
[root@nginx ~]# unzip 2447549_www.testpm.cn_nginx.zip
Archive:  2447549_www.testpm.cn_nginx.zip
Aliyun Certificate Download
  inflating: 2447549_www.testpm.cn.pem  
  inflating: 2447549_www.testpm.cn.key   
[root@nginx ~]# ls
2447549_www.testpm.cn.key  2447549_www.testpm.cn_nginx.zip  2447549_www.testpm.cn.pem
[root@nginx ~]# cp 2447549_www.testpm.cn* /etc/nginx/cert/
[root@nginx ~]# cd /etc/nginx/cert/
[root@nginx cert]# mv 2447549_www.testpm.cn.key www.testpm.cn.key 
[root@nginx cert]# mv 2447549_www.testpm.cn.pem www.testpm.cn.pem


2、证书配置

证书复制完成之后,可以对nginx配置文件进行更改,使用vim命令编辑nginx配置文件,参考命令如下:

[root@nginx ~]# cd /etc/nginx/conf.d/
[root@nginx conf.d]# cp default.conf default.conf.bak
[root@nginx conf.d]# mv default.conf nginx_ssl.conf
[root@nginx conf.d]# vim nginx_ssl.conf
[root@nginx conf.d]# cat /etc/nginx/conf.d/nginx_ssl.conf 
server {
    listen 443 ssl;
    server_name www.testpm.cn;
    access_log  /var/log/nginx/https_access.log  main;

    #ssl on;
    ssl_certificate   /etc/nginx/cert/2447549_www.testpm.cn.pem;
    ssl_certificate_key  /etc/nginx/cert/2447549_www.testpm.cn.key;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
    ssl_prefer_server_ciphers on;

    location / {
        root  /usr/share/nginx/html;
        index index.html index.htm;
    }
}


3、重启Nginx

修改配置文件之后,需要测试nginx配置文件是否正确

[root@nginx cert]# nginx -t 
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@nginx cert]# nginx -s reload 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tlQ18T6u-1602213757603)(assets/1562135276893.png)]

如果看到浏览器,展示安全,并且显示绿色就说明大功告成了!



6、nginx 性能优化

当我需要进行性能优化时,说明我们服务器无法满足日益增长的业务。性能优化是一个比较大的课题,需要从以下几个方面进行探讨

  • 当前系统结构瓶颈
  • 了解业务模式
  • 性能与安全



1、当前系统结构瓶颈

首先需要了解的是当前系统瓶颈,用的是什么,跑的是什么业务。里面的服务是什么样子,每个服务最大支持多少并发。比如针对nginx而言,我们处理静态资源效率最高的瓶颈是多大?能支持多少qps访问请求?怎么得出系统当前的结构瓶颈?

可以通过查看当前cpu负荷,内存使用率,进程使用率来做简单判断。还可以通过操作系统的一些工具来判断当前系统性能瓶颈,如分析对应的日志,查看请求数量。也可以通过nginx http_stub_status_module模块来查看对应的连接数,总握手次数,总请求数。也可以对线上进行压力测试,来了解当前的系统能性能,并发数,做好性能评估。



2、了解业务模式

虽然我们是在做性能优化,但还是要熟悉业务,最终目的都是为业务服务的。我们要了解每一个接口业务类型是什么样的业务,比如电子商务抢购模式,这种情况平时流量会很小,但是到了抢购时间,流量一下子就会猛涨。也要了解系统层级结构,每一层在中间层做的是代理还是动静分离,还是后台进行直接服务。需要我们对业务接入层和系统层次要有一个梳理



3、性能与安全

性能与安全也是一个需要考虑的因素,往往大家注重性能忽略安全或注重安全又忽略性能。比如说我们在设计防火墙时,如果规则过于全面肯定会对性能方面有影响。如果对性能过于注重在安全方面肯定会留下很大隐患。所以大家要评估好两者的关系,把握好两者的孰重孰轻,以及整体的相关性。权衡好对应的点。



4、系统与nginx性能优化

大家对相关的系统瓶颈及现状有了一定的了解之后,就可以根据影响性能方面做一个全体的评估和优化。

  • 网络(网络流量、是否有丢包,网络的稳定性都会影响用户请求)
  • 系统(系统负载、饱和、内存使用率、系统的稳定性、硬件磁盘是否有损坏)
  • 服务(连接优化、内核性能优化、http服务请求优化都可以在nginx中根据业务来进行设置)
  • 程序(接口性能、处理请求速度、每个程序的执行效率)
  • 数据库、底层服务

上面列举出来每一级都会有关联,也会影响整体性能,这里主要关注的是nginx服务这一层。



1、文件句柄

在linux/unix操作系统中一切皆文件,我们的设备是文件,文件是文件,文件夹也是文件。当我们用户每发起一次请求,就会产生一个文件句柄。文件句柄可以简单的理解为

文件句柄就是一个索引

。文件句柄就会随着请求量的增多,进程调用频繁增加,那么产生的文件句柄也就会越多。

系统默认对文件句柄是有限制的,不可能会让一个进程无限制的调用句柄。因为系统资源是有限的,所以我们需要限制每一个服务能够使用多大的文件句柄。操作系统默认使用的文件句柄是1024个句柄。



2、设置方式
  • 系统全局性修改
  • 用户局部性修改
  • 进程局部性修改


3、系统全局性修该和用户局部性修改
[root@nginx-server ~]# vim /etc/security/limits.conf 
#*               soft    core            0
#*               hard    rss             10000
#@student        hard    nproc           20
#@faculty        soft    nproc           20
#@faculty        hard    nproc           50
#ftp             hard    nproc           0
#@student        -       maxlogins       4

#root只是针对root这个用户来限制,soft只是发提醒,操作系统不会强制限制,一般的站点设置为一万左右就ok了
root soft nofile 65535
root hard nofile 65535
# *代表通配符 所有的用户
*    soft nofile 25535
*    hard nofile 25535

可以看到

root



*

,root代表是root用户,*代表的是所有用户,后面的数字就是文件句柄大小。大家可以根据个人业务来进行设置。



4、进程局部性修改
[root@nginx-server ~]# vim /etc/nginx/nginx.conf
user  nginx;
worker_processes  1;  

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

worker_rlimit_nofile 65535; #进程限制

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$http_user_agent' '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" '
                      '"$args" "$request_uri"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on; 
    #tcp_nopush     on; 

    keepalive_timeout  65; 

    #gzip  on; 

    include /etc/nginx/conf.d/*.conf;
}


worker_rlimit_nofile

是在进程上面进行限制。



5、cpu的亲和配置

cpu的亲和能够使nginx对于不同的work工作进程绑定到不同的cpu上面去。就能够减少在work间不断切换cpu,把进程通常不会在处理器之间频繁迁移,进程迁移的频率小,来减少性能损耗。

nginx 亲和配置

查看物理cpu

[root@nginx-server ~]# cat /proc/cpuinfo | grep "physical id" | sort|uniq | wc -l

查看cpu核心数

[root@nginx-server ~]# cat /proc/cpuinfo|grep "cpu cores"|uniq

查看cpu使用率

[root@nginx-server ~]#top  回车后按 1


6、配置worker_processes
[root@nginx-server ~]# vim /etc/nginx/nginx.conf
将刚才查看到自己cpu * cpu核心就是worker_processes
worker_processes 2; #根据自己cpu核心数配置/这里也可以设置为auto


7、cpu亲和配置

假如配置是2cpu,每个cpu是8核。配置如下

worker_processes 16;
  1010101010101010 0101010101010101;

配置完成后可以通过下面命令查看nginx进程配置在哪个核上

[root@nginx-server ~]# ps -eo pid,args,psr |grep [n]ginx

在nginx 1.9版本之后,就帮我们自动绑定了cpu;

worker_cpu_affinity auto;



5、nginx通用配置优化

#将nginx进程设置为普通用户,为了安全考虑
user nginx; 

#当前启动的worker进程,官方建议是与系统核心数一致
worker_processes 2;
#方式一, 第一个work进程绑定第一个cpu核心,第二个work进程绑定到第二个cpu核心,依次内推 直到第16个
#wokrer_cpu_affinity 0000000000000000 0000000000000001 0000000000000010 0000000000000100 ... 1000000000000000

#方式二,当 worker_processes 2 时,表明 第一work进程可以绑定第 2 4 6 8 10 12 14 16 核心,那么第二work进程就绑定 奇数核心
#worker_cpu_affinity 1010101010101010 0101010101010101;

#方式三,就是自动分配绑定
worker_cpu_affinity auto;

#日志配置成warn
error_log /var/log/nginx/error.log warn; 
pid /var/run/nginx.pid;

#针对 nginx 句柄的文件限制
worker_rlimit_nofile 35535;
#事件模型
events {
    #使用epoll内核模型
    user epoll;
    #每一个进程可以处理多少个连接,如果是多核可以将连接数调高 worker_processes * 1024
    worker_connections 10240;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    charset utf-8;  #设置字符集

    #设置日志输出格式,根据自己的情况设置
    log_format  main  '$http_user_agent' '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" '
                      '"$args" "$request_uri"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;   #对静态资源的处理比较有效
    #tcp_nopush     on;   #如果做静态资源服务器可以打开
    #tcp_nodeny     on;   #当nginx做动态的服务时可以选择打开

    keepalive_timeout  65; 

    ########
    #Gzip module
    gzip  on;    #文件压缩默认可以打开
    gzip_disable "MSIE [1-6]\."; #对于有些浏览器不能识别压缩,需要过滤如ie6
    gzip_http_version 1.1;

    include /etc/nginx/conf.d/*.conf;
}



6、ab接口压力测试工具

ab是Apache超文本传输协议(HTTP)的性能测试工具。其设计意图是描绘当前所安装的Apache的执行性能,主要是显示你安装的Apache每秒可以处理多少个请求。

[root@nginx-server ~]# yum install httpd-tools
[root@nginx-server ~]# ab -n 2000 -c 2 http://127.0.0.1/
-n 总的请求数
-c 并发数
-k 是否开启长连接


1、参数选项

-n:即requests,用于指定压力测试总共的执行次数
-c:即concurrency,用于指定的并发数
-t:即timelimit,等待响应的最大时间(单位:秒)
-b:即windowsize,TCP发送/接收的缓冲大小(单位:字节)
-p:即postfile,发送POST请求时需要上传的文件,此外还必须设置-T参数
-u:即putfile,发送PUT请求时需要上传的文件,此外还必须设置-T参数
-T:即content-type,用于设置Content-Type请求头信息,例如:application/x-www-form-urlencoded,默认值为text/plain
-v:即verbosity,指定打印帮助信息的冗余级别
-w:以HTML表格形式打印结果
-i:使用HEAD请求代替GET请求
-x:插入字符串作为table标签的属性
-y:插入字符串作为tr标签的属性
-z:插入字符串作为td标签的属性
-C:添加cookie信息,例如:"Apache=1234"(可以重复该参数选项以添加多个)
-H:添加任意的请求头,例如:"Accept-Encoding: gzip",请求头将会添加在现有的多个请求头之后(可以重复该参数选项以添加多个)
-A:添加一个基本的网络认证信息,用户名和密码之间用英文冒号隔开
-P:添加一个基本的代理认证信息,用户名和密码之间用英文冒号隔开
-X:指定使用的和端口号,例如:"126.10.10.3:88"
-V:打印版本号并退出
-k:使用HTTP的KeepAlive特性
-d:不显示百分比
-S:不显示预估和警告信息
-g:输出结果信息到gnuplot格式的文件中
-e:输出结果信息到CSV格式的文件中
-r:指定接收到错误信息时不退出程序
-H:显示用法信息,其实就是ab -help


2、内容解释

Server Software:        nginx/1.10.2 (服务器软件名称及版本信息)
Server Hostname:        192.168.1.106(服务器主机名)
Server Port:            80 (服务器端口)

Document Path:          /index1.html. (供测试的URL路径)
Document Length:        3721 bytes (供测试的URL返回的文档大小)

Concurrency Level:      1000 (并发数)
Time taken for tests:   2.327 seconds (压力测试消耗的总时间)
Complete requests:      5000 (的总次数)
Failed requests:        688 (失败的请求数)
Write errors:           0 (网络连接写入错误数)
Total transferred:      17402975 bytes (传输的总数据量)
HTML transferred:       16275725 bytes (HTML文档的总数据量)
Requests per second:    2148.98 [#/sec] (mean) (平均每秒的请求数) 这个是非常重要的参数数值,服务器的吞吐量 
Time per request:       465.338 [ms] (mean) (所有并发用户(这里是1000)都请求一次的平均时间)
Time  request:       	0.247 [ms] (mean, across all concurrent requests) (单个用户请求一次的平均时间)
Transfer rate:          7304.41 [Kbytes/sec] received 每秒获取的数据长度 (传输速率,单位:KB/s)
...
Percentage of the requests served within a certain time (ms)
  50%    347  ## 50%的请求在347ms内返回 
  66%    401  ## 60%的请求在401ms内返回 
  75%    431
  80%    516
  90%    600
  95%    846
  98%   1571
  99%   1593
  100%   1619 (longest request)


3、示例演示



注意事项

● 测试机与被测试机要分开

● 不要对线上的服务器做压力测试

● 观察测试工具ab所在机器,以及被测试的前端机的CPU、内存、网络等都不超过最高限度的75%

[root@nginx-server ~]# ab -n 50 -c 2 http://www.testpm.cn/
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.testpm.cn (be patient).....done


Server Software:        nginx/1.16.0
Server Hostname:        www.testpm.cn
Server Port:            80

Document Path:          /
Document Length:        612 bytes

Concurrency Level:      2
Time taken for tests:   2.724 seconds
Complete requests:      50
Failed requests:        0
Write errors:           0
Total transferred:      42250 bytes
HTML transferred:       30600 bytes
Requests per second:    18.35 [#/sec] (mean)
Time per request:       108.968 [ms] (mean)
Time per request:       54.484 [ms] (mean, across all concurrent requests)
Transfer rate:          15.15 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       42   52  17.3     46     137
Processing:    43   54  20.8     47     170
Waiting:       42   53  20.7     47     170
Total:         84  106  28.9     93     219

Percentage of the requests served within a certain time (ms)
  50%     93
  66%     96
  75%    101
  80%    130
  90%    153
  95%    161
  98%    219
  99%    219
 100%    219 (longest request)


4、ab性能指标



1、吞吐率(Requests per second)

服务器并发处理能力的量化描述,单位是reqs/s,指的是在某个并发用户数下单位时间内处理的请求数。某个并发用户数下单位时间内能处理的最大请求数,称之为最大吞吐率。记住:吞吐率是基于并发用户数的。这句话代表了两个含义:

● 吞吐率和并发用户数相关

● 不同的并发用户数下,吞吐率一般是不同的

计算公式:总请求数/处理完成这些请求数所花费的时间,即

Request per second=Complete requests/Time taken for tests

必须要说明的是,这个数值表示当前机器的整体性能,值越大越好



2、并发连接数(The number of concurrent connections)

并发连接数指的是某个时刻服务器所接受的请求数目,简单的讲,就是一个会话。



3、并发用户数(Concurrency Level)

要注意区分这个概念和并发连接数之间的区别,一个用户可能同时会产生多个会话,也即连接数。在HTTP/1.1下,IE7支持两个并发连接,IE8支持6个并发连接,FireFox3支持4个并发连接,所以相应的,我们的并发用户数就得除以这个基数。



4.用户平均请求等待时间(Time per request)

计算公式:处理完成所有请求数所花费的时间/(总请求数/并发用户数),即:

Time per request=Time taken for tests/(Complete requests/Concurrency Level)


5.服务器平均请求等待时间(Time per request:across all concurrent requests)

计算公式:处理完成所有请求数所花费的时间/总请求数,即:

Time taken for/testsComplete requests

可以看到,它是吞吐率的倒数。同时,它也等于用户平均请求等待时间/并发用户数,即

Time per request/Concurrency Level



7、特例: https 为什么慢,把nginx 跑到 qat 卡上

Intel QAT 助力 Nginx 压缩处理



1、什么是Intel® QAT?

Intel® QuickAssist Technology是Intel®公司提供的一种高性能数据安全和压缩的加速方案。该方案利用QAT芯片分担对称/非对称加密计算,DEFLATE无损压缩等大计算量的任务,来降低CPU使用率并提高整体平台性能。该方案可以主板芯片,独立的PCI-E加速卡或者SOC三种方式部署。

QAT支持硬件加速Deflate无损压缩算法,在处理海量数据时,QAT在不增加CPU开销的前提下,通过压缩来减少需要传输和存盘的数据量,从而减少了网络带宽和磁盘读写的开销,最终提高了整体的系统性能。 例如,在Web Serer上使用QAT硬件加速压缩处理,可将CPU从繁重的压缩计算中解放出来,以处理更多的连接请求。



2、什么是Nginx Web Server

Nginx(发音同engine x)是一款使用异步框架的高性能Web服务器,相较于Apache、lighttpd等其他Web Server,Nginx具有占有内存少,稳定性高等优势。根据Netcraft 2018年一月的Web Server市场调查报告, Nginx的装机率达25.39%,位列Web Server市场第三,并在持续增长中[1]。

Nginx中的GZIP模块实现了对HTTP压缩的支持,该模块通过调用Zlib库实现对网页内容进行Deflate压缩。由于使用软件实现无损压缩,需要消耗大量CPU运算时间进行压缩运算。

然而,在大并发流量的网站接入层的Nginx需要处理相当多的业务,包括https连接建立,安防攻击,流量镜像,链路追踪等等。使得CPU进行HTTP压缩处理成为Web Server最主要的CPU开销,进而限制了网站支持最大并发连接数。根据客户提供的接入层流量模型分析来看, GZIP 单个模块 CPU 消耗占比达到 15%-20% 左右,且占比呈上升趋势。所以,若能使用加速Nginx的网页压缩处理,可以极大的提高网站性能

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gptSl1T1-1602213757606)(assets/1562145313085.png)]



3、Zlib-SHIM API

Zlib作为广泛部署的软件压缩库,提供Deflate压缩的软件实现,被包括Nginx在内的广大应用程序所采用。Intel® QuickAssist Technology提供了与Zlib类似接口的Zlib-SHIM 软件库来适配上层应用,减少了应用迁移的开发量。该库提供了与Zlib一致的Deflate API,只需对源代码做少量修改,并将原有应用与Zlib-SHIM编译链接,就能使用QuickAssit提供的硬件加速功能。

Zlib-SHIM库实现了DeflateInit,DeflateInit2,Deflate,DeflateEnd等常用API并支持Stateful和Stateless压缩,可以替代Zlib的绝大部分功能。

Zlib-SHIM提供了Deflate API同步模式接口(调用程序阻塞在Deflate API上,直到压缩任务完成),而其内部实现调用了异步模式API,即在CPU上运行的QAT驱动程序向QAT协处理器提交了一个数据压缩请求后即返回,期间使用Polling 接口定期检查压缩请求是否完成,等到QAT硬件完成压缩处理后通过回调函数通知CPU端的应用程序进行下一步操作。这样的设计, 在不影响上层应用程序原有设计的前提下,实现了高并发场景中CPU和QAT的协同工作:CPU专注于网络链接处理而QAT处理复杂的压缩计算, 各司其职,最终提高了系统整体性能。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iDptf3kT-1602213757607)(assets/1562145340420.png)]



4、Zlib-SHIM工作原理

采用这种异步模式API实现对外的同步接口,在实现上有三种方法:Direct Polling,Indirect Polling Spinning和Indirect Polling Semaphore模式。

使用Direct Polling模式时,Deflate调用者会在当前线程直接调用Polling接口,若压缩结果还没有返回则休眠一段时间后再检查,直到压缩成功后Deflate 调用才返回。Direct Polling模式下CPU开销小,但是单个Request从发出到返回结果延迟较长,只有在多进程多线程高并发模式下才能充分发挥QAT的压缩性能。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5WAhrJGz-1602213757608)(assets/1562145358390.png)]

Direct polling 模式流程图

Indirect Polling模式,是通过创建一个轮询线程定期调用Polling接口来检查压缩请求在QAT 协处理器中的执行状态,轮询线程一旦发现有请求执行完毕就通过回调函数(Callback Function)通知CPU端程序进行后续处理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-03WwbdSZ-1602213757610)(assets/1562145381885.png)]

Indirect pooling模式时序图

根据回调通知方式的不同,Indirect Polling模式又可细分为Spinning模式和Semaphore模式:

Spinning模式中,Deflate函数调用线程定期轮询与回调函数共享的任务完成标志,若尚未完成则主动休眠,若已完成则进行后续处理。

Semaphore模式中,Deflate调用线程通过互斥锁来隔离与轮询线程共享的任务完成标志的访问操作,若任务尚未完成则Deflate调用线程休眠,一旦任务完成,轮询线程中的回调函数会通过信号量唤醒调用线程进行后续处理。

采用Indirect Polling模式无论Deflate调用数目多少都需要启动轮询线程,在压缩请求数不大的情况下增加了CPU的Polling开销,但是当压缩任务作为CPU的主要任务时可以减少不必要的Polling调用,提高CPU的使用率。

Nginx为了实现高性能的高并发处理能采用了单进程单线程异步工作模式,如果使用Indirect Polling模式,就需要在每个Nginx Worker进程中创建一个轮询线程 (Polling Thread), 从而增加了线程间切换的开销和共享数据的互斥的操作, 所以在Nginx和Zlib-SHIM集成中,我们使用了Direct Polling Mode。



5、USDM优化

USDM是QAT为了优化内存使用效率而提供的用户态内存管理工具。由于QAT和CPU之前需要频繁高速的交换数据,使用传统的Memory Copy方式不仅效率低而且消耗CPU资源,所以QAT支持DMA方式进行数据传输,CPU只需分配好拥有物理连续内存的输入地址和输出地址,QAT会自动完成的从该地址的输入读取或输出写回的操作。QAT Driver中的USDM模块,利用了Linux 内核提供的Huge-Page特性来获得物理地址连续内存并使用SLAB算法进行内存管理。

对于每一个新分配的Huge-Page,USDM会对应地在Devfs中新建一个临时文件,并通过mmap将临时文件描述符与新申请到的Huge-Page关联上,当USDM发现该Huge-Page上所有内存块均已释放,并不需将其留作缓存时,就关闭该临时文件以释放Huge-Page。当Zlib-SHIM运行过程中需要使用连续的内存时,就使用USDM Alloc接口从内存池中申请连续物理地址的内存, 并在发送压缩请求时将申请到的连续内存地址作为参数传递给QAT。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8zU1jqZc-1602213757611)(assets/1562145408201.png)]



6、测试结果

基于以上设计的Zlib-SHIM可以很方便的替换原有Zlib库,使得调用者可以方便的利用QAT进行压缩加速。 在实际应用中, 客户就是将Zlib-SHIM与基于Nginx定制的Web Server进行集成,在接入层的性能优化上取得了理想的效果。

客户的Web Server运行在Intel® Xeon® CPU上(CPU型号:Intel® Xeon® CPU E5-2650 v2 @ 2.60GHz 32核 内核:2.6.32 Zlib版本:zlib-1.2.8 QAT驱动版本:intel-qatOOT40052 ),在相同网络流量条件下,未开启QAT加速的CPU平均使用率为48%左右,而开启QAT加速后CPU平均使用率为41%左右。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9O45IUfF-1602213757613)(assets/1562145434782.png)]

综合以上数据,Web Server在QPS 10K的压力下,使用QAT加速后可以节省CPU 15%左右,且Gzip基本上完全卸载、随着其占比变高,优化效果将越好。



7、总结

拥有和Zlib相同API接口的Zlib-SHIM可以很方便的与上层应用集成,利用QAT的硬件加速性能。此外,为了最大限度的发挥QAT的新能,Intel还提供了使用专有API的QATzip软件库,相应的Nginx module也在开发中,相信不久的将来集成QATzip的Web Server能够提供更好的性能。

性能测试中使用的软件和工作负荷可能仅在英特尔微处理器上进行了性能优化。诸如SYSmark和MobileMark等测试均系基于特定计算机系统、硬件、软件、操作系统及功能。上述任何要素的变动都有可能导致测试结果的变化。请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。



版权声明:本文为kakaops_qing原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。