Web服务器(Nginx,Apache)和I/O模型

本文深入探讨了IO模型的概念,包括阻塞IO、非阻塞IO、IO复用、信号驱动IO和异步IO,以及它们在Apache和Nginx等Web服务器中的应用。详细分析了Apache和Nginx的工作模式、配置参数和性能特点。

IO模型

什么是io模型?

要弄清什么是IO,我们首先要从系统层面上去解释一个web请求的过程。先盗个图,如下:
在这里插入图片描述
描述: 1.客户发起情况到服务器网卡;
2.服务器网卡接受到请求后转交给内核处理;
3.内核根据请求对应的套接字,将请求交给工作在用户空间的Web服务器进程
4.Web服务器进程根据用户请求,向内核进行系统调用,申请获取相应资源(如index.html)
5.内核发现web服务器进程请求的是一个存放在硬盘上的资源,因此通过驱动程序连接磁盘
6.内核调度磁盘,获取需要的资源
7.内核将资源存放在自己的缓冲区中,并通知Web服务器进程
8.Web服务器进程通过系统调用取得资源,并将其复制到进程自己的缓冲区中
9.Web服务器进程形成响应,通过系统调用再次发给内核以响应用户请求
10.内核将响应发送至网卡
11.网卡发送响应给用户
这就是整个web请求的全过程。这个过程涉及到2个IO,一个就是客户端请求的网络I/O,另一个就是Web服务器请求资源的磁盘I/O。我们不妨把这个IO放大详谈。
在这里插入图片描述
如上图就是一个完整的IO调度。现在根据wait和copy阶段处理等待的机制不同,可将I/O动作分为如下五种模式: 阻塞I/O,非阻塞I/O,I/O复用(select和poll),信号(事件)驱动I/O(SIGIO),异步I/O(aio)。

同步 和异步?

同步:A请求B,B直接把结果返回给A。
异步:两种:
(1)轮训异步:A请求B,B暂时无法完成逻辑处理,并将等会儿处理好的结果传递给C,A就先继续执行其他操作,同时要轮训查询C是否拿到结果,如果拿到了,就把结果返回给A。
(2)通知异步:A请求B,B暂时无法完成逻辑处理,并将等会儿处理好的结果传递给C,A就先继续执行其他操作,当C是否拿到B处理好的结果后,将结果主动通知给A。

阻塞和非阻塞?

阻塞:执行一个操作等操作结束再返回。A请求B,B暂时无法完成逻辑处理,A就停止继续往下执行,等待B返回了结果后,继续往下执行。
非阻塞:执行一个操作不等操作结束,直接返回。A请求B,B暂时无法完成逻辑处理,但是B马上返回给A。

阻塞I/O:(过程全阻塞)

在这里插入图片描述
说明:应用程序调用一个IO函数,导致应用程序阻塞,[wait]数据准备好。 如果数据没有准备好,一直等待数据准备好了,从内核[copy]到用户空间的web服务器,IO函数返回成功指示。这个就是阻塞套接字。

非阻塞I/O:(wait非阻塞,copy阻塞)

在这里插入图片描述
说明:我们把一个套接口设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误。这样我们的I/O操作函数将不断 的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间,所有一般Web服务器都 不使用这种I/O模型。

I/O复用:(wait和copy两个阶段分别阻塞)

在这里插入图片描述
说明:I/O复用模型会用到select或poll函数或epoll函数(Linux2.6以后的内核开始支持),这两个函数也会使进程阻塞,但是和阻塞 I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。

信号驱动I/O(SIGIO):(wait不阻塞【信号驱动I/O,即通知】,copy阻塞)

在这里插入图片描述
说明:首先,我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。

异步I/O(AIO):(完全无阻塞方式)

在这里插入图片描述
说明:当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作。

阻塞的总结:

