Linux运维 第三阶段 (十五)理解LNMP

  1. 云栖社区>
  2. 博客>
  3. 正文

Linux运维 第三阶段 (十五)理解LNMP

技术小阿哥 2017-11-27 15:01:00 浏览755
展开阅读全文

一、相关概念:

nginxengine-Xnginx.orgperformancestabilityrich featuresimple configurationlow  resource consumption):

HTTP-server(轻量级、高性能WEB服务器)

reverse proxy(反向代理服务器,mail proxy servertcp proxy server,任何一种proxy必须要能精确理解某种协议的内容及操作,例如mysql-proxy能理解mysql的各种SQL语句从而实现rw-splitting

注:nginx能理解三种协议(httpmailsmtppop3imap);tcp);早期nginx就是用来做reverse  proxy;它起步就是用来解决C10K problemconnection 10K,连接数达到一定程度会产生诸多问题,网络服务在处理数以万计的客户端连接时,往往会出现效率低下甚至完全瘫痪)

 

 

1、单进程模型(阻塞模型,如第一个用户的请求与server建立连接后,当有第二个用户向服务器请求就要等待,等第一个用户断开后才能连接)

 

2、多进程模型:

httpdprefork模型,httpd是主进程,当有用户请求,httpd会派生出子进程予以响应,主进程管理这些子进程(创建、销毁等);

每个进程响应1个用户请求,进程是重量级的资源实体,是CPU的执行单位(调度的单位),CPU要给它分配时间片、内存资源等,开销大,内核在某一时刻还要调度这些进程,进程很多时会带来大量的进程切换CScontent switch,进程很多,但CPU有限,由内核处理进程切换),某一时刻1CPU只处理1个进程(1个用户请求),要让每个用户都觉得自己处于活动状态,进程会轮流在CPU上执行,每个进程仅占用CPU很短的时间;

如有1234四个进程,内核通过自己的内部程序(进程调度程序),观测每个进程的属性,发现下一次轮到了3号进程,将3号进程唤醒并将其载入到CPU的寄存器,将CPU的指针指向3号进程的地址空间,然后内核自己退出并转为睡眠状态,3号进程运行结束(如只有5ms),内核将3号进程退出转为睡眠,再将4号进程载入到CPU,指针指向4号进程地址空间,每一次进程切换,内核就要占用CPU,就要花费一些时间,若切换频繁会有大量时间浪费在进程切换上,进程切换并没有帮助用户响应内容,在内核中消耗的时间都是额外的内核开销(sys),这是多进程模型的缺陷(每个进程是独立的运行单位);

每个进程响应一个用户请求,若请求的是主页面,进程调用内核,内核从硬盘中取得数据返回给该进程(IO调用,在这过程中进程处于睡眠状态,不可中断睡眠,uninterruptable,阻塞状态,IO产生的阻塞,若强行中断睡眠,这时IO调用还未结束,内核还未返回数据,将其唤醒也无法响应用户请求),内核从硬盘中获取数据是先载入到内存的内核空间(内存分内核空间和用户空间),再将载入的数据复制到进程的地址空间,这时进程才能访问到;

数据如何从硬盘载入到内存?内存是分页的(buffer/cache,有些内存不分页那是堆内存),一次仅读取一个页面的数据,若每个页框大小为4K,每个磁盘块也是4K(磁盘块分1K2K4K),每个文件占用1个或多个磁盘块,每次读一个磁盘块填满页面,若磁盘块是2K则要读取2个磁盘块填满1个页面,数据加载结束才开始复制(数据从内存的内核空间-->进程的地址空间),如果是访问一个文件的一部分这是另外一种机制;

若一个文件占据10个内存页,每个内存页是4K,每个磁盘块是4K,这要涉及到10个磁盘块,每次要读哪个块,读完后数据要放至内存的哪个地址中(如何选择一个内存页面从哪分配),内核操作文件系统FSFS对应磁盘块,对应的是哪个块这由驱动程序管理,驱动程序运行是内核的功能,内核运行是要有CPU参与的,为加载一个文件内核会很繁忙,为尽可能降低内核对CPU的占用,让当前主机的CPU尽可能运行程序,现在硬件都具有DMA机制(direct memory access),在DMA下,内核在其内核空间(内存的缓冲区)中找一段连续的内存空间,将起始地址给DMA芯片(有访问内存的能力,有访问硬盘数据的能力,有数据传输占据系统总线的能力,有控制系统总线、数据总线的能力),交由DMA控制,由DMA指挥着将数据从硬盘加载至内存中,数据加载完成DMA会产生一个中断,强行告知CPU已完成,CPU这时将当下正在运行的进程中断并将其转为睡眠状态,内核将自己空间中加载好的数据复制到进程的地址空间,进程再通过与网卡交互(通过PCI总线与网卡联系)通过网线返回给用户,若多个进程处理的都是一个主页面的内容,每一个进程IO-->内核调用(IO调用)-->磁盘空间-->内核空间-->进程空间,内核有加速机制,当第一个进程读完后会在内存的内核空间中缓存下来,第二个进程访问的是同样内容直接从内核空间复制

若主页2M4个进程访问同一主页,在用户空间占据的内存大小4*2M=8M,本来一个主页面只需占据内存2M大小,但实际占用了8M内存(多进程模型的缺陷之一)

多进程模型中,系统调用结束(内核准备好数据),当前进程与client的连接处于等待状态,连接在tcp/ip协议栈是由内核管理的,内核怎么通知进程程哪个IO完成了?采用select()机制(内核要输出一个数据结构,假设这个数据结构是一排灯,第123的灯对应第123的进程,哪个灯亮了就说明哪个进程的数据准备好了,当第2个灯亮起时第二个进程就知道它要的数据准备好了,每一次内核准备好数据都要向用户空间输出整个数据结构,不输出进程是无法理解的,这种每次都逐个扫描IO事件描述符,将它从内核空间输出到用户空间的方式叫selectselect最多支持1024个灯泡,1024个进程,多余1024的进程只能在一边等待,早期的系统调用采用这种方式)

 

多进程模型总结:

每个进程响应一个请求;

进程量大,进程切换次数过多,会消耗大量资源,若是C10K1W个请求connection,可能无法应付;

每个进程的地址空间是独立的,很多空间是重复的数据,所以内存利用率低

wKiom1aCN9WiLgkIAABlS7OMhAM134.jpg

注:上图是进程在内存的分布

注:下图是多线程在内存的分布

wKiom1aCN-az1RWxAABJkX0ozKY075.jpg

 

3、线程thread

lwplight weight process轻量级的进程,是进程内部的子运行单位;linux不支持原生态的线程(winsolaris支持),linux把线程当作进程对待,在管理上略有区别,在linux上实现线程功能需借助glibc提供的线程库来完成(以完成线程的创建、撤销等管理),linux上提供的线程库有多种(有内核自带的,也有redhat提供的),不同的线程库对系统资源的占用是不一样的;

所以线程是运行在进程内部的子单位,在进程内部可以启动多个执行流(线程是并发执行流),

程序=指令+数据;单个进程中指令在CPU上运行,是将指令从上至下按控制流程一条一条执行(仅一条流水线),用到数据时到堆或栈中去取(堆空间中的数据通常是打开的文件、栈空间中的数据存放变量),每一个进程的数据都是自己管理的,如果用到循环也是在局部循环;有了线程后,执行流是并发的是多个的,所有执行流共享这个进程的数据区域(共享全局变量、堆,打开的文件等,但栈是不能共享的,栈中各层函数帧代表着一条执行线索,一个线程是一条执行线索,每个线程独占一个栈,这些栈必须在所属进程的内存空间中),每一个执行流都是一个独立的实体,所以线程就是运行在同一个进程内部(进程的内存空间)共享了很多资源的运行的实体;

不同用户访问同一个文件,多线程比多进程性能要好很多:当第一个用户请求进来,server用一个线程响应,若这个线程要加载文件,经系统调用-->内核的内存空间-->复制到进程的内存空间,响应给前端,当第二个用户请求访问同样的文件,这个进程的内存空间已有数据,则直接从进程的内存空间返回(不需经系统调用到内核空间再复制到进程空间这个过程);

