正向代理。简单的说,我是一个用户,我没法直接访问一个网站,可是我能访问一个代理服务器,这个代理服务器能访问那个我不能访问的网站,因而我先连上代理服务器,告诉它我须要那个没法访问网站的内容,代理服务器去取回来,而后返回给我。从网站的角度,只在代理服务器来取内容的时候有一次记录。结论就是,正向代理,是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),而后代理向原始服务器转交请求并将得到的内容返回给客户端。客户端必需要进行一些特别的设置才能使用正向代理。nginx
反向代理。例如我要访问 localhost:8080/views/test1 这个页面,但view对应的服务器上并无test1这个资源,它是从另外一台服务器上调用的资源。这样view对应的那个服务器就使用了反向代理。即用户只须要把请求发给特定的反向代理服务器,具体请求是谁处理的用户不须要知道(其实也不知道),由代理服务器统一处理。结论就是 反向代理正好相反,对于客户端而言它就像是原始服务器,而且客户端不须要进行任何特别的设置。客户端向反向代理 的命名空间(name-space)中的内容发送普通请求,接着反向代理将判断向何处(原始服务器)转交请求,并将得到的内容返回给客户端,就像这些内容 本来就是它本身的同样。web
正向代理的典型用途是为在防火墙内的局域网客户端提供访问Internet的途径。正向代理还可使用缓冲特性减小网络使用率。反向代理的典型用途是将 防火墙后面的服务器提供给Internet用户访问。反向代理还能够为后端的多台服务器提供负载平衡,或为后端较慢的服务器提供缓冲服务。算法
正向代理:针对客户端而言, 代理服务器代理客户端,转发请求,并将得到的内容返回给客户端。
反向代理:针对客户端而言, 代理服务器就像是原始服务器,代理集群的web节点服务器返回结果。apache
负载均衡也是Nginx经常使用的一个功能,负载均衡其意思就是分摊到多个操做单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工做任务。简单而言就是当有2台或以上服务器时,根据规则随机的将请求分发到指定的服务器上处理,负载均衡配置通常都须要同时配置反向代理,经过反向代理跳转到负载均衡。而Nginx目前支持自带3种负载均衡策略,还有2种经常使用的第三方策略。编程
RR
按照轮询(默认)方式进行负载,每一个请求按时间顺序逐一分配到不一样的后端服务器,若是后端服务器down掉,能自动剔除。虽然这种方式简便、成本低廉。但缺点是:可靠性低和负载分配不均衡。后端
权重
指定轮询概率,weight和访问比率成正比,用于后端服务器性能不均的状况。缓存
此时8080和8081分别占90%和10%。upstream test{ server localhost:8080 weight=9; server localhost:8081 weight=1; }
ip_hash
上面的2种方式都有一个问题,那就是下一个请求来的时候请求可能分发到另一个服务器,当咱们的程序不是无状态的时候(采用了session保存数据),这时候就有一个很大的很问题了,好比把登陆信息保存到了session中,那么跳转到另一台服务器的时候就须要从新登陆了,因此不少时候咱们须要一个客户只访问一个服务器,那么就须要用iphash了,iphash的每一个请求按访问ip的hash结果分配,这样每一个访客固定访问一个后端服务器,能够解决session的问题。bash
upstream test {
ip_hash;
server localhost:8080; server localhost:8081; }
fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。服务器
upstream backend {
fair;
server localhost:8080; server localhost:8081; }
url_hash(第三方)
按访问url的hash结果来分配请求,使每一个url定向到同一个后端服务器,后端服务器为缓存时比较有效。 在upstream中加入hash语句,server语句中不能写入weight等其余的参数,hash_method是使用的hash算法。markdown
upstream backend {
hash $request_uri; hash_method crc32; server localhost:8080; server localhost:8081; }
Nginx自己也是一个静态资源的服务器,当只有静态资源的时候,就可使用Nginx来作服务器,同时如今也很流行动静分离,就能够经过Nginx来实现,动静分离是让动态网站里的动态网页根据必定规则把不变的资源和常常变的资源区分开来,动静资源作好了拆分之后,咱们就能够根据静态资源的特色将其作缓存操做,这就是网站静态化处理的核心思路。
在工做方式上,Nginx分为单工做进程和多工做进程两种模式。在单工做进程模式下,除主进程外,还有一个工做进程,工做进程是单线程的;在多工做进程模式下,每一个工做进程包含多个线程。Nginx默认为单工做进程模式。
Nginx在启动后,会有一个master进程和多个worker进程。
主要用来管理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进程的个数是能够设置的,通常咱们会设置与机器cpu核数一致,这里面的缘由与nginx的进程模型以及事件处理模型是分不开的。
worker进程之间是平等的,每一个进程,处理请求的机会也是同样的。当咱们提供80端口的http服务时,一个链接请求过来,每一个进程都有可能处理这个链接,怎么作到的呢?首先,每一个worker进程都是从master进程fork过来,在master进程里面,先创建好须要listen的socket(listenfd)以后,而后再fork出多个worker进程。
全部worker进程的listenfd会在新链接到来时变得可读,为保证只有一个进程处理该链接,全部worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该链接。当一个worker进程在accept这个链接以后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开链接,这样一个完整的请求就是这样的了。
咱们能够看到,一个请求,彻底由worker进程来处理,并且只在一个worker进程中处理。worker进程之间是平等的,每一个进程,处理请求的机会也是同样的。当咱们提供80端口的http服务时,一个链接请求过来,每一个进程都有可能处理这个链接,怎么作到的呢?首先,每一个worker进程都是从master进程fork过来,在master进程里面,先创建好须要listen的socket(listenfd)以后,而后再fork出多个worker进程。
全部worker进程的listenfd会在新链接到来时变得可读,为保证只有一个进程处理该链接,全部worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该链接。当一个worker进程在accept这个链接以后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开链接,这样一个完整的请求就是这样的了。咱们能够看到,一个请求,彻底由worker进程来处理,并且只在一个worker进程中处理。
首先,对于每一个worker进程来讲,独立的进程,不须要加锁,因此省掉了锁带来的开销,同时在编程以及问题查找时,也会方便不少。
其次,采用独立的进程,可让互相之间不会影响,一个进程退出后,其它进程还在工做,服务不会中断,master进程则很快启动新的worker进程。固然,worker进程的异常退出,确定是程序有bug了,异常退出,会致使当前worker上的全部请求失败,不过不会影响到全部请求,因此下降了风险。
虽然nginx采用多worker的方式来处理请求,每一个worker里面只有一个主线程,那可以处理的并发数颇有限啊,多少个worker就能处理多少个并发,何来高并发呢?非也,这就是nginx的高明之处,nginx采用了异步非阻塞的方式来处理请求,也就是说,nginx是能够同时处理成千上万个请求的。
一个worker进程能够同时处理的请求数只受限于内存大小,并且在架构设计上,不一样的worker进程之间处理并发请求时几乎没有同步锁的限制,worker进程一般不会进入睡眠状态,所以,当Nginx上的进程数与CPU核心数相等时(最好每个worker进程都绑定特定的CPU核心),进程间切换的代价是最小的。
而apache的经常使用工做方式(apache也有异步非阻塞版本,但因其与自带某些模块冲突,因此不经常使用),每一个进程在一个时刻只处理一个请求,所以,当并发数上到几千时,就同时有几千的进程在处理请求了。这对操做系统来讲,是个不小的挑战,进程带来的内存占用很是大,进程的上下文切换带来的cpu开销很大,天然性能就上不去了,而这些开销彻底是没有意义的。
为何nginx能够采用异步非阻塞的方式来处理呢,或者异步非阻塞究竟是怎么回事呢?
咱们先回到原点,看看一个请求的完整过程:首先,请求过来,要创建链接,而后再接收数据,接收数据后,再发送数据。
具体到系统底层,就是读写事件,而当读写事件没有准备好时,必然不可操做,若是不用非阻塞的方式来调用,那就得阻塞调用了,事件没有准备好,那就只能等了,等事件准备好了,你再继续吧。阻塞调用会进入内核等待,cpu就会让出去给别人用了,对单线程的worker来讲,显然不合适,当网络事件越多时,你们都在等待呢,cpu空闲下来没人用,cpu利用率天然上不去了,更别谈高并发了。
好吧,你说加进程数,这跟apache的线程模型有什么区别,注意,别增长无谓的上下文切换。因此,在nginx里面,最忌讳阻塞的系统调用了。不要阻塞,那就非阻塞喽。非阻塞就是,事件没有准备好,立刻返回EAGAIN,告诉你,事件还没准备好呢,你慌什么,过会再来吧。
好吧,你过一会,再来检查一下事件,直到事件准备好了为止,在这期间,你就能够先去作其它事情,而后再来看看事件好了没。虽然不阻塞了,但你得不时地过来检查一下事件的状态,你能够作更多的事情了,但带来的开销也是不小的。