龙空技术网

nginx系列之一:nginx入门

姓文刀 264

前言:

当前我们对“nginxluavarnish”都比较讲究,咱们都需要分析一些“nginxluavarnish”的相关文章。那么小编在网上汇集了一些对于“nginxluavarnish””的相关知识,希望你们能喜欢,大家快快来了解一下吧!

一、nginx 功能介绍

Nginx因为它的稳定性、丰富的模块库、灵活的配置和低系统资源的消耗而闻名.业界一致认为它是Apache2.2+mod_proxy_balancer的轻量级代替者,不仅因为响应静态页面的速度非常快,而且它的模块数量是Apache的2/3。对proxy和rewrite模块的支持很彻底,还支持mod_fcgi、ssl、vhosts ,适合用做mongrel clusters前端HTTP响应。

nginx和Apache一样用模块化设计,nginx模块包括内置模块和第三方模块,其中内置模块中包含主模块和事件模块。

nginx处理请求逻辑图

二、nginx可以提供的服务

web 服务.

负载均衡 (反向代理)

web cache(web 缓存)

三、nginx 的优点

高并发。静态小文件

占用资源少。2万并发、10个线程,内存消耗几百M。

功能种类比较多。web,cache,proxy。每一个功能都不是特别强。

支持epoll模型,使得nginx可以支持高并发。

nginx 配合动态服务和Apache有区别。(FASTCGI 接口)

利用nginx可以对IP限速,可以限制连接数。

配置简单,更灵活。

四、nginx应用场合

静态服务器(图片,视频服务),另个lighttpd。并发几万,html,js,css,flv,jpg,gif等。

动态服务,nginx—fastcgi 方式运行PHP,jsp。(PHP并发约500-1500,MySQL 并发约300-1500)。

反向代理,负载均衡。日pv2000W以下,都可直接用nginx做代理。

缓存服务。类似 SQUID,VARNISH。

五、主流web服务产品对比说明

5.1 Apache-特性

2.2版本本身稳定强大,据官方说:其2.4版本性能更强。

prefork模式取消了进程创建开销,性能很高。

处理动态业务数据时,因关联到后端的引擎和数据库,瓶颈不在与Apache本身。

高并发时消耗系统资源相对多一些。

基于传统的select模型。

扩展库,DSO方法。

5.2 nginx-特性

基于异步IO模型(epoll,kqueue),性能强,能够支持上万并发。

对小文件支持很好,性能很高(限静态小文件1M)。

代码优美,扩展库必须编译进主程序。

消耗代码资源比较低。

lighttpd(百度贴吧,豆瓣)

基于异步IO模式,性能和nginx相近。

扩展库是SO模式,比nginx要灵活。

8.通过差距(mod_secdownload)可实现文件URL地址加密。

5.3 web服务产品性能对比测试

5.3.1 静态数据性能对比

处理静态文件Apache性能比nginx和lighttpd要差。

nginx在处理小文件优势明显。

处理静态小文件(小于1M),nginx和lighttpd比Apache更有优势,lighttpd最强。

5.3.2 动态数据性能对比

处理动态内容三者相差不大,主要取决于PHP和数据库压力。

当处理动态数据时,三者差距不大,从测试结果看,Apache更有优势。这是因为处理动态数据能力取决于PHP和后端数据的提供服务能力。即瓶颈不在web服务器上。

一般PHP引擎的并发值300-1000,JAVA引擎并发300-1000,数据库并发300-1000.

5.3.3 为什么nginx总体性能比Apache高。

nginx用最新epoll、kqueue网络IO模型,而Apache使用床头的select模式。

目前Linux下能承受高并发访问的squid、Memcached 都采用的是epoll网络IO模型。

5.3.4 如何选择WEB服务器:

静态业务:高并发、采用nginx,lighttpd,根据自己掌握程度或公司要求。

动态业务:采用nginx和Apache均可。

既有静态业务又有动态业务:nginx或Apache,不要多选要单选。