在这里插入图片描述
上面五种IO模型按照同步异步可以归纳几点:

  1. 同步I/O:包括(阻塞I/O,非阻塞I/O,I/O复用(select和poll))
  2. 异步I/O:包括(信号驱动I/O(SIGIO) (半异步),异步I/O(aio) (真正的异步))
  3. 异步 I/O 和 信号驱动I/O的区别:
    (1) 信号驱动 I/O 模式下,内核可以复制的时候通知给我们的应用程序发送SIGIO 消息。
    (2)异步 I/O 模式下,内核在所有的操作都已经被内核操作结束之后才会通知我们的应用程序。

Apache

工作原理(3种工作模式)

MPM(Multi-Processing Modules,多路处理模块)是Apache的核心组件之一,Apache通过MPM来使用操作系统的资源,对进程和线程池进行管理。Apache为了能够获得更好的运行性能,针对不同的平台 (Unix/Linux、Window)提供了不同的MPM,用户可以根据实际情况进行选择,其中最常使用的MPM有 prefork和worker两种。

(1) prefork:多进程,每个请求用一个进程响应,这个过程会用到select机制来通知。

•工作原理:
Prefork是非线程、预生成进程型MPM,会预先启动一些子进程,每个子进程一个时间只能处理一个请求,并且会根据并发请求数量动态生成更多子进程
•配置参数:
StartServices 服务器启动默认启动的子进程;
MinSpareServers 最小空闲进程数量;
MaxSpareServers 最大空闲进程数量;
MaxClients 最高的并发量;
ServerLimit 最大限制的并发量;
MaxRequestsPerChild 每个子进程默认最多处理多少个请求。当达到设定值时,这个进程就会被kill掉,重新生成一个新的进程(避免内存泄露等安全性问题,运行太久怕出一些bug,可能出现假死,或者占用太多内存等);

(2) worker:多线程,一个进程可以生成多个线程,每个线程响应一个请求,但通知机制还是select,可以接受更多的请求。

•工作原理:
Worker是线程化、多进程的MPM,每个进程可以生成多个线程,每个线程处理一个请求;不需要启用太多的子进程,每个进程能够拥有的线程数量是固定的。服务器会根据负载情况增加或减少进程数量。一个单独的控制进程(父进程)负责子进程的建立。每个子进程能够建立ThreadsPerChild数量的服务线程和一个监听线程,该监听线程监听接入请求并将其传递给服务线程处理和应答。
•配置参数:
StartServers 服务器启动时建立的子进程数,默认值是"3"。
MaxClients 允许同时服务的最大接入请求数量(最大线程数量)。任何超过MaxClients限制的请求都将进入等候队列,默认值是"400"。
MinSpareThreads 最小空闲线程数,默认值是"75"。
MaxSpareThreads 设置最大空闲线程数。默认值是"250"。
ThreadsPerChild 每个子进程建立的常驻的执行线程数。默认值是25
MaxRequestsPerChild 设置每个子进程在其生存期内允许处理的最大请求数量。

(3) event:基于异步I/O模型,一个进程或线程,每个进程或线程响应多个用户请求,它是基于事件驱动(也就是epoll机制)实现的。

•工作原理:
event是一个进程响应多个用户请求,利用callback机制,让套接字复用,请求过来后进程并不处理请求,而是直接交由其他机制来处理,通过epoll机 制来通知请求是否完成;在这个过程中,进程本身一直处于空闲状态,可以一直接收用户请求。可以实现一个进程程响应多个用户请求。支持持海量并发连接数,消 耗更少的资源。
•配置参数:
StartServers 3 服务启动时初始的进程数,默认3
MinSpareThreads 75 最小的空闲子进程数,默认75
MaxSpareThreads 250 最大的空闲子进程数,默认250
ThreadsPerChild 25 每个子进程产生的线程数量,默认是25
MaxRequestWorkers 400 限定同一时间内客户端最大接入的请求数量,默认是400
MaxConnectionsPerChild 0 每个子进程在其生命周期内允许最大的请求数量,如果请求总数已经达到这个数值,子进程将会结束,如果设置为0,子进程将永远不会结束。将该值设置为非0值,可以防止运行PHP导致的内存泄露。