若连接请求很多,如有10K个连接,若由一个进程管理,内部很复杂,首先线程间要切换,线程过多,导致大量切换,会带来线程抖动(很严重,线程刚占用CPU啥事没干就切换出去了),切换本身靠内核来完成,也仍会占用过多资源;还要解决彼此间资源争用就超出我们预料,例如第一个线程要执行写操作,第二个线程若要读这时就要等待,等待写完成才能读(读是共享的,但读写不能共享),若要等待那CPU就要浪费给这个线程了,若进程切换,线程来读读不了,就要等

注:等有忙等和闲等;忙等,用的是自旋锁spin lock,看到第一个线程在写,读不到,若马上切换,要是刚切换出去,数据准备好了可读了,那就白切换了,再多等一会,CPU是不允许空闲的,CPU只要一供电,时钟频率就一直在那,无论有无程序执行CPU都一直在转,只不过计算能力白白流逝了,这就要运行指令,如每隔1ms来看下,这个线程始终占用着CPU不退出,时间未耗完事情没干完就不走,这叫忙等;闲等,切换出去,时间没耗完,事情没干完自愿退出

尽管多个线程可并行执行,但若主机仅一颗CPU,线程优势几乎发挥不出来,若是多颗CPU,多个线程在多个CPU上同时执行,而且程序在编写时采用并行编程技术(在一个进程内部或一个线程内部可有多个执行流)可实现更好的分配系统资源,如10CPU处理1W个连接,每个CPU只需1000个切换

在线程模型下,系统调用结束(内核准备好数据),内核如何通知线程它要的数据已准备好了?仍旧使用select模型(在linux中线程也是进程,只不过是轻量级进程,一个进程管理多个线程,IO是由进程管理,只不过减少了进程的数量,相应IO文件描述符的数量也少了,可响应更多的用户请求,这背后IO机制并没变,只不过多个线程可共享资源,至少提高了内存使用效率,所以在C10K上仍有问题,这时就要用到一个线程响应多个请求

线程总结:

每个线程响应一个请求,线程仍然要切换,但切换是轻量级的;

同一个进程的线程可以共享进程的诸多资源(如打开的文件);

对内存的需求较之进程略有下降;

快速切换时会带来线程抖动

 

多进程多线程模型:

N个进程,每个进程中有100个线程,使得资源争用不是很严重,例如有8CPU,单独拿出一颗用来运行内核和其它进程,另7颗独立空闲出来,隔离出来仅让web服务使用,web服务启动时只启动7个进程,每一个进程绑定一个CPU(一颗CPU对应一个进程,这将没有进程切换,但进程内部的线程切换是不可避免的),这样速度就会快很多,系统提供这种功能但要手动完成进程与CPU绑定

 

一个线程响应多个请求(多线程<-->N个请求):

一个线程要处理多个IO,若一个用户请求了主页,建立连接,系统调用结束后返回数据,如何唤醒当前建立的连接,怎么通知线程?event-driven IO(事件驱动IO

一旦有IO,线程是不能阻塞的(若阻塞,这个线程就无法完成其它用户请求,这与我们的初衷相悖(一个线程响应多个请求)),例如去饭店吃饭(顾客是用户请求,收银员是线程或者进程,后厨是内核):

阻塞(10个顾客,一个收银员,第一个顾客报饭给收银员-->收银员告诉后厨-->后厨做好后-->收银员-->顾客,这样第一顾客接待完后,第二个顾客才能建立连接;为提高效率,增加收银员的数量,同时三个收银员,多队列阻塞,同样第一个顾客拿到饭,第二个顾客才能连接,才能向收银员报饭)

非阻塞(第一个顾客向收银员报上饭后在一旁等着,营业员告知后厨,之后就与后厨暂时失去联系,继续接待下个顾客,第二个顾客也在一旁等着,后厨做好饭后在大屏幕上显示用这种方式通知(第一种通知方式,每做好一个将做好的和没做好的全部显示;第二种通知方式,只通知做好的,这样内核耗费资源就少了),若通知了一遍某个顾客没看到,要么就不管了,要么再通知直到顾客拿到饭,这种属通知机制,就不需用户始终盯着收银员)

同步(第一个顾客向收银员报上饭,收银员立即告知后厨,来自前端的直接交给后端)

异步(每一个顾客向收银员报上饭,收银员汇总后一并告知后厨)

多个顾客同时请求,又都能让顾客知道请求的内容是否就绪这称作IO复用机制

以上举例不精确,但有助于理解

 

注:drbd的同步是存储到对方磁盘后才返回成功,drbd的异步是发至自己的tcp/ip协议栈就返回成功;mysql的同步是主将二进制日志传至一个从,从应用后才返回成功,mysql异步是只要写入本地数据库即返回成功

从硬件方面讲,异步是双方不需同步时钟信号,同步是双方要同步时钟信号(数据传输时是用01信号表示的,如0V表示低电平发送信号是00.5V表示高电平代表发送信号是1,若有很长一段都是高电平,如何区分是多少个1,若时钟频率是1GHZ,则1/1GHZ这就是一个长度,是11,按时间来判断是多少个1

IP报文的传送是异步的(把数据报文丢上去就不管了,至于什么时候收到,什么时候响应不关心),发送方发送出去就不管了(发送方发送前封装报文时在数据的基础上添加协议报文),无论什么时候到达接收方,接收方接收下来自己拆包就知道里面是什么信号,IP报文传输最终都要转为最底层的同步或异步的物理通信,同步/异步这种机制已应用在比物理连接方式更高一级的通信上,用来判定通信双方在实现通信的时候,是不是要跟对方建立连接关系,并等待对方能将数据完完整整的接收下来(等待对方响应)


AIOasynchronous IO,异步IO

一个thread维持多个用户,在某一时刻用户的请求尚未满足,这个用户要处于等待状态,当为这个用户请求的数据准备完成之后,与前端用户的连接再次重新建立起来,并通知用户可以取数据,在网络上完成多路IO的唤醒,在非阻塞模型下通过内核中的多路IO复用机制来完成(每一个请求在本机就是一堆文件描述符,一堆套接字的文件,当为某一个用户请求的数据准备好了,必须要将这个文件描述符激活,并让client过来取数据)

 

4、看清以下这五种模型:

wKioL1aCOPnjAbYXAAAknmW95wY800.jpg

IO动作如何执行?(进程无法直接操作IO设备,必须通过系统调用请求kernel来协助完成IO动作;内核会为每个IO设备维护一个buffer(内核的内存空间);对于输入而言,等待wait数据输入至buffer需要时间,而从buffer复制copy数据到进程也需要时间;根据等待模式的不同,IO动作可分为五种模式)

五种模式:

blocking IOblocked all the way(阻塞IOsynchronous-blocking

nonblocking IOif no datain buffer,immediate returns ewouldblock(非阻塞IOsynchronous-nonblocking

IO multiplexingselect|poll):blocked separately in wait and copyIO复用,asynchronous-blocking

signal driven IOSIGIOevent drivenIO):nonblocked in wait but blocked in copysignaled when IO canbe initiated)(只要一个进程处理多个IO时(处理多个文件描述符)必须得复用,甚至一个进程响应一个请求时也要复用(与用户交互数据(接受键盘输入的数据,交互式IO),处理网络连接(网络IO))

asynchronous IOAIOasynchronous-nonblocking):nonblocked all the waysignaled when IO is complete)(异步IO,不导致请求进程阻塞;synchronousIO,引起请求进程阻塞,直到IO完成)

注:只要是synchronous,都通过read/write系统调用来完成IO

wKiom1aCOPKhVgdDAAApelRbK9Y816.jpg

如下图:synchronous-blocking,整个过程都是阻塞状态,是闲等

processthread向内核发起系统调用,内核将数据从磁盘复制到kernel’s buffer processthread一直监控着内核缓冲区

数据从kernel’s buffer复制到进程地址空间,这段时间内不能连入其它请求

wKioL1aCOSixf52yAAAgcJ-Z1EA746.jpg

如下图:synchronous-nonblocking,是忙等(不断询问),此模型性能差,几乎不用

发起系统调用后是非阻塞状态,可以连入其它请求,但同时还要不断检查之前的IO返回状态,若再次连入的请求是打开其它文件,又要重新发起新的系统调用,进程又要发起新的IO请求,这同时就要不停地检查两个IO状态,第一个IO完成了,阻塞,复制数据到进程地址空间,再检查第二个IO,这个进程会很忙,若是很多个,性能会很差,所以这种模型几乎不用

wKiom1aCOR7R46tiAAAnEdtYHh4141.jpg

如下图:asynchronous-blockingIO multiplexing),分两段,两段都阻塞,第二段是由进程主导数据复制(进程再次发起系统调用)

