nginx是一个非常好的技术点,javaWeb可以用到。php更是与nginx有lnmp的组合说法。网站开发可以说nginx是炙手可热的一个技术点了。本篇博客把我对nginx的了解记载下来。后续对nginx的了解也会补充到这篇文章。文章会从如下几方面进行记载。
一.nginx是干什么用的?
Nginx是一款轻量级高性能的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器
(1)上述说的反向代理服务器是什么?
反向代理(Reverse Proxy)方式是指以代理服务器(nginx)来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器(真实处理请求的服务器),并将从内部网络上的服务器上得到的结果返回给Internet上请求连接的客户端。
比如你想访问一个PHP网站。此时你只需要访问nginx上配置的域名www.hello.com→(172.30.22.173)并监听listen一个port 80。 而你真实请求的资源来源是 192.3.4.5:9000这个端口给的。
而你打开网站访问的是www.hello.com你根本不知道192.3.4.5:9000 的存在。
正向代理指的是,一个位于客户端和原始服务器之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。
比如你想访问Google 被墙了。这时你可以访问谷歌在中国的代理服务器,然后代理服务器根据你的请求去Google服务器获取相应并返回给你。
二. nginx的优势是什么?
Nginx可以轻松处理高并发。而且占用的资源相对其他服务器较少,CPU消耗时间少。
性价比高。没有那么多线程之间切换的花销。
其他优势:延迟处理,SSL(安全套接字层),静态内容,压缩和缓存,连接和请求限制所需的关键功能,甚至可以从应用程序中传输HTTP媒体流层到更有效的Web服务器层。
三.Nginx的优势是怎么做到的?
Nginx 采用的是多进程(单线程) & 多路IO复用模型(epoll)。使用了 I/O 多路复用技术是”并发事件驱动“的服务器。
1.进程&线程&协程之间的关系是什么?
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,也是基本的执行单元,是操作系统结构的基础。
每个进程都有自己的独立内存空间,不同进程通过(管道,信号量,共享内存,socket,消息队列等)来通信。由于进程比较重量,占据独立的内存,所以进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
线程是操作系统能够进行运算调度的最小单位!!意味着一定是通过多线程来实现多核CPU的利用的。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
关于协程的一些额外描述:
一个线程可以多个协程,即一个内核线程对应多个用户协程(用户进程)。
进程、线程,都是有内核进行调度,有CPU时间片的概念,进行抢占式调度(有多种调度算法)
协程的调度与内核无关,完全有程序进行控制。只能进行非抢占式调度。
线程进程都是同步机制,而协程则是异步。
协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态
在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。
操作系统为了程序运行的高效性,每个线程都有自己缓存Cache等数据,操作系统会自动实现数据的恢复操作,所以线程的切换非常耗性能。
但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住。
以上:如果是单线程所述的异步都是由协程完成的。但是还是由一个CPU调度的,线程是CPU调度的最小单位。即使我们看到的现象是一个异步的现象。但底层实际上是一个线程不断切换协程来实现的。所以单核的切换速度并不变快反而更慢。
一个线程运行多个协程就像一个人推箱子,先推箱子A,推一会推箱子B,箱子AB的位置是被保留现场的。但实际上就一个人。多个人推多个箱子就像多线程多协程工作。
因此如果想利用多核CPU的优势,要么开启多线程,使多协程多线程一起跑。 要么开多进程,使多进程多协程一起跑,来达到异步的效果。
而Nginx就是采用的多进程多协程的事件驱动运行方式来实现的。
golang python 都是有协程的这个概念的。 golang的 goroutines Python的gevent 查了一下发现java居然也有,但是我没用过。。 看网上说java的Quasar也是可以的。
2.什么叫多路IO复用?
IO多路复用,将IO过程分为等待内核数据准备好和读取/写入内核两部分。一个IO函数监控多个IO可读/可写事件,任意1个IO设备准备好时返回(需要代码中轮询查看是哪个IO文件描述符,什么事件),再调用对应的read/write函数操作,减少不必要的等待时间,高效了很多。具体的实现有select、poll和epoll三种。
nginx采用的是epoll ,线程主函数不断接收请求。epoll的实现是通过事件驱动。
通过3个函数来实现,更加高效,当前使用也最多。在epoll_ctl中注册事件到epoll文件描述符中,把fd全部拷贝进内核,而不是在epoll_wait中重复拷贝。实现中内核通过为每个fd指定一个回调函数,当fd就绪时调用回调函数把就绪fd加入一个就绪链表,epoll_wait只需要查看这个就绪链表是否有就绪fd就可。可监控文件描述符数是系统可同时打开文件数
原型:
int epoll_create(int size); //返回epoll文件描述符,size表示要监听的数目 (这个返回的fd要记得close)
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); //epoll事件注册函数
epfd是epoll_create返回的值
op是动作:EPOLL_CTL_ADD/EPOLL_CTL_MOD/EPOLL_CTL_DEL分别表示:注册fd到epfd,修改已注册的fd,从epfd删除1个fd
fd是要监听的fd
event是告诉内核要监听什么事件
struct epoll_event{
__uint32_t events; //epoll events
epoll_data_t data; //user data variable
}
event是宏的集合:EPOLLIN可读;EPOLLOUT可写;EPOLLPRI紧急数据可读;EPOLLERR发生错误;EPOLLHUP被挂断;EPOLLET将EPOLL设置为边缘触发模式;EPOLLONESHOT只监听1次事件
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); //等待事件发生,events是返回的事件链表;maxevents是events链表元素个数,timeout是等待毫秒数(0表示立即返回,非阻塞;正值表示等待的毫秒数;负值表示无限等待,阻塞模式 ),函数返回值是需要处理的事件数,通过events返回需要处理的事件。(通过events[i].data.fd和events[i].events匹配判断)
当某个进程调用epoll_create方法时,linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关。
struct eventpoll
{
struct rb_root rbr;//红黑树的根结点,这棵树中存储着所有添加到epoll中的事件,也就是这个epoll监控的事件。线程间共享内存。一个全局变量相当于。
struct list_head rdllist;//双向链表rdllist保存着将要通过epoll_wait返回给用户的满足条件的事件
};
每一个epoll对象都有一个独立的eventpoll结构体,这个结构体会在内核空间中创造独立的内存,用于存储使用epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂到rbr红黑树中,这样,重复添加的事件就可以通过红黑树高效地识别出来。
当相应的事件发生时会调用这里的回调方法,这个回调方法在内核中叫做ep_poll_callback,它会把这样的事件放到上面的rdllist双向链表中。当调用epoll_wait检查是否有发生事件的连接时,只是检查eventepoll对象中的rdllist双向链表是否有epitem元素而已,如果rdllist链表不为空,则把这里的事件复制到用户态内存中,同时将事件数量返回给用户。
3.什么叫事件驱动?
有了线程之后,我们处理并发最直观的做法就是加线程,为了减少线程的启动时间,我们开始使用线程池,预先启动一些线程。随着并发进一步提高,加上外部请求基本上都是IO密集型,使用线程带来的效益开始下降,也就是说在线程的生命周期中IO等待时间远远大于CPU计算时间,另外每个线程大约需要4M的内存,由于内存的限制,单机线程数不会很多。所以初期的Apache、tomcat服务器通常只能处理几千的并发。为了突破单机下的并发问题,以nginx为首的一种叫事件驱动的方案开始流行。
事件驱动编程的架构是预先设计一个事件循环,这个事件循环程序不断地检查目前要处理的信息,根据要处理的信息运行一个触发函数。
在一个进程一个线程中,server接收客户端的请求。创建function解决IO,如果发生阻塞就执行别的请求。不断的循环。
事件驱动和协程的对比
共性
其目的都是为了消除IO阻塞的问题
都是使用单个或少量的线程,减少线程切换带来的性能消耗
不同点
事件驱动较之比较初级,需要用异步回调的方式来写代码
协程可以使用同步的方式写代码,通过库或者语言的调度来实现并发
四.配置文件相关。
可以在nginx.conf中引用别的配置文件。很清晰。 include /etc/nginx/conf.d/*.conf; 这个指令写在http里面。然后加一个配置文件 xxx.conf 引用外部的配置文件,拆的比较清晰。
server {
listen 80;
server_name www.hello.com;
root /etc/nginx/html;
index index.html;
location / {
}
}
需要注意这里面的 server_name 所配置的域名。如果你想访问得在 /etc/hosts中配置 127.0.0.1 www.hello.com 使本地dns可以解析到。
然后你其他机器想访问配置 www.hello.com 到上面这个主机的ip就行了。
几个顶级指令
events 一般连接处理
http HTTP协议流量
mail Mail协议流量
stream TCP协议流量
内部指令
location有两种类型的参数: 前缀字符串和正则表达式
前缀字符串
location /home/asd {
}
正则表达式
location ~ \.php { # ~ 表示区分大小写。 ~* \.php 这个 ~*表示不区分大小写 其余规则就是Perl中的正则表达式。
}
nginx 日志相关
nginx日志相关的配置有:access_log(访问日志)、log_format(日志格式)、open_log_file_cache、log_not_found、log_subrequest、rewrite_log、error_log。
下面以一个nginx配置文件的所有配置为例。
#定义Nginx运行的用户和用户组
user www www;
#nginx进程数,通常设置成和cpu的数量相等 #这个是配置用几个CPU跑nginx。 相似的go里面的goroutines设置的 runtime.GOMAXPROCS 一个意思。
worker_processes 4;
#全局错误日志定义类型,[debug | info | notice | warn | error | crit] #error_log file level 错误日志可配置全局和server的。level尽量高,不然垃圾日志多。
#error_log /data/nginx/logs/error.log;
#error_log /data/nginx/logs/error.log notice;
#日志文件存放路径 access_log path [format [buffer=size | off]]
access_log /data/nginx/logs/lazyegg.com/web/access.log combinedio; #这个就是客户端访问生成的日志。比如访问403 404 等等你可以来这里看、
#combinedio 是日志格式名称 access_log file format gzip
#进程pid文件 里面存一个数。这个数正好是ps 命令查出来的那个进程的pid
#root 15774 1 0 Dec10 ? 00:00:00 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
#pid logs/nginx.pid;
#指定进程可以打开的最大描述符:数目
#工作模式与连接数上限
##这个指令是指当(一个nginx进程!!!!)打开的最多文件描述符数目,理论值应该是最多打开文件数(ulimit -n)与nginx进程数相除,但是nginx分配请求并不是那么均匀,所以最好与ulimit -n 的值保持一致。
#这是因为nginx调度时分配请求到进程并不是那么的均衡,所以假如填写10240,总并发量达到3-4万时就有进程可能超过10240了,这时会返回502错误。
worker_rlimit_nofile 65535;
event
########## events ####### 这个模块一般配置的是全局的一些东西,连接、请求等等。
events {
#参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型
use epoll #epoll 是最顶级的模型
#单个进程最大连接数(最大连接数=连接数+进程数)
worker_connections 1024;
#默认是on 使每个可用的worker进程逐个接受新连接。 设置成off会导致惊群现象。会唤醒不必要的worker进程。
accept_mutex on;
#keepalive 超时时间
keepalive_timeout 60;
#客户端请求头部的缓冲区大小。
client_header_buffer_size 4k;
#这个将为打开文件指定缓存,默认是没有启用的,max指定缓存数量,建议和打开文件数一致,inactive是指经过多长时间文件没被请求后删除缓存。
open_file_cache max=65535 inactive=60s;
#这个是指多长时间检查一次缓存的有效信息。
open_file_cache_valid 80s;
#open_file_cache指令中的inactive参数时间内文件的最少使用次数,如果超过这个数字,文件描述符一直是在缓存中打开的,如上例,如果有一个文件在inactive时间内一次没被使用,它将被移除。
open_file_cache_min_uses 1;
#语法:open_file_cache_errors on | off 默认值:open_file_cache_errors off 使用字段:http, server, location 这个指令指定是否在搜索一个文件是记录cache错误.
open_file_cache_errors on;
}
http
############################## http ##################################
#设定http服务器,利用它的反向代理功能提供负载均衡支持
http{
#文件扩展名与文件类型映射表
#Nginx通过服务器端文件的后缀名来判断这个文件属于什么类型,再将该数据类型写入HTTP头部的Content-Type字段中,发送给客户端。
#比如,当我们打开一个页面,看到一个PNG格式的图片的时候,Nginx是这样发送格式信息的:
#服务器上有asd.png这个文件,后缀名是png;
#根据mime.types,这个文件的数据类型应该是image/png;
#将Content-Type的值设置为image/png,然后发送给客户端。
include mime.types;
#默认文件类型 这个类型是直接下载而不是浏览。下载文件的时候可以用这个类型。
default_type application/octet-stream;
#默认编码
charset utf-8;
#服务器名字的hash表大小
server_names_hash_bucket_size 128;
#客户端请求头部的缓冲区大小。
client_header_buffer_size 32k;
#客户请求头缓冲大小。
large_client_header_buffers 4 64k;
#允许客户端请求的最大单个文件字节数
client_max_body_size 8m;
#开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。
sendfile on;
#开启目录列表访问,适合下载服务器,默认关闭。
autoindex on;
#此选项允许或禁止使用socke的TCP_CORK的选项,此选项仅在使用sendfile的时候使用
tcp_nopush on;
tcp_nodelay on;
#长连接超时时间,单位是秒
keepalive_timeout 120;
#FastCGI相关参数是为了改善网站的性能:减少资源占用,提高访问速度。下面参数看字面意思都能理解。
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 64k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 128k;
#gzip模块设置
gzip on; #开启gzip压缩输出。 在响应给client时进行压缩,可以有效的节约带宽,提高响应速度。 不建议压图片视频还有大文件资源,消耗CPU。
gzip_min_length 1k; #最小压缩文件大小
gzip_buffers 4 16k; #压缩缓冲区
gzip_http_version 1.0; #压缩版本(默认1.1,前端如果是squid2.5请使用1.0)
gzip_comp_level 2; #压缩等级
gzip_types text/plain application/x-javascript text/css application/xml; #压缩类型,默认就已经包含text/html,所以下面就不用再写了,写上去也不会有问题,但是会有一个warn。
gzip_vary on;
#开启限制IP连接数的时候需要使用
#limit_zone crawler $binary_remote_addr 10m;
upstream 负载均衡(这个也是属于http下面)
upstream lazyegg.net {
#upstream的负载均衡,weight是权重,可以根据机器配置定义权重。weigth参数表示权值,权值越高被分配到的机率越大。
server 192.168.80.121:80 weight=3;
server 192.168.80.122:80 weight=2;
server 192.168.80.123:80 weight=3;
#nginx的upstream目前支持4种方式的分配
#1、轮询(默认)
#每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
#2、weight
#指定轮询机率,weight和访问比率成正比,用于后端服务器性能不均的情况。
#例如:
#upstream bakend { #注意此处的bakend(名称自定义) 在配置server的时候需要配置 proxy_pass http://bakend; 代理到这个服务器组中的一台机器上。
#某台Server允许请求失败的次数,超过最大次数后,在fail_timeout时间内,新的请求将不会分配给这台机器。如果设置为0,Nginx会将这台Server置为永久无效状态
#默认为10秒。某台Server达到max_fails次失败请求后,在fail_timeout期间内,nginx会认为这台Server暂时不可用,不会将请求分配给它
# server 192.168.0.14 weight=10 max_fails=3 fail_timeout=15; #max_fails默认为1
# server 192.168.0.15 backup; # 后面注明backup表示其余机器挂掉了/都忙时使用这个机器。备胎、
# server 192.168.0.16 max_conns=100; # 默认为0.不限制 表示限制分配给这台机器的最大连接数量。
#}
#2、ip_hash
#每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
#一个主机多次请求时访问的是一个后台服务器。但是这个挂掉了,就凉了。别的机器没有用户的session
#例如:
#upstream bakend {
# ip_hash; #这块加这条命令就代表使用ip_hash方式
# server 192.168.0.14:88;
# server 192.168.0.15:80;
#}
#3、fair(第三方)
#按后端服务器的响应时间来分配请求,响应时间短的优先分配。 就是谁处理的快就表示谁请求少,就让他处理。
#upstream backend {
# server server1;
# server server2;
# fair;
#}
#4、url_hash(第三方)
#按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。
#例:在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法
#upstream backend {
# server squid1:3128;
# server squid2:3128;
# hash $request_uri;
# hash_method crc32;
#}
#tips:
#upstream bakend{#定义负载均衡设备的Ip及设备状态}{
# ip_hash;
# server 127.0.0.1:9090 down;
# server 127.0.0.1:8080 weight=2;
# server 127.0.0.1:6060;
# server 127.0.0.1:7070 backup;
#}
#在需要使用负载均衡的server中增加 proxy_pass http://bakend/;
#每个设备的状态设置为:
#1.down表示单前的server暂时不参与负载
#2.weight为weight越大,负载的权重就越大。
#3.max_fails:允许请求失败的次数默认为1.当超过最大次数时,返回proxy_next_upstream模块定义的错误
#4.fail_timeout:max_fails次失败后,暂停的时间。
#5.backup: 其它所有的非backup机器down或者忙的时候,请求backup机器。所以这台机器压力会最轻。
#nginx支持同时设置多组的负载均衡,用来给不用的server来使用。
#client_body_in_file_only设置为On 可以讲client post过来的数据记录到文件中用来做debug
#client_body_temp_path设置记录文件的目录 可以设置最多3层目录
#location对URL进行匹配.可以进行重定向或者进行新的代理 负载均衡
}
server (server也是属于http的)
#虚拟主机的配置
server {
#监听端口
listen 80;
#域名可以有多个,用空格隔开
server_name lazyegg.net;
#默认入口文件名称 这个是相对于root的相对路径
index index.html index.htm index.php;
#你要访问的项目的目录 这个要绝对路径
root /data/www/lazyegg;
#对******进行负载均衡
location ~ .*.(php|php5)?$
{
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
#配置URL重写和重定向 这个语法是 rewrite src desc command; 将src的请求重定向到 desc 注意这个是在当前location的基础上搞得。
#这个重定向操作是很有价值的。
rewrite .*.a.php /found/ redirect;
include fastcgi.conf;
}
#图片缓存时间设置
location ~ .*.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 10d;
}
#JS和CSS缓存时间设置
location ~ .*.(js|css)?$
{
expires 1h;
}
#日志格式设定
#$remote_addr与$http_x_forwarded_for用以记录客户端的ip地址;
#$remote_user:用来记录客户端用户名称;
#$time_local: 用来记录访问时间与时区;
#$request: 用来记录请求的url与http协议;
#$status: 用来记录请求状态;成功是200,
#$body_bytes_sent :记录发送给客户端文件主体内容大小;
#$http_referer:用来记录从那个页面链接访问过来的;
#$http_user_agent:记录客户浏览器的相关信息;
#通常web服务器放在反向代理的后面,这样就不能获取到客户的IP地址了,通过$remote_add拿到的IP地址是反向代理服务器的iP地址。反向代理服务器在转发请求的http头信息中,可以增加x_forwarded_for信息,用以记录原有客户端的IP地址和原来客户端的请求的服务器地址。
log_format access '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $http_x_forwarded_for';
#定义本虚拟主机的访问日志
access_log /usr/local/nginx/logs/host.access.log main;
access_log /usr/local/nginx/logs/host.access.404.log log404;
#对 "/connect-controller" 启用反向代理
location /connect-controller {
proxy_pass http://192.168.1.100:8888; #不能监听127.0.0.1:80
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
#后端的Web服务器可以通过X-Forwarded-For获取用户真实IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#以下是一些反向代理的配置,可选。
proxy_set_header Host $host;
#允许客户端请求的最大单文件字节数
client_max_body_size 10m;
#缓冲区代理缓冲用户端请求的最大字节数,
#如果把它设置为比较大的数值,例如256k,那么,无论使用firefox还是IE浏览器,来提交任意小于256k的图片,都很正常。如果注释该指令,使用默认的client_body_buffer_size设置,也就是操作系统页面大小的两倍,8k或者16k,问题就出现了。
#无论使用firefox4.0还是IE8.0,提交一个比较大,200k左右的图片,都返回500 Internal Server Error错误
client_body_buffer_size 128k;
#表示使nginx阻止HTTP应答代码为400或者更高的应答。
proxy_intercept_errors on;
#后端服务器连接的超时时间_发起握手等候响应超时时间
#nginx跟后端服务器连接超时时间(代理连接超时)
proxy_connect_timeout 90;
#后端服务器数据回传时间(代理发送超时)
#后端服务器数据回传时间_就是在规定时间之内后端服务器必须传完所有的数据
proxy_send_timeout 90;
#连接成功后,后端服务器响应时间(代理接收超时)
#连接成功后_等候后端服务器响应时间_其实已经进入后端的排队之中等候处理(也可以说是后端服务器处理请求的时间)
proxy_read_timeout 90;
#设置代理服务器(nginx)保存用户头信息的缓冲区大小
#设置从被代理服务器读取的第一部分应答的缓冲区大小,通常情况下这部分应答中包含一个小的应答头,默认情况下这个值的大小为指令proxy_buffers中指定的一个缓冲区的大小,不过可以将其设置为更小
proxy_buffer_size 4k;
#proxy_buffers缓冲区,网页平均在32k以下的设置
#设置用于读取应答(来自被代理服务器)的缓冲区数目和大小,默认情况也为分页大小,根据操作系统的不同可能是4k或者8k
proxy_buffers 4 32k;
#高负荷下缓冲大小(proxy_buffers*2)
proxy_busy_buffers_size 64k;
#设置在写入proxy_temp_path时数据的大小,预防一个工作进程在传递文件时阻塞太长
#设定缓存文件夹大小,大于这个值,将从upstream服务器传
proxy_temp_file_write_size 64k;
}
#本地动静分离反向代理配置
#所有jsp的页面均交由tomcat或resin处理
location ~ .(jsp|jspx|do)?$ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:8080;
}
}
}
五.nginx的常规操作&一些疑惑解答
下面以ubuntu直接apt-get install 安装的nginx为例。
配置文件的位置在/usr/local/nginx/conf, /etc/nginx, 或 /usr/local/etc/nginx
ubuntu16.04: /etc/nginx/nginx.conf
ubuntu安装的nginx 在/etc/nginx/sites-available 下的default文件中有默认的配置。指向 /var/www/html 可以改这个文件。
改完了 nginx -s reload 然后再访问就可以了、
1.fastCGI 代理。 快速通用网关接口 common gateway interface
PHP有这个服务器。(php-fpm) 其他程序也可以装。 php-fpm fastCGI process manager进程管理器。
FastCGI也是语言无关的。其主要行为是将CGI解释器进程保持在内存中并因此获得高效的性能。下面来个配置例子。
server {
location \.php(.*)$ {
fastcgi_pass localhost:9000; # 如果是PHP一般php-fpm默认是9000端口。按照路由规则将请求路由到PHP服务器
fastcgi_index index.php;
include fastcgi.conf; #这个是关键。使用fastcgi.conf文件中的参数,而不是fastcgi_param
}
}
两者有区别。 变量$fastcgi_script_name的值是请求的uri
#fastcgi.conf的 SCRIPT_FILENAME是 $document_root$fastcgi_script_name; 表示脚本执行前面有根目录。
#fastcgi_param 中的那个参数是没有document_root的。会导致404 也可以用fastcgi_param 并在location中重写。建议用conf
nginx是多进程的反向代理服务器。 包括一个master分支和多个worker分支。 master负责管理worker分支和读取nginx.conf配置文件。 改完worker会保持原有的配置。直到reload后worker进程才会加载新的配置文件。
2.windows windows的nginx使用有一些小问题。
使用 start nginx 后台启动nginx 默认使用 conf/nginx.conf配置文件。
关掉nginx 使用 taskkill /F /IM nginx.exe 全干掉。 因为别的 quit什么的命令不知道什么原因有时候没作用。
nginx -s reload 也可以重新加载配置文件。 但是nginx在windows需要非常注意这个命令执行的位置 保证在nginx.exe这个文件夹里执行。
配置环境变量也不行。应该是nginx.conf 配置文件中有一些相对路径的原因导致的。
对于阿里云服务器。 有一个坑。 配置nginx的域名 访问需要在阿里云进行备案
3.有关master进程的强杀和优雅杀掉的区别。
kill -9 (sigkill) 和普通 kill (默认 sigteam) 传给进程的信号量不一样产生的结果。
kill sigteam 是默认不加数字参数传递的信号量。 进程接到信号量执行类似 nginx -s quit(或stop)的命令 会将worker和master都杀掉、
kill sigkill sigkill 信号不能被捕获。 所以进程不知道自己咋死的。因此捕获不了master就没法控制worker进程.从而导致worker进程仍是运行状态,还可以接受请求。
nginx -s quit 会等现有的连接请求处理完再关闭。 nginx -s stop 就直接关闭了。 nginx -s reload 是重新加载配置文件。 不会杀master会杀worker
4.nginx 进程间通信的方式
信号量、socket套接字、共享内存
1.其中信号量是我们执行nginx -s quit等这种命令是通过信号量进行通信给master进程的,然后master进程在作为对应的相应。
2.socket套接字是在master与worker进程之间,通过发送socket进行通信。
3.共享内存,master和worker是父子进程,正常是不会有共享内存的。共享内存我们知道一般是存在在多线程的环境下。而nginx中的master进程会开辟一块内存空间作为共享内存来进行与worker进行一些数据的同步,例如http的连接数信息等。worker与worker进程之间的通信也是通过共享内存来实现的。
nginx有一个现象较惊群现象,就是来了一个请求,正常交给一个worker进程来处理就好了。但是如果共享变量没有加互斥锁的话会导致大量worker进程监听到这个请求,但是真正能拿走这个请求的worker进程只能有一个。这就会导致一个问题。就是其余的worker被唤醒从而增加了CPU开销和资源的消耗。因此共享内存中变量加个互斥锁就会解决这个问题。
5.为什么worker与master采用socketpair这种套接字进行通信?
nginx会将这个socketpair加入到epoll中,通过事件模块获取该套接字。
6.一些重要指令的用法
location ~ \.php{
set $var_test "index.php"; #定义变量名为 var_test 值为 index.php 在使用时 $var_test 这么用。 只有字符串类型。
#配置URL重写和重定向 这个语法是 rewrite src desc command; 将src的请求重定向到 desc 注意这个是在当前location的基础上搞得。
#这个重定向操作是很有价值的。 需要注意 src的位置必须是 regex正则表达式。
rewrite .*.a.php /found/ redirect;
return 404 http://www.hello.com; #这个是可选的。可以仅返回状态码,也可以还返回重定向的uri
}
rewrite 后面的那个指令有四个 last break permanent redirect
last 和break 出现在location外差不多。都是rewrite这行命令之后直接跳转到 desc指定的location了。
如果在location里面写rewrite是last表示跳出当前location,重新一个请求重走server路、 break就是停止了。
try_files 在上下文的root目录下进行处理。指令可用于检查指定的文件或目录是否存在并进行内部重定向,如果没有指定的文件或目录,则返回特定的状态代码如下这个try_files 可以应对yii框架 将路由写在index.php?controller/action 这种。如果不进行内部uri重写,访问不到的。
location / {
try_files $uri/ /index.php?$args @backend ; #比如请求admin-zy.com 然后没找到。就会重写为 /index.php?admin-zy.com 然后走yii的逻辑。还可以重定向到location
}
location @backend {
proxy_pass http://www.s.com;
sendfile on; #默认情况下,nginx会自动处理文件传输,并在发送文件之前就将其复制到缓冲区中。sendfile将直接从一个fd复制到另一个fd
sendfile_max_chunk 1m; #设置调用单个sendfile()传输的数据量、
tcp_nopush on; #与sendfile一起用。使nginx通过sendfile获取数据块之后,在一个数据包中发送http响应头。
tcp_nodelay on; #默认就是on 禁用nagle算法。这个算法发包是有大概200ms延迟的并将小包组成大包一起发。禁用了就直接发了。省去延时。
}
location / {
proxy_pass http://backend; #这个是一个负载均衡的 upstream配置,配置名为 backend 其中有三个ip
proxy_bind 192.168.172.38; #这个就是绑定到这个ip这样每次访问代理不会发生负载均衡操作。
}
关于location的匹配。 先查前缀表达式,再查正则表达式。 前缀表达式的结果作为备胎使用, 正则表达式也匹配则使用正则表达式的location。只有正则表达式不匹配时才用之前的前缀location
location值匹配URI部分,参数是啥跟匹配没关。除非你定义一个 / 的location 然后定义try_files重定向。
如果过程中发生的重定向,将重新匹配location。例如访问 / 然后重定向到 /index.php 会找匹配 /index.php的location 去匹配。
请求匹配要处理的server是通过Host请求头信息
多个server,当访问的域名映射的ip是你的nginx时,会先查找listen的端口,找完端口会寻找server_name 如果有对应就会返回,没有对应的就返回这个listen监听的端口的默认server
这个默认server可以在listen 80 default_server; 声明这个default_server就可以了。 如果没声明就把相同listen的第一个server作为默认server
如果不允许一个没有host头的请求,可以server_name "";
server {
listen 80;
server_name "";
return 444; #444是一个nginx走向。
}
六.https相关
https 是在 http 和 TCP 中间加上一层加密层 SSL
浏览器向服务端发送消息时:本质上是浏览器(客户端)使用服务端的公钥来加密信息,服务端使用自己的私钥解密,
浏览器从服务端获取消息是:服务端使用自己私钥加密,浏览器(客户端)使用服务端的公钥来解密信息
在这个过程中,需要保证服务端给浏览器的公钥不是假冒的。证明服务端公钥信息的机构是 CA(数字认证中心)
https默认的端口是443端口。http默认端口是80 所以如果是https的网址不写端口号默认是443
https相对于http消耗的资源要多很多。http是tcp三次握手建立连接。 而https除了这三次握手还需要ssl握手的9个包。
openssl生成证书server.key server.crt 生成这个证书是需要域名的。本地可以测试的。随便生成一个域名。做HTTPS的测试
Key是私用秘钥,通常是RSA算法
Csr是证书请求文件,用于申请证书。在制作csr文件时,必须使用自己的私钥来签署申,还可以设定一个密钥。
crt是CA认证后的证书文,签署人用自己的key给你签署凭证。
key的生成(第一步)
openssl genrsa -out server.key 2048
这样是生成RSA密钥,openssl格式,2048位强度。server.key是密钥文件名。
csr的生成 (第二步)(基于刚刚生成的server.key)
openssl req -new -key server.key -out server.csr,需要依次输入国家,地区,组织,email。最重要的是有一个common name,可以写你的名字或者域名。如果为了https申请,这个必须和域名吻合,否则会引发浏览器警报。生成的csr文件讲给CA签名后形成服务端自己的证书。
crt的生成 (第三步)(基于上两步生成的csr key)
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
注意: 生成的这个目录nginx一定要有访问权限 不然403
如果想将http的请求重定向到https,则还需配置一个server 监听80端口然后跳到https
server {
listen 443 ssl;
server_name hello-zy.cn;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
ssl on;
root /etc/nginx/html;
index index.html;
location / {
}
}
server {
listen 80;
server_name hello-zy.cn;
root /etc/nginx/html;
index 80index.html;
location / {
#return 301 https://$server_name$request_uri;
#rewrite ^(.*)$ https://$host$1 permanent; #跟上面这种都行。就是重定向。 permanent就是301的永久重定向。
}
}
linux curl 301 的问题
需要使用-L参数 如果访问的是https 还需要加 --insecure参数
curl --insecure -L hello-zy.cn
curl --insecure https://hello-zy.cn