Linux运维 第三阶段 (七) nginx1

 

一、相关概念:

nginxengine-Xnginx.orgperformancestabilityrich featuresimpleconfigurationlow resource consumption):

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

reverse proxy(反向代理服务器,mail proxyservertcp 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,可能无法应付;

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

                             wKioL1aI6RigeIcxAABlS7OMhAM133.jpg

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

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

wKiom1aI6QbhggT9AABJkX0ozKY261.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、看清以下这五种模型:

wKioL1aI6T3BZL1AAAAknmW95wY413.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):blockedseparately in wait and copyIO复用,asynchronous-blocking

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

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

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

wKioL1aI6U_CzoaJAAApelRbK9Y310.jpg

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

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

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

wKiom1aI6UDixvO8AAAgcJ-Z1EA018.jpg

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

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

wKiom1aI6VCg5I6TAAAnEdtYHh4934.jpg

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

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

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

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

wKioL1aI6YHBf5oIAAApNjl1dNo817.jpg

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

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

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

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

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

wKioL1aI6ZvBaLYVAAAm0EohyAo968.jpg

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

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

wKioL1aI6ceDLmKgAAAdtS1VyT0654.jpg

wKioL1aI6dqy5vxwAACF0CkixaI242.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分析有无syntaxerror,就算重新装载有语法错误也不会影响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方式存储并查找),所以NOSQL虽很牛但无法替代RDB

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

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

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

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

 

通过以上介绍,对于mysql查询和phpsession都可缓存在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

 

 

二、操作(1location的使用;2、基于IP的访问控制;3、基于用户的访问控制;4、在网页端查看状态;5SSL通信;6、基于域名的虚拟主机):

安装请参考《第三阶段(十五)理解LNMP

环境:

[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的配置有不同的上下文(分段配置,每段支持的指令及完成的功能是不同的,每条指令用分号结尾)

main{}(核心配置,对任何功能都生效的配置,如httpmailreverseproxy

http{}(对web-server有效)

mail{}(对mail-server有效)

server{}(通常与http一起,是http的子段)

upstream{}(是http的子段)

 

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

[root@node1 ~]# vim /etc/nginx/nginx.conf(默认已启用或注销的指令是根据编译时所指定的选项自动修改的)

#user nobody;(已使用nginx用户,nginx组)

worker_processes  2;(启动的worker进程数,与CPU相关,每个核心绑定一个进程;若负载以CPU密集型为主(如ssl或压缩应用),worker数应与CPU数相同;若负载以IO密集型为主(如下载站点要响应大量内容给client),worker数应是CPU数的1.5倍或2倍)

#error_log logs/error.log;

#error_log logs/error.log  notice;

#error_log logs/error.log  info;

#pid       logs/nginx.pid;

events {

   worker_connections  1024;event-driven中每个worker所支持的连接数,当前所支持的连接数worker_processes*worker_connections=2*1024

}

http {

   include       mime.types;multiinternet mail extendweb-servermime识别非文本文档)

   default_type  application/octet-stream;

   #log_format  main  '$remote_addr - $remote_user [$time_local]"$request" '

   #                  '$status$body_bytes_sent "$http_referer" '

   #                 '"$http_user_agent" "$http_x_forwarded_for"';

   #access_log  logs/access.log  main;

   sendfile        on;(0复制响应)

   #tcp_nopush     on;tcp_nodelay,与nagle算法有关)

#keepalive_timeout  0;

   keepalive_timeout  65;

   #gzip  on;(响应时压缩,可节省带宽)

   server {(每个server段定义一个虚拟主机,就算只有一个也要定义在server中)

       listen       80;

        server_name localhost;(可指定基于域名的虚拟主机)

       #charset koi8-r;

       #access_log logs/host.access.log  main;

       location / {

           root   html;

           index  index.php index.htmlindex.htm;

       }

       #error_page  404              /404.html;

       # redirect server error pages to the static page /50x.html

       #

       error_page   500 502 503 504  /50x.html;

       location = /50x.html {

           root   html;

       }

    }

  ……

}

 

1location的使用:

httpd的配置文件中DocumentRoot来指定本地FS中的目录位置,再通过Directory段定义具体哪个目录下的网页文件,Location是基于URI路径(相对DocumentRoot指定的位置而言);而nginx的配置文件中是用root指定本地FS中的位置,紧跟location后的路径是相对于root指定的位置而言的,例如:

举例1(此例中root指定的是相对位置,相对于安装路径而言,在安装nginx时指定的安装路径是/usr,所以root指定的路径是本地FS中的/usr/htmllocation后的/指的是/usr/html/下的网页文件):

location /  {

 root  html;

 index  index.html  index.htm;

}

举例2

location /URI  {

 root  /web/htdocs;

 index  index.html

}

 

location语法(用在server段中或嵌套在location段中):

location [=|~|~*|^~]  URI  {……}

优先级顺序:=-->^~-->~ or ~*

用法一:

location  URI  {……}(表示{}内定义的内容对URI路径下的所有对象生效,包括当前路径及其子路径)

用法二:

location =  URI  {……}(表示精确匹配指定的当前路径,不包括子路径,URI若是个目录名,则对其下的资源不生效)

用法三:

location ~  URI  {……}(模式匹配URI,可用正则表达式,~表示区分字符大小写)

location ~*  URI  {……}(模式匹配URI,可用正则表达式,~*表示不区分大小写)

location ^~  URI  {……}(不使用正则表达式,做逐字符匹配)


官方举例:

location =  /  { A }

location /  { B }

location /documents/  { C }

location ^~  /p_w_picpaths/  { D }

location ~*  \.(gif|jpg|jpeg)$  { E }

访问时:

http://DOMAIN/(选A配置)

http://DOMAIN/index.html(选B配置)

http://DOMAIN/documents/documents.html(选C配置)

http://DOMAIN/p_w_picpaths/1.gif(选D配置)

http://DOMAIN/documents/1.gif(选E配置)

 

2、基于IP的访问控制:

[root@node1 ~]# cd /etc/nginx

[root@node1 nginx]# vim nginx.conf

location /bbs  {

 root  /web;

 index  index.html;

 allow  192.168.41.135;

 deny  all;

}

[root@node1 nginx]# mkdir /web/bbs -pv

mkdir: 已创建目录"/web/bbs"

[root@node1 nginx]# cd /web/bbs

[root@node1 bbs]# echo "bbs.magedu.com" > index.html

[root@node1 bbs]# 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                                           [确定]

 

测试:

[root@node2 ~]# ifconfig | grep -A 1 eth0

eth0     Link encap:Ethernet  HWaddr00:0C:29:2A:AF:97 

         inet addr:192.168.41.135 Bcast:192.168.41.255 Mask:255.255.255.0

[root@node2 ~]# elinks -dump http://192.168.41.134/bbs

  bbs.magedu.com

 

[root@node3 ~]# ifconfig | grep -A 1 eth0

eth0     Link encap:Ethernet  HWaddr00:0C:29:F6:B1:39 

          inet addr:192.168.41.136  Bcast:192.168.41.255  Mask:255.255.255.0

[root@node3 ~]# elinks -dumphttp://192.168.41.134/bbs

                                 403 Forbidden

 

  --------------------------------------------------------------------------

 

                                  nginx/1.8.0

 

3、基于用户的访问控制(要用到httpdhtpasswd这个工具,注意不能让httpd启动或开机自启):

[root@node1 bbs]# cd

[root@node1 ~]# rpm -q httpd

httpd-2.2.15-26.el6.x86_64

[root@node1 ~]# which htpasswd

/usr/bin/htpasswd

[root@node1 ~]# htpasswd -c -m /etc/nginx/.userstom-cCreate the passwdfile-mUse MD5 encryption for passwords

New password:

Re-type new password:

Adding password for user tom

[root@node1 ~]# htpasswd -m /etc/nginx/.users jerry

New password:

Re-type new password:

Adding password for user jerry

[root@node1 ~]# cat /etc/nginx/.users

tom:$apr1$0Z/OxWFF$nPxPo.BH2Cg94oWcHYBvk0

jerry:$apr1$l/7bqqw7$SiJCmJUHg4Gj29/pdrjtf.

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

       location /bbs {

           root    /web;

           index   index.html;

           auth_basic  "restrictedArea...";

           auth_basic_user_file /etc/nginx/.users;

       }

[root@node1 ~]# nginx -t(检测配置文件语法是否正确,也可用#service nginx configtest

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

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

[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                                           [确定]

wKioL1aI7A_ge1q1AABsGrxiITw836.jpg

wKiom1aI7AjTMTbpAABIe_uIbDY713.jpg

 

4、查看状态:

[root@node1 ~]# vim /etc/nginx/nginx.conf(添加如下段)

       location /status {

           stub_status on;

       }

[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                                           [确定]

wKioL1aI7D2DyCaoAABSx-K1E9E556.jpg

注:27 27 31分别表示:已接受的连接个数;已处理的连接个数;已处理的请求个数(连接和请求是两码事,长连接可能有多个请求)

reading(正在读取其首部请求的个数)

writing(正在读取其主体的请求的个数,正处理着其请求内容的个数,正在向client发送响应的个数)

waiting(长连接模式保持的连接的个数)

 

5SSL通信(CA服务器与nginx服务在同一主机):

[root@node1 ~]# cd /etc/nginx

[root@node1 nginx]# vim nginx.conf(在此配置文件末将以下启用(光标定位至#server处,在vim末行模式下输入:.,$s/^\([[:space:]]\)*#/\1/g),并修改如下)

    #HTTPSserver

   server {

       listen       443 ssl;

       server_name  localhost;

       ssl_certificate     /etc/nginx/ssl/nginx.csr;

       ssl_certificate_key /etc/nginx/ssl/nginx.key;

       ssl_session_cache   shared:SSL:1m;

       ssl_session_timeout  5m;

       ssl_ciphers  HIGH:!aNULL:!MD5;

       ssl_prefer_server_ciphers  on;

       location / {

           root   /web/ssl;

           index  index.html index.htm;

       }

}

 

[root@node1 nginx]# mkdir ssl

[root@node1 nginx]# cd /etc/pki/CA

[root@node1 CA]# (umask 077;openssl genrsa -out private/cakey.pem 2048)(生成ca私钥)

Generating RSA private key, 2048 bit longmodulus

......................................................+++

..............................................................................................+++

e is 65537 (0x10001)

 

[root@node1 CA]# openssl req -new -x509 -key private/cakey.pem -out cacert.pemca自签证书)

……

Country Name (2 letter code) [XX]:CN

State or Province Name (full name) []:SH

Locality Name (eg, city) [Default City]:SH

Organization Name (eg, company) [DefaultCompany Ltd]:itownet

Organizational Unit Name (eg, section)[]:TECH

Common Name (eg, your name or your server'shostname) []:ca.magedu.com

Email Address []:ca@magedu.com

[root@node1 CA]# touch index.txt

[root@node1 CA]# echo 01 > serial

[root@node1 CA]# ls

cacert.pem certs  crl  index.txt newcerts  private  serial

 

[root@node1 CA]# cd /etc/nginx/ssl

[root@node1 ssl]# (umask 077;openssl genrsa -out nginx.key 1024)(本机服务nginx私钥)

Generating RSA private key, 1024 bit longmodulus

.++++++

...++++++

e is 65537 (0x10001)

[root@node1 ssl]# openssl req -new -key nginx.key -out nginx.csr(本机证书签署请求)

……

Country Name (2 letter code) [XX]:CN

State or Province Name (full name) []:SH

Locality Name (eg, city) [Default City]:SH

Organization Name (eg, company) [DefaultCompany Ltd]:itownet

Organizational Unit Name (eg, section)[]:TECH

Common Name (eg, your name or your server'shostname) []:nginx.magedu.com

Email Address []:nginx@magedu.com     

Please enter the following 'extra'attributes

to be sent with your certificate request

A challenge password []:

An optional company name []:

 

[root@node1 ssl]# openssl ca -in nginx.csr -out nginx.crt -days 365(签署证书)

……

1 out of 1 certificate requests certified,commit? [y/n]y

Write out database with 1 new entries

Data Base Updated

[root@node1 ssl]# ls

nginx.crt nginx.csr  nginx.key

 

[root@node1 ssl]# cd

[root@node1 ~]# mkdir /web/ssl

[root@node1 ~]# echo "ssl.magedu.com" > /web/ssl/index.html

[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                                           [确定]

wKioL1aI7GfyJInLAABGOYv9YMI133.jpg

6、基于域名的虚拟主机:

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

   server {

       listen       80;

       server_name  www.magedu.com;

       location / {

           root   html;

           index  index.html index.htm;

       }

   server {

       listen   80;

       server_name  www.a.org;

       location / {

           root  /web/a.org;

           index  index.html;

       }

    }

[root@node1 ~]# mkdir /web/a.org

[root@node1 ~]# echo "www.a.org"  > /web/a.org/index.html

[root@node1 ~]# echo "www.magedu.com" >> /usr/html/index.html

[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                                           [确定]

 

测试:

win下将c:\Windows\System32\drivers\etc\hosts.txt文件追加两行

192.168.41.134   www.magedu.com

192.168.41.134   www.a.org

wKiom1aI7FjCedQDAABV483q3RA687.jpg

wKiom1aI7FiBU4LmAABERJRHJIU965.jpg

以上是学习《马哥运维课程》做的笔记》