可实现多个进程响应多个请求,如httpd,主进程接收用户请求,接进来分配一个子进程来处理这个请求,一个子进程只负责完成这一个IO,完成多个子进程响应多个请求,也可以一个进程响应多个请求,但性能会很差

select()函数,进程要知道内核准备的数据是否到kernel’sbuffer中,就要监控着缓冲区,在缓冲区中会打开一个文件描述符,进程需要read()这个缓冲区,返回一个状态,进程就知道它请求的数据是否准备好,若只考虑一个进程打开了一个文件(在真实环境中,一个用户请求了一个页面,这个页面由很多资源组成,每个资源就得一个进程来完成),每个子进程响应一个用户请求,每个子进程都打开一个文件,子进程是属于主进程的,主进程自身对select而言最多只支持1024个请求,也就是最多1024个子进程响应了(同时1024个并发连接)

要想实现multiplexing就要用selectpoll这种机制(pollselect的工作机制一样,但无文件数限制,由于工作机制一样,即使没有设置上限,性能与select差不多,select之所以有限制,说明它知道超过这个数性能会很差)

wKioL1aCOUripQUBAAApNjl1dNo389.jpg

如下图:event-driven,解决了IO multiplexingasynchronous-blocking)前半段的阻塞和synchronous-nonblocking前半段一直询问。刚开始在进程发起请求时,留有回调机制(可理解为给kernel留了联系方式),内核在数据准备完成后向进程通知(水平触发和边缘触发;水平触发,每隔一段时间通知一次,多次通知直到拿走数据;边缘触发,仅通知一次,不管你拿没拿到),边缘触发性能要好

一个线程响应多个请求,一个线程内部维护的有多个连接

一个线程接受用户请求,向内核发起系统调用,给内核留了联系方式,再接受第二个用户请求,发起系统调用,再留一个联系方式,依次接受第三个请求……,当内核准备好第一个的数据,使用回调机制告诉第一个联系方式数据准备完成,然后阻塞(第二段阻塞)等数据从内核空间复制到进程空间响应给用户

若内核将自己的内核空间共享给用户空间,连复制都不用了,这将更快,这是内存共享,注意不是内存映射(mmapmemory map指的是数据从磁盘到内核的内存空间,要流动过去,这要复制,内存映射是复制就不用了,直接将要打开的文件在磁盘中的数据结构映射到内存,在两者之间建立起关联关系,将磁盘中的文件与内存的一块区域建立关联关系,当访问时直接取数据即可)

event-driven模型使用selectpoll无法完成,它使用的是epolllinux上叫epoll),在solaris上叫/dev/poll,在FreeBSD上叫kqueue

wKiom1aCOUujSq0xAAAm0EohyAo090.jpg

如下图:asynchronous-nonblocking,在数据复制到进程的内存空间后才给进程通知(给进程信号),而event-driven是在内核中通知的

nginx在磁盘IO上支持此模型(网络IO不是)

wKiom1aCOWCT9F0oAAAdtS1VyT0902.jpg

wKioL1aCOY-Cyr15AACF0CkixaI578.jpg

 

5nginx特性:

file AIO(文件或磁盘IO,基于异步IO),direct IO(对于正常的系统调用read/write流程是,read(数据从硬盘-->内核空间-->用户空间),write(数据从用户空间-->复制到内核空间的页缓存write直接返回-->OS会在恰当的时间写入磁盘,这是buffered IO),对于自缓存的应用程序来说,buffered IO不是好的选择,因此出现direct IO,不经内核空间直接写磁盘,必须阻塞,所以通常direct IOAIO会一同出现);

asynchronous(异步通信);

event-driven edge trigger(事件驱动边缘触发);

作为web-server处理静态文件,要依赖其它模块才能提供动态功能;索引文件(主页面)及自动索引;打开文件描述符缓存(nginx可以缓存源文件和文件描述符(路径));

使用缓存加速反向代理;简单负载均衡及容错(实现后端real serverhealth_check,一旦发现后端server故障直接剔除,类似keepalived,对于容错要安装第三方模块);

注:淘宝在nginx代码的基础上对其作了诸多扩充,直接将很多第三方模块(插件)整合进了nginx中,并对其作出大量改进,在nginx上作了第二次发行版Tengine,淘宝也将不断改进的代码反馈给nginx官方,有些代码被nginx吸纳并融入到后续的发行版中

远程fastcgiphpnginx不支持模块方式使用php,只有fastcgi这种方式);uwsgi(用来支持pythonweb框架,比fastcgi高效,不是php);scgimemcached服务的缓存加速支持(nginx已原生态支持memcachednginx自身作为代理它也可以提供缓存,只不过它默认是缓存在磁盘上的,但它能在内存中缓存打开的文件描述符);

注:缓存方面:varnishsquidnginxhttpdvarnish(是专业的缓存server,它在设计时考虑的是现代的计算机体系结构,缓存时可优先选择内存缓存,可在内存中实现对数据结构的创建、回收、销毁等,它引入很好的算法,在专业级别讲,varnish有更好的特性);squid(由于比较早期,它在开发时考虑的是原先计算机结构);varnishsquid的关系相当于nginxhttpd的关系;nginxdisk);httpddiskmemory

模块化架构设计;过滤器包括gzip压缩、ranges支持、chunked响应、XSLTSSI及图像缩放

注:SSIserver side include,服务器端包含,可实现将一个页面,某些内容作成动态,某些内容作成静态);图像缩放(节约网络带宽、提高用户体验)

支持SSLTLS SNI

基于主机名及IP的虚拟主机;

keepalivepipelined连接支持;

重新加载配置及在线升级时,不需要中断正在处理的请求(架构设计先进性的体现,热部署、平滑升级);

自定义访问日志格式,带缓存的日志写操作,快速日志轮转(若用户访问的记录马上写到磁盘中,会影响系统性能,这个功能使日志先在内存中缓存,过段时间再flush到磁盘上);

3XX-5XX错误代码重定向;

重写rewrite模块,使用正则表达式改变URI(尤其作为reverse proxy serverrewrite是个重要的功能);

根据client地址执行不同的功能(例如根据client的浏览器类型响应不同的页面内容,若用户使用的是手机,则返回wap页面,可节省流量等);

基于客户端IP地址和HTTP基本认证机制的访问控制;

支持验证HTTP referer(防盗链机制,ngx_http_referer_module允许拦截referer请求头中含有非法值的请求,referer指通过哪些链接进的网站(用户访问网站,要么在浏览器上输入,要么通过链接进入),正是因为referer的存在,很多网站可盗链我们网站,例如:有人在我们网站上传了一堆图片,在他的网站通过链接指向我们的服务器,这样我们就给他提供页面,消耗我们的流量,过段时间若图片过多我们的网站就有可能打不开了)

支持putdeletemkcolcopymove等方法;

支持flv流和mp4流(边下载边播放);

速度限制(限制同一client连接的带宽);

来自同一地址的同时连接数和请求数限制;

支持的IO框架机制,epolllinux),/dev/pollsolaris),kqueueFreeBSD),编程时基于这个框架就支持asynchronousevent-driven,另还有event portsselect/poll,这两种最不可取,当前三个不支持时再使用这两个;