特点分析

(1)稳定。
(2)模块丰富,可以满足多种需求。
(3)rewrite ,比nginx 的rewrite 强大 。

Nginx

工作原理

在工作方式上,Nginx分为单工作进程和多工作进程两种模式。Nginx在启动后,会有一个master进程和多个worker进程。

  • 单工作进程模式:
    此时系统中仅有一个进程,该进程既充当master进程的角色,也充当worker进程的角色。
  • 多工作进程模式:
    此时系统有且仅有一个master进程,至少有一个worker进程工作。master进程主要进行一些全局性的初始化工作和管理worker的工作;事件处理是在worker中进行的。Nginx默认为单工作进程模式。
master和worker解释?
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这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。我们可以看到,一个请求,完全由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进程中处理。

常用配置
########### 每个指令必须有分号结束。#################
#user administrator administrators;  #配置用户或者组,默认为nobody nobody。
#worker_processes 2;  #允许生成的进程数,默认为1
#pid /nginx/pid/nginx.pid;   #指定nginx进程运行文件存放地址
error_log log/error.log debug;  #制定日志路径,级别。这个设置可以放入全局块,http块,server块,级别以此为:debug|info|notice|warn|error|crit|alert|emerg
events {
    accept_mutex on;   #设置网路连接序列化,防止惊群现象发生,默认为on
    multi_accept on;  #设置一个进程是否同时接受多个网络连接,默认为off
    #use epoll;      #事件驱动模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport
    worker_connections  1024;    #最大连接数,默认为512
}
http {
    include       mime.types;   #文件扩展名与文件类型映射表
    default_type  application/octet-stream; #默认文件类型,默认为text/plain
    #access_log off; #取消服务日志    
    log_format myFormat '$remote_addr$remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for'; #自定义格式
    access_log log/access.log myFormat;  #combined为日志格式的默认值
    sendfile on;   #允许sendfile方式传输文件,默认为off,可以在http块,server块,location块。
    sendfile_max_chunk 100k;  #每个进程每次调用传输数量不能大于设定的值,默认为0,即不设上限。
    keepalive_timeout 65;  #连接超时时间,默认为75s,可以在http,server,location块。

    upstream mysvr {   
      server 127.0.0.1:7878;
      server 192.168.10.121:3333 backup;  #热备
    }
    error_page 404 https://www.baidu.com; #错误页
    server {
        keepalive_requests 120; #单连接请求上限次数。
        listen       4545;   #监听端口
        server_name  127.0.0.1;   #监听地址       
        location  ~*^.+$ {       #请求的url过滤,正则匹配,~为区分大小写,~*为不区分大小写。
           #root path;  #根目录
           #index vv.txt;  #设置默认页
           proxy_pass  http://mysvr;  #请求转向mysvr 定义的服务器列表
           deny 127.0.0.1;  #拒绝的ip
           allow 172.18.5.54; #允许的ip           
        } 
    }
}
特点分析

(1)轻量级,同样起web 服务,比apache 占用更少的内存及资源
(2)抗并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能
(3)高度模块化的设计,编写模块相对简单
(4)社区活跃,各种高性能模块出品迅速啊

Nginx和Apache比较

