zl程序教程

您现在的位置是:首页 >  IT要闻

当前栏目

Nginx后端开发人员必学神器-并发编程经典之作剖析和名企热点面试v1.21.3

2023-02-18 16:40:35 时间

概述

**本人博客网站 **IT小神 www.itxiaoshen.com

Nginx官网 最新版本为1.21.3

Nginx (engine x) 是一个开源的、高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务,由俄罗斯的程序设计师IgorSysoev所开发,官方测试nginx能够支撑5万并发连接,并且cpu、内存等资源消耗却非常低,运行非常稳定,支持热部署,几乎可以实现7*24小时不间断运行。

可以说只要有网站或者后台服务的企业就会需要用到Nginx,其使用极为广泛。

部署方式

预先构建好的包安装

#以RHEL/CentOS为例
#安装先决条件
sudo yum install yum-utils
#在新机器上第一次安装nginx之前,需要设置nginx包存储库。之后,您可以从存储库安装和更新nginx,要设置yum存储库,创建文件/etc/yum.repos.d/nginx
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

#默认情况下,使用稳定nginx包的存储库。如果你想使用主线nginx包,请运行以下命令:
sudo yum-config-manager --enable nginx-mainline
#使用实例安装nginx:
sudo yum install nginx

image-20211006133414806

源码安装

如果一些特殊的功能是必需的,而不是包和端口提供的,nginx也可以从源文件编译。虽然这种方法更灵活,但对于初学者来说可能比较复杂,下面我们进行Nginx源码安装

#编译前提,需要安装必要的包
yum install gcc pcre-devel openssl-devel zlib-devel -y
#从Nginx官网下载源码文件
wget https://nginx.org/download/nginx-1.21.3.tar.gz
#解压下载的压缩文件
tar -xvf nginx-1.21.3.tar.gz
#进入解压的nginx目录
cd nginx-1.21.3
#参数使用示例(所有这些都需要在一行中输入),需要定制特别参数可以详细参考官网源代码安装说明,https://nginx.org/en/docs/configure.html
./configure --prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--with-pcre \
--with-stream \
--with-stream_ssl_module \
--with-stream_realip_module
#配置完成后,使用make编译并安装nginx,-j 8 表示CPU八核,可以按照实际CPU核数定义,越多越快
make -j 8 && make install

image-20211006142949217

image-20211006143105583

修改/usr/local/nginx/conf/nginx.conf,将监听端口改为8082,

image-20211006144127074

启动nginx和访问本机的8082端口,http://localhost:8082,返回nginx欢迎页面,nginx部署完毕

image-20211006144252762

常用命令

cd /usr/local/nginx/sbin/
#启动nginx
./nginx
#停止nginx
./nginx -s stop
#安全退出nginx
./nginx -s quit
#重新加载配置文件启动nginx,相当于热启动
./nginx -s reload
#查看nginx进程
ps aux | grep nginx

Nginx常用特性

概述

关于nginx https://nginx.org/en/ ,详细可以查阅这个官网文档

image-20211006175317457

从HTTP缓存看Nginx进程架构

image-20211006180920191

nginx是C语言编写的,采用的多进程架构模式,一个master和多个worker(worker数量一般为cpu核数),Master负责管理worker进程,worker进程负责处理网络事件,整个框架被设计为一种依赖事件驱动、异步、非阻塞的模式。这样设计有点如下:

  • 可以充分利用多核机器,增强并发处理能力。
  • 多worker间可以实现负载均衡。
  • Master监控并统一管理worker行为。在worker异常后,可以主动拉起worker进程,从而提升了系统的可靠性。并且由Master进程控制服务运行中的程序升级、配置项修改等操作,从而增强了整体的动态可扩展与热更的能力。

