8. 开始编译
编译前可以先看一下configure支持哪些参数。
./configure --help | more 复制代码
首先就是确定nginx执行中会去找哪些目录下的文件作为辅助文件。比如用动态模块时--modules-path就会产生作用。--lock-path确定nginx.lock文件放在哪里等。
如果没有任何变动的话只需要指定--prefix=PATH就可以了,设定一个安装目录。
第二类参数主要是用来确定使用哪些模块和不使用哪些模块的,前缀通常是--with和--without。
比如说--with-http_ssl_module或者--with-http_v2_module通常需要主动加--with的时候,意味着模块默认是不会编译进nginx的。
而模块中显示--without比如说--without-http_charset_module意味着默认他会编译进nginx中,加了参数是把他移除默认的nginx的模块中。
第三类参数中指定nginx编译需要的一些特殊的参数,比如说用cc编译的时候需要加一些什么样的优化参数,或者说要打印debug级别的日志(--with-debug)以及需要加一些第三方的模块(--with-zlib-asm=CPU)
这里指定的nginx的安装目录是在/home/nginx目录下。
./configure --prefix=/home/nginx/nginx/ 复制代码
如果没有任何报错nginx就已经编译成功了,所有nginx的配置特性以及nginx运行时的目录都会列在最下方。
在config执行完之后,会看到生成了一些中间文件。中间文件会放在objs文件夹下。最重要的是会生成一个文件叫做ngx_modules.c他决定了接下来执行编译时哪些模块会被编译进nginx。可以打开看一下所有被编译进nginx的模块都会列在这里,他们最后会形成一个叫做ngx_modules的数组。
执行make编译。
make 复制代码
编译完成以后如果没有任何错误,就可以看见生成了大量的中间文件,以及最终的nginx二进制文件。
cd objs/ ll 复制代码
最后进行make install。
make install 复制代码
安装完成之后在--prefix指定的安装目录中可以看到很多目录,nginx的执行文件就在sbin目录下。
决定nginx功能的配置文件在conf下,access.log和error.log在log文件夹下。
可以看到在conf目录下所有文件就是在源代码中conf目录copy过来的,其中的内容也是完全相同的。
9. 配置语法
nginx可执行文件中已经指定了他包含了哪些模块,但每一个模块都会提供独一无二的配置语法。
这些所有的配置语法,会遵循同样的语法规则。
nginx的配置文件是一个ascii的文本文件,主要有两部分组成,指令和指令快。
http { include mime.types; upstream thwp { server 127.0.0.1:8000; } server { listen 443 http2; # nginx配置语法 limit_req_zone $binary_remote_addr zone=one:10 rate=1r/s; location ~* \.(gif|jpg|jpeg)$ { proxy_cache my_cache; expires 3m; } } } 复制代码
上面http就是一个指令快,include mime.types;就是一条指令。
每条指令以分号结尾,指令和参数间以空格分隔。include mime.types;中include是一个指令名,mime.types是参数中间可以用一个或多个空格分隔。参数可以有多个,比如下面的limit_req_zone有三个参数,多个参数之间也是用空格分隔。
两条指令间是以;作为分隔符的,两条指令放在一行中写也是没有问题的。只不过可读性会变得很差。
第三个指令块是以 {} 组成的,他会将多条指令组织到一起,比如upstream,他把一条指令server放在了thwp指令块下面。
server中也放置了listen,limit_req_zone这些指令,他也可以包含其他的指令块,比如说location。
有些指令可以有名字,比如upstream,后面有个thwp作为他的名字。
具体什么样的指令有名字什么样的指令没有名字是由提供指令块的nginx模块来决定的,他也可以决定指令块后面有一个或者说多个参数,或者说没有参数。
include语句允许引入多个配置文件以提升可维护性。在例子中mime.types文件中其实里面是含有很多条不同的文件的后缀名与http协议中mime格式的对照关系表。
include是导入其他配置模块的意思。
#符号可以添加注释,提升可读性,比如在listen后面加了一个nginx配置语法的注释,以描述下面一些配置的表达。
使用$符号可以使用变量,可以看下limit_req_zone这里用了一个参数叫做$binary_remote_addr,这是一个变量描述的是远端的地址。
部分指令的参数是支持正则表达式的,比如location后面可以看到,他可以支持非常复杂的正则表达式,而且可以把正则表达式括号里的内容通过$1,$2,$3的方式取出来。
在nginx的配置文件中当涉及到时间的时候,还有许多表达方式,比如下面的方式:
ms -> 毫秒 s -> 秒 m -> 分钟 h -> 小时 d -> 天 w -> 周 M -> 月 y -> 年 复制代码
比如location中的expires 3m;就表示3分钟后希望cache刷新。
空间也是有单位的,当后面不加任何后缀名时表示字节bytes,加了k或者K表示千字节,m表示兆字节,g表示G字节。
http大括号里面所有的指令都是由http模块去解析和执行的,非http模块,比如说像stream或mime是没有办法去解析指令的。
upstream表示上游服务,当nginx需要与Tomcat等企业内网的其它服务交互的时候呢,可以定义一个upstream。
server对应的一个或一组域名,location是url表达式。
10. 重载,热部署,日志切割
需要帮助的时候可以用-? 或者 -h获取帮助信息。
nginx -? nginx -h 复制代码
默认情况下编译出来的nginx会寻找执行configure命令时指定的配置文件。在命令行中可以指定另一个配置文件用-c 路径。
还能指定一些配置用-g,指令就是在nginx的configure目录里的指令。
nginx操作运行中的进程一般是通过发送信号,可以通过linux的kill命令也可以用nginx -s子命令,子命令后可以用stop,quit,reload,reopen。
nginx -s stop # 停止nginx服务 nginx -s quit # 优雅的停止nginx服务 nginx -s reload # 重载配置文件 nginx -s reopen # 重新开始记录日志文件。 复制代码
-t可以测试一下配置文件是否合法问题。
-V是在编译时用configure脚本执行所加的所有参数。
1. 重载配置文件
修改nginx配置文件中的一些值,比如说conf/nginx.conf文件中,打开tcp_nopush。
当修改完配置文件以后,可以直接执行nginx -s reload命令nginx是在不停止对客户服务的情况下使用了tcp_nopush新的配置项,非常的简单。
2. 热部署
nginx在运行的情况下想更换最新版本的nginx,根据之前所说的,nginx编译方法下载一个新的nginx。
把最新版本的nginx编译后的可执行文件nginx,copy到目录中替换掉正在运行的nginx文件。copy完成需要给正在运行的nginx的master进程发送一个信号,告诉他开始进行热部署做一次版本升级,给nginx的master进程发送一个信号,USR2信号。
kill -USR2 进程号(13195) 复制代码
nginx会新启一个master进程使用的正式刚刚复制过来的最新的nginx二进制文件。
旧的worker也在运行,新的master会生成新的worker,他们会平滑的把所有的请求过渡到新的进程中。
新的请求新的连接会进入新的nginx进程中,这时需要向老的nginx进程发送一个信号叫做WINCH,告诉他优雅的关闭所有进程。
kill -WINCH 13195 复制代码
这时老的worker进程会优雅的退出,但是老的master进程还在,只是是没有worker进程了。
这说明所有的请求已经全部切换到新的nginx中了,如果需要把新版本退回到老版本,可以向老的进程发送reload命令,让他重新把worker进程拉起来。再把新版本关掉。所以保留master是为了允许做版本回退。
3. 日志切割
比如说当前的日志已经很大了。需要把以前的日志备份到另外一个文件中,但是nginx还是正常运行的。
这就要通过reopen命令来做,首先需要把当前正在使用的日志copy一份放在另外的位置.
mv access_log bak.log 复制代码
接着执行命令reopen。
nginx -s reopen 复制代码
就重新生成了一个access.log, 原本的log备份成了bak.log,就实现了日志切割。
当然这种方法会非常不好用,实际上往往是每一天,或者是每一周执行一次日至切割,可以先写成一个bash脚本。
在bash脚本中首先把文件复制一下,再执行-s reopen命令,最后把脚本放在crontab中。
11. 静态资源Web服务器
编辑conf/nginx.conf文件找到server代码块中,listen配置监听端8080端口,然后需要配置一个location,使用/让所有的请求都访问到www文件夹。
这里需要指定url的后缀与文件的后缀一一对应,有两种用法,root和alias,root是系统的跟目录,所以通常使用alias,alias是nginx的安装目录。
server { listen 8080; ... location / { alias www/; ... } ... } 复制代码
做完配置之后启动nginx在浏览器中访问localhost:8080就可以了。
nginx -s reload 复制代码
1. 开启gzip
做完gzip压缩传输的字节数会大幅度减少,所以通常会打开gzip。
首先打开nginx.conf文件,找到http代码块中的gzip相关选项,打开gzip(off -> on), gzip_min_length是小于多少字节不再执行压缩,因为小于一定的字节http传输直接就可以发送了,压缩反而消耗cpu性能,gzip_comp_level代表压缩级别,gzip_types是针对某些类型的文件才做gzip压缩。
http { ... gzip on; gzip_min_length 1; gzip_comp_level 2; gzip_types text/plain applicaton/x-javascript text/css image/png; ... } 复制代码
配置好后重启nginx, 浏览器中查看就会发现,传输的文件已经减少了很多,响应头中多出了Content-encoding: gzip。使用gzip以后整个web服务传输效率会高很多。
2. 打开目录结构
nginx给提供了一个官方模块叫做autoindex,他可以提供当访问以/结尾的url时,显示目录的结构。使用方法也特别简单,就是autoindex on加入一个指令就可以了。
location / { autoindex on; } 复制代码
他会把所访问的文件夹内所有文件列出来,当打开一个目录时,可以继续显示目录中的文件,这是一个很好的静态资源帮助功能。
3. 网速限制
比如公网带宽是有限的,当有很多并发用户使用带宽时,他们会形成一个争抢关系,可以让用户访问某些大文件的时候来限制他的速度,节省足够的带宽给用户访问一些必要的小文件。
就可以使用set命令,配合一些内置的变量实现这种功能,比如说加上set $limit_rate 1k,限制nginx向客户浏览器发送响应的一个速度。意思是每秒传输多少数据到浏览器中。
location / { set $limit_rate 1k; } 复制代码
4. 日志
首先需要设置access日志格式,找到一个指令叫做log_format, 他用来定义日志的格式,这里可以使用变量。
http { log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; } 复制代码
$remote_addr为远端的地址,也就是浏览器客户端的ip地址,$time_local表示当时的时间。$status是返回的状态码。格式定义好之后需要定义一个名字,这里是main。
不同的名字可以对不同的域名下,做不同格式的日志记录,或者对不同的url记录不同日志格式。
配置好log_format之后,就可以用access_log指令,配置日志了。access_log所在的代码块决定了日志的位置比如access_log这里放在了server下,也就是所有请求这个路径和端口的请求日志,都会记录到logs/yindong.log文件中,使用的格式就是main。
server { listen 8080; access_log logs/yindong.log main; location / { alias dlib; } } 复制代码
配置好yindong.log后,所有的请求在完成之后都会记录下一条日志,可以进入logs/yindong.log中查看每一条都是设置的格式。
12. 反向代理服务
由于上游服务要处理非常复杂的业务逻辑而且强调开发效率,所以他的性能并不怎么样,使用nginx作为反向代理以后,可以由一台nginx把请求按照负载均衡算法代理分配给多台上游服务器工作。
这就实现了水平扩展的可能,在用户无感知的情况下,添加更多的上游服务器,来提升处理性能,而当上游服务器出现问题的时候,nginx可以自动的把请求从有问题的服务器,转交给正常的服务器。
反向代理需要添加一个upstream,就是上游服务server,访问地址是127.0.0.1:8080如果有很多台上游服务可以依次的放在这里。
upstream设置的一批服务叫local。对所有的请求使用proxy_pass一条指令,代理到local里。
upstream local{ server 127.0.0.1:8080; } server { server_name yindong.com; listen 80; location / { proxy_set_header Host $host; proxt_set_header X-Real_IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 反向代理转发 proxy_pass http://local; } } 复制代码
因为反向代理的原因,真实的服务器拿到的信息是经过nginx代理服务器转发的,所以很多信息都不是真实的,比如说域名,ip都是代理服务器发送过来的,所以需要在location中做一些配置处理。
通过proxy_set_header可以把有一些值添加一条新的header发送到上游,比如说叫x-real-ip,然后把他的值设为从tcp链接里面拿到的远端ip地址。
$host也是同样的因为用户直接访问的域名,是他在浏览器输入的,既可以让他在上游服务器可以处理域名,也可以由反向代理来处理。
所有这些配置特性都可以在官网中的http_proxy_module找到。
1. 缓存
这里有个很重要的特性proxy_cache, 因为当nginx作为反向代理时,通常只有动态的请求,也就是不同的用户访问同一个url看到的内容是不同的,才会交由上游服务处理。
但是有一些内容可能是一段时间不会发生变化的,为了减轻上游服务器的压力,就会让nginx把上游服务返回的内容缓存一段时间,比如缓存一天,在一天之内即使上游服务器对内容的响应发生了变化,也不管,只会去拿缓存住的这段内容向浏览器做出响应。
因为nginx的性能远远领先于上游服务器的性能。所以使用一个特性后,对一些小的站点会有非常大的性能提升。
配置缓存服务器首先要去通过proxy_cache_path这条指令去设置缓存文件写在哪个目录下。
比如这里是/tmp/nginxcache, 以及这些文件的命名方式,这些文件的关键词key,要放在共享内存中的。这里开了10MB的共享内存,命名为my_cache。
proxy_cache_patj /tmp/nginxcache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path_off; 复制代码
缓存的使用方法就是在需要做缓存的url路径下,添加proxy_cache, 后面所跟的参数就是刚刚开辟的那个共享内存,在共享内存中所设置的key就是同一个url访问时对不同的用户可能展示的东西是不一样的,所以用户这个变量就要放在key中。
这里做一个非常简单的key,比如说访问的host url可能加了一些参数,这些参数可能已经指明了是哪个用户哪个资源,$host$uri$is_args$args; 这些作为一个整体的key。
location / { proxy_cache my_cache; proxy_cache_key $host$uri$is_args$args; proxy_cache_valid 200 304 302 1d; } 复制代码
加完这些参数以后,可以尝试停掉上游服务,然后访问站点,可以发现站点仍然是可以访问的。就是因为被缓存了。
13. 监控access日志
Access日志记录了nginx非常重要的信息,可以用日志来分析定位问题,也可以用它来分析用户的运营数据,但是如果想实时分析Access.log相对来说还比较困难。
有一款工具叫GoAccess可以以图形化的方式,通过websocket协议实时的把Access.log的变迁反应到浏览器中,方便分析问题。
GoAccess的站点是 https://goaccess.io, 以一种非常友好的图形化方式显示。
GoAccess使用-o参数生成新的html文件,把当前access.log文件中的内容以html图表的方式展示出来,当access.log变迁的时候GoAccess会新起一个socket进程,通过端口的方式把新的access.log推送到客户端。
goaccess access.log -o report.html --log-format=COMBINED 复制代码
首先制定access.log程序制定的位置(yindong.log), 把它输出到../html/report.html文件中,使用的是--real-time-html就是实时更新页面的方式,时间格式--time-format='%H:%M:%S', 日期格式--date-format='%d/%b/%Y', 以及日志格式--log-format=COMBINED。
cd logs goaccess yindong.log -o ../html/report.html --real-time-html --time-format='%H:%M:%S' --date-format='%d/%b/%Y' --log-format=COMBINED 复制代码
GoAccess的安装可以用yum或者wget,也可以下载源码进行编译。
启动完成之后可以看到一条log叫做 WebSocket server ready to accept new client connections, 也就是他已经打开了一个新的websocket监口,当访问report.html的时候,会向进程发起连接, 由进程给推送最新的log变更。
接下来还要在nginx.conf中添加location,当访问/report.html时候用alias重定向到report.html。
server { ... location /report.html { alias /usr/local/openresty/nginx/html/report.html; } ... } 复制代码
打开localhost:8080/report.html就可以看到效果了。
使用GoAccess.log可以非常直观的看到access.log统计信息上的变迁,对分析网站的运营情况非常有帮助,可以看到每个时间点,每一周每一天,甚至不同的国家地区使用不同浏览器和操作系统的人使用站点的一个比例和分布。
14. SSL安全协议
SSL的全称是Secure Sockets Layer,现在很多时候使用的是TLS也就是Transport Layer Security。可以将TLS看做是SSL的升级版。
SSL是网景公司在1995年推出的,后来因为微软把自己的IE浏览器捆绑windows一起卖出导致网景遇到很大的发展困境,网景把SSL协议交给IETF组织。
在1999年,应微软的要求IETF把SSL更名为TLS1.0,在06,08到2018年TLS分别发布了1.1,1.2和1.3协议。
那么TLS协议究竟是怎样保证http的明文消息被加密的呢?
在ISO/OSI七层模型中,应用层是http协议,在应用层之下,表示层也就是TLS所发挥作用的这一层,通过握手,交换密钥,告警,对称加密的方式使http层没有感知的情况下做到了数据的安全加密。
当抓包或者观察服务端配置时,可以看到安全密码的配置,安全密码的配置决定了TLS协议是怎样保证明文被加密的。这里大概有四个组成部分。
第一个组成部分叫做密钥交换,也就是ECDHE,这实际上是一个椭圆曲线加密算法的表达,密钥交换是为了让浏览器和服务器之间怎样各自独立的生成密钥,数据传输时他们会用密钥去加密数据。加解密是需要使用到对方的密钥的所以需要进行交换。
在密钥交换过程中,需要让浏览器和服务器各自去验证对方的身份,而验证身份是需要一个算法的,叫做RSA。
进行数据加密,解密这种通讯的时候,需要用到对称加密算法AES_128——GCM,其中第一个部分AES表达了是怎样一种算法,128表示了AES算法里支持了3种加密强度,使用128位这种一个加密强度。AES中有很多分组模式GCM是一种比较新的分组模式,可以提高多核CPU情况下加密和解密的一个性能。
SHA_256是摘要算法,他用来把不定长度的字符串生成固定长度的更短的摘要。