支持sendfilesendfile64sendfileV(尽可能避免数据拷贝操作,用户请求进来,请求的是静态页面,页面内容在磁盘分区中某个FS上,内核处理通过80port到某个worker进程上,通过建立连接,请求分析,发现用户请求的是静态页面,系统调用,内核为其准备缓冲,内核将数据加载至缓冲区,将数据再复制到进程地址空间,worker进程再将数据封装成一个响应报文(http首部封装实际是在内核中完成的),将报文再传至内核,内核封装tcp首部、ip首部,再响应给用户,可见数据是从硬盘-->内核空间-->用户空间-->内核空间,若封装时只在内核中完成,不用到用户空间,封装完后只告诉worker已替你响应过去,这样就避免了两次复制(内核空间-->用户空间,用户空间-->内核空间),内核任何时候与进程交互都是复制,除非内存共享,当并发连接很多时sendfile功能若关闭的话将严重影响系统性能,由此sendfile实现数据从硬盘到内核空间直接响应给clientsendfile只支持小文件,sendfile64支持大文件);

支持accept_filters(连接过滤器,只接受有限的连接),tcp_defer_accept

10K个非活跃的HTTP keepalive连接仅占用2.5M内存(事件驱动机制,只扫描活动连接,对于非活动连接不作管理,nginx只需很少内存就能为一个连接维持一个文件描述符);

 

 

6nginx的基本工作框架(一个主进程和多个工作进程,工作进程以非特权用户运行):

master(主进程,监控worker进程(或叫worker线程)启动是否够数目及运行状况是否正常等,以管理员身份启动(web服务默认80port1023以下端口只有管理员才能使用))

workerworker进程真正负责响应用户请求,是master的子进程,由master负责启动,以普通用户身份运行(系统安全性得到提升))

cache loader(与缓存相关的进程)

nginx高度模块化(masterworker进程处理web应用非常简单,像额外的其它功能,例如sslflvgzipfastcgi等都不是由nginx自己提供,而是由额外的模块提供,在nginx内部会调用这些模块,用到哪个装载哪个,就连它自己的基本功能也是模块化的,如接入的请求是getput、请求哪些内容等,worker不负责,转交给模块,这些模块以流水线的方式工作,如第一个模块分析头部、第二个模块取得数据、第三个创建响应等等,每一个请求所请求的内容会不一样(如有的是静态内容,要求压缩再响应;有的是动态内容,要调用fastcgi模块),所以响应时所串连的模块也会不一样,每个请求接入,worker一分析,要用到几个模块,这几个模块就组合成流水线,各就各位,随时准备响应)

master负责装载主配置文件,若改动了配置文件,由master分析有无syntax error,就算重新装载有语法错误也不会影响worker进程,装载成功后,它也不会让启动的worker使用这个新装载的配置文件,让这些已建立的连接仍然使用老旧配置,当某一worker上的连接都退出了,把这个worker挂掉,再重新启动一新worker,新worker响应新请求,新worker就用了新的配置,所以之前的连接与新连接是没影响的,varnish的处理机制与nginx在设计哲学上是相通的

 

master主进程的工作内容:读取并验证配置信息(核心功能);启动、中止及维护worker进程的个数(核心功能);创建、绑定并关闭套接字;无须中止服务而重新配置工作特性;控制非中断式程序升级,启用新的二进制程序并在需要时回滚至老版本(热部署、平滑升级);重新打开日志文件,实现日志滚动;编译嵌入式perl脚本

 

worker进程主要完成的工作:接收、传入并处理来自客户端的连接;提供反向代理及过滤功能;nginx任何能完成的其它任务

 

cache loader进程的主要任务:检查缓存存储中的缓存对象;使用缓存元数据建立的内存数据库

 

cache manager进程完成的任务:缓存的失效及过期检验


7web架构方面:

nginx+php-fpmnginx只能通过fastcgi这种方式使用php

nginx响应静态页面,当发现用户请求的是动态内容,它处理不了于是通过fastcgi协议转发至后端php-fpm上,当php-fpm server处理完将内容返回至nginx,由nginx构建http响应报文给前端用户,考虑转发前的这个用户连接怎么办,nginx进程怎么处理这个连接,连接是不能断开的,可理解为nginx进程要为这个连接维护一段内存空间,里面保存的有这个连接的相关信息(如client地址、什么时候连进来的、端口等等),请求报文中的数据有可能也会暂存在这段内存中(要读这个报文并分析http首部,使用哪种方式请求的,请求的是哪个资源等),一旦发现要请求的资源,在本地作出处理(是动态内容就要转发),处理完要构建响应报文,基于tcp的连接是两个(发送和接收,这是两个不同的管道,读管道和写管道,对应的是接收缓冲区,发送缓冲区),对每一个连接可理解为是会话缓冲区,这都需要内存来维护这些会话信息,转发的这段时间前端用户要处于等待状态

 

同步/异步:

当后端php-fpm server处理完将结果返回至nginxnginx是立即将结果返回到前端用户,还是先暂存在本地,再封装报文响应给前端用户

php-fpm server处理完每向nginx传来一位(数据都是一位一位传送的),nginx就将放到发送缓冲区,直接返回给前端用户,后端php-fpm server是通过fastcgi协议封装的响应报文给nginx的,如果前端用户能理解fastcgi协议封装的报文,也就是只要发送缓冲区有一位,前端用户就能收到一位,前端用户收到的报文是php-fpm server封装的,这种通信过程就是同步的,nginx实现了透明转发,它只做牵线,在直接通信的双方间建立连接(同步)

但前端用户是很难理解fastcgi协议的,这意味着php-fpm server的响应只有完完全全送给nginx后,nginx将报文拆掉再二次封装成前端用户能理解的方式继而传送(如通过http报文方式),php-fpm server响应的报文nginx要先接收下来暂存在本地(仍需要内存空间),再次封装响应给前端用户,这种方式就是异步的,nginx牵线后负责将对方请求暂存下来由它来理解,它能处理的直接响应,它不能处理的交至后端php-fpm,是它自己向后转发的,而不是将用户请求直接转到后端,它在通信双方间作为协调员的角色(异步)

 

异步模型+本地缓存(利用nginx的缓存功能,缓存的有静态内容+php的执行结果):

若用户请求的是index.phpnginx将请求接进来,查本地缓存中有没,若没有再封装报文转发至后端php-fpmphp-fpm处理完,响应时先放在本地缓存再传给nginx,当第二个用户请求的是同样的内容,检查本地缓存若有则直接从本地缓存中返回(缓存中有且没有失效),这种情况可能出现前端建立的连接有1000个,而查看后端php-fpm的连接只有50个,这将大大降低后端php-server的压力

 

分层:

随着连接量的增加,一台server已扛不住,可将这几种角色分开,每一个模块只负责它自己最擅长的事情,以此达到整个系统优化的目的,如前端是nginx-server,后端有php-fpm server(在网站架构中通常叫app-server);用户请求到达nginx,由nginx处理所有请求,所有会话都由nignx建立,由nginx自己的内存来维护(据测nginx维持10K个非活动连接只需2.5M左右内存);若将用户请求的内容都交至app-server处理(既要封装静态内容,又要处理动态内容,它与每个连接的建立可能要用到单独的进程,仅进程自身开销至少都2M左右),开销非常大,这样一台app-server不能满足(如前端请求5000个,到后端需500个动态请求,若每一个app-server最多处理100个的动态内容,这时就要增加至5app-server),多加几台app-server解决,程序=指令+数据,很多程序运行都要用到数据,这时数据库用来专门存储数据,若数据量不大可将数据库放在app-server上,最好也单独分层(因为phpmysql都是CPU密集型),数据库要共享给这几台app-server以方便查询数据,若后端一台server上的数据库无法满足时(若每一个app-server处理100个动态内容就有30个需要操作数据库,另70个只是程序自身的运行逻辑,那这时有5app-server数据库就要处理150个请求,对于PC级的mysql一般每秒50-100tps每秒事务量,这时一台DB-server会扛不住),再多加几台DB-server用读写分离解决,这样就有了三层结构

 

memcached(两个功能:实现缓存后端DB-server的查询结果,提供给多个app-server使用;缓存app-serversession信息):