动态业务可由前端代理(haproxy),根据页面元素的类型,向后转发给相应的服务器处理。

思想:工作都不要追求一步到位,满足需求的前提下,先用再逐步完善。

提示:nginx做web(Apache,lighttpd)、反向代理(haproxy、lvs、nat)及缓存服务器(squid)也是不错的。

最终建议:对外业务nginx,对内业务Apache(yum httpd mysql-server php)。

六、nginx实战过程

6.1 安装依赖包

nginx安装依赖GCC、openssl-devel、pcre-devel、zlib-devel软件库。

Pcre全称(Perl Compatible Regular Expressions),中文perl兼容正则表达式,pcre官方站点。

yum install pcre pcre-devel -y

yum install openssl openssl-devel -y

1

2

6.2 开始编译

使用./configure --help查看各模块使用情况,用 --without-http_ssi_module 方式关闭不需的模块。可用 --with-http_perl_modules 方式安装需要的模块。

6.2.1 编译命令

tar -zxf nginx-1.10.1.tar.gz

cd nginx-1.10.1/

./configure --prefix=/data/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/nginx-1.10.1 /data/nginx

1

2

3

4

5

6

7

6.2.2 测试nginx配置文件是否正常

/data/nginx/sbin/nginx -t

nginx: the configuration file /data/nginx-1.10.1/conf/nginx.conf syntax is ok

nginx: configuration file /data/nginx-1.10.1/conf/nginx.conf test is successful

1

2

3

6.2.3 启动nginx服务器

/data/nginx/sbin/nginx -t # 检查配置文件

/data/nginx/sbin/nginx # 确定nginx服务

netstat -lntup |grep nginx # 检查进程是否正常

curl # 确认结果

1

2

3

4

6.2.4 nginx其他命令

nginx -s signal

signal:

stop — fast shutdown

quit — graceful shutdown

reload — reloading the configuration file

reopen — reopening the log files

用来打开日志文件,这样nginx会把新日志信息写入这个新的文件中

1

2

3

4

5

6

7

/data/nginx/sbin/nginx -V 查看已经编译的参数。

使用kill命令操作nginx。格式:kill -信号 PID

信号名称

TERM,INT 快速关闭

QUIT 优雅的关闭,保持吸纳有的客户端连接

HUP 重启应用新的配置文件

USR1 重新打开日志文件

USR2 升级程序

WINCH 优雅的关闭工作进程

例子

kill -QUIT `cat /data/nginx/nginx.pid`

kill -HUP `cat /data/nginx/nginx.pid`

1

2

七、nginx配置文件

配置基础配置文件

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;

}

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

测试配置文件是否正常

shell> /data/nginx/sbin/nginx -t

nginx: the configuration file /data/nginx-1.10.3/conf/nginx.conf syntax is ok

nginx: configuration file /data/nginx-1.10.3/conf/nginx.conf test is successful

shell> curl -I

HTTP/1.1 200 OK

1

2

3

4

5

八、nginx监控

开启 nginx 监控服务

8.1 开启状态页

# 设定查看 Nginx 状态地址

location /status {

stub_status on; # 表示开启stubStatus的工作状态统计功能。

access_log off; # access_log off; 关闭access_log 日志记录功能。

#auth_basic "status"; # auth_basic 是nginx的一种认证机制。

#auth_basic_user_file conf/htpasswd; # 用来指定密码文件的位置。

}

1

2

3

4

5

6

7

8.2 配置登录密码

yum install -y httpd-tools

/usr/local/apache/bin/htpasswd -c /data/nginx/conf/htpasswd biglittleant

New password:

1

2

3

完成后会在 /data/nginx/conf/ 目录下生成 htpasswd 文件。

8.3 访问URL

curl

Active connections: 1

server accepts handled requests

16 16 18

Reading: 0 Writing: 1 Waiting: 0

1

2

3

4

5

6

active connections – 活跃的连接数量

server accepts handled requests — 总共处理了16个连接 , 成功创建16次握手, 总共处理了18个请求

Reading — 读取客户端的连接数:

Writing 响应数据到客户端的数量;

Waiting 开启 keep-alive 的情况下,这个值等于 active – (reading+writing), 即 Nginx 已处理完正在等候下一次请求指令的驻留连接.

8.3 编写zabbix监控脚本

nginx_status_fun(){

NGINX_PORT=$1

NGINX_COMMAND=$2

nginx_active(){

/usr/bin/curl ";$NGINX_PORT"/status/" 2>/dev/null| grep 'Active' | awk '{print $NF}'

}

nginx_reading(){

/usr/bin/curl ";$NGINX_PORT"/status/" 2>/dev/null| grep 'Reading' | awk '{print $2}'

}

nginx_writing(){

/usr/bin/curl ";$NGINX_PORT"/status/" 2>/dev/null| grep 'Writing' | awk '{print $4}'

}

nginx_waiting(){

/usr/bin/curl ";$NGINX_PORT"/status/" 2>/dev/null| grep 'Waiting' | awk '{print $6}'

}

nginx_accepts(){

/usr/bin/curl ";$NGINX_PORT"/status/" 2>/dev/null| awk NR==3 | awk '{print $1}'

}

nginx_handled(){

/usr/bin/curl ";$NGINX_PORT"/status/" 2>/dev/null| awk NR==3 | awk '{print $2}'

}

nginx_requests(){

/usr/bin/curl ";$NGINX_PORT"/status/" 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

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

九、nginx优化

9.1 nginx内核优化

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

十、扩展一: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_body_file`: 客户端请求主体的临时文件名。

$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可能和最初的值有不同,比如经过重定向之类的。它可以通过内部重定向,或者使用index指令进行修改。不包括协议和主机名,例如/foo/bar.html。

例子:

访问链接是:

网站路径是:/var/www/html

$host:localhost

$server_port:88

$request_uri:

$document_uri:/test1/test.php

$document_root:/var/www/html

$request_filename:/var/www/html/test1/test.php

1

2

3

4

5

6

7

8

9

10

nginx plus – ngx_http_status_module

商业版的 nginx plus 通过他的 ngx_http_status_module 提供了比 nginx 更多的监控指标,可以参看

nginx access log 分析

nginx 的 access log 中可以记录很多有价值的信息,通过分析 access log,可以收集到很多指标。

python 编写的 linux 工具 ngxtop 就实现了对 access log 的分析功能。

NDK – ngx_devel_kit

NDK 是一个拓展nginx服务器核心功能的模块,第三方模块开发可以基于它来快速实现。NDK提供函数和宏处理一些基本任务,减轻第三方模块开发的代码量。

nginx lua – lua-nginx-module

nginx的lua模块,通过这个模块,可以对nginx做定制开发

十、扩展二:web服务器事件处理模型

select

select 最早于1983年出现在4.2BSD中,通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使进程可获得这些文件描述符从而进行后续的读写操作。

select 目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一。

select 的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,Linux上一般为1024,不过可通过修改宏定义甚至重新编译内核的方式提升这一限制。

另外,select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增长。同时,由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用 select() 会对所有socket进行一次线性扫描,所以这也浪费了一定开销。

poll

poll 在1986年诞生于System V Release 3,它和 select 在本质上没多大差别,但是 poll 没有最大文件描述符数量的限制。

poll 和select 同样存在的缺点是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间间,而不论文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

另外,select() 和 poll() 将就绪的文件描述符告诉进程后,如果进程没对其进行IO操作,那下次调用 select() 和 poll() 时将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。

epoll

直到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 -s reload 过程

nginx主进程读取配置文件,如果发现配置文件变更,会创建一个新的主进程,然后同时旧的进程,及旧的子进程关闭,旧进程会拒绝新的连接,服务到自己的连接结束,然后关闭。

Apache select模型和 nginx epoll 模型对比讲解

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表边缘触发,两者在性能以及代码实现上差别也是非常大的。

不管是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密集型服务,它们各有各的优势,并不存在谁取代谁的倾向。

标签: #nginxluavarnish