Nginx由于它的稳定性、丰富的模块库、灵活的配置和低系统资源的消耗而闻名.业界一致认为它是Apache2.2+mod_proxy_balancer的轻量级代替者,不只是由于响应静态页面的速度很是快,并且它的模块数量达到Apache的近2/3。对proxy和rewrite模块的支持很完全,还支持mod_fcgi、ssl、vhosts ,适合用来作mongrel clusters的前端HTTP响应。
nginx和Apache同样使用模块化设计,nginx模块包括内置模块和第三方模块,其中内置模块中包含主模块和事件模块。php
nginx处理请求逻辑图
css
处理静态小文件(小于1M),nginx和lighttpd比Apache更有优点,lighttpd最强。html
通常PHP引擎支持的并发参考值300-1000,JAVA引擎并发300-1000,数据库的并发300-1000.前端
静态业务:高并发、采用nginx,lighttpd,根据本身的掌握程度或公司的要求。
动态业务:采用nginx和Apache都可。
既有静态业务又有动态业务:nginx或Apache,不要多选要单选。
动态业务能够由前端代理(haproxy),根据页面元素的类型,向后转发相应的服务器进行处理。
思想:咱们工做都不要追求一步到位,知足需求的前提下,先用,而后逐步完善。
提示:nginx作web(Apache,lighttpd)、反向代理(haproxy,lvs,nat)及缓存服务器(squid)也是不错的。
最终建议:对外的业务nginx,对内的业务Apache(yum httpd mysql-server php)。python
yum install pcre pcre-devel -y yum install openssl openssl-devel -y
使用./configure --help
查看各个模块的使用状况,使用--without-http_ssi_module
的方式关闭不须要的模块。可使用--with-http_perl_modules
方式安装须要的模块。mysql
编译命令linux
tar -zxf nginx-1.10.1.tar.gz cd nginx-1.10.1/ ./configure --prefix=/data/app/nginx-1.10.1 --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module useradd nginx -M -s /sbin/nologin make && make install ln -s /data/app/nginx-1.10.1 /data/app/nginx
测试nginx配置文件是否正常nginx
/data/app/nginx/sbin/nginx -t nginx: the configuration file /data/app/nginx-1.10.1/conf/nginx.conf syntax is ok nginx: configuration file /data/app/nginx-1.10.1/conf/nginx.conf test is successful
启动nginx服务器web
/data/app/nginx/sbin/nginx -t ##检查配置文件 /data/app/nginx/sbin/nginx ##肯定nginx服务 netstat -lntup |grep nginx ## 检查进程是否正常 curl 192.168.56.12 ## 确认结果
nginx其余命令正则表达式
nginx -s signal signal: stop — fast shutdown quit — graceful shutdown reload — reloading the configuration file reopen — reopening the log files 用来打开日志文件,这样nginx会把新日志信息写入这个新的文件中
/data/app/nginx/sbin/nginx -V
查看已经编译的参数。
使用kill命令操做nginx。格式:kill -信号 PID
信号名称
例子
kill -QUIT `cat /data/app/nginx/nginx.pid` kill -HUP `cat /data/app/nginx/nginx.pid`
worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location / { root html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
### 测试配置文件是否正常 shell> /data/app/nginx/sbin/nginx -t nginx: the configuration file /data/app/nginx-1.10.3/conf/nginx.conf syntax is ok nginx: configuration file /data/app/nginx-1.10.3/conf/nginx.conf test is successful shell> curl -I http://192.168.56.12 HTTP/1.1 200 OK
开启nginx的监控服务
#设定查看Nginx状态的地址 location /NginxStatus { stub_status on; access_log off; # auth_basic "NginxStatus"; # auth_basic_user_file conf/htpasswd; }
auth_basic_user_file conf/htpasswd; 用来指定密码文件的位置。
yum install -y httpd-tools /usr/local/apache/bin/htpasswd -c /data/app/nginx/conf/htpasswd biglittleant New password:
完成后会在/data/app/nginx/conf/
目录下生成htpasswd
文件。
# curl http://127.0.0.1/NginxStatus Active connections: 11921 server accepts handled requests 11989 11989 11991 Reading: 0 Writing: 7 Waiting: 42
Waiting — 开启 keep-alive 的状况下,这个值等于 active – (reading+writing), 意思就是 Nginx 已经处理完正在等候下一次请求指令的驻留链接.
nginx_status_fun(){ NGINX_PORT=$1 NGINX_COMMAND=$2 nginx_active(){ /usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/NginxStatus/" 2>/dev/null| grep 'Active' | awk '{print $NF}' } nginx_reading(){ /usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/NginxStatus/" 2>/dev/null| grep 'Reading' | awk '{print $2}' } nginx_writing(){ /usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/NginxStatus/" 2>/dev/null| grep 'Writing' | awk '{print $4}' } nginx_waiting(){ /usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/NginxStatus/" 2>/dev/null| grep 'Waiting' | awk '{print $6}' } nginx_accepts(){ /usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/NginxStatus/" 2>/dev/null| awk NR==3 | awk '{print $1}' } nginx_handled(){ /usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/NginxStatus/" 2>/dev/null| awk NR==3 | awk '{print $2}' } nginx_requests(){ /usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/NginxStatus/" 2>/dev/null| awk NR==3 | awk '{print $3}' } case $NGINX_COMMAND in active) nginx_active; ;; reading) nginx_reading; ;; writing) nginx_writing; ;; waiting) nginx_waiting; ;; accepts) nginx_accepts; ;; handled) nginx_handled; ;; requests) nginx_requests; esac }
net.ipv4.tcp_fin_timeout = 2 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_keepalive_time = 600 net.ipv4.ip_local_port_range = 4000 65000 net.ipv4.tcp_max_syn_backlog = 16384 net.ipv4.tcp_max_tw_buckets = 36000 net.ipv4.route.gc_timeout = 100 net.ipv4.tcp_syn_retries = 1 net.ipv4.tcp_synack_retries = 1 net.core.somaxconn = 16384 net.core.netdev_max_backlog = 16384 net.ipv4.tcp_max_orphans = 16384 #如下参数是对iptables防火墙的优化,防火墙不开会提示,能够忽略不理。 net.ipv4.ip_conntrack_max = 25000000 net.ipv4.netfilter.ip_conntrack_max=25000000 net.ipv4.netfilter.ip_conntrack_tcp_timeout_established=180 net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait=120 net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait=60 net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait=120
./configure: error: the HTTP rewrite module requires the PCRE library. You can either disable the module by using —without-http_rewrite_module(伪静态) option, or install the PCRE library into the system, or build the PCRE library statically from the source with nginx by using --with-pcre=<path> option
yum install pcre pcre-devel -y
libpcre.so.1
解决:
ln -s /ser/local/lib/libpcre.so.l /lib64
。--with-pcre=/data/tools/pcre-8.33
。yum install pcre-devel -y
不会出现上述报错。[root@centos6 tools]# /application/nginx/sbin/nginx nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) nginx: [emerg] still could not bind()
解决办法:(由于开启了Apache服务)
[root@nfs-client application]# lsof -i :80 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME httpd 35833 www 4u IPv6 129886 0t0 TCP *:http (LISTEN) httpd 35834 www 4u IPv6 129886 0t0 TCP *:http (LISTEN) httpd 98511 root 4u IPv6 129886 0t0 TCP *:http (LISTEN) [root@nfs-client application]# /application/apache/bin/apachectl stop [root@nfs-client application]# /application/nginx/sbin/nginx ##从新启动nginx。
$args
:这个变量等于请求行中的参数,同$query_string。$is_args
: 若是已经设置$args
,则该变量的值为"?",不然为""。$content_length
: 请求头中的Content-length字段。$content_type
: 请求头中的Content-Type字段。$document_uri
: 与$uri相同。$document_root
: 当前请求在root指令中指定的值。$host
: 请求主机头字段,不然为服务器名称。$http_user_agent
: 客户端agent信息。$http_cookie
: 客户端cookie信息。$limit_rate
: 这个变量能够限制链接速率。$request_method
: 客户端请求的动做,一般为GET或POST。$remote_addr
: 客户端的IP地址。$remote_port
: 客户端的端口。$remote_user
: 已经通过Auth Basic Module验证的用户名。$request_uri
: 请求的URI,带参数$request_filename
: 当前请求的文件路径,由root或alias指令与URI请求生成。$scheme
: 所用的协议,好比http或者是https,好比rewrite ^(.+)$ $scheme://example.com$1 redirect;
。$server_protocol
: 请求使用的协议,一般是HTTP/1.0或HTTP/1.1。$server_addr
: 服务器地址,在完成一次系统调用后能够肯定这个值。$server_name
: 服务器名称。$server_port
: 请求到达服务器的端口号。$request_uri
: 包含请求参数的原始URI,不包含主机名,如:/foo/bar.php?arg=baz
。$uri
: 不带请求参数的当前URI,$uri不包含主机名,如/foo/bar.html
可能和最初的值有不一样,好比通过重定向之类的。例子:
访问连接是:http://localhost:88/test1/test2/test.php 网站路径是:/var/www/html $host:localhost $server_port:88 $request_uri:http://localhost:88/test1/test2/test.php $document_uri:/test1/test2/test.php $document_root:/var/www/html $request_filename:/var/www/html/test1/test2/test.php
商业版的 nginx plus 经过他的 ngx_http_status_module 提供了比 nginx 更多的监控指标,能够参看 http://demo.nginx.com/status.html
nginx 的 access log 中能够记录不少有价值的信息,经过分析 access log,能够收集到不少指标。
python 编写的 linux 工具 ngxtop 就实现了对 access log 的分析功能。
select最先于1983年出如今4.2BSD中,它经过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程能够得到这些文件描述符从而进行后续的读写操做。
select目前几乎在全部的平台上支持,其良好跨平台支持也是它的一个优势,事实上从如今看来,这也是它所剩很少的优势之一。
select的一个缺点在于单个进程可以监视的文件描述符的数量存在最大限制,在Linux上通常为1024,不过能够经过修改宏定义甚至从新编译内核的方式提高这一限制。
另外,select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增加。同时,因为网络响应时间的延迟使得大量TCP链接处于非活跃状态,但调用select()会对全部socket进行一次线性扫描,因此这也浪费了必定的开销。
poll在1986年诞生于System V Release 3,它和select在本质上没有多大差异,可是poll没有最大文件描述符数量的限制。
poll和select一样存在一个缺点就是,包含大量文件描述符的数组被总体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增长而线性增大。
另外,select()和poll()将就绪的文件描述符告诉进程后,若是进程没有对其进行IO操做,那么下次调用select()和poll()的时候将再次报告这些文件描述符,因此它们通常不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。
直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具有了以前所说的一切优势,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。
epoll能够同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,若是咱们没有采起行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,可是代码实现至关复杂。
epoll一样只告知那些就绪的文件描述符,并且当咱们调用epoll_wait()得到就绪文件描述符时,返回的不是实际的描述符,而是一个表明就绪描述符数量的值,你只须要去epoll指定的一个数组中依次取得相应数量的文件描述符便可,这里也使用了内存映射(mmap)技术,这样便完全省掉了这些文件描述符在系统调用时复制的开销。
另外一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用必定的方法后,内核才对全部监视的文件描述符进行扫描,而epoll事先经过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用相似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便获得通知。
nginx主进程读取配置文件,若是发现配置文件变动,会建立一个新的主进程,而后同时旧的进程,及旧的子进程关闭,旧进程会拒绝新的链接,服务到本身的链接结束,而后关闭。
Nginx的高并发得益于其采用了epoll模型,与传统的服务器程序架构不一样,epoll是linux内核2.6之后才出现的。下面经过比较Apache和Nginx工做原理来比较。
传统Apache都是多进程或者多线程来工做,假设是多进程工做(prefork),apache会先生成几个进程,相似进程池的工做原理,只不过这里的进程池会随着请求数目的增长而增长。对于每个链接,apache都是在一个进程内处理完毕。具体是 recv(),以及根据 URI 去进行磁盘I/O来寻找文件,还有 send()都是阻塞的。其实说白了都是 apche 对于套接字的I/O,读或者写,可是读或者写都是阻塞的,阻塞意味着进程就得挂起进入sleep状态,那么一旦链接数不少,Apache必然要生成更多的进程来响应请求,一旦进程多了,CPU对于进程的切换就频繁了,很耗资源和时间,因此就致使apache性能降低了,说白了就是处理不过来这么多进程了。其实仔细想一想,若是对于进程每一个请求都没有阻塞,那么效率确定会提升不少。
Nginx采用epoll模型,异步非阻塞。对于Nginx来讲,把一个完整的链接请求处理都划分红了事件,一个一个的事件。好比accept(), recv(),磁盘I/O,send()等,每部分都有相应的模块去处理,一个完整的请求多是由几百个模块去处理。真正核心的就是事件收集和分发模块,这就是管理全部模块的核心。只有核心模块的调度才能让对应的模块占用CPU资源,从而处理请求。拿一个HTTP请求来讲,首先在事件收集分发模块注册感兴趣的监听事件,注册好以后不阻塞直接返回,接下来就不须要再管了,等待有链接来了内核会通知你(epoll的轮询会告诉进程),cpu就能够处理其余事情去了。一旦有请求来,那么对整个请求分配相应的上下文(其实已经预先分配好),这时候再注册新的感兴趣的事件(read函数),一样客户端数据来了内核会自动通知进程能够去读数据了,读了数据以后就是解析,解析完后去磁盘找资源(I/O),一旦I/O完成会通知进程,进程开始给客户端发回数据send(),这时候也不是阻塞的,调用后就等内核发回通知发送的结果就行。整个下来把一个请求分红了不少个阶段,每一个阶段都到不少模块去注册,而后处理,都是异步非阻塞。异步这里指的就是作一个事情,不须要等返回结果,作好了会自动通知你。
select/epoll的特色
select的特色:select 选择句柄的时候,是遍历全部句柄,也就是说句柄有事件响应时,select须要遍历全部句柄才能获取到哪些句柄有事件通知,所以效率是很是低。可是若是链接不多的状况下, select和epoll的LT触发模式相比, 性能上差异不大。
这里要多说一句,select支持的句柄数是有限制的, 同时只支持1024个,这个是句柄集合限制的,若是超过这个限制,极可能致使溢出,并且很是不容易发现问题, 固然能够经过修改linux的socket内核调整这个参数。
epoll的特色:epoll对于句柄事件的选择不是遍历的,是事件响应的,就是句柄上事件来就立刻选择出来,不须要遍历整个句柄链表,所以效率很是高,内核将句柄用红黑树保存的。
对于epoll而言还有ET和LT的区别,LT表示水平触发,ET表示边缘触发,二者在性能以及代码实现上差异也是很是大的。
能够举一个简单的例子来讲明Apache的工做流程,咱们平时去餐厅吃饭。餐厅的工做模式是一个服务员全程服务客户,流程是这样,服务员在门口等候客人(listen),客人到了就接待安排的餐桌上(accept),等着客户点菜(request uri),去厨房叫师傅下单作菜(磁盘I/O),等待厨房作好(read),而后给客人上菜(send),整个下来服务员(进程)不少地方是阻塞的。这样客人一多(HTTP请求一多),餐厅只能经过叫更多的服务员来服务(fork进程),可是因为餐厅资源是有限的(CPU),一旦服务员太多管理成本很高(CPU上下文切换),这样就进入一个瓶颈。
再来看看Nginx得怎么处理?餐厅门口挂个门铃(注册epoll模型的listen),一旦有客人(HTTP请求)到达,派一个服务员去接待(accept),以后服务员就去忙其余事情了(好比再去接待客人),等这位客人点好餐就叫服务员(数据到了read()),服务员过来拿走菜单到厨房(磁盘I/O),服务员又作其余事情去了,等厨房作好了菜也喊服务员(磁盘I/O结束),服务员再给客人上菜(send()),厨房作好一个菜就给客人上一个,中间服务员能够去干其余事情。整个过程被切分红不少个阶段,每一个阶段都有相应的服务模块。咱们想一想,这样一旦客人多了,餐厅也能招待更多的人。
无论是Nginx仍是Squid这种反向代理,其网络模式都是事件驱动。事件驱动实际上是很老的技术,早期的select、poll都是如此。后来基于内核通知的更高级事件机制出现,如libevent里的epoll,使事件驱动性能得以提升。事件驱动的本质仍是IO事件,应用程序在多个IO句柄间快速切换,实现所谓的异步IO。事件驱动服务器,最适合作的就是这种IO密集型工做,如反向代理,它在客户端与WEB服务器之间起一个数据中转做用,纯粹是IO操做,自身并不涉及到复杂计算。反向代理用事件驱动来作,显然更好,一个工做进程就能够run了,没有进程、线程管理的开销,CPU、内存消耗都小。
因此Nginx、Squid都是这样作的。固然,Nginx也能够是多进程 + 事件驱动的模式,几个进程跑libevent,不须要Apache那样动辄数百的进程数。Nginx处理静态文件效果也很好,那是由于静态文件自己也是磁盘IO操做,处理过程同样。至于说多少万的并发链接,这个毫无心义。随手写个网络程序都能处理几万的并发,但若是大部分客户端阻塞在那里,就没什么价值。
再看看Apache或者Resin这类应用服务器,之因此称他们为应用服务器,是由于他们真的要跑具体的业务应用,如科学计算、图形图像、数据库读写等。它们极可能是CPU密集型的服务,事件驱动并不合适。例如一个计算耗时2秒,那么这2秒就是彻底阻塞的,什么event都没用。想一想MySQL若是改为事件驱动会怎么样,一个大型的join或sort就会阻塞住全部客户端。这个时候多进程或线程就体现出优点,每一个进程各干各的事,互不阻塞和干扰。固然,现代CPU愈来愈快,单个计算阻塞的时间可能很小,但只要有阻塞,事件编程就毫无优点。因此进程、线程这类技术,并不会消失,而是与事件机制相辅相成,长期存在。
总言之,事件驱动适合于IO密集型服务,多进程或线程适合于CPU密集型服务,它们各有各的优点,并不存在谁取代谁的倾向。