它本身只是个提供缓存功能的server,到底缓不缓存,谁来缓存,怎么缓存,缓存过期时间,缓存清除等它自身不管,它可以根据你的请求,替你做出决策,例如,app-server查询DB,觉的这个结果可以缓存,由app-server自己把数据放至memcaced 上,当app-server下次找数据时就自动去memcached上去找,memcached是个公共缓存,可被多个node共同使用,若命中率达到40%,对系统性能的提升非常明显(互联网时代缓存为王,缓存的内容是静态的,处理逻辑在有限的几个CPU时钟周期内就能完成,假设缓存中的内容响应要3个时钟周期,那到后端DB-server去查询可能30个时钟周期都不止)

 

xcache(实现在同一app-server上多个进程间共用opcode):

在一个app-server上,一个连接要用一个fastcgi进程处理,第一个进程要将php程序编译成opcode才能执行,编译出的这个opcode只能为这一个进程使用(同一个请求若多次请求同一个内容,那这个opcode才有意义),若在同一app-server上的第二个请求同第一个请求的内容一样,这时用第二个fastcgi进程处理,它无法使用第一个进程已编译好的opcode需要重新编译,编译是需要大量CPU时间的,这时使用xcache在一个app-server上为多个进程提供opcode缓存(编译的结果,供多个进程使用,性能又提升了)

php程序只有编译成opcode后,在执行时,若需要数据才查询后端的DB,才与后端的DB-server交互

 

若是电商站点,后端某个app-server挂了,每一个app-server为了维持某个用户的购物车、http等会话信息会结合cookieapp-server本地内存中要维护一个session,每一个client只要连接,在app-server上都有session信息,若某个用户第一次连接在app-server5上,第二次被分配到了app-server1上,那之前的会话信息就没了,为使会话继续使用,可用持久连接(同一client的请求始终定位至同一个app-server上,虽会破坏LB效果,但能实现),但要是app-server5挂掉,用户一刷新同样会定位到其它app-server上,会话也照样没了,如果用户请求有5万个,某一个app-server挂掉将会丢失1万个session信息(很可怕)

注:session信息是保存在当前某一个app-server上的本地内存中一份,而且在FS上也会保存一份,方便以后读取,所以只能被这一个主机所使用

 

如果app-server建成集群,session信息在内存中可完成同步,如用户请求app-server1app-server1在建立会话以后,直接通过高可用集群同步至任一node的内存中去,这将有额外的开销(每一个app-server都要通过组播发送出去),若app-server很多每一次这样同步数据量会很大,在小规模场景中勉强可用,但大规模场景就不理想了

 

这时要在各app-server间共享session,使用一个公共区域来保存session信息,让每一个app-server都到这个公共区域中去读会话信息,memcached正是这么一个公共缓存

当某一用户请求第一次被发到app-server1上时,app-server1发现需要为这个请求建立会话,这个会话数据不再保存在本地,直接保存到memcached中,之后哪怕这个用户的请求被转至app-server2上,app-server2会根据用户的cookie信息自动地去memcached中找session信息,把session读过来与用户建立会话,于是本地就有,不过这时风险更大,若memcached挂了,那所有会话都没了,想到为memcached做高可用,memcached本身并不支持高可用,但若做成高可用了也并不理想(它会这样假设,你保存在我这的都是缓存数据,是非关键性数据,是读取的原始数据,我这里的数据就算丢了,你无非是重建而已,我这里不会使用高可用的),但它可很好的实现分布式的功能(memcached的强项),这就意味着1memcached不够用可用多个memcached,如有3memcachedapp-server在存数据时会选一个来存,查找时通过某种算法(例如将用户的cookiehash运算再除以3取余,hash运算的结果相同除3取余的结果就相同)再定位至这个memcached-server

注:若把memcached做成HA-server,意义并不大也不理想(因为缓存数据是在内存中,除非解决两个进程间通过内存将某一node的缓存通知给另一node

 

这样memcached不仅可以缓存DB中查询的数据,还能缓存session信息,所以有人把memcached称万金油(银蛋),它能应付各种情况和场景,只要数据可缓存且缓存的是可序列化的数据(如string、各object等,网络上传输的是01代码,若发一张图片或其它对象最终都可被抽成一根线,再通过网络发送,接收端接收下来将其合并并还原即可)它都能缓存下来,不能序列化就无法在内存中存储,就更不用说发送了

缓存方法就是在内存中开辟一段很小的区域,每个缓存对象将占用一个小区域将数据缓存下来

 

若缓存了有1KW个数据,之后要怎么找,若要遍历这1000W个对象那也太慢了,memcached是键值存储(key:value),在存储时首先给这个数据创建个查找键key,然后再存储它的值valuekey:value存储通常都是基于hash方式,每存一个数据时这个key也需要地方存储,key存储在叫做hash bucket桶中(hash bucket有多个),key存在哪一桶个中是根据hash函数计算得出的,每个桶中比如规定只存100key,将来要找某一个key时做hash运算(hash函数),运算完后就知道在哪个hash bucket中,找到这个桶后,桶中数据相对就很少了,在hash bucket中迅速的做下字符串比较(二次计算)立即找出键,基于这种方式查找速度是无与伦比的快(hash索引非常快,所以查找都是O1)的),这样1000个键无非是在某个桶中找一次,1000万个键无非是找出在哪一个桶中

但存储太多个对象也不好,每存储一个数据都要开辟一小段内存,不同的场景所需要的空间也不一样,对象多了导致内存碎片化,在内存中的这些对象要被管理(如对象过期要回收等),要有很好的内存管理策略(策略即算法,通过程序不定期的去执行,执行就要消耗CPU时间,会有额外开销),当对象少时没什么问题,但对象超多,内存随时都要释放和回收、分配,这是不小的工作量,所以并不建议在memcached中缓存过多的数据

 

memcached缓存时支持的单个对象最大1M空间,最小48byte

memcacheC/S架构,client就是app-server,应用程序通过协议把数据通过网络传送给memcached-servermemcached根据client的请求将数据缓存下来,所以client可向server-side发送不同指令,如建立缓存、清除缓存、在缓存中附加新数据等有很多命令

有些数据不仅仅是key:value的(如用户信息:姓名、年龄、性别、身高、浏览了本网站的多少帖子等等,若以用户名为key,则有用户对应30,用户对应30,第一个30表示年龄,第二个30表示浏览帖子的数量,这很难区分),因此memcached固然很有用很简单,简单到高效但有些场景是应付不来的(key:value无法应付在一个对象下存储多个数据),如果可以把几个key:value组合在一起起个名,建立一个自定义的数据结构,当某一server缓存数据时,会开辟一个大对象,对象名ZSkey,对象里是一大堆键值对,这些键值对是大对象所对应的value(如年龄key(对应value),性别key(对应value)),这样即可存储复杂的数据结构,像这种可存储列表的数据结构,有些服务器可提供这种功能,如redis,它能存储比memcached更复杂的数据结构,可以提供类似DB的功能,它可存储N个对象,对象中每个用户如ZSLS等都有自己的数据(每一行是独立存储的),如ZS这个对象中只有年龄和性别,过段时间发现又要增加浏览帖子数量和家乡地址等,随时都可以添加(mysql实际上也可以,但要求多个字段要事先建立好,用不着也得空着,这将白白浪费空间,而且不是key:value方式存储并查找)

Redis将数据存储在内存中一份,磁盘中也有一份(过段时间再同步到磁盘(实现持久存储),磁盘中保存的数据是单独的对象,这样看redis其实是DBNOSQL,不能用SQL语句查找,是用别的方式查找的,如NOSQL独有的client,支持自己的查询语句、查询命令)),它可存储计数器(如某微博被转发和评论了多少次,这些数据随时都要更新,若要每次更新都存储到mysql中,写操作太多,压力可想而知),由于它只能存储有限的数据结构,且持久存储的是非结构化数据,将来应用很不方便,如要做数据挖掘仍需要要存储在关系型数据库中,所以NOSQL虽很牛但无法替代RDB

memcached仅在内存中存储,不能实现持久存储

