Linux运维第三阶段(十五)理解LNMP
一、相关概念:
nginx(engine-X,nginx.org,performance、stability、rich feature、simple configuration、low resource consumption):
HTTP-server(轻量级、高性能WEB服务器)
reverse proxy(反向代理服务器,mail proxy server、tcp proxy server,任何一种proxy必须要能精确理解某种协议的内容及操作,例如mysql-proxy能理解mysql的各种SQL语句从而实现rw-splitting)
注:nginx能理解三种协议(http;mail(smtp、pop3、imap);tcp);早期nginx就是用来做reverse proxy;它起步就是用来解决C10K problem(connection 10K,连接数达到一定程度会产生诸多问题,网络服务在处理数以万计的客户端连接时,往往会出现效率低下甚至完全瘫痪)
1、单进程模型(阻塞模型,如第一个用户的请求与server建立连接后,当有第二个用户向服务器请求就要等待,等第一个用户断开后才能连接)
2、多进程模型:
httpd的prefork模型,httpd是主进程,当有用户请求,httpd会派生出子进程予以响应,主进程管理这些子进程(创建、销毁等);
每个进程响应1个用户请求,进程是重量级的资源实体,是CPU的执行单位(调度的单位),CPU要给它分配时间片、内存资源等,开销大,内核在某一时刻还要调度这些进程,进程很多时会带来大量的进程切换CS(content switch,进程很多,但CPU有限,由内核处理进程切换),某一时刻1个CPU只处理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(磁盘块分1K,2K,4K),每个文件占用1个或多个磁盘块,每次读一个磁盘块填满页面,若磁盘块是2K则要读取2个磁盘块填满1个页面,数据加载结束才开始复制(数据从内存的内核空间-->进程的地址空间),如果是访问一个文件的一部分这是另外一种机制;
若一个文件占据10个内存页,每个内存页是4K,每个磁盘块是4K,这要涉及到10个磁盘块,每次要读哪个块,读完后数据要放至内存的哪个地址中(如何选择一个内存页面从哪分配),内核操作文件系统FS,FS对应磁盘块,对应的是哪个块这由驱动程序管理,驱动程序运行是内核的功能,内核运行是要有CPU参与的,为加载一个文件内核会很繁忙,为尽可能降低内核对CPU的占用,让当前主机的CPU尽可能运行程序,现在硬件都具有DMA机制(direct memory access),在DMA下,内核在其内核空间(内存的缓冲区)中找一段连续的内存空间,将起始地址给DMA芯片(有访问内存的能力,有访问硬盘数据的能力,有数据传输占据系统总线的能力,有控制系统总线、数据总线的能力),交由DMA控制,由DMA指挥着将数据从硬盘加载至内存中,数据加载完成DMA会产生一个中断,强行告知CPU已完成,CPU这时将当下正在运行的进程中断并将其转为睡眠状态,内核将自己空间中加载好的数据复制到进程的地址空间,进程再通过与网卡交互(通过PCI总线与网卡联系)通过网线返回给用户,若多个进程处理的都是一个主页面的内容,每一个进程IO-->内核调用(IO调用)-->磁盘空间-->内核空间-->进程空间,内核有加速机制,当第一个进程读完后会在内存的内核空间中缓存下来,第二个进程访问的是同样内容直接从内核空间复制
若主页2M,4个进程访问同一主页,在用户空间占据的内存大小4*2M=8M,本来一个主页面只需占据内存2M大小,但实际占用了8M内存(多进程模型的缺陷之一)
多进程模型中,系统调用结束(内核准备好数据),当前进程与client的连接处于等待状态,连接在tcp/ip协议栈是由内核管理的,内核怎么通知进程程哪个IO完成了?采用select()机制(内核要输出一个数据结构,假设这个数据结构是一排灯,第1、2、3的灯对应第1、2、3的进程,哪个灯亮了就说明哪个进程的数据准备好了,当第2个灯亮起时第二个进程就知道它要的数据准备好了,每一次内核准备好数据都要向用户空间输出整个数据结构,不输出进程是无法理解的,这种每次都逐个扫描IO事件描述符,将它从内核空间输出到用户空间的方式叫select,select最多支持1024个灯泡,1024个进程,多余1024的进程只能在一边等待,早期的系统调用采用这种方式)
多进程模型总结:
每个进程响应一个请求;
进程量大,进程切换次数过多,会消耗大量资源,若是C10K,1W个请求connection,可能无法应付;
每个进程的地址空间是独立的,很多空间是重复的数据,所以内存利用率低
注:上图是进程在内存的分布
注:下图是多线程在内存的分布
3、线程thread:
lwp,light weight process轻量级的进程,是进程内部的子运行单位;linux不支持原生态的线程(win和solaris支持),linux把线程当作进程对待,在管理上略有区别,在linux上实现线程功能需借助glibc提供的线程库来完成(以完成线程的创建、撤销等管理),linux上提供的线程库有多种(有内核自带的,也有redhat提供的),不同的线程库对系统资源的占用是不一样的;
所以线程是运行在进程内部的子单位,在进程内部可以启动多个执行流(线程是并发执行流),
程序=指令+数据;单个进程中指令在CPU上运行,是将指令从上至下按控制流程一条一条执行(仅一条流水线),用到数据时到堆或栈中去取(堆空间中的数据通常是打开的文件、栈空间中的数据存放变量),每一个进程的数据都是自己管理的,如果用到循环也是在局部循环;有了线程后,执行流是并发的是多个的,所有执行流共享这个进程的数据区域(共享全局变量、堆,打开的文件等,但栈是不能共享的,栈中各层函数帧代表着一条执行线索,一个线程是一条执行线索,每个线程独占一个栈,这些栈必须在所属进程的内存空间中),每一个执行流都是一个独立的实体,所以线程就是运行在同一个进程内部(进程的内存空间)共享了很多资源的运行的实体;
不同用户访问同一个文件,多线程比多进程性能要好很多:当第一个用户请求进来,server用一个线程响应,若这个线程要加载文件,经系统调用-->内核的内存空间-->复制到进程的内存空间,响应给前端,当第二个用户请求访问同样的文件,这个进程的内存空间已有数据,则直接从进程的内存空间返回(不需经系统调用到内核空间再复制到进程空间这个过程);
若连接请求很多,如有10K个连接,若由一个进程管理,内部很复杂,首先线程间要切换,线程过多,导致大量切换,会带来线程抖动(很严重,线程刚占用CPU啥事没干就切换出去了),切换本身靠内核来完成,也仍会占用过多资源;还要解决彼此间资源争用就超出我们预料,例如第一个线程要执行写操作,第二个线程若要读这时就要等待,等待写完成才能读(读是共享的,但读写不能共享),若要等待那CPU就要浪费给这个线程了,若进程切换,线程来读读不了,就要等
注:等有忙等和闲等;忙等,用的是自旋锁spin lock,看到第一个线程在写,读不到,若马上切换,要是刚切换出去,数据准备好了可读了,那就白切换了,再多等一会,CPU是不允许空闲的,CPU只要一供电,时钟频率就一直在那,无论有无程序执行CPU都一直在转,只不过计算能力白白流逝了,这就要运行指令,如每隔1ms来看下,这个线程始终占用着CPU不退出,时间未耗完事情没干完就不走,这叫忙等;闲等,切换出去,时间没耗完,事情没干完自愿退出
尽管多个线程可并行执行,但若主机仅一颗CPU,线程优势几乎发挥不出来,若是多颗CPU,多个线程在多个CPU上同时执行,而且程序在编写时采用并行编程技术(在一个进程内部或一个线程内部可有多个执行流)可实现更好的分配系统资源,如10颗CPU处理1W个连接,每个CPU只需1000个切换
在线程模型下,系统调用结束(内核准备好数据),内核如何通知线程它要的数据已准备好了?仍旧使用select模型(在linux中线程也是进程,只不过是轻量级进程,一个进程管理多个线程,IO是由进程管理,只不过减少了进程的数量,相应IO文件描述符的数量也少了,可响应更多的用户请求,这背后IO机制并没变,只不过多个线程可共享资源,至少提高了内存使用效率,所以在C10K上仍有问题,这时就要用到一个线程响应多个请求
线程总结:
每个线程响应一个请求,线程仍然要切换,但切换是轻量级的;
同一个进程的线程可以共享进程的诸多资源(如打开的文件);
对内存的需求较之进程略有下降;
快速切换时会带来线程抖动
多进程多线程模型:
N个进程,每个进程中有100个线程,使得资源争用不是很严重,例如有8颗CPU,单独拿出一颗用来运行内核和其它进程,另7颗独立空闲出来,隔离出来仅让web服务使用,web服务启动时只启动7个进程,每一个进程绑定一个CPU(一颗CPU对应一个进程,这将没有进程切换,但进程内部的线程切换是不可避免的),这样速度就会快很多,系统提供这种功能但要手动完成进程与CPU绑定
一个线程响应多个请求(多线程<-->N个请求):
一个线程要处理多个IO,若一个用户请求了主页,建立连接,系统调用结束后返回数据,如何唤醒当前建立的连接,怎么通知线程?event-driven IO(事件驱动IO)
一旦有IO,线程是不能阻塞的(若阻塞,这个线程就无法完成其它用户请求,这与我们的初衷相悖(一个线程响应多个请求)),例如去饭店吃饭(顾客是用户请求,收银员是线程或者进程,后厨是内核):
阻塞(10个顾客,一个收银员,第一个顾客报饭给收银员-->收银员告诉后厨-->后厨做好后-->收银员-->顾客,这样第一顾客接待完后,第二个顾客才能建立连接;为提高效率,增加收银员的数量,同时三个收银员,多队列阻塞,同样第一个顾客拿到饭,第二个顾客才能连接,才能向收银员报饭)
非阻塞(第一个顾客向收银员报上饭后在一旁等着,营业员告知后厨,之后就与后厨暂时失去联系,继续接待下个顾客,第二个顾客也在一旁等着,后厨做好饭后在大屏幕上显示用这种方式通知(第一种通知方式,每做好一个将做好的和没做好的全部显示;第二种通知方式,只通知做好的,这样内核耗费资源就少了),若通知了一遍某个顾客没看到,要么就不管了,要么再通知直到顾客拿到饭,这种属通知机制,就不需用户始终盯着收银员)
同步(第一个顾客向收银员报上饭,收银员立即告知后厨,来自前端的直接交给后端)
异步(每一个顾客向收银员报上饭,收银员汇总后一并告知后厨)
多个顾客同时请求,又都能让顾客知道请求的内容是否就绪这称作IO复用机制
以上举例不精确,但有助于理解
AIO,asynchronous IO,异步IO
一个thread维持多个用户,在某一时刻用户的请求尚未满足,这个用户要处于等待状态,当为这个用户请求的数据准备完成之后,与前端用户的连接再次重新建立起来,并通知用户可以取数据,在网络上完成多路IO的唤醒,在非阻塞模型下通过内核中的多路IO复用机制来完成(每一个请求在本机就是一堆文件描述符,一堆套接字的文件,当为某一个用户请求的数据准备好了,必须要将这个文件描述符激活,并让client过来取数据)
4、看清以下这五种模型:
IO动作如何执行?(进程无法直接操作IO设备,必须通过系统调用请求kernel来协助完成IO动作;内核会为每个IO设备维护一个buffer(内核的内存空间);对于输入而言,等待wait数据输入至buffer需要时间,而从buffer复制copy数据到进程也需要时间;根据等待模式的不同,IO动作可分为五种模式)
五种模式:
blocking IO:blocked all the way(阻塞IO,synchronous-blocking)
nonblocking IO:if no datain buffer,immediate returns ewouldblock(非阻塞IO,synchronous-nonblocking)
IO multiplexing(select|poll):blocked separately in wait and copy(IO复用,asynchronous-blocking)
signal driven IO(SIGIO或event drivenIO):nonblocked in wait but blocked in copy(signaled when IO canbe initiated)(只要一个进程处理多个IO时(处理多个文件描述符)必须得复用,甚至一个进程响应一个请求时也要复用(与用户交互数据(接受键盘输入的数据,交互式IO),处理网络连接(网络IO))
asynchronous IO(AIO,asynchronous-nonblocking):nonblocked all the way(signaled when IO is complete)(异步IO,不导致请求进程阻塞;synchronousIO,引起请求进程阻塞,直到IO完成)
注:只要是synchronous,都通过read/write系统调用来完成IO
如下图:synchronous-blocking,整个过程都是阻塞状态,是闲等
process或thread向内核发起系统调用,内核将数据从磁盘复制到kernel’s buffer, process或thread一直监控着内核缓冲区
数据从kernel’s buffer复制到进程地址空间,这段时间内不能连入其它请求
如下图:synchronous-nonblocking,是忙等(不断询问),此模型性能差,几乎不用
发起系统调用后是非阻塞状态,可以连入其它请求,但同时还要不断检查之前的IO返回状态,若再次连入的请求是打开其它文件,又要重新发起新的系统调用,进程又要发起新的IO请求,这同时就要不停地检查两个IO状态,第一个IO完成了,阻塞,复制数据到进程地址空间,再检查第二个IO,这个进程会很忙,若是很多个,性能会很差,所以这种模型几乎不用
如下图:asynchronous-blocking(IO multiplexing),分两段,两段都阻塞,第二段是由进程主导数据复制(进程再次发起系统调用)
可实现多个进程响应多个请求,如httpd,主进程接收用户请求,接进来分配一个子进程来处理这个请求,一个子进程只负责完成这一个IO,完成多个子进程响应多个请求,也可以一个进程响应多个请求,但性能会很差
select()函数,进程要知道内核准备的数据是否到kernel’sbuffer中,就要监控着缓冲区,在缓冲区中会打开一个文件描述符,进程需要read()这个缓冲区,返回一个状态,进程就知道它请求的数据是否准备好,若只考虑一个进程打开了一个文件(在真实环境中,一个用户请求了一个页面,这个页面由很多资源组成,每个资源就得一个进程来完成),每个子进程响应一个用户请求,每个子进程都打开一个文件,子进程是属于主进程的,主进程自身对select而言最多只支持1024个请求,也就是最多1024个子进程响应了(同时1024个并发连接)
要想实现multiplexing就要用select或poll这种机制(poll同select的工作机制一样,但无文件数限制,由于工作机制一样,即使没有设置上限,性能与select差不多,select之所以有限制,说明它知道超过这个数性能会很差)
如下图:event-driven,解决了IO multiplexing(asynchronous-blocking)前半段的阻塞和synchronous-nonblocking前半段一直询问。刚开始在进程发起请求时,留有回调机制(可理解为给kernel留了联系方式),内核在数据准备完成后向进程通知(水平触发和边缘触发;水平触发,每隔一段时间通知一次,多次通知直到拿走数据;边缘触发,仅通知一次,不管你拿没拿到),边缘触发性能要好
一个线程响应多个请求,一个线程内部维护的有多个连接
一个线程接受用户请求,向内核发起系统调用,给内核留了联系方式,再接受第二个用户请求,发起系统调用,再留一个联系方式,依次接受第三个请求……,当内核准备好第一个的数据,使用回调机制告诉第一个联系方式数据准备完成,然后阻塞(第二段阻塞)等数据从内核空间复制到进程空间响应给用户
若内核将自己的内核空间共享给用户空间,连复制都不用了,这将更快,这是内存共享,注意不是内存映射(mmap,memory map指的是数据从磁盘到内核的内存空间,要流动过去,这要复制,内存映射是复制就不用了,直接将要打开的文件在磁盘中的数据结构映射到内存,在两者之间建立起关联关系,将磁盘中的文件与内存的一块区域建立关联关系,当访问时直接取数据即可)
event-driven模型使用select和poll无法完成,它使用的是epoll(linux上叫epoll),在solaris上叫/dev/poll,在FreeBSD上叫kqueue
如下图:asynchronous-nonblocking,在数据复制到进程的内存空间后才给进程通知(给进程信号),而event-driven是在内核中通知的
nginx在磁盘IO上支持此模型(网络IO不是)
5、nginx特性:
file AIO(文件或磁盘IO,基于异步IO),direct IO(对于正常的系统调用read/write流程是,read(数据从硬盘-->内核空间-->用户空间),write(数据从用户空间-->复制到内核空间的页缓存write直接返回-->OS会在恰当的时间写入磁盘,这是buffered IO),对于自缓存的应用程序来说,buffered IO不是好的选择,因此出现direct IO,不经内核空间直接写磁盘,必须阻塞,所以通常direct IO与AIO会一同出现);
asynchronous(异步通信);
event-driven edge trigger(事件驱动边缘触发);
作为web-server处理静态文件,要依赖其它模块才能提供动态功能;索引文件(主页面)及自动索引;打开文件描述符缓存(nginx可以缓存源文件和文件描述符(路径));
使用缓存加速反向代理;简单负载均衡及容错(实现后端real server的health_check,一旦发现后端server故障直接剔除,类似keepalived,对于容错要安装第三方模块);
注:淘宝在nginx代码的基础上对其作了诸多扩充,直接将很多第三方模块(插件)整合进了nginx中,并对其作出大量改进,在nginx上作了第二次发行版Tengine,淘宝也将不断改进的代码反馈给nginx官方,有些代码被nginx吸纳并融入到后续的发行版中
远程fastcgi(php,nginx不支持模块方式使用php,只有fastcgi这种方式);uwsgi(用来支持python的web框架,比fastcgi高效,不是php);scgi和memcached服务的缓存加速支持(nginx已原生态支持memcached;nginx自身作为代理它也可以提供缓存,只不过它默认是缓存在磁盘上的,但它能在内存中缓存打开的文件描述符);
注:缓存方面:varnish、squid、nginx、httpd。varnish(是专业的缓存server,它在设计时考虑的是现代的计算机体系结构,缓存时可优先选择内存缓存,可在内存中实现对数据结构的创建、回收、销毁等,它引入很好的算法,在专业级别讲,varnish有更好的特性);squid(由于比较早期,它在开发时考虑的是原先计算机结构);varnish与squid的关系相当于nginx同httpd的关系;nginx(disk);httpd(disk,memory)
模块化架构设计;过滤器包括gzip压缩、ranges支持、chunked响应、XSLT、SSI及图像缩放
注:SSI(server side include,服务器端包含,可实现将一个页面,某些内容作成动态,某些内容作成静态);图像缩放(节约网络带宽、提高用户体验)
支持SSL、TLS SNI;
基于主机名及IP的虚拟主机;
keepalive和pipelined连接支持;
重新加载配置及在线升级时,不需要中断正在处理的请求(架构设计先进性的体现,热部署、平滑升级);
自定义访问日志格式,带缓存的日志写操作,快速日志轮转(若用户访问的记录马上写到磁盘中,会影响系统性能,这个功能使日志先在内存中缓存,过段时间再flush到磁盘上);
3XX-5XX错误代码重定向;
重写rewrite模块,使用正则表达式改变URI(尤其作为reverse proxy server,rewrite是个重要的功能);
根据client地址执行不同的功能(例如根据client的浏览器类型响应不同的页面内容,若用户使用的是手机,则返回wap页面,可节省流量等);
基于客户端IP地址和HTTP基本认证机制的访问控制;
支持验证HTTP referer(防盗链机制,ngx_http_referer_module允许拦截referer请求头中含有非法值的请求,referer指通过哪些链接进的网站(用户访问网站,要么在浏览器上输入,要么通过链接进入),正是因为referer的存在,很多网站可盗链我们网站,例如:有人在我们网站上传了一堆图片,在他的网站通过链接指向我们的服务器,这样我们就给他提供页面,消耗我们的流量,过段时间若图片过多我们的网站就有可能打不开了)
支持put、delete、mkcol、copy、move等方法;
支持flv流和mp4流(边下载边播放);
速度限制(限制同一client连接的带宽);
来自同一地址的同时连接数和请求数限制;
支持的IO框架机制,epoll(linux),/dev/poll(solaris),kqueue(FreeBSD),编程时基于这个框架就支持asynchronous、event-driven,另还有event ports,select/poll,这两种最不可取,当前三个不支持时再使用这两个;
支持sendfile、sendfile64、sendfileV(尽可能避免数据拷贝操作,用户请求进来,请求的是静态页面,页面内容在磁盘分区中某个FS上,内核处理通过80port到某个worker进程上,通过建立连接,请求分析,发现用户请求的是静态页面,系统调用,内核为其准备缓冲,内核将数据加载至缓冲区,将数据再复制到进程地址空间,worker进程再将数据封装成一个响应报文(http首部封装实际是在内核中完成的),将报文再传至内核,内核封装tcp首部、ip首部,再响应给用户,可见数据是从硬盘-->内核空间-->用户空间-->内核空间,若封装时只在内核中完成,不用到用户空间,封装完后只告诉worker已替你响应过去,这样就避免了两次复制(内核空间-->用户空间,用户空间-->内核空间),内核任何时候与进程交互都是复制,除非内存共享,当并发连接很多时sendfile功能若关闭的话将严重影响系统性能,由此sendfile实现数据从硬盘到内核空间直接响应给client,sendfile只支持小文件,sendfile64支持大文件);
支持accept_filters(连接过滤器,只接受有限的连接),tcp_defer_accept;
10K个非活跃的HTTP keepalive连接仅占用2.5M内存(事件驱动机制,只扫描活动连接,对于非活动连接不作管理,nginx只需很少内存就能为一个连接维持一个文件描述符);
6、nginx的基本工作框架(一个主进程和多个工作进程,工作进程以非特权用户运行):
master(主进程,监控worker进程(或叫worker线程)启动是否够数目及运行状况是否正常等,以管理员身份启动(web服务默认80port,1023以下端口只有管理员才能使用))
worker(worker进程真正负责响应用户请求,是master的子进程,由master负责启动,以普通用户身份运行(系统安全性得到提升))
cache loader(与缓存相关的进程)
nginx高度模块化(master、worker进程处理web应用非常简单,像额外的其它功能,例如ssl、flv、gzip、fastcgi等都不是由nginx自己提供,而是由额外的模块提供,在nginx内部会调用这些模块,用到哪个装载哪个,就连它自己的基本功能也是模块化的,如接入的请求是get、put、请求哪些内容等,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进程完成的任务:缓存的失效及过期检验
二、操作:
环境:
[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 “DesktopPlatform” “Desktop Platform Development” "Server Platform Development" “Development tools” “Compatibility libraries”
[root@node1 ~]# yum -y install pcre-devel(perl扩展的正则表达式)
[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 = 150(the maximum number of children that can be alive at the same time)
pm.start_servers = 8(the number of children created on startup)
pm.min_spare_servers = 5(the 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 = 10(the 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 php(1个masterprocess,8个children process)
4、整合nginx和php:
[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: [确定]
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
三、扩展:架构方面
nginx支持FS的AIO,支持mmap,支持event-driven(比httpd支持的连接数多的多)
一主机(4G内存,2颗CPU),若使用nginx,支持3-5W个并发连接一点问题都没有;而要使用httpd,在prefork模型下,一个进程响应一个请求,最多同时并发1024个,再多就拒绝响应了,若在worker模型下(虽一个线程响应一个请求,一个进程会生成并管理多个线程,这只不过进程切换数少了些),并发连接数并没提升(因为select机制的限制)
nginx程序本身才1M,但功能单一,众多httpd的功能nginx不支持;而httpd有众多的模块,整体与nginx比,它是重量级
很少将nginx拿来用作web-server,因为它的功能不够丰富,stable上讲,一个线程响应多个请求固然很好,但若一个线程崩溃了,N个请求都玩完,像httpd一个进程响应一个请求,一个进程崩溃不影响其它请求,httpd的prefork模型要稳定的多;所以nginx通常拿来做前端的reverse proxy反向代理(如mysql-proxy,它本身不是mysql-server,但通过它能连后后端的mysql-server,而nginx是web的proxy,解析http协议),而httpd用来做后端的web-server
web object分静态和动态(静态,html文档、图片、CSS样式表等;动态,php脚本)
nginx作reverse proxy(可精确理解web协议、各种用户操作、URLrewrite、资源重定向等);LVS并不能理解哪些是静态内容哪些是动态内容,在高级功能设定上不具备
2颗CPU、4G内存的主机(nginx作web-server,若请求的是5-10K的图片,每秒处理5000-10000个,另还取决于带宽及磁盘IO;若响应动态内容大约1000个,要占据CPU时间生成html文档才返回)
varnish(用来缓存动态内容,如64G内存500G固态硬盘,一般要命中率要达到50%)
memcached(缓存mysql-server的内容,它是个旁路缓存,php程序要用到数据先找memcached,memcached中没有得自己找mysql-server,返回时先到memcached中,再返回给php程序)
可在haproxy前端加MQ(rabbitmq或zeromq,可理解为连接池,所有连接进来并不是马上提供服务,而是先缓存下来;在异步管理中,MQ很常用)
地理位置法则,缓存策略(CDN,content distribute network),将所有静态内容(包括动态内容的缓存)放至全国各地机房中的varnish缓存集群中,做智能DNS解析用户请求,如华北区的用户直接解析到离他最近的缓存服务器集群上,命中率要90%(缓存放在用户的家门口,访问时直接返回),做内容路由(如华北区用户访问最近的缓存中没有,到其它区域的缓存中找,若都没,再找原始服务器)
淘宝在抢购时tps(mysql的每秒事务量transaction per second,5W多),一般500个并发连接都扛不住
NOSQL(工作在内存,支持事务,如redis、mongodb),若要实时写操作,而且量很大,就要用到NOSQL,内存数据库,需要持久存储(将结构化数据放至mysql中,非结构化或半结构化数据放在FS上)并用来后期分析,以后在FS上对数据进行分析就不像mysql那样,要检索有效数据集来分析,而要对文件全量提取进行分析,要用并行处理平台才能完成(如hadoop)
注:NOSQL解决抢购时随时更新商品剩余量,在前端的内存服务器(在内存中执行事务,小事务,每次抢购完就结束,快速执行,快速反馈,对于商品的更新只要事务一完成立即更新)
redis(作为计数器,快速法则响应,如微博中有多少人转载、评论等要计数,在内存工作,将数据同步到硬盘,并且实现将数据转存至mysql中(持久化存储法则);而memcached只提供缓存,不能保存数据)
mongodb(对于写数据多时使用)
日志服务器(可将日志放在mysql-server中,mysql放在更高IO能力的硬盘上,再导入到分布式FS上;或将日志放在10个server上,在每个server上布署个日志小程序(facebook有个日志收集器,让每个server管理自己的日志),每天将日志读一份放至分布式FS上,在分布式FS上通过并行处理程序完成日志分析(如完成前一天抢购中哪个区的用户,哪个店铺的成交量最多等,每天的日志可能上G多达则T;hadoop对这些数据作批处理,hadoop的分析是异步的,而且有两段执行过程(各自分开执行))
若将一个机房的主机做成高可用集群(云平台),在云平台上开一堆的虚拟机,各种虚拟机完成各种任务(不同的服务在不同的虚拟机上),哪一个虚机故障直接kill掉(进程而已),再启动一个新的,或者实时迁移到其它虚拟机上;对于共享存储(监控存储解决方案,任何一个虚拟机开了,自动在存储上找个空间,创建虚拟磁盘,实现虚拟机进程的启动);hadoop不能放在虚拟机上,hadoop需要大师的磁盘IO,虚拟机的IO能力是很差的,所以要用云+hadoop实现;另可将mysql放在云里,云有三种模型(iaas、paas、saas,将mysql做成saas向外提供软件服务;这样mysql、hadoop、web、云各自都模块化
架构师:
网络
自动化运维编程(shell、python)
各服务的性能、特性、适用场景、优缺点等要做到通盘掌握
DBA能力
服务运维能力
运维经验
以上是学习《马哥运维课程》做的笔记。
本文出自 “Linux运维重难点学习笔记” 博客,谢绝转载!
原文:http://jowin.blog.51cto.com/10090021/1729562