Nginx入门学习(1):一些概念

写在前面

我们知道在传统的Web服务器中,每个客户端连接需要一个单独的进程或者线程来处理,在切换任务的时候需要将CPU切换到新的任务并创建一个新的运行时上下文,这样不仅会消耗额外的内存还会花费一定的CPU时间。当并发请求增加时,服务器负担将会加重,进而对性能产生一定影响。而本文将学习的Nginx服务器则很好解决了这个问题。

Nginx简介

Nginx是一个高性能的HTTP(处理静态文件)和反向代理(负载均衡)服务器,也是一个IMAP/POP3/SMTP代理服务器。现在大部分的Web服务器都使用Nginx作为负载均衡器。

Nginx特点

(1)速度更快。在单次请求下会得到更快的响应,而在高并发环境下,Nginx比其他Web服务器有更快的响应。
(2)高扩展性。Nginx基于模块化设计,由多个耦合度极低的模块组成,具有很高的扩展性。这种强扩展性,非常适合高流量的网站系统。
(3)高可靠性。Nginx的可靠性来源于其核心代码的优秀设计,模块设计的简单性。Nginx官方提供的常用模块非常稳定,每个worker进程相对独立,master进程在一个worker进程出错时可以快速拉起一个新的worker子进程以继续提供服务。
(4)低内存消耗。一般情况下,1万个非活跃的HTTP Keep-Alive连接在Nginx中仅消耗2.5MB的内存,这是Nginx支持高并发的基础。理论上来说Nginx支持的并发上限取决于机器的内存,只要内存足够,单机实现10万、100万以上的并发都是可能的,而实际上10万个并发已经都是基本操作了。
(5)热部署。前面提到master进程和worker进程是分离设计的,这使得Nginx支持热部署,也就是可以在不停掉当前服务的情况下,对Nginx可执行文件进行升级。除此之外,Nginx还支持不停止服务就可以更新配置文件中的配置项,和更换日志文件等功能。
(6)使用最自由的BSD协议。使用BSD协议就意味着用户不仅可以免费使用Nginx,还可以直接或者间接修改Nginx的源码,然后发布使用。
(7)CPU亲和。所谓的CPU亲和是指将CPU核心和Nginx工作进程绑定,将Nginx每个worker进程固定在一个CPU上执行,可以减少切换CPU的cache miss,能获得更好的性能。

Nginx社区分支

(1)Openresty。作者章宜春,其最大特点就是引入了ngx_lua 模块,支持使用Lua开发插件,且集合了很多丰富的模块以及 Lua库。
(2)Tengine。由淘宝团队开发,主要引入了淘宝自身业务需要的新特性。
(3)Nginx官方版本。它更新迭代较快,提供免费版本和商业版本。当然在官方版本中也有三种类型:Mainline Version是主线版,也就是开发版;Stable Version是最新稳定版,一般生产环境都使用这个;Legacy Version是历史的稳定老版本。

Nginx主要使用场景

(1)静态资源服务。通过使用本地文件系统来提供访问静态资源的服务。
(2)反向代理服务。这里面较为复杂,包括缓存、负载均衡等。
(3)API服务。主要是OpenResty。
(4)安全防御。
(5)灰度发布。
(6)静态化。
(7)消息推送。
(8)防盗链。
(9)智能路由,包括企业级的灰度测试、地图POI一键切流。
(10)图片实时压缩。

Nginx的企业应用场景

(1)作为Web服务软件使用;(2)作为反向代理或者负载均衡服务;(3)作为前端业务数据缓存服务。以上三个应用场景是Nginx在企业开发过程中比较常用的功能。

几个重要的概念

并发:指一个时间段内,有几个程序在同一个CPU上运行,但是任意时刻只有一个程序在CPU上运行。

并行:指在任意的时刻点上,有多个程序运行在多个CPU上面。(与CPU的数量保持一致)。