若要用到复杂数据结构时(要为一个对象存储更多复杂数据结构)可使用redis,若仅需要key:value存储首选memcached

NOSQL通常被用来某一特定领域的应用,它是一类技术,不像RDBMSrelation data base management systemmysqloracle概念基本是一样的),NOSQL有各种各样的类别,如有文档DB、图片DB、键值DBredis),应用场景各有不同

 

通过以上介绍,对于mysql查询和php session都可缓存在memcached上,而计数器用redis实现

用哪种应用解决当前问题,这个应用要依赖哪种硬件(服务器类型),要用到多少种这样的硬件设备(性能评估),都要做到心中有数才能设计

这种架构功能很多看似复杂,这些功能都是逐步增加的(如淘宝刚开始的功能简陋,到现在可抢购),当app-server的处理逻辑越来越复杂,所需提供的功能也越来越多,用户所能执行的操作也越来越多,这样app-server可能会扛不住(app-server过于复杂,各处理逻辑间复杂了会产生bug,会产生各种难以追踪的莫名其妙的问题),这时可考虑将网站分区(如新闻、论坛、社会等在不同的服务器集群,对应的后端服务器分组,如搜索组、应用程序服务器组、图片组、抢购服务器组、论坛组等,主页将不同服务器集群的各种功能整合在一个页面上,但背后的服务器是在不同的区域),若主页每次访问都要用程序动态生成这会很慢,因此在设计高性能网站时有一个重要的思路,首页一定要静态化(因为首页访问量最大,主页通常都是静态html格式的,里面有些框架,有些子版块是动态生成的,这些动态生成的内容本身也是可缓存的),否则访问主页也会导致server瘫痪

 

前端的分发服务器(主要负责转发用户请求),LVSnginxreverse proxy)、haproxy

LVS(四层,在内核中工作,性能无与伦比的快,经优化的LVS能扛住并发几百万请求都没问题,而nginxhaproxy能达到数万个就不错了,LVSnginx|haproxy不是一个档次和级别的,LVS在实现后端RShealth check上要借助于ldirectord,在DR模型上配置较复杂但性能是最好的,对于动态或静态内容的转发LVS不具备这种能力但nginxhaproxy可以)

Nginxhaproxy(七层,用户空间工作,都基于event-driven,都基于线程,在同样的硬件条件下haproxy的转发能力比nginx要强(差别不是太大),但nginx消耗的系统资源是最小的,在缓存功能上nginx要比haproxy要强)

 