如果有兴趣要深入了解nginx源码可研究nginx的upstream(工作方式)、线程池(线程管理,)、内存池(建立一个大块内存缓冲,不需要每次使用去申请)、网络IO、进程间通信如共享内存、epoll、多进程、日志处理、conf配置文件管理、原子操作、http模块、链表、红黑树以及进一步理解Nginx业务流程架构;Nginx提供高度灵活性,通常我们可以自己编写C模块嵌入到nginx程序中,可以基于nginx模块化开发实现类似限流、黑白名单、认证鉴权验证等功能。

Nginx官方功能和特性描述如下:

image-20211006145950083

image-20211006150051696

反向代理

正向代理

正向代理作用在客户端的,类似一个跳板机,代理访问外部资源,比如我们国内访问谷歌,直接访问访问不到,我们可以通过一个正向代理服务器,请求发到代理服,代理服务器能够访问谷歌,这样由代理去谷歌取到返回数据,再返回给我们,这样我们就能访问谷歌了。正向代理即是客户端代理, 代理客户端, 服务端不知道实际发起请求的客户端。

image-20211006190622482

反向代理

反向代理是作用在服务器端的,代理服务器对外就表现为一个服务器,实际运行方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端;反向代理即是服务端代理, 代理服务端, 客户端不知道实际提供服务的服务端

反向代理的作用:

  • 保证内网的安全,阻止web攻击,大型网站,通常将反向代理作为公网访问地址,Web服务器是内网。
  • 负载均衡,通过反向代理服务器来优化网站的负载。

image-20211006190137447

动静分离

Nginx本身就是一个非常强劲的静态资源服务器,如通过location配置实现动态页面转发后端tomcat服务器,在nginx站点下存放静态资源文件或站点,加快网站的解析速度,可以把动态页面和静态页面由不同的服务器来解析,加快解析速度,降低原来单个服务器的压力。 简单来说,就是使用正则表达式匹配过滤,然后交个不同的服务器。

image-20211006194339511

动静分离的原理很简单,通过location对请求url进行匹配即可,在/usr/local/nginx/html/(任意目录)下创建 /static/imgs 配置如下:

###静态资源访问
server {
  listen       80;
  server_name  static.itxiaoshen.com;
  location /static/imgs {
       root /usr/local/nginx/html/imgs;
       index  index.html index.htm;
   }
}
###动态资源访问
 server {
  listen       80;
  server_name  www.itxiaoshen.com;    
  location / {
     proxy_pass http://127.0.0.1:8080; //这里如果有多台也可以通过配置upstream然后在这里引用,详细可以看下一节
     index  index.html index.htm;
   }
}

负载均衡

Nginx提供丰富的负载均衡算法,包括轮询、加权轮询、最少连接、响应时间最小、iphash、urlhash。

image-20211007102601758

轮询(Round Robin)

nginx默认负载均衡算法,可以配合权重使用,默认情况权重是1。

upstream backend {
   #没有负载均衡算法定义则默认为Round Robin
   server backend1.example.com;
   server backend2.example.com;
}

加权轮询

upstream backend {
   server backend1.example.com weight=10;
   server backend2.example.com weight=10;
}

最少连接(Least Connections)

请求会转发到当前有效连接最少的服务器,可以配合权重使用。

upstream backend {
    least_conn;
    server backend1.example.com;
    server backend2.example.com;
}

IP哈希(IP Hash)

由请求客户端的IP地址决定请求发往哪台服务器,可以保证同一个IP地址的请求可以转发到同一台服务器。IPV4的前三位或者IPV6的全部地址参与哈希运算。

upstream backend {
    ip_hash;
    server backend1.example.com;
    server backend2.example.com;
}

如果想要暂时移除当前负载服务器组中的某一台服务器,可以用down 参数标记服务器,已经通过IP哈希到当前服务器的请求会暂时保持,并自动被转移到组中的下一台服务器。

upstream backend {
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com down;
}

URL哈希

请求会根据自定义的字符串、变量或者二者的组合作为key进行哈希,决定发往哪台服务器。比如按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。Nginx本身是不支持url_hash的,如果需要使用这种调度算法,必须安装Nginx 的hash软件包。