异步: 用于多任务情况, 多个任务之间执行没有先后顺序,可以同时运行,执行的先后顺序不会有什么影响,存在多条运行主线;(指代码调用IO操作时,不需要等待IO操作完成,就可以返回的调用方式)(多线程)。

同步: 用于多任务情况, 多个任务之间执行的时候要求有先后顺序,必须一个先执行完成之后,另一个才能继续执行, 只有一个主线;(指代码调用IO操作时,必须等待IO操作完成,才返回的调用方式)。

阻塞:从调用者的角度出发,如果在调用的时候,被卡住,不能再继续向下运行,需要等待,就说是阻塞;(调用函数的时候,当前线程被挂起,不能立即返回)。

非阻塞: 从调用者的角度出发, 如果在调用的时候,没有被卡住,能够继续向下运行,无需等待,就说是非阻塞。(调用函数的时候,当前线程不会被挂起,而是立即返回)。

HTTP的请求与响应报文

HTTP请求报文

下面是HTTP请求报文的基本语法:

Nginx入门学习(1):一些概念


请求行:①是请求方法,GET和POST是最常见的HTTP方法,除此以外还包括DELETE、HEAD、OPTIONS、PUT和TRACE;②是请求对应的URL地址,它和报文头的Host属性组成完整的请求URL;③是协议名称及版本号。

请求头:④是HTTP的报文头,报文头包含若干个属性,格式为“属性名:属性值”,服务端据此获取客户端的信息。请注意与缓存相关的规则信息,均包含在header中。

请求体:⑤是报文体,它将一个页面表单中的组件值通过param1=value1¶m2=value2的键值对形式编码成一个格式化串,它承载多个请求参数的数据。请注意,不仅报文体可以传递请求参数,请求URL也是可以通过类似于/envythink/index.html? param1=value1¶m2=value2的方式来传递请求参数。

将下来简单介绍一下HTTP请求报文头属性:
(1)Accept请求报文可通过一个“Accept”报文头属性告诉服务端,客户端可以接受什么类型的响应。举个例子,
Accept:text/plain表示告诉服务器,客户端只能接收响应类型仅为纯文本的数据。

实际上Accept属性的值可以为一个或多个MIME类型的值(这些值用于描述消息内容类型的因特网标准, 消息能包含文本、图像、音频、视频以及其他应用程序专用的数据)。

(2)cookie。客户端的Cookie就是通过这个报文头属性传给服务端。这里通常会有一个sessionId,用于告诉服务端如何辨别哪些请求来自于同一个客户端发起的。其实就是通过HTTP请求报文头的Cookie属性中sessioId的值来关联的。其实也可以通过重写URL的方式将会话ID附带在每个URL的后面,不过这种方式不太安全,不建议这么操作。

(3)Referer。Referer表示这个请求是从哪个URL过来的。

(4)Cache-Control。表示对缓存进行控制,如一个请求希望响应返回的内容在客户端要被缓存一年,或不希望被缓存就可以通过设置这个报文头来达到目的。

HTTP响应报文

HTTP响应报文也是由三部分组成,不同于请求报文中的请求行、请求头和请求体;响应报文包括响应行、响应头和响应体,如下所示:

Nginx入门学习(1):一些概念


响应行:其中①报文协议及版本;②状态码及状态描述;

响应头:其中③响应报文头,也是由多个属性组成;

响应体:其中④响应报文体,即服务器返回给客户端的数据。

将下来简单介绍一下常见的HTTP响应报文头属性:
(1)Cache-Control。又看到这个属性,这里表示响应输出到客户端之后,服务端通过该报文头属性来告诉客户端如何控制响应内容的缓存。

Cache-Control这一属性常见的取值有private、public、no-cache、max-age、no-store,默认值为private。private表示客户端可以缓存;public表示客户端和代理服务器都可以缓存;no-cache表示需要使用对比缓存来验证缓存数据;max-age表示缓存的内容将在max-age秒之后失效;no-store表示所有的内容都不会缓存。