1、作为 Web 服务器:相比 Apache,Nginx 使用更少的资源,支持更多的并发连接,体现更高的效率,这点使 Nginx 尤其受到虚拟主机提供商的欢迎。在高连接并发的情况下,Nginx是Apache服务器不错的替代品: Nginx在美国是做虚拟主机生意的老板们经常选择的软件平台之一. 能够支持高达 50,000 个并发连接数的响应, 感谢Nginx为我们选择了 epoll and kqueue 作为开发模型. Nginx作为负载均衡服务器: Nginx 既可以在内部直接支持 Rails 和 PHP 程序对外进行服务, 也可以支持作为 HTTP代理 服务器对外进行服务. Nginx采用C进行编写, 不论是系统资源开销还是CPU使用效率都比 Perlbal 要好很多. 作为邮件代理服务器: Nginx 同时也是一个非常优秀的邮件代理服务器(最早开发这个产品的目的之一也是作为邮件代理服务器), Last.fm 描述了成功并且美妙的使用经验. Nginx 是一个安装非常的简单 , 配置文件非常简洁(还能够支持perl语法), Bugs 非常少的服务器: Nginx 启动特别容易, 并且几乎可以做到7*24不间断运行,即使运行数个月也不需要重新启动. 你还能够不间断服务的情况下进行软件版本的升级 .
2、Nginx 配置简洁, Apache 复杂
Nginx 静态处理性能比 Apache 高 3倍以上
Apache 对 PHP 支持比较简单,Nginx 需要配合其他后端用
Apache 的组件比 Nginx 多
现在 Nginx 才是 Web 服务器的首选
3、最核心的区别在于apache是同步多进程模型,一个连接对应一个进程;nginx是异步的,多个连接(万级别)可以对应一个进程

4、nginx处理静态文件好,耗费内存少.但无疑apache仍然是目前的主流,有很多丰富的特性.所以还需要搭配着来.当然如果能确定nginx就适合需求,那么使用nginx会是更经济的方式.

5、从个人过往的使用情况来看,nginx的负载能力比apache高很多。最新的服务器也改用nginx了。而且nginx改完配置能-t测试一下配置有没有问题,apache重启的时候发现配置出错了,会很崩溃,改的时候都会非常小心翼翼现在看有好多集群站,前端nginx抗并发,后端apache集群,配合的也不错。

6、nginx处理动态请求是鸡肋,一般动态请求要apache去做,nginx只适合静态和反向。

7、從我個人的經驗來看,nginx是很不錯的前端服務器,負載性能很好,在老奔上開nginx,用webbench模擬10000個靜態文件請求毫不吃力。apache對php等語言的支持很好,此外apache有強大的支持網路,發展時間相對nginx更久,bug少但是apache有先天不支持多核心處理負載雞肋的缺點,建議使用nginx做前端,後端用apache。大型網站建議用nginx自代的集群功能

8、Nginx优于apache的主要两点:1.Nginx本身就是一个反向代理服务器 2.Nginx支持7层负载均衡;其他的当然,Nginx可能会比apache支持更高的并发,但是根据NetCraft的统计,2011年4月的统计数据,Apache依然占有62.71%,而Nginx是7.35%,因此总得来说,Aapche依然是大部分公司的首先,因为其成熟的技术和开发社区已经也是非常不错的性能。

9、你对web server的需求决定你的选择。大部分情况下nginx都优于APACHE,比如说静态文件处理、PHP-CGI的支持、反向代理功能、前端Cache、维持连接等等。在Apache+PHP(prefork)模式下,如果PHP处理慢或者前端压力很大的情况下,很容易出现Apache进程数飙升,从而拒绝服务的现象。

10、可以看一下nginx lua模块:https://github.com/chaoslaw...apache比nginx多的模块,可直接用lua实现apache是最流行的,why?大多数人懒得更新到nginx或者学新事物

11、对于nginx,我喜欢它配置文件写的很简洁,正则配置让很多事情变得简单运行效率高,占用资源少,代理功能强大,很适合做前端响应服务器

12、Apache在处理动态有优势,Nginx并发性比较好,CPU内存占用低,如果rewrite频繁,那还是Apache吧

Nginx+Apache实现反向代理负载均衡(参考席飞剑的博客)

反向代理
什么是反向代理?

使用代理服务器可以将请求转发给内部的Web服务器,使用这种加速模式显然可以提升静态网页的访问速度。因此也可以考虑使用这种技术,让代理服务器将请求均匀转发给多台内部Web服务器之一上,从而达到负载均衡的目的。这种代理方式与普通的代理方式有所不同,标准代理方式是客户使用代理访问多个外部Web 服务器,而这种代理方式是多个客户使用它访问内部Web服务器,因此也被称为反向代理模式。【详情笔者前几天写过一篇专门介绍代理服务器的文章,移步到:https://blog.youkuaiyun.com/jeremy_ke/article/details/84938210】

实现反向代理负载均衡?

1)针对不同请求的负载均衡:
a. 在最简单地构建反向代理的时候(nginx仅仅处理静态不处理动态内容,动态内容交给后台的apache server来处理),具体的设置为:在nginx.conf中修改:

location ~ \.php$ {
	proxy_pass 158.37.70.143:80;
}

这样当客户端访问localhost:8080/index.html的时候,前端的nginx会自动进行响应;当用户访问localhost:8080/test.php的时候(这个时候nginx目录下根本就没有该文件),但是通过上面的设置location ~ .php$(表示正则表达式匹配以.php结尾的文件,详情参看location是如何定义和匹配的),nginx服务器会自动pass给158.37.70.143的apache服务器了。该服务器下的test.php就会被自动解析,然后将html的 结果页面返回给nginx,然后nginx进行显示(如果nginx使用memcached模块或者squid还可以支持缓存),输出结果为打印 server2。
b. 我们现在对如上例子进行扩展,使其支持如上的两台服务器。设置nginx.conf的server模块部分,将对应部分修改为:

location ^~ /phpMyAdmin/ {
	proxy_pass 127.0.0.1:80;
}
location ~ \.php$ {
	proxy_pass 158.37.70.143:80;
}

上面第一个部分location ^~ /phpMyAdmin/,表示不使用正则表达式匹配(^~),而是直接匹配,也就是如果客户端访问的URL是以http://localhost:8080/phpMyAdmin/开头的话(本地的nginx目录下根本没有phpMyAdmin目录),nginx会自动pass到127.0.0.1:80的Apache服务器,该服务器对phpMyAdmin目录下的页面进行解析,然后将结果发送给nginx,后者显示;如果客户端访问URL是http://localhost/test.php的话,则会被pass到158.37.70.143:80的apache进行处理。

2)访问同一页面的负载均衡:
因此重新配置nginx.conf为:
首先在nginx的配置文件nginx.conf的http模块中添加,服务器集群server cluster(我们这里是两台)的定义:

upstream myCluster{
	server 127.0.0.1:80 weight=5 max_fails=2 fail_timeout=30s;;
	server 158.37.70.143:80 weight=1 max_fails=2 fail_timeout=30s;
}

表示这个server cluster包含2台服务器
然后在server模块中定义,负载均衡:

location ~ \.php$ {
	proxy_pass http://myCluster; #这里的名字和上面的cluster的名字相同
	proxy_redirect off;
	proxy_set_header Host $host;
	proxy_set_header X-Real-IP $remote_addr;
	proxy_set_header X-Forwarded-For$proxy_add_x_forwarded_for;
}
总结

两种均衡:
1)可以在nginx中定义访问不同的内容,代理到不同的后台server;如上例子中的访问phpMyAdmin目录代理到第一台server上;访问test.php代理到第二台server上;
2)可以在nginx中定义访问同一页面,均衡(当然如果服务器性能不同可以定义权重来均衡)地代理到不同的后台server上。如上的例子访问test.php页面,会均衡地代理到server1或者server2上。
实际应用中,server1和server2上分别保留相同的app程序和数据,需要考虑两者的数据同步。
简单总结:
主要通过以下手段实现负载均衡:
location:定义需要匹配的请求(文件或目录,可以使用正则或不使用正则直接匹配)
proxy_pass:定义需要将请求proxy到地方(某台主机或者某个upstream cluser集群)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值