一、初识Nginx
Nginx是一款高性能、轻量级的Web服务器和代理服务器,它采用事件驱动的异步模型,可以支持高并发的访问量。
Nginx既可以作为静态资源服务器,又可以支持动态请求,同时还能够快速地处理HTTP请求。因此,除了用作Web服务器之外,许多大型网站和应用程序也将Nginx作为负载均衡服务器使用,以提高可用性和性能。
【Nginx的优点】
- 1、更快
这表现在两个方面:一方面,在正常情况下,单次请求会得到更快的响应;另一方面,在高峰期(如有数以万计的并发请求),Nginx可以比其他Web服务器更快地响应请求。 - 2、高扩展性,跨平台
Nginx的设计极具扩展性,它完全是由多个不同功能、不同层次、不同类型且耦合度极低的模块组成。因此,当对某一个模块修复Bug或进行升级时,可以专注于模块自身,无须在意其他。而且在HTTP模块中,还设计了HTTP过滤器模块:一个正常的HTTP模块在处理完请求后,会有一串HTTP过滤器模块对请求的结果进行再处理。这样,当我们开发一个新的HTTP模块时,不但可以使用诸如HTTP核心模块、events模块、log模块等不同层次或者不同类型的模块,还可以原封不动地复用大量已有的HTTP过滤器模块。 - 3、高可靠性:用于反向代理,宕机的概率微乎其微
高可靠性是我们选择Nginx的最基本条件,因为Nginx的可靠性是大家有目共睹的,很多家高流量网站都在核心服务器上大规模使用Nginx。Nginx的高可靠性来自于其核心框架代码的优秀设计、模块设计的简单性;另外,官方提供的常用模块都非常稳定,每个worker进程相对独立,master进程在1个worker进程出错时可以快速“拉起”新的worker子进程提供服务。 - 4、低内存消耗
一般情况下,10000个非活跃的HTTP Keep-Alive连接在Nginx中仅消耗2.5MB的内存,这是Nginx支持高并发连接的基础。 - 5、单机支持10万以上的并发连接
随着互联网的迅猛发展和互联网用户数量的成倍增长,各大公司、网站都需要应付海量并发请求,一个能够在峰值期顶住10万以上并发请求的Server,无疑会得到大家的青睐。理论上,Nginx支持的并发连接上限取决于内存,10万远未封顶。当然,能够及时地处理更多的并发请求,是与业务特点紧密相关的。 - 6、热部署
master管理进程与worker工作进程的分离设计,使得Nginx能够提供热部署功能,即可以在7×24小时不间断服务的前提下,升级Nginx的可执行文件。当然,它也支持不停止服务就更新配置项、更换日志文件等功能。 - 7、最自由的BSD许可协议
这是Nginx可以快速发展的强大动力。BSD许可协议不只是允许用户免费使用Nginx,它还允许用户在自己的项目中直接使用或修改Nginx源码,然后发布。这吸引了无数开发者继续为Nginx贡献自己的智慧。
选择Nginx的核心理由还是它能在支持高并发请求的同时保持高效的服务。
1.1 Nginx架构
Nginx在启动后,在unix系统中会以daemon(守护线程)的方式在后台运行,后台进程包含一个master进程和多个worker进程。master进程主要用来管理worker进程
,包含:接收来自外界的信号、向各worker进程发送信号和监控worker进程的运行状态等。当worker进程退出后(异常情况下),会自动重新启动新的worker进程。而基本的网络事件,则是放在worker进程中来处理。
多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的
。一个请求,只可能在一个worker进程中处理,一个worker进程,不可能处理其它进程的请求。worker进程的个数是可以设置的,一般我们会设置与机器cpu核数一致
,这里面的原因与nginx的进程模型以及事件处理模型是分不开的。
Nginx的进程模型:
master来管理worker进程,所以我们只需要与master进程通信就行了。master进程会接收来自外界发来的信号,再根据信号做不同的事情。所以我们要控制nginx,只需要通过kill向master进程发送信号就行了。比如kill -HUP pid,则是告诉nginx,从容地重启nginx,我们一般用这个信号来重启nginx,或重新加载配置,因为是从容地重启,因此服务是不中断的。master进程在接收到HUP信号后是怎么做的呢?首先master进程在接到信号后,会先重新加载配置文件,然后再启动新的worker进程,并向所有老的worker进程发送信号,告诉他们可以光荣退休了。新的worker在启动后,就开始接收新的请求,而老的worker在收到来自master的信号后,就不再接收新的请求,并且在当前进程中的所有未处理完的请求处理完成后,再退出。当然,直接给master进程发送信号,这是比较老的操作方式,nginx在0.8版本之后,引入了一系列命令行参数,来方便我们管理。比如,./nginx -s reload,就是来重启nginx,./nginx -s stop,就是来停止nginx的运行。如何做到的呢?我们还是拿reload来说,我们看到,执行命令时,我们是启动一个新的nginx进程,而新的nginx进程在解析到reload参数后,就知道我们的目的是控制nginx来重新加载配置文件了,它会向master进程发送信号,然后接下来的动作,就和我们直接向master进程发送信号一样了。
worker进程之间是平等的,每个进程,处理请求的机会也是一样的。当我们提供80端口的http服务时,一个连接请求过来,每个进程都有可能处理这个连接,怎么做到的呢?首先,每个worker进程都是从master进程fork过来,在master进程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多个worker进程。所有worker进程的listenfd会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。
nginx采用这种进程模型有什么好处呢?首先,对于每个worker进程来说,独立的进程,不需要加锁,所以省掉了锁带来的开销。其次,采用独立的进程,可以让互相之间不会影响,一个进程退出后,其它进程还在工作,服务不会中断,master进程则很快启动新的worker进程。
nginx采用多worker的方式来处理请求,每个worker里面只有一个主线程,那能够处理的并发数很有限啊,多少个worker就能处理多少个并发,何来高并发呢?非也,这就是nginx的高明之处,nginx采用了异步非阻塞的方式来处理请求,也就是说,nginx是可以同时处理成千上万个请求的。
推荐设置worker的个数为cpu的核数,在这里就很容易理解了,更多的worker数,只会导致进程来竞争cpu资源了,从而带来不必要的上下文切换。而且,nginx为了更好的利用多核特性,提供了cpu亲缘性的绑定选项,我们可以将某一个进程绑定在某一个核上,这样就不会因为进程的切换带来cache的失效。
1.2 Nginx处理请求的过程
1、当HTTP请求交给Nginx处理时,首先Nginx会取出header头中的Host ,然后将其与所有的配置文件中的每个server段中的server_name进行匹配,以此决定到底有哪个server模块来处理这个请求(当然有时也可能一个Host与多个server块中的server_name都匹配,这时会根据匹配的优先级选择实际处理的server块)。
2、HTTP请求和server_name匹配后,这样接下来nginx就会根据header中的Request URI字段进行与location匹配。
server { # 第一个Server区块开始
listen 80; # 提供服务的端口,默认80
server_name localhost; # 提供服务的域名主机名
location / { # 第一个location区块开始
root html; # 站点的根目录,相当于Nginx的安装目录
index index.html index.htm; # 默认的首页文件,多个用空格分开
} # 第一个location区块结果
3、接下来继续由Nginx进行反向代理实现。
4、根据真正的目标服务器地址,再次进行代理请求。
5、当代理HTTP请求到达目标IP地址后,若目标IP地址也是Nginx,则服务器会对其进行类似上面的Nginx处理HTTP请求一样,进行server_name 和 location匹配,并将相应的请求资源返回代理服务器。
6、Nginx反向代理服务器接收到目标服务器的返回资源后,再将其返回给客户端浏览器。
1.3 正向代理和反向代理*
- 1、正向代理
1)用户发送请求到自己的代理服务器。
2)自己的代理服务器发送请求到服务器。
3)服务器将数据返回到自己的代理服务器。
4)自己的代理服务器再将数据返回给用户。
图示:
正向代理隐藏了用户,用户的请求被代理服务器接收代替
,服务器并不知道用户是谁。
- 2、反向代理
1)用户发送请求到服务器(访问的其实是反向代理服务器,但用户不知道)。
2)反向代理服务器发送请求到真正的服务器。
3)真正的服务器将数据返回给反向代理服务器。
4)反向代理服务器再将数据返回给用户。
图示:
使用反向代理服务器接受请求,再用均衡负载将请求分布给多个真实的服务器。既能提高效率还有一定的安全性
。
正向代理与反向代理最简单的区别:正向代理隐藏的是用户,反向代理隐藏的是服务器
。
1.4 Nginx的应用场景*
- 1、反向代理
反向代理应该是Nginx做的最多的一件事了。反向代理方式是指以代理服务器来接受网络上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
简单来说就是真实的服务器不能直接被外部网络访问,所以需要一台代理服务器,而代理服务器能被外部网络访问的同时又跟真实服务器在同一个网络环境,当然也可能是同一台服务器,端口不同而已。
示例:
server {
listen 80;
server_name localhost;
client_max_body_size 1024M; #限制请求体的大小,若超过所设定的大小,返回413错误。
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host:$server_port; #用来设定被代理服务器接收到的header信息
}
}
- 2、负载均衡
负载均衡也是Nginx常用的一个功能,负载均衡其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。
简单而言就是当有2台或以上服务器时,根据规则随机的将请求分发到指定的服务器上处理,负载均衡配置一般都需要同时配置反向代理,通过反向代理跳转到负载均衡
。
示例(权重方式。weight:权重,默认为1, weight越大,负载的权重就越大。):
upstream test {
server localhost:8080 weight=9;
server localhost:8081 weight=1;
}
- 3、HTTP服务器
Nginx本身也是一个静态资源的服务器
,当只有静态资源的时候,就可以使用Nginx来做服务器,同时现在也很流行动静分离,就可以通过Nginx来实现。
示例(Nginx做静态资源服务器):
server {
listen 80;
server_name localhost;
client_max_body_size 1024M;
location / {
root E:/wwwroot;
index index.html;
}
}
- 4、动静分离
动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来
,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路。
示例:
upstream test{
server localhost:8080;
server localhost:8081;
}
server {
listen 80;
server_name localhost;
location / {
root e:/wwwroot;
index index.html;
}
# 所有静态请求都由nginx处理,存放目录为html
location ~ .(gif|jpg|jpeg|png|bmp|swf|css|js)$ {
root e:/wwwroot;
}
# 所有动态请求都转发给tomcat处理
location ~ .(jsp|do)$ {
proxy_pass http://test;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root e:/wwwroot;
}
}
这样我们就可以吧HTML以及图片和css以及js放到wwwroot目录下,而tomcat只负责处理jsp和请求,例如当我们后缀为gif的时候,Nginx默认会从wwwroot获取到当前请求的动态图文件返。当然这里的静态文件跟Nginx是同一台服务器,我们也可以在另外一台服务器,然后通过反向代理和负载均衡配置过去就好了,只要搞清楚了最基本的流程,很多配置就很简单了。另外localtion后面其实是一个正则表达式,所以非常灵活。
二、Nginx配置
2.1 解决前端跨域问题
使用Nginx转发请求:把跨域的接口写成调本域的接口,然后将这些接口转发到真正的请求地址。
示例(原先):
1)调试页面的IP及端口:
http://192.168.1.100:8080/
。
2)请求的接口是:http://ni.hao/api/get/info
。
- 步骤一
请求的接口改成:http://192.168.1.100:8080/api/get/info
,这样就解决了跨域问题。 - 步骤二
安装好Nginx后,去到conf目录,修改nginx.conf文件。 - 步骤三
把默认的server配置注释掉,增加如下内容:
server{
listen 8888;
server_name 192.168.1.100;
location /{
proxy_pass http://192.168.1.100:8080;
}
location /api{
proxy_pass http://ni.hao/api;
}
}
保存后,启动Nginx。访问http://192.168.1.100:8888/
即可。
2.2 location的作用
location的作用是根据用户请求的URI来执行不同的应用,也就是根据用户请求的网站URL进行匹配,匹配成功即进行相关的操作。
- 1、location的语法
location [ = | ~ | ~* | ^~ ] uri { .... }
# | 指令 | | 匹配标识 | |匹配的网站网址| |匹配URI之后要执行的配置段|
- 2、location正则案例
#优先级1,精确匹配,根路径
location =/ {
return 400;
}
#优先级2,以某个字符串开头,以av开头的,优先匹配这里,区分大小写
location ^~ /av {
root /data/av/;
}
#优先级3,区分大小写的正则匹配,匹配/media*****路径
location ~ /media {
alias /data/static/;
}
#优先级4 ,不区分大小写的正则匹配,所有的****.jpg|gif|png 都走这里
location ~* .*\.(jpg|gif|png|js|css)$ {
root /data/av/;
}
#优先7,通用匹配
location / {
return 403;
}
2.3 Rewrite常用全局变量
变量 | 说明 |
---|---|
$args | 变量中存放了请求URL中的请求指令。比如http://192.168.200.133:8080?arg1=value1&args2=value2中的"arg1=value1&arg2=value2" |
$http_user_agent | 变量存储的是用户访问服务的代理信息(如果通过浏览器访问,记录的是浏览器的相关版本信息) |
$host | 变量存储的是访问服务器的server_name值 |
$document_uri | 变量存储的是当前访问地址的URI。比如http://192.168.200.133/server?id=10&name=zhangsan中的"/server",功能和$uri一样 |
$document_root | 变量存储的是当前请求对应location的root值,如果未设置,默认指向Nginx自带html目录所在位置 |
$content_length | 变量存储的是请求头中的Content-Length的值 |
$content_type | 变量存储的是请求头中的Content-Type的值 |
$http_cookie | 变量存储的是客户端的cookie信息,可以通过add_header Set-Cookie 'cookieName=cookieValue’来添加cookie数据 |
$limit_rate | 变量中存储的是Nginx服务器对网络连接速率的限制,也就是Nginx配置中对limit_rate指令设置的值,默认是0,不限制。 |
$remote_addr | 变量中存储的是客户端的IP地址 |
$remote_port | 变量中存储了客户端与服务端建立连接的端口号 |
$remote_user | 变量中存储了客户端的用户名,需要有认证模块才能获取 |
$scheme | 变量中存储了访问协议 |
$server_addr | 变量中存储了服务端的地址 |
$server_name | 变量中存储了客户端请求到达的服务器的名称 |
$server_port | 变量中存储了客户端请求到达服务器的端口号 |
$server_protocol | 变量中存储了客户端请求协议的版本,比如"HTTP/1.1" |
$request_body_fifile | 变量中存储了发给后端服务器的本地文件资源的名称 |
$request_method | 变量中存储了客户端的请求方式,比如"GET","POST"等 |
$request_fifilename | 变量中存储了当前请求的资源文件的路径名 |
$request_uri | 变量中存储了当前请求的URI,并且携带请求参数,比如http://192.168.200.133/server?id=10&name=zhangsan中的"/server?id=10&name=zhangsan" |
2.4 限流
Nginx官方版本限制IP的连接和并发分别有两个模块:
limit_req_zone 用来限制单位时间内的请求数,即速率限制,采用的漏桶算法 “leaky bucket”。
limit_req_conn 用来限制同一时间连接数,即并发限制。
limit_req_zone参数:
Syntax: limit_req zone=name [burst=number] [nodelay];
Default: —
Context: http, server, location
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
第一个参数:$binary_remote_addr
表示通过remote_addr这个标识来做限制,“binary_”的目的是缩写内存占用量,是限制同一客户端ip地址。
第二个参数:zone
=one:10m表示生成一个大小为10M,名字为one的内存区域,用来存储访问的频次信息。
第三个参数:rate
=1r/s表示允许相同标识的客户端的访问频次,这里限制的是每秒1次,还可以有比如30r/m的。
limit_req zone=one burst=5 nodelay;
第一个参数:zone
=one 设置使用哪个配置区域来做限制,与上面limit_req_zone 里的name对应。
第二个参数:burst
=5,重点说明一下这个配置,burst爆发的意思,这个配置的意思是设置一个大小为5的缓冲区当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内。
第三个参数:nodelay
,如果设置,超过访问频次而且缓冲区也满了的时候就会直接返回503,如果没有设置,则所有请求会等待排队。
limit_req_zone示例:
http {
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
location /search/ {
limit_req zone=one burst=5 nodelay;
}
}
下面配置可以限制特定UA(比如搜索引擎)的访问:
limit_req_zone $anti_spider zone=one:10m rate=10r/s;
limit_req zone=one burst=100 nodelay;
if ($http_user_agent ~* "googlebot|bingbot|Feedfetcher-Google") {
set $anti_spider $http_user_agent;
}
ngx_http_limit_conn_module模块用来限制单个IP的请求数。并非所有的连接都被计数。只有在服务器处理了请求并且已经读取了整个请求头时,连接才被计数。
- 限制访问速率
示例:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server {
location / {
limit_req zone=mylimit;
}
}
此时如果使用单个IP在10ms内发并发送了6个请求,只有1个成功,剩下的5个都被拒绝。我们设置的速度是2r/s,为什么只有1个成功呢,是不是Nginx限制错了?当然不是,是因为Nginx的限流统计是基于毫秒的,我们设置的速度是2r/s,转换一下就是500ms内单个IP只允许通过1个请求,从501ms开始才允许通过第二个请求。
- burst缓存处理
我们短时间内发送了大量请求,Nginx按照毫秒级精度统计,超出限制的请求直接拒绝。这在实际场景中未免过于苛刻,真实网络环境中请求到来不是匀速的,很可能有请求“突发”的情况,也就是“一股子一股子”的。Nginx考虑到了这种情况,可以通过burst关键字开启对突发请求的缓存处理,而不是直接拒绝。
配置:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server {
location / {
limit_req zone=mylimit burst=4;
}
}
burst=4,意思是每个key(此处是每个IP)最多允许4个突发请求的到来。如果单个IP在10ms内发送6个请求,结果会怎样呢?
相比实例一成功数增加了4个,这个我们设置的burst数目是一致的。具体处理流程是:1个请求被立即处理,4个请求被放到burst队列里,另外一个请求被拒绝。通过burst参数,我们使得Nginx限流具备了缓存处理突发流量的能力。
burst的作用是让多余的请求可以先放到队列里,慢慢处理。如果不加nodelay参数,队列里的请求不会立即处理,而是按照rate设置的速度,以毫秒级精确的速度慢慢处理。
2.5 漏桶流算法和令牌桶算法
- 1、漏桶算法
漏桶算法是网络世界中流量整形或速率限制
时经常使用的一种算法,它的主要目的是控制数据注入到网络的速率,平滑网络上的突发流量
。漏桶算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量。也就是我们刚才所讲的情况。漏桶算法提供的机制实际上就是刚才的案例:突发流量会进入到一个漏桶,漏桶会按照我们定义的速率依次处理请求,如果突发流量过大就会直接溢出,则多余的请求会被拒绝
。所以漏桶算法能控制数据的传输速率。
1、水(请求)从上方倒入水桶,从水桶下方流出(被处理);
2、来不及流出的水存在水桶中(缓冲),以固定速率流出;
3、水桶满后水溢出(丢弃)。
这个算法的核心是:缓存请求、匀速处理、多余的请求直接丢弃。 - 2、令牌桶算法
令牌桶算法是网络流量整形和速率限制中最常使用的一种算法。典型情况下,令牌桶算法用来控制发送到网络上的数据的数目,并允许突发数据的发送
。令牌桶算法的机制如下:存在一个大小固定的令牌桶,会以恒定的速率源源不断产生令牌。如果令牌消耗速率小于生产令牌的速度,令牌就会一直产生直至装满整个令牌桶
。
1、令牌以固定速率产生,并缓存到令牌桶中;
2、令牌桶放满时,多余的令牌被丢弃;
3、请求要消耗等比例的令牌才能被处理;
4、令牌不够时,请求被缓存或者被拒绝。
相比漏桶算法,令牌桶算法不同之处在于它不但有一只“桶”,还有个队列,这个桶是用来存放令牌的,队列才是用来存放请求的。
从两者的作用上赖看,漏桶算法比较直接,限制实时限制数据的传输速率与频率,对于突发流量不管那么多就是规定死的,而令牌桶能够在限制的数据的平均传输速率的同时允许某种程度高并发的突发传输。
2.7 负载均衡*
为了避免服务器崩溃,大家会通过负载均衡的方式来分担服务器压力。将对台服务器组成一个集群,当用户访问时,先访问到一个转发服务器,再由转发服务器将访问分发到压力更小的服务器。
Nginx负载均衡实现的策略有以下五种:
- 1、轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器
,如果后端某个服务器宕机,能自动剔除故障系统。
upstream backserver {
server 192.168.0.12;
server 192.168.0.13;
}
- 2、权重weight
weight的值越大分配到的访问概率越高,主要用于后端每台服务器性能不均衡的情况下
。其次是为在主从的情况下设置不同的权值,达到合理有效的地利用主机资源。
upstream backserver {
server 192.168.0.12 weight=2;
server 192.168.0.13 weight=8;
}
权重越高,在被访问的概率越大,如上例,分别是20%,80%。
- 3、ip_hash( IP绑定)
每个请求按访问IP的哈希结果分配,使来自同一个IP的访客固定访问一台后端服务器,并且可以有效解决动态网页存在的session共享问题
。
upstream backserver {
ip_hash;
server 192.168.0.12:88;
server 192.168.0.13:80;
}
- 4、fair(第三方插件)
安装upstream_fair模块。对 weight、ip_hash更加智能的负载均衡算法,fair算法可以根据页面大小和加载时间长短智能地进行负载均衡,响应时间短的优先分配。
upstream backserver {
server server1;
server server2;
fair;
}
哪个服务器的响应速度快,就将请求分配到那个服务器上。
- 5、url_hash(第三方插件)
安装Nginx的hash软件包。按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。
upstream backserver {
server squid1:3128;
server squid2:3128;
hash $request_uri;
hash_method crc32;
}
2.8 限制浏览器访问
使用user_agent控制客户端浏览器访问,示例:
## 不允许谷歌浏览器访问 如果是谷歌浏览器返回500
if ($http_user_agent ~ Chrome) {
return 500;
}
2.9 nginx.conf的属性模块
worker_processes 1; # worker进程的数量
events { # 事件区块开始
worker_connections 1024; # 每个worker进程支持的最大连接数
} # 事件区块结束
http { # HTTP区块开始
include mime.types; # Nginx支持的媒体类型库文件
default_type application/octet-stream; # 默认的媒体类型
sendfile on; # 开启高效传输模式
keepalive_timeout 65; # 连接超时
server { # 第一个Server区块开始
listen 80; # 提供服务的端口,默认80
server_name localhost; # 提供服务的域名主机名
location / { # 第一个location区块开始
root html; # 站点的根目录,相当于Nginx的安装目录
index index.html index.htm; # 默认的首页文件,多个用空格分开
} # 第一个location区块结果
error_page 500502503504 /50x.html; # 出现对应的http状态码时,使用50x.html回应客户
location = /50x.html { # location区块开始,访问50x.html
root html; # 指定对应的站点目录为html
}
}
......
三、相关问题
3.1 使用反向代理服务器的优点是什么
- 1、提高访问速度
反向代理负载均衡可以将优化的负载均衡策略和代理服务器的高速缓存技术结合在一起,提升访问速度,提供有益的性能。 - 2、提高安全性
由于所有的客户机请求都必须通过代理服务器访问远程站点,因此可在代理服务器上设限,过滤某些不安全信息。
3.2 Nginx的优缺点
- 优点
占内存小,可实现高并发连接,处理响应快。
可实现http服务器、方向代理、负载均衡。
Nginx配置简单。
可以不暴露正式的服务器IP地址。 - 缺点
动态处理差:Nginx处理静态文件好,耗费内存少。但是处理动态页面则很鸡肋,一般前端用Nginx作为反向代理抗住压力。
3.3 Nginx是如何处理一个HTTP请求的
Nginx是一个高性能的Web服务器,能够同时处理大量的并发请求,使用的机制是:多进程机制和异步非阻塞方式。
- 1、多进程机制
服务器每当收到一个客户端时,就有 服务器主进程(master)生成一个 子进程(worker process)出来和客户端建立连接进行交互,直到连接断开,该子进程就结束了。
使用进程的好处是各个进程之间相互独立,不需要加锁,减少了使用锁对性能造成影响,同时降低编程的复杂度,降低开发成本。其次,采用独立的进程,可以让进程互相之间不会影响 ,如果一个进程发生异常退出时,其它进程正常工作, master进程则很快启动新的worker进程,确保服务不会中断,从而将风险降到最低。
缺点是操作系统生成一个子进程需要进行 内存复制等操作,在资源和时间上会产生一定的开销。当有大量请求时,会导致系统性能下降 。 - 2、异步非阻塞机制
每个工作进程使用异步非阻塞方式 ,可以处理多个客户端请求 。
当某个工作进程接收到客户端的请求以后,调用IO进行处理,如果不能立即得到结果,就去处理其他请求 (即为非阻塞 );而客户端在此期间也无需等待响应 ,可以去处理其他事情(即异步)。
当IO返回时,就会通知此工作进程 ;该进程得到通知,暂时挂起当前处理的事务去响应客户端请求。
3.4 Nginx服务器上的Master和Worker进程分别是什么
主程序Master process启动后,通过一个for循环来接收和处理外部信号 。
主进程通过fork()函数产生worker子进程 ,每个子进程执行一个for循环来实现Nginx服务器对事件的接收和处理 。
3.5 Nginx怎么禁止IP不可访问
# 如果访问的ip地址为192.168.9.115,则返回403
if ($remote_addr = 192.168.9.115) {
return 403;
}
3.6 Nginx设置重定向
return形式:
# 301永久重定向,302临时重定向
return 301 https://example.com$request_uri;
# return 返回形式
return code;
return code URL;
return URL;
rewrite形式:
rewrite ^/$ http://bbs.gitlib.com permanent;
rewrite flag说明:
last:停止处理后续rewrite指令集,然后对当前重写的新URI在rewrite指令集上重新查找。
break:停止处理后续rewrite指令集,并不在重新查找,但是当前location内剩余非rewrite语句和location外的非rewrite语句可以执行。
redirect:如果replacement不是以http:// 或https://开始,返回302临时重定向。
permant:返回301永久重定向。
3.7 Nginx条件判断
if ($http_user_agent ~ (125LA|WinHttpRequest|360Spider)) {
return 444;
}
if ($http_referer ~* "filter=author&orderby=dateline") {
return 444;
}
if ($host = 'bbs.gitlib.com') {
rewrite ^/$ http://bbs1.gitlib.com permanent;
}
比较符说明:
使用
=
、!=
比较的一个变量和字符串,true/false。
使用~
、~*
与正则表达式匹配的变量,如果这个正则表达式中包含右花括号}或者分号;则必须给整个正则表达式加引号。
使用-f
、!-f
检查一个文件是否存在。
使用-d
、!-d
检查一个目录是否存在。
使用-e
、!-e
检查一个文件、目录、符号链接是否存在。
使用-x
、!-x
检查一个文件是否可执行。
四、负载均衡
负载均衡建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量 、加强网络数据处理能力、提高网络的灵活性和可用性。
四层负载均衡vs七层负载均衡:
4.1 四层负载均衡( 目标地址和端口交换 )
主要通过报文中的目标地址和端口,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。
以常见的TCP为例,负载均衡设备在接收到第一个来自客户端的SYN请求时,即通过上述方式选择一个最佳的服务器,并对报文中目标IP地址进行修改(改为后端服务器IP),直接转发给该服务器。TCP的连接建立,即三次握手是客户端和服务器直接建立的,负载均衡设备只是起到一个类似路由器的转发动作。在某些部署情况下,为保证服务器回包可以正确返回给负载均衡设备,在转发报文的同时可能还会对报文原来的源地址进行修改。实现四层负载均衡的软件有:
F5 :硬件负载均衡器,功能很好,但是成本很高。
lvs :重量级的四层负载软件 。
nginx :轻量级的四层负载软件,带缓存功能,正则表达式较灵活 。
haproxy :模拟四层转发,较灵活 。
4.2 七层负载均衡(内容交换)
七层负载均衡,也称为“内容交换”,也就是主要通过报文中的真正有意义的应用层内容,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。
七层应用负载的好处,是使得整个网络更智能化。例如访问一个网站的用户流量,可以通过七层的方式,将对图片类的请求转发到特定的图片服务器并可以使用缓存技术;将对文字类的请求可以转发到特定的文字服务器并可以使用压缩技术。实现七层负载均衡的软件有:
haproxy :天生负载均衡技能,全面支持七层代理,会话保持,标记,路径转移。
nginx :只在 http 协议和 mail 协议上功能比较好,性能与 haproxy 差不多。
apache :功能较差。
Mysql proxy :功能尚可。
4.3 负载均衡策略
- 1、轮循均衡(Round Robin)
每一次来自网络的请求轮流分配给内部中的服务器,从1至N然后重新开始。此种均衡算法适合于服务器组中的所有服务器都有相同的软硬件配置并且平均服务请求相对均衡的情况。 - 2、权重轮循均衡(Weighted Round Robin)
根据服务器的不同处理能力,给每个服务器分配不同的权值,使其能够接受相应权值数的服务请求。例如:服务器A的权值被设计成1,B的权值是3,C的权值是6,则服务器A、B、C将分别接受到10%、30%、60%的服务请求。此种均衡算法能确保高性能的服务器得到更多的使用率,避免低性能的服务器负载过重。 - 3、随机均衡(Random)
把来自网络的请求随机分配给内部中的多个服务器。 - 4、权重随机均衡(Weighted Random)
此种均衡算法类似于权重轮循算法,不过在处理请求分担时是个随机选择的过程。 - 5、响应速度均衡(Response Time 探测时间)
负载均衡设备对内部各服务器发出一个探测请求(例如 Ping),然后根据内部中各服务器对探测请求的最快响应时间来决定哪一台服务器来响应客户端的服务请求。此种均衡算法能较好的反映服务器的当前运行状态,但这最快响应时间仅仅指的是负载均衡设备与服务器间的最快响应时间,而不是客户端与服务器间的最快响应时间。 - 6、最少连接数均衡(Least Connection)
最少连接数均衡算法对内部中需负载的每一台服务器都有一个数据记录,记录当前该服务器正在处理的连接数量,当有新的服务连接请求时,将把当前请求分配给连接数最少的服务器,使均衡更加符合实际情况,负载更加均衡。此种均衡算法适合长时处理的请求服务,如 FTP。 - 7、处理能力均衡(CPU、内存)
此种均衡算法将把服务请求分配给内部中处理负荷(根据服务器 CPU 型号、CPU 数量、内存大小及当前连接数等换算而成)最轻的服务器,由于考虑到了内部服务器的处理能力及当前网络运行状况,所以此种均衡算法相对来说更加精确,尤其适合运用到第七层(应用层)负载均衡的情况下。 - 8、DNS响应均衡(Flash DNS)
在此均衡算法下,分处在不同地理位置的负载均衡设备收到同一个客户端的域名解析请求,并在同一时间内把此域名解析成各自相对应服务器的 IP 地址并返回给客户端,则客户端将以最先收到的域名解析 IP 地址来继续请求服务,而忽略其它的 IP 地址响应。在种均衡策略适合应用在全局负载均衡的情况下,对本地负载均衡是没有意义的。 - 9、哈希算法
一致性哈希一致性Hash,相同参数的请求总是发到同一提供者。当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。 - 10、URL散列
通过管理客户端请求 URL 信息的散列,将发送至相同 URL 的请求转发至同一服务器的算法。
4.4 Nginx如何实现四层负载均衡
负载均衡可以分为静态负载均衡和动态负载均衡。
- 静态负载均衡
Nginx的四层静态负载均衡需要启用ngx_stream_core_module模块,默认情况下,ngx_stream_core_module是没有启用的,需要在安装Nginx时,添加–with-stream配置参数启用,如下所示。
./configure
--prefix=/usr/local/nginx-1.17.2
--with-openssl=/usr/local/src/openssl-1.0.2s
--with-pcre=/usr/local/src/pcre-8.43
--with-zlib=/usr/local/src/zlib-1.2.11
--with-http_realip_module
--with-http_stub_status_module
--with-http_ssl_module
--with-http_flv_module
--with-http_gzip_static_module
--with-cc-opt=-O3
--with-stream
--with-http_ssl_module
配置HTTP负载均衡时,都是配置在http指令下,配置四层负载均衡,则是在stream指令下,结构如下:
stream {
upstream mysql_backend {
......
}
server {
......
}
}
配置upstream:
upstream mysql_backend {
server 192.168.175.201:3306 max_fails=2 fail_timeout=10s weight=1;
server 192.168.175.202:3306 max_fails=2 fail_timeout=10s weight=1;
least_conn;
}
配置server:
server {
#监听端口,默认使用的是tcp协议,如果需要UDP协议,则配置成listen 3307 udp;
listen 3307;
#失败重试
proxy_next_upstream on;
proxy_next_upstream_timeout 0;
proxy_next_upstream_tries 0;
#超时配置
#配置与上游服务器连接超时时间,默认60s
proxy_connect_timeout 1s;
#配置与客户端上游服务器连接的两次成功读/写操作的超时时间,如果超时,将自动断开连接
#即连接存活时间,通过它可以释放不活跃的连接,默认10分钟
proxy_timeout 1m;
#限速配置
#从客户端读数据的速率,单位为每秒字节数,默认为0,不限速
proxy_upload_rate 0;
#从上游服务器读数据的速率,单位为每秒字节数,默认为0,不限速
proxy_download_rate 0;
#上游服务器
proxy_pass mysql_backend;
}
配置完之后,就可以连接Nginx的3307端口,访问数据库了。
完整的Nginx配置如下:
user hadoop hadoop;
worker_processes auto;
error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
use epoll;
worker_connections 1024;
}
stream {
upstream mysql_backend {
server 192.168.175.100:3306 max_fails=2 fail_timeout=10s weight=1;
least_conn;
}
server {
#监听端口,默认使用的是tcp协议,如果需要UDP协议,则配置成listen 3307 udp;
listen 3307;
#失败重试
proxy_next_upstream on;
proxy_next_upstream_timeout 0;
proxy_next_upstream_tries 0;
#超时配置
#配置与上游服务器连接超时时间,默认60s
proxy_connect_timeout 1s;
#配置与客户端上游服务器连接的两次成功读/写操作的超时时间,如果超时,将自动断开连接
#即连接存活时间,通过它可以释放不活跃的连接,默认10分钟
proxy_timeout 1m;
#限速配置
#从客户端读数据的速率,单位为每秒字节数,默认为0,不限速
proxy_upload_rate 0;
#从上游服务器读数据的速率,单位为每秒字节数,默认为0,不限速
proxy_download_rate 0;
#上游服务器
proxy_pass mysql_backend;
}
- 动态负载均衡
配置Nginx四层静态负载均衡后,重启Nginx时,Worker进程一直不退出,会报错,如下所示。
nginx: worker process is shutting down;
这是因为Worker进程维持的长连接一直在使用,所以无法退出,只能杀掉进程。可以使用Nginx的四层动态负载均衡解决这个问题。
使用Nginx的四层动态负载均衡有两种方案:使用商业版的Nginx和使用开源的nginx-stream-upsync-module模块。注意:四层动态负载均衡可以使用nginx-stream-upsync-module模块,七层动态负载均衡可以使用nginx-upsync-module模块。
使用如下命令为Nginx添加nginx-stream-upsync-module模块和nginx-upsync-module模块,此时,Nginx会同时支持四层动态负载均衡和HTTP七层动态负载均衡。
git clone https://github.com/xiaokai-wang/nginx-stream-upsync-module.git
git clone https://github.com/weibocom/nginx-upsync-module.git
git clone https://github.com/CallMeFoxie/nginx-upsync.git
cp -r nginx-stream-upsync-module/* nginx-upsync/nginx-stream-upsync-module/
cp -r nginx-upsync-module/* nginx-upsync/nginx-upsync-module/
./configure --prefix=/usr/local/nginx-1.17.2 --with-
openssl=/usr/local/src/openssl-1.0.2s --with-pcre=/usr/local/src/pcre-8.43 --
with-zlib=/usr/local/src/zlib-1.2.11 --with-http_realip_module --with-
http_stub_status_module --with-http_ssl_module --with-http_flv_module --with-
http_gzip_static_module --with-cc-opt=-O3 --with-stream --add-
module=/usr/local/src/nginx-upsync --with-http_ssl_module
配置HTTP负载均衡时,都是配置在http指令下,配置四层负载均衡,则是在stream指令下,结构如下:
stream {
upstream mysql_backend {
......
}
server {
......
}
}
配置upstream:
upstream mysql_backend {
server 127.0.0.1:1111; #占位server
upsync 192.168.175.100:8500/v1/kv/upstreams/mysql_backend upsync_timeout=6m
upsync_interval=500ms upsync_type=consul strong_dependency=off;
upsync_dump_path /usr/local/nginx-1.17.2/conf/mysql_backend.conf;
}
upsync指令指定从consul哪个路径拉取上游服务器配置;
upsync_timeout配置从consul拉取上游服务器配置的超时时间;
upsync_interval配置从consul拉取上游服务器配置的间隔时间;
upsync_type指定使用consul配置服务器;
strong_dependency配置nginx在启动时是否强制依赖配置服务器,如果配置为on,则拉取配置失败时Nginx启动同样失败。
upsync_dump_path指定从consul拉取的上游服务器后持久化到的位置,这样即使consul服务器出现问题,本地还有一个备份。
配置server:
server {
#监听端口,默认使用的是tcp协议,如果需要UDP协议,则配置成listen 3307 udp;
listen 3307;
#失败重试
proxy_next_upstream on;
proxy_next_upstream_timeout 0;
proxy_next_upstream_tries 0;
#超时配置
#配置与上游服务器连接超时时间,默认60s
proxy_connect_timeout 1s;
#配置与客户端上游服务器连接的两次成功读/写操作的超时时间,如果超时,将自动断开连接
#即连接存活时间,通过它可以释放不活跃的连接,默认10分钟
proxy_timeout 1m;
#限速配置
#从客户端读数据的速率,单位为每秒字节数,默认为0,不限速
proxy_upload_rate 0;
#从上游服务器读数据的速率,单位为每秒字节数,默认为0,不限速
proxy_download_rate 0;
#上游服务器
proxy_pass mysql_backend;
}
从Consul添加上游服务器:
curl -X PUT -d "{\"weight\":1, \"max_fails\":2, \"fail_timeout\":10}"
http://192.168.175.100:8500/v1/kv/upstreams/mysql_backend/192.168.175.201:3306
curl -X PUT -d "{\"weight\":1, \"max_fails\":2, \"fail_timeout\":10}"
http://192.168.175.100:8500/v1/kv/upstreams/mysql_backend/192.168.175.202:3306
从Consul删除上游服务器:
curl -X DELETE
http://192.168.175.100:8500/v1/kv/upstreams/mysql_backend/192.168.175.202:3306
配置upstream_show:
server {
listen 13307;
upstream_show;
}
配置upstream_show指令后,可以通过curl http://192.168.175.100:13307/upstream_show查看当前动态负载均衡上游服务器列表。
Nginx的完整配置如下:
user hadoop hadoop;
worker_processes auto;
error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
use epoll;
worker_connections 1024;
}
stream {
upstream mysql_backend {
server 127.0.0.1:1111; #占位server
upsync 192.168.175.100:8500/v1/kv/upstreams/mysql_backend upsync_timeout=6m
upsync_interval=500ms upsync_type=consul strong_dependency=off;
upsync_dump_path /usr/local/nginx-1.17.2/conf/mysql_backend.conf;
}
server {
#监听端口,默认使用的是tcp协议,如果需要UDP协议,则配置成listen 3307 udp;
listen 3307;
#失败重试
proxy_next_upstream on;
proxy_next_upstream_timeout 0;
proxy_next_upstream_tries 0;
#超时配置
#配置与上游服务器连接超时时间,默认60s
proxy_connect_timeout 1s;
#配置与客户端上游服务器连接的两次成功读/写操作的超时时间,如果超时,将自动断开连接
#即连接存活时间,通过它可以释放不活跃的连接,默认10分钟
proxy_timeout 1m;
#限速配置
#从客户端读数据的速率,单位为每秒字节数,默认为0,不限速
proxy_upload_rate 0;
#从上游服务器读数据的速率,单位为每秒字节数,默认为0,不限速
proxy_download_rate 0;
#上游服务器
proxy_pass mysql_backend;
}
server {
listen 13307;
upstream_show;
}