upstream backend {
    hash $request_uri consistent;
    hash_method crc32;
    server backend1.example.com;
    server backend2.example.com;
}

hash指令中consistent 参数是可选的,如果指定了consistent参数,负载均衡会使用一致性哈希中的ketama算法,请求会根据定义的哈希键值哈希均匀的发往组中的所有服务器上。给服务器组中增加新服务器或者移除要原来的服务器,只会有少数的哈希键会重新映射。

响应时间最小fair(第三方)

比上面两个更加智能的负载均衡算法。根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx本身是不支持fair的,如果需要使用这种调度算法,必须下载Nginx的upstream_fair模块。或者使用商业版本的Nginx Plus,此外在在upstream中每个主机后面配置 max_conns可以限制每个后端服务器的处理连接数。

upstream backend {    
    server server1;    
    server server2;    
    fair;    
}

缓存

nginx配置缓存的优点:可以在一定程度上,减少服务器的处理请求压力。比如对一些图片,css或js做一些缓存,那么在每次刷新浏览器的时候,就不会重新请求了,而是从缓存里面读取。这样就可以减轻服务器的压力。详细可参考官网说明

image-20211007115934714

nginx可配置的缓存有2种:

  • 客户端的缓存(一般指浏览器的缓存,Cache-Control)。网页缓存是由HTTP消息头中的"Cache-control"来控制的,常见的取值有private、no-cache、max-age、must-revalidate等,默认为private。
  • 服务端的缓存(使用proxy-cache实现的)。
worker_processes  1;
events {
  worker_connections  1024;
}
http {
  include       mime.types;
  default_type  application/octet-stream;
  sendfile        on;
  #tcp_nopush     on;
  #keepalive_timeout  0;
  keepalive_timeout  65;
  include nginx_proxy.conf;
  proxy_cache_path  /data/nuget-cache levels=1:2 keys_zone=nuget-cache:20m max_size=50g inactive=168h;
  #gzip  on;
  server {
    listen       8081;
    server_name  xxx.abc.com;
    location / {
      proxy_pass http://localhost:7878;
      add_header  Cache-Control  max-age=no-cache;
    }
    location ~* \.(css|js|png|jpg|jpeg|gif|gz|svg|mp4|ogg|ogv|webm|htc|xml|woff)$ {
      access_log off;
      add_header Cache-Control "public,max-age=30*24*3600";
      proxy_pass http://localhost:7878;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
      root   html;
    }
  }
}
  • 缓存文件放在哪儿?
#指定缓存位置、缓存名称、内存中缓存内容元数据信息大小限制、缓存总大小限制。缓存位置是一个目录应该先创建好,nginx并不会帮我们创建这个缓存目录
proxy_cache_path /data/nginx/cache  keys_zone=one:10m  max_size=10g;
#指定使用前面设置的缓存名称
proxy_cache  one;
  • 如何指定哪些请求被缓存?
    • Nginx默认会缓存所有get和head方法的请求结果,缓存的key默认使用请求字符串.
    • 自定义key,例如 proxy_cache_key “host request_uri$cookie_user”;
    • 指定请求至少被发送了多少次以上时才缓存,可以防止低频请求被缓存,例如:proxy_cache_min_uses 5;
    • 指定哪些方法的请求被缓存,例如 proxy_cache_methods GET HEAD POST;
  • 缓存有效期?
    • 默认情况下,缓存的内容是长期存留的,除非缓存的总量超出限制。可以指定缓存的有效期,例如:
      • 响应状态码为200 302时,10分钟有效 proxy_cache_valid 200 302 10m;
      • 对应任何状态码,5分钟有效 proxy_cache_valid any 5m;
  • 如何指定哪些请求不被缓存?
    • proxy_cache_bypass 该指令响应来自原始服务器而不是缓存,例如:proxy_cache_bypass $cookie_nocache \(arg_nocache\)arg_comment;
    • 如果任何一个参数值不为空或者不等0,nginx就不会查找缓存,直接进行代理转发。

Rewrite 配置功能

