随着现代web开发的发展,restful,前后端分离,前端js框架的应用越来越普遍。很多web应用请求的接口可能根本就存在于不同的服务器,类似于微信,支付宝等等。这其中就会存在跨域的问题。简单来说,跨域就是浏览器为了防止黑客能够随意改变表单的请求地址的一种安全防护。如果黑客可以随意将一个表单的请求地址改成其控制的服务器地址,在返回一个可以获取用户填写信息的页面,就会造成用户信息泄露。这种浏览器跨域防护跟现在的开发模式又有相悖的地方。笔者最初使用nginx 就是为了解决前后端分离带来的跨域问题。
nginx是一个高性能的http/反向代理服务器,也可以用于邮件服务。具有反向代理/负载均衡的功能。起源于俄罗斯的一个大型网站,现在国内新浪、163、腾讯、Discuz、豆瓣都有使用。
笔者会记录学习nginx的过程,毕竟很多东西的学习光靠百度百科远远不够。
一. nginx为什么具有高性能
nginx在启动后,服务器会出现至少两个进程(具体看配置),也可以看成是两类进程,一个是master,一个是workers,二者是管理与被管理的关系。 master进程能够将接收到的http请求合理的分配到works进程中去。所以一般的http请求其实是跟master进程通信,master进程保证每个请求只被一个works执行,至于master分配请求与worker处理请求的机制这里不做表述。
那么,nginx采用这种进程模型有什么好处呢?当然,好处肯定会很多了。首先,对于每个worker进程来说,独立的进程,不需要加锁,所以省掉了锁带 来的开销,同时在编程以及问题查找时,也会方便很多。其次,采用独立的进程,可以让互相之间不会影响,一个进程退出后,其它进程还在工作,服务不会中 断,master进程则很快启动新的worker进程。当然,worker进程的异常退出,肯定是程序有bug了,异常退出,会导致当前worker上的 所有请求失败,不过不会影响到所有请求,所以降低了风险。当然,好处还有很多,大家可以慢慢体会。
nginx提供一种异步非阻塞的机制。当一个请求通过master到达worker时,操作系统 提供select/poll/epoll/kqueue这样的系统调用,当请求发现对应的接口尚未准备好(这里的未准备好有多种原因,可能没有响应,正在初始化,正在建立连接等),这个时候请求放到内核中,不占用CPU的内存,同时 select/poll/epoll/kqueue这样的系统调用 监听该请求的目标是否准备好,这个时候该worker进程可以接收另外的请求来处理,如果前面的请求目标被监听到准备好时,worker在调用回,这样使得CPU处于一种高效的使用状态而不用开辟对于的线程占用内存空间。
当然,这里的并发请求,是指未处理完的请求,线程只有一个,所以同时能处理的请求当然只有一个了,只是在请求间进行不断地切换而已,切换也是因为异步事件 未准备好,而主动让出的。这里的切换是没有任何代价,你可以理解为循环处理多个准备好的事件,事实上就是这样的。与多线程相比,这种事件处理方式是有很大 的优势的,不需要创建线程,每个请求占用的内存也很少,没有上下文切换,事件处理非常的轻量级。并发数再多也不会导致无谓的资源浪费(上下文切换)。更多 的并发数,只是会占用更多的内存而已。 我之前有对连接数进行过测试,在24G内存的机器上,处理的并发请求数达到过200万。现在的网络服务器基本都采用这种方式,这也是nginx性能高效的 主要原因。
nginx在实现时,是通过一个连接池来管理的,每个worker进程都有一个独立的连接池,连接池的大小是 worker_connections。这里的连接池里面保存的其实不是真实的连接,它只是一个worker_connections大小的一个 ngx_connection_t结构的数组。并且,nginx会通过一个链表free_connections来保存所有的空闲 ngx_connection_t,每次获取一个连接时,就从空闲连接链表中获取一个,用完后,再放回空闲连接链表里面。
在这里,很多人会误解worker_connections这个参数的意思,认为这个值就是nginx所能建立连接的最大值。其实不然,这个值是表 示每个worker进程所能建立连接的最大值,所以,一个nginx能建立的最大连接数,应该是worker_connections * worker_processes。当然,这里说的是最大连接数,对于HTTP请求本地资源来说,能够支持的最大并发数量是 worker_connections * worker_processes,而如果是HTTP作为反向代理来说,最大并发数量应该是worker_connections * worker_processes/2。因为作为反向代理服务器,每个并发会建立与客户端的连接和与后端服务的连接,会占用两个连接。
(未完待续)