前端分发服务器转发用户请求至app-server,若在nginx本地缓存php执行结果,若请求量很大它本身既要提供转发功能,又要缓存,有可能扛不住,这时可将本地缓存独立出来(varnish),直接面向前端nginx(用户请求到达先查找varnish缓存中有没,若没varnish到后端app-server上请求,若第一次的请求到varnish1上,第二次的请求到varnish2上,为提高命中率做持久连接(会破坏LB效果)或缓存同步,app-server处理动态内容,也可在图片服务器组前端加varnish,以提供图片缓存,这样varnish{1,2}对应应用程序服务器组,varnish{3,4}对应图片服务器组

也可在前端转发用LVSvarnish的后端使用haproxy

以上架构可达到亿级PV

 

 

 

二、操作:

环境:

[root@node1 ~]# uname -a

Linux node1.magedu.com2.6.32-358.el6.x86_64 #1 SMP Tue Jan 29 11:47:41 EST 2013 x86_64 x86_64 x86_64GNU/Linux

准备软件包:

nginx-1.8.0.tar.gz

mysql-5.6.28-linux-glibc2.5-x86_64.tar.gz

libmcrypt-2.5.8-9.el6.x86_64.rpm

libmcrypt-devel-2.5.8-9.el6.x86_64.rpm

mcrypt-2.6.8-10.el6.x86_64.rpm

mhash-0.9.9.9-3.el6.x86_64.rpm

mhash-devel-0.9.9.9-3.el6.x86_64.rpm

php-5.4.6.tar.bz2

xcache-3.0.4.tar.gz

准备好yum

 

1、安装nginx

[root@node1 ~]# yum -y groupinstall “Desktop Platform” “Desktop Platform Development” "Server Platform Development" “Development tools” “Compatibility libraries”

[root@node1 ~]# yum -y install pcre-develperl扩展的正则表达式)

[root@node1 ~]# groupadd -r -g 108 nginx

[root@node1 ~]# useradd -r -g 108 -u 108nginx

[root@node1 ~]# tar xf nginx-1.8.0.tar.gz

[root@node1 ~]# cd nginx-1.8.0

[root@node1 nginx-1.8.0]# ./configure   --prefix=/usr   --sbin-path=/usr/sbin/nginx   --conf-path=/etc/nginx/nginx.conf  --error-log-path=/var/log/nginx/error.log  --http-log-path=/var/log/nginx/access.log   --pid-path=/var/run/nginx/nginx.pid    --lock-path=/var/lock/nginx.lock   --user=nginx   --group=nginx   --with-http_ssl_module   --with-http_flv_module   --with-http_stub_status_module   --with-http_gzip_static_module   --http-client-body-temp-path=/var/tmp/nginx/client/   --http-proxy-temp-path=/var/tmp/nginx/proxy/  --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/  --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi   --http-scgi-temp-path=/var/tmp/nginx/scgi   --with-pcre

[root@node1 nginx-1.8.0]# make && make install

[root@node1 nginx-1.8.0]# cd

[root@node1 ~]# vim /etc/init.d/nginx(为nginx提供启动脚本,内容见文末附)

[root@node1 ~]# chmod +x /etc/init.d/nginx

[root@node1 ~]# chkconfig --add nginx

[root@node1 ~]# chkconfig nginx on

[root@node1 ~]# chkconfig --list nginx

nginx              0:off 1:off 2:on 3:on 4:on 5:on 6:off

[root@node1 ~]# service nginx start

正在启动 nginx                                           [确定]

 

2、安装mysql

[root@node1 ~]# fdisk -l

Disk /dev/sdb: 10.7 GB, 10737418240 bytes

[root@node1 ~]# pvcreate /dev/sdb

 Physical volume "/dev/sdb" successfully created

[root@node1 ~]# vgcreate myvg /dev/sdb

 Volume group "myvg" successfully created

[root@node1 ~]# lvcreate -L 8G -n mylv/dev/myvg

 Logical volume "mylv" created

[root@node1 ~]# lvs

……

 mylv    myvg     -wi-ao--- 8.00g  

[root@node1 ~]# mkfs.ext4 /dev/myvg/mylv

[root@node1 ~]# mkdir /mydata

[root@node1 ~]# mount -t ext4 /dev/myvg/mylv /mydata

[root@node1 ~]# ls /mydata

lost+found

[root@node1 ~]# vim /etc/fstab

/dev/myvg/mylv  /mydata         ext4   defaults    0 0

[root@node1 ~]# umount /mydata

[root@node1 ~]# mount -a

[root@node1 ~]# mount(查看是否有以下此行)

/dev/mapper/myvg-mylv on /mydata type ext4(rw)

 

[root@node1 ~]# useradd -r mysql

[root@node1 ~]# id mysql

uid=498(mysql) gid=498(mysql) =498(mysql)

[root@node1 ~]# mkdir /mydata/data

[root@node1 ~]# chown -R mysql.mysql /mydata/data/

[root@node1 ~]# ll -d /mydata/data

drwxr-xr-x 2 mysql mysql 4096 8  31 18:53 /mydata/data

[root@node1 ~]# tar xvf mysql-5.6.28-linux-glibc2.5-x86_64.tar.gz -C /usr/local/

[root@node1 ~]# cd /usr/local/

[root@node1 local]# ln -sv mysql-5.6.28-linux-glibc2.5-x86_64/ mysql

"mysql" ->"mysql-5.6.28-linux-glibc2.5-x86_64/"

[root@node1 local]# cd mysql

[root@node1 mysql]# chown -R root.mysql ./

[root@node1 mysql]#scripts/mysql_install_db --user=mysql --datadir=/mydata/data

[root@node1 mysql]# vim my.cnf

[mysqld]

datadir = /mydata/data

innodb_file_per_table = 1

socket = /tmp/mysql.sock

log-bin = mysql-bin

[root@node1 mysql]# vim /etc/profile.d/mysql.sh

export PATH=$PATH:/usr/local/mysql/bin

[root@node1 mysql]# . !$

. /etc/profile.d/mysql.sh

[root@node1 mysql]# vim /etc/ld.so.conf.d/mysql.conf

/usr/local/mysql/lib

[root@node1 mysql]# ldconfig -v

[root@node1 mysql]# ln -sv /usr/local/mysql/include/ /usr/include/mysql

[root@node1 mysql]# cp support-files/mysql.server /etc/init.d/mysqld

[root@node1 mysql]# chkconfig --add mysqld

[root@node1 mysql]# chkconfig --list mysqld

mysqld            0:关闭      1:关闭      2:启用      3:启用      4:启用      5:启用      6:关闭

[root@node1 mysql]# service mysqld start

Starting MySQL.......                                      [确定]

 

3、安装php

[root@node1 ~]# rpm -Uvh libmcrypt-2.5.8-9.el6.x86_64.rpm

[root@node1 ~]# rpm -Uvh libmcrypt-devel-2.5.8-9.el6.x86_64.rpm

[root@node1 ~]# rpm -Uvh mhash-0.9.9.9-3.el6.x86_64.rpm

[root@node1 ~]# rpm -Uvh mhash-devel-0.9.9.9-3.el6.x86_64.rpm

[root@node1 ~]# rpm -Uvh mcrypt-2.6.8-10.el6.x86_64.rpm

[root@node1 ~]# yum -y install net-snmpnet-snmp-devel libcurl-devel bzip2-devel

注:不安装这几个rpm包,会出现错误,例如(类似如下错误,都要安装其对应的devel包):

configure: error: Could not findnet-snmp-config binary.

configure: error: Please reinstall theBZip2 distribution

[root@node1 ~]# tar xvf php-5.4.6.tar.bz2

[root@node1 ~]# cd php-5.4.6

[root@node1 ~]# ./configure --prefix=/usr/local/php --with-mysql=/usr/local/mysql --with-openssl --enable-fpm  --enable-sockets  --enable-sysvshm  --with-mysqli=/usr/local/mysql/bin/mysql_config  --enable-mbstring--with-freetype-dir  --with-jpeg-dir  --with-png-dir  --with-zlib-dir --with-libxml-dir=/usr  --enable-xml  --with-mhash  --with-mcrypt  --with-config-file-path=/etc  --with-config-file-scan-dir=/etc/php.d --with-bz2  --with-curl  --with-snmp

[root@node1 ~]# make && make install

 

[root@node1 php-5.4.6]# cp php.ini-production /etc/php.ini

[root@node1 php-5.4.6]# cp sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm

[root@node1 php-5.4.6]# chmod +x /etc/init.d/php-fpm

[root@node1 php-5.4.6]# chkconfig --add php-fpm

[root@node1 php-5.4.6]# chkconfig php-fpm on

[root@node1 php-5.4.6]# chkconfig --list php-fpm

php-fpm           0:off 1:off 2:on 3:on 4:on 5:on 6:off

[root@node1 php-5.4.6]# cd

[root@node1 ~]# cp /usr/local/php/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf

[root@node1 ~]# vim /usr/local/php/etc/php-fpm.conf(根据主机性能调整)

[global]

pid = /usr/local/php/var/run/php-fpm.pid

[www]

listen = 127.0.0.1:9000

pm.max_children = 150the maximum number of children that can be alive at the same time

pm.start_servers = 8the number of children created on startup

pm.min_spare_servers = 5the minimum number of children in 'idle' state (waiting to process). If the number of'idle' processes is less than this number then some children will be created

pm.max_spare_servers = 10the maximum number of children in 'idle' state (waiting to process). If the number of'idle' processes is greater than this number then some children will be killed

[root@node1 ~]# service php-fpm start

Starting php-fpm  done

[root@node1 ~]# netstat -tnlp | grep :9000

tcp       0      0 127.0.0.1:9000              0.0.0.0:*                   LISTEN      5983/php-fpm   

[root@node1 ~]# ps aux | grep php1masterprocess8children process

 

4、整合nginxphp

[root@node1 ~]# cp /etc/nginx/nginx.conf.default /etc/nginx/nginx.conf

[root@node1 ~]# vim /etc/nginx/nginx.conf

  ……

http {

server {

    ……

       location / {

           root   html;

           index  index.php index.htmlindex.htm;

       }  

       ……

       location ~ \.php$ {

           root  html;

           fastcgi_pass  127.0.0.1:9000;

           fastcgi_index  index.php;

           fastcgi_param  SCRIPT_FILENAME/script$fastcgi_script_name

           include  fastcgi_params;

       }

       ……

}

[root@node1 ~]# vim /etc/nginx/fastcgi_params

#-----------content start-----------------

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;

fastcgi_param  SERVER_SOFTWARE    nginx;

fastcgi_param  QUERY_STRING       $query_string;

fastcgi_param  REQUEST_METHOD     $request_method;

fastcgi_param  CONTENT_TYPE       $content_type;

fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_FILENAME   $document_root$fastcgi_script_name;fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;

fastcgi_param  REQUEST_URI        $request_uri;

fastcgi_param  DOCUMENT_URI       $document_uri;

fastcgi_param  DOCUMENT_ROOT      $document_root;

fastcgi_param  SERVER_PROTOCOL    $server_protocol;

fastcgi_param  REMOTE_ADDR        $remote_addr;

fastcgi_param  REMOTE_PORT        $remote_port;

fastcgi_param  SERVER_ADDR        $server_addr;

fastcgi_param  SERVER_PORT        $server_port;

fastcgi_param  SERVER_NAME        $server_name;

#--------------contentend--------------------

[root@node1 ~]# vim /usr/html/index.php

<?php

phpinfo();

?>

[root@node1 ~]# service nginx reload

nginx: the configuration file/etc/nginx/nginx.conf syntax is ok

nginx: [emerg] mkdir()"/var/tmp/nginx/client/" failed (2: No such file or directory)

nginx: configuration file/etc/nginx/nginx.conf test failed

[root@node1 ~]# mkdir /var/tmp/nginx/client/ -pv

mkdir: created directory `/var/tmp/nginx'

mkdir: created directory`/var/tmp/nginx/client/'

[root@node1 ~]# service nginx reload

nginx: the configuration file/etc/nginx/nginx.conf syntax is ok

nginx: configuration file/etc/nginx/nginx.conf test is successful

重新载入 nginx                                           [确定]

wKiom1aCOY3Q8ZhbAAB74SjDqE0506.jpg

 

5、安装Xcache

[root@node1 ~]# tar xvf xcache-3.0.4.tar.gz

[root@node1 ~]# cd xcache-3.0.4

[root@node1 xcache-3.0.4]#/usr/local/php/bin/phpize

Configuring for:

PHP Api Version:         20100412

Zend Module Api No:      20100525

Zend Extension Api No:   220100525

[root@node1 xcache-3.0.4]# ./configure --enable-xcache --with-php-config=/usr/local/php/bin/php-config

[root@node1 xcache-3.0.4]# make && make install

 

[root@node1 xcache-3.0.4]# mkdir /etc/php.d/

[root@node1 xcache-3.0.4]# cp xcache.ini /etc/php.d

[root@node1 xcache-3.0.4]# vim /etc/php.d/xcache.ini

[xcache-common]

extension ="/usr/local/php/lib/php/extensions/no-debug-non-zts-20100525/xcache.so"

[root@node1 ~]# service php-fpm restart

Gracefully shutting down php-fpm . done

Starting php-fpm  done

wKiom1aCOZ6j6J3VAACmDfbJnQM336.jpg

 

 

三、扩展:架构方面

nginx支持FSAIO,支持mmap,支持event-driven(比httpd支持的连接数多的多)

一主机(4G内存,2CPU),若使用nginx,支持3-5W个并发连接一点问题都没有;而要使用httpd,在prefork模型下,一个进程响应一个请求,最多同时并发1024个,再多就拒绝响应了,若在worker模型下(虽一个线程响应一个请求,一个进程会生成并管理多个线程,这只不过进程切换数少了些),并发连接数并没提升(因为select机制的限制)

nginx程序本身才1M,但功能单一,众多httpd的功能nginx不支持;而httpd有众多的模块,整体与nginx比,它是重量级

很少将nginx拿来用作web-server,因为它的功能不够丰富,stable上讲,一个线程响应多个请求固然很好,但若一个线程崩溃了,N个请求都玩完,像httpd一个进程响应一个请求,一个进程崩溃不影响其它请求,httpdprefork模型要稳定的多;所以nginx通常拿来做前端的reverse proxy反向代理(如mysql-proxy,它本身不是mysql-server,但通过它能连后后端的mysql-server,而nginxwebproxy,解析http协议),而httpd用来做后端的web-server

wKioL1aCOcqz4cbBAABt3kCL9XU370.jpg

web object分静态和动态(静态,html文档、图片、CSS样式表等;动态,php脚本)

nginxreverse proxy(可精确理解web协议、各种用户操作、URLrewrite、资源重定向等);LVS并不能理解哪些是静态内容哪些是动态内容,在高级功能设定上不具备

2CPU4G内存的主机(nginxweb-server,若请求的是5-10K的图片,每秒处理5000-10000个,另还取决于带宽及磁盘IO;若响应动态内容大约1000个,要占据CPU时间生成html文档才返回)

varnish(用来缓存动态内容,如64G内存500G固态硬盘,一般要命中率要达到50%

memcached(缓存mysql-server的内容,它是个旁路缓存,php程序要用到数据先找memcachedmemcached中没有得自己找mysql-server,返回时先到memcached中,再返回给php程序)

可在haproxy前端加MQrabbitmqzeromq,可理解为连接池,所有连接进来并不是马上提供服务,而是先缓存下来;在异步管理中,MQ很常用)

地理位置法则,缓存策略(CDNcontent distribute network),将所有静态内容(包括动态内容的缓存)放至全国各地机房中的varnish缓存集群中,做智能DNS解析用户请求,如华北区的用户直接解析到离他最近的缓存服务器集群上,命中率要90%(缓存放在用户的家门口,访问时直接返回),做内容路由(如华北区用户访问最近的缓存中没有,到其它区域的缓存中找,若都没,再找原始服务器)

淘宝在抢购时tpsmysql的每秒事务量transaction per second5W多),一般500个并发连接都扛不住

NOSQL(工作在内存,支持事务,如redismongodb),若要实时写操作,而且量很大,就要用到NOSQL,内存数据库,需要持久存储(将结构化数据放至mysql中,非结构化或半结构化数据放在FS上)并用来后期分析,以后在FS上对数据进行分析就不像mysql那样,要检索有效数据集来分析,而要对文件全量提取进行分析,要用并行处理平台才能完成(如hadoop

注:NOSQL解决抢购时随时更新商品剩余量,在前端的内存服务器(在内存中执行事务,小事务,每次抢购完就结束,快速执行,快速反馈,对于商品的更新只要事务一完成立即更新)

redis(作为计数器,快速法则响应,如微博中有多少人转载、评论等要计数,在内存工作,将数据同步到硬盘,并且实现将数据转存至mysql中(持久化存储法则);而memcached只提供缓存,不能保存数据)

mongodb(对于写数据多时使用)

日志服务器(可将日志放在mysql-server中,mysql放在更高IO能力的硬盘上,再导入到分布式FS上;或将日志放在10server上,在每个server上布署个日志小程序(facebook有个日志收集器,让每个server管理自己的日志),每天将日志读一份放至分布式FS上,在分布式FS上通过并行处理程序完成日志分析(如完成前一天抢购中哪个区的用户,哪个店铺的成交量最多等,每天的日志可能上G多达则Thadoop对这些数据作批处理,hadoop的分析是异步的,而且有两段执行过程(各自分开执行))

若将一个机房的主机做成高可用集群(云平台),在云平台上开一堆的虚拟机,各种虚拟机完成各种任务(不同的服务在不同的虚拟机上),哪一个虚机故障直接kill掉(进程而已),再启动一个新的,或者实时迁移到其它虚拟机上;对于共享存储(监控存储解决方案,任何一个虚拟机开了,自动在存储上找个空间,创建虚拟磁盘,实现虚拟机进程的启动);hadoop不能放在虚拟机上,hadoop需要大师的磁盘IO,虚拟机的IO能力是很差的,所以要用云+hadoop实现;另可将mysql放在云里,云有三种模型(iaaspaassaas,将mysql做成saas向外提供软件服务;这样mysqlhadoopweb、云各自都模块化

架构师:

网络

自动化运维编程(shellpython

各服务的性能、特性、适用场景、优缺点等要做到通盘掌握

DBA能力

服务运维能力

运维经验

 


附:nginx启动关闭脚本

---------------script start--------------

#!/bin/sh

#

# nginx - this script starts and stops the nginx daemon

#

# chkconfig:   - 85 15 

# description:  Nginx is an HTTP(S) server, HTTP(S) reverse \

#               proxy and IMAP/POP3 proxy server

# processname: nginx

# config:      /etc/nginx/nginx.conf

# config:      /etc/sysconfig/nginx

# pidfile:     /var/run/nginx.pid

 

# Source function library.

. /etc/rc.d/init.d/functions

 

# Source networking configuration.

. /etc/sysconfig/network

 

# Check that networking is up.

[ "$NETWORKING" = "no" ] && exit 0

 

nginx="/usr/sbin/nginx"

prog=$(basename $nginx)

 

NGINX_CONF_FILE="/etc/nginx/nginx.conf"

 

[ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx

 

lockfile=/var/lock/subsys/nginx

 

make_dirs() {

   # make required directories

   user=`nginx -V 2>&1 | grep "configure arguments:" | sed 's/[^*]*--user=\([^ ]*\).*/\1/g' -`

   options=`$nginx -V 2>&1 | grep 'configure arguments:'`

   for opt in $options; do

       if [ `echo $opt | grep '.*-temp-path'` ]; then

           value=`echo $opt | cut -d "=" -f 2`

           if [ ! -d "$value" ]; then

               # echo "creating" $value

               mkdir -p $value && chown -R $user $value

           fi

       fi

   done

}

 

start() {

    [ -x $nginx ] || exit 5

    [ -f $NGINX_CONF_FILE ] || exit 6

    make_dirs

    echo -n $"Starting $prog: "

    daemon $nginx -c $NGINX_CONF_FILE

    retval=$?

    echo

    [ $retval -eq 0 ] && touch $lockfile

    return $retval

}

 

stop() {

    echo -n $"Stopping $prog: "

    killproc $prog -QUIT

    retval=$?

    echo

    [ $retval -eq 0 ] && rm -f $lockfile

    return $retval

}

 

restart() {

    configtest || return $?

    stop

    sleep 1

    start

}

 

reload() {

    configtest || return $?

    echo -n $"Reloading $prog: "

    killproc $nginx -HUP

    RETVAL=$?

    echo

}

 

force_reload() {

    restart

}

 

configtest() {

  $nginx -t -c $NGINX_CONF_FILE

}

 

rh_status() {

    status $prog

}

 

rh_status_q() {

    rh_status >/dev/null 2>&1

}

 

case "$1" in

    start)

        rh_status_q && exit 0

        $1

        ;;

    stop)

        rh_status_q || exit 0

        $1

        ;;

    restart|configtest)

        $1

        ;;

    reload)

        rh_status_q || exit 7

        $1

        ;;

    force-reload)

        force_reload

        ;;

    status)

        rh_status

        ;;

    condrestart|try-restart)

        rh_status_q || exit 0

            ;;

    *)

        echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"

        exit 2

esac

------------------script stop----------------



 本文转自 chaijowin 51CTO博客,原文链接:http://blog.51cto.com/jowin/1729562,如需转载请自行联系原作者

网友评论

登录后评论
0/500
评论
技术小阿哥
+ 关注