URL重写有利于网站首选域的确定,对于同一资源页面多条路径的301重定向有助于URL权重的集中,rewrite的组要功能是实现URL地址的重定向。Nginx的rewrite功能需要PCRE软件的支持,即通过perl兼容正则表达式语句进行规则匹配的。默认参数编译nginx就会支持rewrite的模块,但是也必须要PCRE的支持,rewrite是实现URL重写的关键指令,根据regex(正则表达式)部分内容,重定向到replacement,结尾是flag标记。详细可参考官网说明

image-20211007120455550

限流

概述

Nginx限流的实现主要是ngx_http_limit_conn_module和ngx_http_limit_req_module这两个模块,按请求速率限速模块使用的是漏桶算法,即能够强行保证请求的实时处理速度不会超过设置的阈值。Nginx官方版本限制IP的连接和并发分别有两个模块:zone 用来限制单位时间内的请求数,即速率限制,采用的漏桶算法 “leaky bucket”。limit_req_conn 用来限制同一时间连接数,即并发限制。

限流算法

令牌桶算法

算法思想:

  • 令牌以固定速率产生,并缓存到令牌桶中;
  • 令牌桶放满时,多余的令牌被丢弃;
  • 请求要消耗等比例的令牌才能被处理;
  • 令牌不够时,请求被缓存。

image-20211007123221870

漏桶算法

算法思想:

  • 水(请求)从上方倒入水桶,从水桶下方流出(被处理);
  • 来不及流出的水存在水桶中(缓冲),以固定速率流出;
  • 水桶满后水溢出(丢弃)。
  • 这个算法的核心是:缓存请求、匀速处理、多余的请求直接丢弃。相比漏桶算法,令牌桶算法不同之处在于它不但有一只“桶”,还有个队列,这个桶是用来存放令牌的,队列才是用来存放请求的。从作用上来说,漏桶和令牌桶算法最明显的区别就是是否允许突发流量(burst)的处理,漏桶算法能够强行限制数据的实时传输(处理)速率,对突发流量不做额外处理;而令牌桶算法能够在限制数据的平均传输速率的同时允许某种程度的突发传输。

image-20211007123402818

请求处理数限流

#先安装压测工具,当前我们java开发在windows熟悉的jmeter有相似的功能
yum install -y httpd-tools

#编辑 vim /usr/local/nginx/conf/nginx.conf
#将limit_req_zone放在http中
limit_req_zone $binary_remote_addr zone=MyReqZone:10m rate=1r/s;
#将limit_req放在server中
limit_req zone=MyReqZone burst=5 nodelay;
#更新nginx配置
./nginx -s reload
#执行访问测试
ab -c 1 -n 50 http://localhost:8082/
  • limit_req_zone
    • 第一个参数:\(binary_remote_addr,表示通过remote_addr这个标识来做限制,“binary_”的目的是缩写内存占用量,是限制同一客户端ip地址。使用这个标识来确认用户身份唯一性,\)binary_remote_addr变量的大小对于IPv4地址始终为4字节,对于IPv6地址始终为16字节。存储状态在64位平台上总是占用64字节。一个1兆字节的区域可以保留大约1.6万个64字节状态。如果区域存储已用尽,服务器将把错误返回给所有请求。
    • 第二个参数:zone=MyReqZone:10m表示生成一个大小为10M,名字为one的内存区域,用来存储访问的频次信息。
    • 第三个参数:rate=1r/s表示允许相同标识的客户端的访问频次,这里限制的是每秒1次,还可以有比如30r/m的。
  • limit_req
    • 第一个参数:zone=MyReqZone设置使用哪个配置区域来做限制,与上面limit_req_zone 里的name对应。
    • 第二个参数:burst=5,重点说明一下这个配置,burst爆发的意思,这个配置的意思是设置一个大小为5的缓冲区当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内。
    • 第三个参数:nodelay,如果设置,超过访问频次而且缓冲区也满了的时候就会直接返回503,如果没有设置,则所有请求会等待排队。