前面也说了默认值为private,缓存时间为31536000秒,也就是365天,也就是说在365天之内,再次请求这条数据,都会直接从缓存数据库中获取数据并使用。
(2)ETag。它代表一个响应服务端资源,如页面版本的报文属性,如果某个服务端资源发生了变化,那么这个ETag就会发生变化。可以将这个ETag作为Cache-Control的补充,它可以让客户端更智能
知道,什么时候从服务端获取资源,什么时候可以直接从缓存中返回响应。
(3)Location。如果你之前使用过JSP,且需要让A页面Redirect到B页面,此时其实就是让客户端再发起一个请求到A页面,那么就需要Redirect到A页面的URL,说白了就是通过响应报文头的Location属性来告知客户端。举个例子,下面的报文属性就是将客户端Redirect到envythink的首页:

Location: http://www.envythink.com  

(4)Set-Cookie。服务端可以设置客户端的Cookie,其原理就是通过这个响应报文头属性来实现的。

(5)Cookie机制。客户端请求服务器,如果服务器需要记录该用户状态,就可以使用Response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来,当浏览器再次请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器会检查该Cookie并据此来辨认用户状态。当然服务器还可以根据需要修改Cookie的内容。

Cookie的maxAge决定Cookie的有效期,单位为秒(Second)。Cookie可以通过getMaxAge()setMaxAge(int maxAge)方法来读写maxAge属性。

如果maxAge属性为正数,则表示该Cookie会在maxAge秒之后自动失效。如果maxAge为负数,则表示该Cookie仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该Cookie立即失效。如果maxAge为0,则表示删除该Cookie。

请注意,Cookie并不提供修改和删除操作,如果需要修改某个Cookie,只需要新建一个同名的Cookie,然后添加到Response中来覆盖之前的Cookie。如果要删除某个Cookie,只需要新建一个同名的Cookie,并将maxAge设置为0,并添加到到Response中来覆盖之前的Cookie。举个例子,如下代码:

# 新建一个Cookie Cookie cookie = new Cookie("username","envy"); # 设置Cookie的生命周期为0,注意不能为负数 cookie.setMaxAge(0); # 将cookie输出到客户端 response.addCookie(cookie);

简单请求和非简单请求

在学习了上述HTTP请求和响应报文之后,接下来就来了解简单请求和非简单请求。为什么需要区分简单请求和非简单请求,那是因为浏览器处理简单请求和非简单请求的方式不一样。

如果某个请求同时满足下面两个条件,那么该请求就属于简单请求:
(1)请求方法是GET、POST或者HEAD三者中的任意一个;
(2)HTTP头信息不超过下面几个字段:
AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-Type。请注意Last-Event-IDContent-Type只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain

除此之外,凡是不同时满足这两个条件的,都属于非简单请求。

前面说过浏览器会针对这两种请求采取不同的处理方式,那么具体的处理方式又是怎样的呢?

简单请求

对于简单请求来说,浏览器会在其头信息中增加Origin字段后直接发出,Origin字段用来说明本次请求来自哪个源(协议+域名+端口)。

如果服务器发现Origin指定的源不在许可范围内,服务器会返回一个正常的HTTP回应,浏览器取到回应之后发现回应的头信息中没有包含Access-Control-Allow-Origin字段,就抛出一个错误给XHR的error事件;

如果服务器发现Origin指定的域名在许可范围内,那么服务器返回的响应会多出几个以Access-Control-开头的头信息字段。

非简单请求

说完了简单请求,接下来开始学习非简单请求,非简单请求是指对服务器有特殊要求的请求。举个例子,如请求方法是PUT、DELETE或者Content-Type值为application/json中的某种时,浏览器会在正式通信之前,发送一次HTTP预检OPTIONS请求,先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP请求方法和头信息字段。只有得到肯定答复,浏览器才会发出正式的XHR请求,否则报错。

您可能还会对下面的文章感兴趣: