Nginx 特点:
1,nginx 代理和后端 web 服务器之间无需长连接
2,接收用户请求是异步的,先将用户请求全部接收下来,
再一次性发送给后端服务器,极大的减轻后端服务器的压力
3,发送响应报文时,是边接收来自后端服务器的数据,边发送给客户端的。
4,网络依赖性低,只要能够 ping 通过就可以实施负载均衡,而且有效区分内网和外网流量。
5,支持服务检测,nginx 能够根据应用服务器处理页面返回的状态码,
超时信息等检测服务器是否出现故障,并及时返回错误的请求重新提交到其它节点上。
6,配置简单,非阻塞,高并发连接,事件驱动:通信机制采用epoll 模型,支持更大的并发连接。
Nginx 负载均衡原理:
负载均衡策略分为两类:
内置策略: 加权轮询, ip hash
扩展策略: fair ,通用hash ,一致性 hash
加权轮询:
如果把加权轮询算法分为 先深搜索 和 先广搜索, nginx 采用的是 先深搜索算法,
1: 首先将请求都分给高权重的机器(处理能力最大的),直到该机器的权值降低到了比其他机器低,才开始 将请求分给下一个高权重的机器
2: 当所有后端机器都 down 掉时,nginx 会立即将所有机器的标志清成初初始状态,
以避免造成所有的机器都处在timeout 状态,从而导致整个前端被夯住。
fair :
默认不被编译进 ngixn ,其原理是根据后端服务器的响应时间判断负载情况,从中选出负载最轻的机器进行分流。
这种策略具有很强的自适应性,但是实际的网络环境往往不是那么简单。
f5 是最早提出负载均衡和应用交付理念的
HTTP服务器
Nginx本身也是一个静态资源的服务器,当只有静态资源的时候,就可以使用Nginx来做服务器,
同时现在也很流行动静分离,就可以通过Nginx来实现,
动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,
动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路
nginx进程模型
在工作方式上,Nginx分为 单工作进程 和 多工作进程 两种模式。
在单工作进程模式下,除主进程外,还有一个工作进程,工作进程是单线程的;
在多工作进程模式下,每个工作进程包含多个线程。
Nginx默认为单工作进程模式
master进程
主要用来管理worker进程,包含:接收来自外界的信号,向各worker进程发送信号,监控worker进程的运行状态,
当worker进程退出后(异常情况下),会自动重新启动新的worker进程。
master进程充当整个进程组与用户的交互接口,同时对进程进行监护。它不需要处理网络事件,
不负责业务的执行,只会通过管理worker进程来实现重启服务、平滑升级、更换日志文件、配置文件实时生效等功能。
我们要控制nginx,只需要通过kill向master进程发送信号就行了。
比如kill -HUP pid,则是告诉nginx,从容地重启nginx,
我们一般用这个信号来重启nginx,或重新加载配置,因为是从容地重启,因此服务是不中断的。
master进程在接收到HUP信号后是怎么做的呢?
首先master进程在接到信号后,会先重新加载配置文件,然后再启动新的worker进程,
并向所有老的worker进程发送信号,告诉他们可以光荣退休了。
新的worker在启动后,就开始接收新的请求,而老的worker在收到来自master的信号后,就不再接收新的请求,
并且在当前进程中的所有未处理完的请求处理完成后,再退出。
当然,直接给master进程发送信号,这是比较老的操作方式,nginx在0.8版本之后,引入了一系列命令行参数,来方便我们管理。
比如,./nginx -s reload,就是来重启nginx,./nginx -s stop,就是来停止nginx的运行。
如何做到的呢?我们还是拿reload来说,我们看到,执行命令时,我们是启动一个新的nginx进程,
而新的nginx进程在解析到reload参数后,就知道我们的目的是控制nginx来重新加载配置文件了,
它会向master进程发送信号,然后接下来的动作,就和我们直接向master进程发送信号一样了。
worker进程
基本的网络事件,则是放在worker进程中来处理了。
多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。
一个请求,只可能在一个worker进程中处理,一个worker进程,不可能处理其它进程的请求。
worker进程的个数是可以设置的,一般会设置与机器cpu核数一致,这里面的原因与nginx的进程模型以及事件处理模型是分不开的。
worker进程之间是平等的,每个进程,处理请求的机会也是一样的。
当我们提供80端口的http服务时,一个连接请求过来,每个进程都有可能处理这个连接,
怎么做到的呢?
首先,每个worker进程都是从master进程fork过来,在
master进程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多个worker进程。
所有worker进程的listenfd会在新连接到来时变得可读,
为保证只有一个进程处理该连接,所有worker进程在注册listenfd读事件前抢accept_mutex,
抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。
当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,
最后才断开连接,这样一个完整的请求就是这样的了。
多进程IO模型好处
对于每个worker进程来说,独立的进程,不需要加锁,所以省掉了锁带来的开销,同时在编程以及问题查找时,也会方便很多。
其次,采用独立的进程,可以让互相之间不会影响,一个进程退出后,其它进程还在工作,服务不会中断,
master进程则很快启动新的worker进程。
当然,worker进程的异常退出,肯定是程序有bug了,异常退出,会导致当前worker上的所有请求失败,
不过不会影响到所有请求,所以降低了风险。
异步非阻塞
虽然nginx采用多worker的方式来处理请求,每个worker里面只有一个主线程,那能够处理的并发数很有限啊,
多少个worker就能处理多少个并发,何来高并发呢?
这就是nginx的高明之处,nginx采用了异步非阻塞的方式来处理请求,也就是说,nginx是可以同时处理成千上万个请求的。
一个worker进程可以同时处理的请求数只受限于内存大小,
而且在架构设计上,
不同的worker进程之间处理并发请求时几乎没有同步锁的限制,worker进程通常不会进入睡眠状态,
因此,
当Nginx上的进程数与CPU核心数相等时(最好每一个worker进程都绑定特定的CPU核心),进程间切换的代价是最小的。