image-20211007125528244

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IPS50iJd-1633588453688)(F:\creation\markdown\article\Nginx大厂面试需要掌握多少\Nginx大厂面试需要掌握多少.assets\image-20211007124839533.png)]

连接数限流

#将limit_conn_zone放在http中,limit_conn perip 10:对应的key是 $binary_remote_addr,表示限制单个IP同时最多能持有10个连接。limit_conn perserver 100:对应的key是 $server_name,表示虚拟主机(server) 同时能处理并发连接的总数。注意,只有当 request header 被后端server处理后,这个连接才进行计数。
limit_conn_zone $binary_remote_addr zone=MyPerIp:10m;
limit_conn_zone $server_name zone=MyPerServer:10m;
#将limit_conn放在server中
limit_conn MyPerIp 10;
limit_conn MyPerServer 100;

Nginx的生态

image-20211006170918240

我们通常使用开源Nginx版本,基于Nginx开源版本至上还衍生其他版本,包括商业收费版本的Nginx Plus、淘宝开源的TEngine、OpenResty。

NGINX Plus

NGINX Plus官网 https://www.nginx.com/resources/datasheets/nginx-plus-datasheet/

NGINX Plus以先进的功能和屡获殊荣的支持扩展了NGINX开源,为客户提供了完整的应用交付解决方案。NGINX Plus将负载平衡器、内容缓存、web服务器、安全控制和丰富的应用程序监控和管理整合到一个易于使用的软件包中。

TEngine

TEngine官网 http://tengine.taobao.org/

Tengine是由淘宝网发起的Web服务器项目。它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性。Tengine的性能和稳定性已经在大型的网站如淘宝网天猫商城等得到了很好的检验。它的最终目标是打造一个高效、稳定、安全、易用的Web平台。

从2011年12月开始,Tengine成为一个开源项目,Tengine团队在积极地开发和维护着它。Tengine团队的核心成员来自于淘宝搜狗等互联网企业。Tengine是社区合作的成果,我们欢迎大家参与其中,贡献自己的力量。

TEngine特性:

  • 继承Nginx-1.18.0的所有特性,兼容Nginx的配置;
  • 支持HTTP的CONNECT方法,可用于正向代理场景;
  • 支持异步OpenSSL,可使用硬件如:QAT进行HTTPS的加速与卸载;
  • 增强相关运维、监控能力,比如异步打印日志及回滚,本地DNS缓存,内存监控等;
  • Stream模块支持server_name指令;
  • 更加强大的负载均衡能力,包括一致性hash模块、会话保持模块,还可以对后端的服务器进行主动健康检查,根据服务器状态自动上线下线,以及动态解析upstream中出现的域名;
  • 输入过滤器机制支持。通过使用这种机制Web应用防火墙的编写更为方便;
  • 支持设置proxy、memcached、fastcgi、scgi、uwsgi在后端失败时的重试次数;
  • 动态脚本语言Lua支持。扩展功能非常高效简单;
  • 支持按指定关键字(域名,url等)收集Tengine运行状态;
  • 组合多个CSS、JavaScript文件的访问请求变成一个请求;
  • 自动去除空白字符和注释从而减小页面的体积;
  • 自动根据CPU数目设置进程个数和绑定CPU亲缘性;
  • 监控系统的负载和资源占用从而对系统进行保护;
  • 显示对运维人员更友好的出错信息,便于定位出错机器;
  • 更强大的防攻击(访问速度限制)模块;
  • 更方便的命令行参数,如列出编译的模块列表、支持的指令等;
  • 支持Dubbo协议;
  • 可以根据访问文件类型设置过期时间;

OpenResty

概述

OpenResty官网 http://openresty.org/cn/

OpenResty® 是一个基于Nginx 与 Lua 的高性能Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

OpenResty® 通过汇聚各种设计精良的 Nginx 模块(是国人章亦春发起和创建,目前主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。

OpenResty® 的目标是让你的Web服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。

OpenResty强大之处就是提供许许多多的组件,开箱即用

image-20211007105825045

通过git编译成机器码缓存起来,直接执行机器码,lua git效率更高,lua git将lua虚拟机vm嵌入到进程中,lua nginx可以在设置阶段中通过lua命令嵌入到nginx中并回显。Cosocket实际上也是些协程,采用异步事件网络IO,发送和接收数据是两个流程,Cosocket实现了一个同步非阻塞的模式

#在redis存储hello的String类型key,值为heihei,下面这句代码OpenResty->redis发送并挂起当前的协程,等redis server返回数据的时候激活挂起的协程返回数据
Local val = redis:get("hello")

在Open Resty四个部分,lua nginx module 、测试集、resty lrucache redis 、工具集。lua可以随心所欲的做复杂的访问控制和安全检查,防止一些注入和xss的攻击。比如可以在Open Resty实现黑白名单(黑名单数据可以存储在mysql或者redis中,通过lua访问后端存储服务)、身份授权,还有比如很多做电商行业通过在Open Resty中将一些并发访问极大的商品详细页嵌入到nginx实现,通过Open Resty以使用同步编程方式但实际上内部确是异步非阻塞的模式访问redis获取库存数据等并构建成静态页面返回给用户,lua_shared_dict、lua_resty_lrucache、lua_resty_lock 解决缓存失效风暴问题。

Nginx实质上也即是一个网关,Kong网关也是一款基于OpenResty(Nginx + Lua模块)编写的高可用、易扩展的,由Mashape公司开源的API Gateway项目。Kong是基于NGINX和Apache Cassandra或PostgreSQL构建的,能提供易于使用的RESTful API来操作和配置API管理系统,所以它可以水平扩展多个Kong服务器,通过前置的负载均衡配置把请求均匀地分发到各个Server,来应对大批量的网络请求。

配置文件与模块化设计

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QTWfidgN-1633588453694)(http://www.itxiaoshen.com:3001/assets/16335151013907ck3xNNb.png)]

OpenResty中,Nginx 的模块化设计使得每一个 HTTP 模块可以仅专注于完成一个独立的、简单的功能,而一个请求的完整处理过程可以使由无数个 HTTP 模块共同合作完成。

  • init 阶段:
    • init_by_lua context:http
    • init_worker_by_lua context:http
  • rewrite/access阶段:
    • 请求类型:
      • http类型:
        • set_by_lua context:server,server if, location,location if
        • rewrite_by_lua context:http,server,location,location if
        • access_by_lua context:http,server,location, location if
      • https类型:
        • ssl_certificate_by_lua context:server
        • set_by_lua context:server,server if, location,location if
        • rewrite_by_lua context:http,server,location,location if
        • access_by_lua context:http,server,location, location if
  • content阶段
    • 响应生成方式
      • lua方式
        • content_by_lua context:location,location if
        • header_filter_by_lua context:http,server,location, location if
        • body_filter_by_lua context:http,server.location,location if
      • upstream方式
        • balancer_by_lua context:upstream
        • header_filter_by_lua context:http,server,location, location if
        • body_filter_by_lua context:http,server.location,location if
      • 其他方式(如 echo)
        • header_filter_by_lua context:http,server,location, location if
        • body_filter_by_lua context:http,server.location,location if
  • log 阶段
    • log_by_lua context:http,server,location,location if

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DsQgz4AY-1633588453695)(http://www.itxiaoshen.com:3001/assets/1633577475601QdhNy2Tf.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HXdYP8rG-1633588453697)(http://www.itxiaoshen.com:3001/assets/1633573445328MA8PM3eZ.png)]

CentOS安装OpenResty

# add the yum repo:
wget https://openresty.org/package/centos/openresty.repo
sudo mv openresty.repo /etc/yum.repos.d/

# update the yum index:
sudo yum check-update
sudo yum install -y openresty
sudo yum install -y openresty-resty
#安装完找到/usr/local/openresty

image-20211007135633404

HelloWorld入门示例

image-20211007140434235

#进入/usr/local/openresty/nginx/conf目录,编辑下面这段嵌入一段简单lua脚本,可以执行代码块或者脚本文件
worker_processes  1;
error_log logs/error.log;
events {
    worker_connections 1024;
}
http {
    server {
        listen 8084;
        location / {
            default_type text/html;
            content_by_lua_block {
                ngx.say("<p>hello, world</p>")
            }
        }
    }
}
##进入到openresty的nginx bin目录
cd /usr/local/openresty/nginx/sbin
##启动openresty的nginx
./nginx
#访问nginx,出现我们使用lua代码块回显的内容,至此可以开启openresty的编程大门
curl http://localhost:8084

image-20211007140818835

大厂面试题

惊群效应是怎么产生的?Nginx解决思路?

由于worker都是由master进程fork产生,所以worker都会同时监听相同端口,有IO事件所有进程同时被唤醒然后挂起,但只有一个进程能否获取到,这样多个子进程在accept建立连接时会发生争抢,带来著名的“惊群”问题。

nginx采取在同一个时刻只有一个进程在监听端口的机制,实现如下ngx_shmtx_t 是共享变量、共享内存是多进程通信最快方式,所有worker进程使用同一把锁,采用无锁的CAS实现。

为什么Nginx使用多进程不使用多线程?

Nginx使用业务场景主要是一个web服务器,有处理无状态的如http请求,如果采用多线程就需要加锁和同步问题,采用多进程可以对每次请求做到最大的隔离。

  • 不采用多线程模型管理连接
    • 无状态服务,无需共享进程内存。
    • 采用独立的进程,可以让互相之间不会影响。一个进程异常崩溃,其他进程的服务不会中断,提升了架构的可靠性。
    • 进程之间不共享资源,不需要加锁,所以省掉了锁带来的开销。
  • 不采用多线程处理逻辑业务
    • 进程数已经等于核心数,再新建线程处理任务,只会抢占现有进程,增加切换代价。
    • 作为接入层,基本上都是数据转发业务,网络IO任务的等待耗时部分,已经被处理为非阻塞/全异步/事件驱动模式,在没有更多CPU的情况下,再利用多线程处理,意义不大。并且如果进程中有阻塞的处理逻辑,应该由各个业务进行解决,比如openResty中利用了Lua协程,对阻塞业务进行了优化。

Nginx热启动是如何实现?

我们知道使用nginx -s reload就可以不停止nginx服务情况下更新配置,master负责解析配置文件、监控worker进程,worker进程负责listen,一个worker进程同时监听多个server端口,worker之间是竞争的关系,通过master将内存配置结构更新写入共享内存中下一次就可以获取最新的配置信息。

Nginx支持配置热更新和程序热更新,Nginx热更配置时,可以保持运行中平滑更新配置,具体流程如下:

  • 更新nginx.conf配置文件,向master发送SIGHUP信号或执行nginx -s reload
  • Master进程使用新配置,启动新的worker进程
  • 使用旧配置的worker进程,不再接受新的连接请求,并在完成已存在的连接后退出

image-20211007112327121

谈谈Nginx的事件驱动模型?

Worker进程在处理网络事件时,依靠epoll模型,来管理并发连接,实现了事件驱动、异步、非阻塞等特性。通常海量并发连接过程中,每一时刻(相对较短的一段时间),往往只需要处理一小部分有事件的连接即活跃连接。基于以上情况,epoll通过将连接管理与活跃连接管理进行分离,实现了高效、稳定的网络IO处理能力。其中,epoll利用红黑树高效的增删查效率来管理连接,利用一个双向链表来维护活跃连接。

image-20211007113101508

Nginx如何实现高可用?

可以通过基于VRRP虚拟路由器冗余协议的keepalived方案实现多台nginx故障自动切换,核心也即是控制同一个VIP虚拟IP在多台主机漂移。

后续我们再找时间深入分析Nginx源码、Nginx的C模块化开发、Nginx高阶及高可用实战,OpenResty编程实战。