百万架构师第四十四课:Nginx:Nginx 的扩展-OpenRestry|JavaGuide

news/2025/2/25 23:41:06/文章来源:https://www.cnblogs.com/nogeek-cn/p/18737536

百万架构师系列文章阅读体验感更佳

原文链接:https://javaguide.net

公众号:不止极客

Nginx 的扩展-OpenRestry

课程目标

  1. Nginx 进程模型简介

  2. Nginx 的高可用方案

  3. OpenResty 安装及使用

  4. 什么是 API 网关?

  5. OpenResty 实现灰度发布功能

Nginx 进程模型简介

多进程

  • Tomcat

    • BIO
    • NIO
    • AIO
  • Nginx

    • 多进程+多路复用
    • master 进程 、 worker 进程

​ 当它启动以后,会产生一个主进程和多个工作进程。多个工作进程是通过 Master 进程去管理的。它是基于 Master 进程 Fork 出来的。当我们的 Nginx 收到一个请求的时候,它会向我们的 work 发送一个信号。然后通过 worker 进程去管理。类似于中心进程的意思。

​ 当有一个请求过来的时候,只会有一个 worker 进程去处理。

root /nginx 7473 1 0 20:09 ? 00:00:00 nginx: master process .
nobody 7474 7473 0 20:09 ? 00:00:00 nginx: worker process
[root@Darian1 nginx]# ps -ef|grep nginx
root      47432      1  0 00:14 ?        00:00:00 nginx: master process ./sbin/nginx
nobody    47433  47432  0 00:14 ?        00:00:00 nginx: worker process
root      47438  47405  0 00:14 pts/1    00:00:00 grep --color=auto nginx
[root@Darian1 nginx]# 

JavaGuide_Nginx_扩展_OpenRestry_多路复用.png

​ Master 进程会去管理每一个 worker 进程。当客户端发起一个请求的时候,它会由 worker 进程去处理,而不是由 Master 进程去处理。如果有多个 worker 进程的时候,一个请求过来,多个 worker 会有竞争。每一个进程会去争抢获得这样的一个许可。

​ 相当于多个 worker 进程 组成一个集群去实现,每一个 worker 进程后边是多路复用。

Nginx 配置几个进程
  • 工作进程建议设置成我们的 CPU 总核心数。

  • IO模型

    • epoll . select ....
  • Linux 理论上最大的连接数是 6535 。

最后支持的就是 CPU 数 * 进程数

# 用户组,这个用户组是当前 Linux 的用户组。和用户账号
#user  nobody;         
# Nginx 工作进程数, 建议设置成我们的 CPU 总核心数。 
worker_processes  1;    #error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;#pid        logs/nginx.pid;events {# io 模型use epoll ;  # 理论上  processes* connectionsworker_connections  1024;  
}

JavaGuide_Nginx_扩展_OpenRestry_Nginx进程模型.png


#user  nobody;
worker_processes  1;#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;#pid        logs/nginx.pid;events {worker_connections  1024;
}http {# 媒体类型,include       mime.types;# 默认的配置是二进制字节流default_type  application/octet-stream;# 日志格式,命名,把日志放到某一个文件里边,然后用 Kafka 收集起来,最后做分析log_format  main  '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';# 访问日志,每一个请求都会记录一个日志#access_log  logs/access.log  main;# 是否开启 0 拷贝模式,我们传输文件 0 拷贝效率高sendfile        on;# tcp 超时时间#tcp_nopush     on;#keepalive_timeout  0;keepalive_timeout  65;# 扫描这个目录下的配置文件include    extra/*.conf;# 是否打开 gzipgzip on;# 超过多长长度再进行压缩gzip_min_length 5K;# 压缩的等级越高,压缩后的文件越小,占用的 CPU 越高gzip_comp_level 3;# 对哪些文件去做压缩gzip_types application/javascript image/jpeg;# 设置缓冲区,按照我们我们指定大小的倍数去申请内存,# 按照我们原始文件的大小,以 32K 为单位的四倍去申请内存。gzip_buffers 4 32k;# 是否传输 “vary: Accept-Encoding” 的文件头标志# 根据客户端的头去判断我们是不是要去做压缩。gzip_vary on;
}

Nginx 的高可用方案

JavaGuide_Nginx_扩展_OpenRestry_Nginx一台服务器.png

​ 这模型的整个吞吐量是有限的。一旦大了以后,就会崩溃

​ Nginx 作为反向代理服务器,所有的流量都会经过 Nginx,所以 Nginx 本身的可靠性是我们首先要考虑的问题。

JavaGuide_Nginx_扩展_OpenRestry_Nginx多台服务器.png

​ 问题: HTTP Server 之间的同步,出现问题的切换,DNS 是没有办法解决的。

​ 通过 Nginx 去代理后端应用的服务器。Nginx 对外是一个 IP 地址,七层负载,做一个转发,通过应用层的 URI 做一些转发。 我可以做 IP_Hash 、轮询、权重 ... ... 各种方式去做转发。

​ Nginx 性能比 Tomcat 高得多得多。高性能的反向代理服务器。(DNS 是对域名做解析的。)通过 Nginx 做一个集群。

JavaGuide_Nginx_扩展_OpenRestry_Nginx多台服务器切换.png

​ 我们为了避免单点故障,就需要解决 Nginx 的单点问题!!!

JavaGuide_Nginx_扩展_OpenRestry_Nginx之间通过keepalived切换.png

​ 我一个请求过来,会根据它的一个 IP + 端口号 做一个转发,转发以后,后续请求,不是通过 F5 去做一个返回。而是直接返回,只是通过 F5 做一个解析而已。 F5 的性能 Nginx 更高。

JavaGuide_Nginx_扩展_OpenRestry_F5+Nginx做高可用.png

流量问题,不能纯靠技术问题去解决。

www.baidu.com 它是可以分发到不同的站点的,叫做 地域服务器

在北京访问北京的机房。其他的地方,访问其他地方的机房。

流量是无限的。我们需要通过 多机房,地域来解析。

把你的请求转发到离你最近的服务器上。

keepalived

keeppalived 可以生成 虚拟IP ( vip )。

Keepalived 是 Linux 下一个轻量级别的高可用解决方案,Keepalived 软件起初是专为 LVS 负载均衡软件 设计的,用来管理并监控 LVS 集群系统中各个服务节点的状态,后来又加入了可以实现高可用的 VRRP 功能。因此,Keepalived 除了能够管理 LVS 软件外,还可以作为其他服务(例如:Nginx、Haproxy、MySQL 等)的高可用解决方案软件。

​ Keepalived 软件主要是通过 VRRP 协议实现高可用功能的。VRRP 是 Virtual Router RedundancyProtocol (虚拟路由器冗余协议)的缩写,VRRP 出现的目的就是为了解决静态路由单点故障问题的,它能够保证当个别节点宕机时,整个网络可以不间断地运行;(简单来说,vrrp 就是把两台或多台路由器设备虚拟成一个设备, 实现主备高可用)。(它是虚拟 IP )

​ 所以,Keepalived 一方面具有配置管理 LVS 的功能,同时还具有对 LVS 下面节点进行健康检查的功能,另一方面也可实现系统网络服务的高可用功能。

​ LVS 是 Linux Virtual Server 的缩写,也就是 Linux 虚拟服务器,在 linux2.4 内核以后,已经完全内置了 LVS 的各个功能模块。

​ 它是工作在四层的负载均衡,类似于 Haproxy, 主要用于实现对服务器集群的负载均衡。

​ 关于四层负载,我们知道 osi 网络层次模型的 7 层模型(应用层、表示层、会话层、传输层、网络层、数据链路层、物理层);四层负载就是基于传输层,也就是 ip+端口 的负载;而七层负载就是需要基于 URL 等应用层的信息来做负载,同时还有二层负载(基于 MAC)、三层负载(IP);

常见的四层负载有:LVS、F5 ; 七层负载有 : Nginx、HAproxy ; 在软件层面,

Nginx / LVS / HAProxy 是使用得比较广泛的三种负载均衡软件

对于中小型的 Web 应用,可以使用 Nginx、大型网站或者重要的服务并且服务比较多的时候,可以考虑使用 LVS

轻量级的高可用解决方案

​ LVS 四层负载均衡软件(Linux virtual server) 监控 lvs 集群系统中的各个服务节点的状态VRRP 协议(虚拟路由冗余协议) linux2.4 以后,是内置在 linux 内核中的

lvs(四层) -> HAproxy 七层

lvs(四层) -> Nginx(七层)

JavaGuide_Nginx_扩展_OpenRestry_Nginx的轻量级的主备竞选高可用方案.png

实践

  1. 下载 keepalived 的安装包

  2. tar -zxvf keepalived-2.0.7.tar.gz

  3. /data/program/ 目录下创建一个 keepalived 的文件

  4. cd 到 keepalived-2.0.7 目录下,执行 ./configure -- prefix=/data/program/keepalived --sysconf=/etc

  5. 如果缺少依赖库,则 yum install gcc; yum install openssl-devel ; yum install libnl libnl-devel

  6. 编译安装 make && make install

  7. 进入安装后的路径 cd /data/program/keepalived , 创建软连接: ln -s sbin/keepalived /sbin

  8. cp /data/program/keepalived-2.0.7/keepalived/etc/init.d/keepalived /etc/init.d

    把运行的的服务添加进去

  9. chkconfig --add keepalived

  10. chkconfig keepalived on

  11. service keepalived start

安装步骤

版本,2.0.7 其他版本会有问题。

[root@Darian1 software]# tar -zxvf keepalived-2.0.11.tar.gz 
[root@Darian1 software]# mkdir keepalived
[root@Darian1 software]# cd keepalived-2.0.11/
[root@Darian1 keepalived-2.0.11]# ./configure --prefix=/software/keepalived --sysconf=/etcconfigure: error: 
!!! OpenSSL is not properly installed on your system. !!!
!!! Can not include OpenSSL headers files.            !!![root@Darian1 keepalived-2.0.11]# yum install openssl-devel
Is this ok [y/d/N]: y*** WARNING - this build will not support IPVS with IPv6. Please install libnl/libnl-3 dev libraries to support IPv6 with IPVS.[root@Darian1 keepalived-2.0.11]# yum install libnl libnl-devel
[root@Darian1 keepalived-2.0.11]# yum install gcc[root@Darian1 keepalived-2.0.11]# ./configure --prefix=/software/keepalived --sysconf=/etcconfigure: error: libnfnetlink headers missing[root@Darian3 keepalived-2.0.7]# yum install -y libnfnetlink-devel
[root@Darian1 keepalived-2.0.7]# make && make install
[root@Darian3 software]# mkdir keepalived
[root@Darian1 keepalived]# cd ../keepalived
[root@Darian1 keepalived]# ln -s sbin/keepalived /sbin
[root@Darian1 keepalived]# cp /software/keepalived-2.0.7/keepalived/etc/init.d/keepalived /etc/init.d[root@Darian1 keepalived]# chkconfig --add keepalived
[root@Darian1 keepalived]# chkconfig keepalived on
注意:正在将请求转发到“systemctl enable keepalived.service”。
Created symlink from /etc/systemd/system/multi-user.target.wants/keepalived.service to /usr/lib/systemd/system/keepalived.service.[root@Darian1 keepalived]# service keepalived start
[root@Darian1 keepalived]# service keepalived status

JavaGuide_Nginx_扩展_OpenRestry_keepalived状态.png

请立马配置 keepalived 的日志

keepalived 的配置

[root@Darian1 keepalived]# vim /etc/keepalived/keepalived.conf ! Configuration File for keepalived# 配置一些全局的东西
global_defs {# 邮箱的配置, 当 keepalived 发生错误的时候,发送到你的邮箱里边notification_email {acassen@firewall.locfailover@firewall.locsysadmin@firewall.loc}notification_email_from Alexandre.Cassen@firewall.locsmtp_server 192.168.200.1smtp_connect_timeout 30router_id LVS_DEVELvrrp_skip_check_adv_addrvrrp_strictvrrp_garp_interval 0vrrp_gna_interval 0
}# 虚拟的路由的冗余协议
vrrp_instance VI_1 {state MASTERinterface eth0virtual_router_id 51priority 100advert_int 1authentication {auth_type PASSauth_pass 1111}# 配置多个虚拟 IP 地址virtual_ipaddress {192.168.200.16192.168.200.17192.168.200.18}
}# 虚拟的服务,就是我们的 LVS  了,就是我们对外的一个虚拟的 IP 地址的配置的映射,默认 443 是对 HTTPS 的一个映射,
virtual_server 192.168.200.100 443 {delay_loop 6lb_algo rrlb_kind NATpersistence_timeout 50protocol TCP# 这里是真实服务的一个配置real_server 192.168.201.100 443 {weight 1SSL_GET {url {path /digest ff20ad2481f97b1754ef3e12ecd3a9cc}url {path /mrtg/digest 9b3a0c85a887a256d6939da88aabd8cd}connect_timeout 3retry 3delay_before_retry 3}}
}virtual_server 10.10.10.2 1358 {delay_loop 6lb_algo rrlb_kind NATpersistence_timeout 50protocol TCPsorry_server 192.168.200.200 1358real_server 192.168.200.2 1358 {weight 1HTTP_GET {url {path /testurl/test.jspdigest 640205b7b0fc66c1ea91c463fac6334d}url {path /testurl2/test.jspdigest 640205b7b0fc66c1ea91c463fac6334d}url {path /testurl3/test.jspdigest 640205b7b0fc66c1ea91c463fac6334d}connect_timeout 3retry 3delay_before_retry 3}}real_server 192.168.200.3 1358 {weight 1HTTP_GET {url {path /testurl/test.jspdigest 640205b7b0fc66c1ea91c463fac6334c}url {path /testurl2/test.jspdigest 640205b7b0fc66c1ea91c463fac6334c}connect_timeout 3retry 3delay_before_retry 3}}
}virtual_server 10.10.10.3 1358 {delay_loop 3lb_algo rrlb_kind NATpersistence_timeout 50protocol TCPreal_server 192.168.200.4 1358 {weight 1HTTP_GET {url {path /testurl/test.jspdigest 640205b7b0fc66c1ea91c463fac6334d}url {path /testurl2/test.jspdigest 640205b7b0fc66c1ea91c463fac6334d}url {path /testurl3/test.jspdigest 640205b7b0fc66c1ea91c463fac6334d}connect_timeout 3retry 3delay_before_retry 3}}real_server 192.168.200.5 1358 {weight 1HTTP_GET {url {path /testurl/test.jspdigest 640205b7b0fc66c1ea91c463fac6334d}url {path /testurl2/test.jspdigest 640205b7b0fc66c1ea91c463fac6334d}url {path /testurl3/test.jspdigest 640205b7b0fc66c1ea91c463fac6334d}connect_timeout 3retry 3delay_before_retry 3}}

​ 我们需要在 Keepalived 里边配置 Nginx 的一些东西。通过 Keepalived 实现了对 Nginx 的高可用,keepalived 是对 LVS 的一个检测。这个 虚拟 IP 会根据你的配置去进行映射。

JavaGuide_Nginx_扩展_OpenRestry_keepalived_LVS.png

master

[root@Darian1 keepalived]# vim /etc/keepalived/keepalived.conf ! Configuration File for keepalived global_defs {# 运行 keepalived 服务器的标识,在一个网络内应该是唯一的router_id LVS_DEVEL   
}# vrrp 实例定义部分
vrrp_instance VI_1 {# 设置 lvs 的状态,MASTER 和 BACKUP 两种,必须大写state MASTER  # 设置对外服务的接口,网卡的地址interface ens33   # 设置虚拟路由标示,这个标示是一个数字,同一个 vr rp 实例使用唯一标示virtual_router_id 51   # 定义优先级,数字越大优先级越高,在一个 vrrp——instance 下,master 的优先级必须大于 backuppriority 100 # 设定 master 与 backup 负载均衡器之间同步检查的时间间隔,单位是秒advert_int 1 authentication {  # 设置验证类型和密码auth_type PASS# 验证密码,同一个 vrrp_instance 下 MASTER 和 BACKUP 密码必须相同auth_pass 1111   }virtual_ipaddress { #设置虚拟 ip 地址,可以设置多个,每行一个192.168.40.100}
}# 设置虚拟服务器,需要指定虚拟 ip 和服务端口
virtual_server 192.168.40.100 80 {# 健康检查时间间隔delay_loop 6# 负载均衡调度算法lb_algo rr# 负载均衡转发规则lb_kind NAT   # 设置会话保持时间protocol TCP #指定转发协议类型,有 TCP 和 UDP 两种persistence_timeout 50# 配置服务器节点 1,需要指定 real server 的真实 IP 地址和端口real_server 192.168.40.128 80 { # 设置权重,数字越大权重越高weight 1  # realserver 的状态监测设置部分单位秒,用来去检测它的状态TCP_CHECK {  # 超时时间connect_timeout 3  # 重试间隔delay_before_retry 3# 监测端口connect_port 80  }}
}

backup

vim /etc/keepalived/keepalived.conf

! Configuration File for keepalivedglobal_defs { router_id LVS_DEVEL
}vrrp_instance VI_1 { state BACKUP interface ens33virtual_router_id 51priority 50advert_int 1 authentication {auth_type PASSauth_pass 1111}virtual_ipaddress { 192.168.40.100}
}virtual_server 192.168.40.100 80 {delay_loop 6 lb_algo rrlb_kind NATpersistence_timeout 50 protocol TCPreal_server 192.168.40.129 80 {weight 1 TCP_CHECK {connect_timeout 3delay_before_retry 3connect_port 80}}
}
[root@Darian1 software]# service keepalived restart
Restarting keepalived (via systemctl):                     [  确定  ]
[root@Darian1 software]# service keepalived status
● keepalived.service - LVS and VRRP High Availability MonitorLoaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; vendor preset: disabled)Active: active (running) since 日 2019-01-20 20:10:22 CST; 20s agoProcess: 83210 ExecStart=/software/keepalived/sbin/keepalived $KEEPALIVED_OPTIONS (code=exited, status=0/SUCCESS)Main PID: 83211 (keepalived)CGroup: /system.slice/keepalived.service├─83211 /software/keepalived/sbin/keepalived -D├─83212 /software/keepalived/sbin/keepalived -D└─83213 /software/keepalived/sbin/keepalived -D1月 20 20:10:25 Darian1 Keepalived_vrrp[83213]: Sending gratuitous ARP on ens33 for 192.168.11.100
1月 20 20:10:25 Darian1 Keepalived_vrrp[83213]: Sending gratuitous ARP on ens33 for 192.168.11.100
1月 20 20:10:25 Darian1 Keepalived_vrrp[83213]: Sending gratuitous ARP on ens33 for 192.168.11.100
1月 20 20:10:28 Darian1 Keepalived_healthcheckers[83212]: TCP connection to [192.168.40.128]:udp:80 success.
1月 20 20:10:30 Darian1 Keepalived_vrrp[83213]: Sending gratuitous ARP on ens33 for 192.168.11.100
1月 20 20:10:30 Darian1 Keepalived_vrrp[83213]: (VI_1) Sending/queueing gratuitous ARPs on ens33 for 192.168.11.100
1月 20 20:10:30 Darian1 Keepalived_vrrp[83213]: Sending gratuitous ARP on ens33 for 192.168.11.100
1月 20 20:10:30 Darian1 Keepalived_vrrp[83213]: Sending gratuitous ARP on ens33 for 192.168.11.100
1月 20 20:10:30 Darian1 Keepalived_vrrp[83213]: Sending gratuitous ARP on ens33 for 192.168.11.100
1月 20 20:10:30 Darian1 Keepalived_vrrp[83213]: Sending gratuitous ARP on ens33 for 192.168.11.100
[root@Darian1 software]# [root@Darian3 software]# service keepalived restart
Restarting keepalived (via systemctl):                     [  确定  ]
[root@Darian3 software]#  service keepalived status
● keepalived.service - LVS and VRRP High Availability MonitorLoaded: loaded (/usr/lib/systemd/system/keepalived.service; enabled; vendor preset: disabled)Active: active (running) since 日 2019-01-20 20:12:31 CST; 15s agoProcess: 47254 ExecStart=/software/keepalived/sbin/keepalived $KEEPALIVED_OPTIONS (code=exited, status=0/SUCCESS)Main PID: 47255 (keepalived)CGroup: /system.slice/keepalived.service├─47255 /software/keepalived/sbin/keepalived -D├─47256 /software/keepalived/sbin/keepalived -D└─47257 /software/keepalived/sbin/keepalived -D1月 20 20:12:31 Darian3 Keepalived_vrrp[47257]: (Line 3) Unexpected '{' - ignoring
1月 20 20:12:31 Darian3 Keepalived_vrrp[47257]: (Line 4) Unknown keyword 'router_id'
1月 20 20:12:31 Darian3 Keepalived_vrrp[47257]: (Line 5) Unknown keyword '}'
1月 20 20:12:31 Darian3 Keepalived_vrrp[47257]: Assigned address 192.168.40.129 for interface ens33
1月 20 20:12:31 Darian3 Keepalived_vrrp[47257]: Assigned address fe80::d3a1:60b3:dbb3:68c2 for interface ens33
1月 20 20:12:31 Darian3 Keepalived_vrrp[47257]: Registering gratuitous ARP shared channel
1月 20 20:12:31 Darian3 Keepalived_vrrp[47257]: (VI_1) removing VIPs.
1月 20 20:12:31 Darian3 Keepalived_vrrp[47257]: (VI_1) Entering BACKUP STATE (init)
1月 20 20:12:31 Darian3 Keepalived_vrrp[47257]: VRRP sockpool: [ifindex(2), proto(112), unicast(0), fd(8,9)]
1月 20 20:12:37 Darian3 Keepalived_healthcheckers[47256]: TCP connection to [192.168.40.129]:tcp:80 success.
[root@Darian3 software]# 

Nginx 是每秒都可以抗十万的。

踩坑:

配置同一个网段,不是同一个网段,访问会有问题!!!

线程在进程之内是共享的。共享资源相互影响。

线程产生的成本比进程低。

keepalived 日志文件配置

  1. 首先看一下/etc/sysconfig/keepalived 文件

    vim /etc/sysconfig/keepalivedKEEPALIVED_OPTIONS="-D -d -S 0"
    

    -D 就是输出日志的选项

    这里的“-S 0”表示 local0.* 具体地还需要看一下/etc/syslog.conf 文件

  2. /etc/rsyslog.conf 里添加: local0.* /var/log/keepalived.log

    [root@Darian3 keepalived]# vim /etc/rsyslog.conf
    
  3. 重新启动 keepalived 和 rsyslog 服务:

    service rsyslog restart

    service keepalived restart

    [root@Darian1 keepalived]# vim /etc/sysconfig/keepalived 
    [root@Darian1 keepalived]# vim /etc/rsyslog.conf# keepalived 的log
    local0.* /var/log/keepalived.log[root@Darian1 keepalived]# service rsyslog restart 
    [root@Darian1 keepalived]# service keepalived restart
    
  4. 查看日志:

    tail -f /var/log/keepalived.log
    

通过脚本实现动态切换

Keepalived 需要配置主备来实现动态的切换。Nginx 挂掉的时候,keepalived 也要停止掉。keepalived 的存在的意义是用来监听 Nginx 的请求。这个监听的过程实际上是通过 LVS 来转发的。需要脚本来触发。

  1. 在 master 和 slave 节点的 /data/program/nginx/sbin/nginx-ha-check.sh 目录下增加一个脚本

    –no-headers 不打印头文件

    Wc –l

    统计行数

    [root@Darian1 keepalived]# cd ../nginx/sbin/
    [root@Darian1 sbin]# vim nginx_status_check.sh
    
    #!bin/sh   #! /bin/sh 是指此脚本使用/bin/sh 来执行A=`ps -C nginx --no-header |wc -l`if [ $A -eq 0 ]thenecho 'nginx server is died'service keepalived stop
    fi
    
  2. 修改 keepalived.conf 文件,增加如下配置

    track_script: #执行监控的服务。chknginxservice #

    引用 VRRP 脚本,即在 vrrp_script 部分指定的名字。定期运行它们来改变优先级, 并最终引发主备切换。当我们 Nginx 挂掉以后,会触发这个脚本。

操作步骤:

  1. 编写 keepalived.conf 脚本
[root@Darian1 sbin]# vim /etc/keepalived/keepalived.conf global_defs {# 运行 keepalived 服务器的标识,在一个网络内应该是唯一的router_id LVS_DEVELenable_script_security
}vrrp_script nginx_status_process {# 实现当 Nginx 挂掉以后,keepalived 也挂掉script "/software/nginx/sbin/nginx_status_check.sh"# 用户的隔离,用户的运行权限,防止其他用户对这个脚本的执行user root# 检查频次interval 3
}# vrrp 实例定义部分
vrrp_instance VI_1 {# 设置 lvs 的状态,MASTER 和 BACKUP 两种,必须大写state MASTER# 设置对外服务的接口,网卡的地址interface ens33# 设置虚拟路由标示,这个标示是一个数字,同一个 vr rp 实例使用唯一标示virtual_router_id 51# 定义优先级,数字越大优先级越高,在一个 vrrp——instance 下,master 的优先级必须大于 backuppriority 100# 设定 master 与 backup 负载均衡器之间同步检查的时间间隔,单位是秒advert_int 1authentication {# 设置验证类型和密码auth_type PASS# 验证密码,同一个 vrrp_instance 下 MASTER 和 BACKUP 密码必须相同auth_pass 1111}virtual_ipaddress {#设置虚拟 ip 地址,可以设置多个,每行一个192.168.40.100}track_script{# 对应的 上边的 nginx_status_processnginx_status_process}
}# 设置虚拟服务器,需要指定虚拟 ip 和服务端口
virtual_server 192.168.40.100 80 {# 健康检查时间间隔delay_loop 6# 负载均衡调度算法lb_algo rr# 负载均衡转发规则lb_kind NAT# 设置会话保持时间protocol TCP #指定转发协议类型,有 TCP 和 UDP 两种persistence_timeout 50# 配置服务器节点 1,需要指定 real server 的真实 IP 地址和端口real_server 192.168.40.128 80 {# 设置权重,数字越大权重越高weight 1# realserver 的状态监测设置部分单位秒,用来检测它的状态TCP_CHECK {# 超时时间connect_timeout 3# 重试间隔delay_before_retry 3# 监测端口connect_port 80}}
}
  1. 编写 脚本。Nginx/sbin
[root@Darian1 sbin]# vim nginx-ha-check.sh#!bin/sh   #! /bin/sh 是指此脚本使用/bin/sh 来执行A=`ps -C nginx --no-header |wc -l`if [ $A -eq 0 ]thenecho 'nginx server is died'service keepalived stop
fi
  1. 测试脚本是否可用
[root@Darian1 sbin]# sh nginx_status_check.sh 
nginx server is died
Stopping keepalived (via systemctl):                       [  确定  ]
  1. 添加生效
[root@Darian1 sbin]# chmod +x nginx_status_check.sh
[root@Darian1 sbin]# service keepalived restart[root@Darian1 sbin]# ./nginx -s stop

JavaGuide_Nginx_扩展_OpenRestry_keepalived_配置的几点.png

美团的 Camel

OpenResty

Nginx + lua

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

安装

  1. 下载安装包

    https://openresty.org/cn/download.html

    • weget https://openresty.org/download/openresty-1.13.6.2.tar.gz
      
  2. 安装软件包

    [root@Darian1 software]# tar -zxvf openresty-1.13.6.2.tar.gz cd openrestry-1.13.6.2
    [root@Darian1 software]# mkdir openresty
    [root@Darian1 software]# cd openresty-1.13.6.2/
    [root@Darian1 software]# mkdir openresty
    [root@Darian1 openresty-1.13.6.2]# ./configure  --prefix=/software/openresty
    [root@Darian1 openresty-1.13.6.2]# make && make install
    
    ./configure [默认会安装在/usr/local/openresty 目录] --prefix= 指定路径
    make && make install
    
  3. 可能存在的错误,第三方依赖库没有安装的情况下会报错

    yum install readline-devel / pcre-devel /openssl-devel
    

    安装过程和 Nginx 是一样的,因为他是基于 Nginx 做的扩展

HelloWorld

开始第一个程序,HelloWorld

openresty/nginx/conf/nginx.conf 就是 nginx 的配置的东西。

[root@Darian3 software]# cd openresty/nginx/
[root@Darian3 nginx]# vim conf/nginx.conflocation / {default_type         text/html;content_by_lua_block {ngx.say("helloworld");}
}[root@Darian3 nginx]# ./sbin/nginx 

在 sbin 目录下执行.nginx 命令就可以运行,看到 helloworld

JavaGuide_Nginx_扩展_OpenRestry_nginx_hello_world.png

建立工作空间

创建目录

​ 或者为了不影响默认的安装目录,我们可以创建一个独立的空间来练习,先在安装目录下创建 demo 目录,安装目录为 /data/program/openresty/demo

mkdir demo

​ 然后在 demo 目录下创建两个子目录,一个是 logs 、一个是 conf

创建配置文件

执行:./nginx -p /data/program/openresty/demo 【-p 主要是指明 nginx 启动时的配置目录】

[root@Darian1 openresty]# mkdir demo
[root@Darian1 openresty]# cd demo/
[root@Darian1 demo]# mkdir conf
[root@Darian1 demo]# mkdir logs
[root@Darian1 demo]# cd conf/
[root@Darian1 conf]# vim nginx.confworker_processes 1; 
error_log logs/error.log;
events {worker_connections 1024;
}
http {server {listen 8888;location / {default_type text/html;content_by_lua_block {ngx.say("Hello world")}}}
}[root@Darian1 conf]# cd ../../nginx/sbin/
[root@Darian1 sbin]# ./nginx -p /software/openresty/demo/

JavaGuide_Nginx_扩展_OpenRestry_sub.png

JavaGuide_Nginx_扩展_OpenRestry_nginx_test_index.png

总结

​ 我们刚刚通过一个 helloworld 的简单案例来演示了 nginx + lua 的功能,其中用到了 ngx.say 这个表达式,通过在 content_by_lua_block 这个片段中进行访问;这个表达式属于 ngx_lua 模块提供的 api, 用于向客户端输出一个内容。

​ 我们配置了多个 模块,就会有固定的顺序。

写一个完全做认证的模块

[root@Darian1 openresty]# cd demo/conf/
[root@Darian1 conf]# mkdir lua
[root@Darian1 conf]# cd lua
[root@Darian1 lua]# vim add.lualocal args = ngx.req.get_uri_args();
ngx.say(args.a + args.b);[root@Darian1 lua]# vim params.lualocal _M = {}function _M.is_number(...)local arg = {...}local num;for i,v in ipairs(arg) donum=tonumber(v);if nil == num thenreturn false;endendreturn true;
endreturn _M;[root@Darian1 lua]#  vim check.lualocal param=require("params");local args=ngx.req.get_uri_args();if not args.a or not args.b or not param.is_number(args.a, args.b) thenngx.exit(ngx.HTTP_BAD_REQUEST);return;
end[root@Darian1 lua]# vim ../nginx.conf worker_processes 1;
error_log logs/error.log;
events {worker_connections 1024;
}
http {lua_package_path '$prefix/lua/?.lua';lua_code_cache off;server {listen 80;location ~ ^/api/([-_a-zA-Z0_9]+) {access_by_lua_file lua/check.lua;content_by_lua_file lua/$1.lua;}}
}[root@Darian1 lua]# cd ../../../nginx/sbin/
[root@Darian1 sbin]# history   
[root@Darian1 sbin]# ./nginx -p /software/openresty/demo/nginx: [alert] lua_code_cache is off; this will hurt performance in /software/openresty/demo/conf/nginx.conf:11

​ 在 lua 里边有模块的概念,有包的概念。我可以把公共的代码放到一个文件里边。以 API 的方式去提供。可以把他作为一个方法。

​ 正常地来说,我们会有一个 lua 文件夹,按照我们的需求去写。

​ 我可以利用 lua 写一些复杂的逻辑。可以写业务代码的。

注意:

lua 目录在 demo 目录下 openresty/demo/lua/sub.lua

  • JS 、 shell 都叫做脚本语言。
  • history 查看命令的历史。

JavaGuide_Nginx_扩展_OpenRestry_nginx_lua脚本.png

库文件使用

​ 通过上面的案例,我们基本上对 OpenResty 有了一个更深的认识,其中我们用到了自定义的 lua 模块。实际上 openresty 提供了很丰富的模块。让我们在实现某些场景的时候更加方便。可以在 /openresty/lualib 目录下看到;比如在 resty 目录下可以看到 redis.luamysql.lua 这样的操作 redis 和操作数据库的模块。

使用 redis 模块连接 redis

worker_processes 1;
error_log           
logs/error.log; events {worker_connections 1024;
}http {lua_package_path '$prefix/lualib/?.lua;;'; # 添加”;;”表示默认路径下的lualiblua_package_cpath '$prefix/lualib/?.so;;';server {location /demo { content_by_lua_block {local redisModule=require "resty.redis";local redis=redisModule:new();   # lua 的对象实例 redis:set_timeout(1000); ngx.say("===begin connect redis server");local ok,err = redis:connect("127.0.0.1",6379);   #连接 redisif not ok thenngx.say("==connection redis failed,error message:",err);endngx.say("==begin set key and value");ok,err=redis:set("hello","world");if not ok thenngx.say("set value failed");return;endngx.say("===set value result:",ok);redis:close();}}}
}

演示效果

到 nginx 路径下执行 ./nginx -p /data/program/openresty/redisdemo 在浏览器中输入:http://192.168.11.160/demo 即可看到输出内容并且连接到 redis 服务器上以后,可以看到 redis 上的结果

redis 的所有命令操作,在 lua 中都有提供相应的操作。比如 redis:get(“key”)、

redis:set()等

网关

​ 通过扩展以后,在实际过程中应该怎么去应用呢?一般的使用场景: 网关、​web 防火墙、缓存服务器(对响应内容进行缓存,减少到达后端的请求,来提升性能),接下来重点讲讲网关的概念以及如何通过 Openresty 实现网关开发。

网关的概念

​ 从一个房间到另一个房间,必须要经过一扇门,同样,从一个网络向另一个网络发送信息,必须经过一道“关口”,这道关口就是网关。顾名思义,网关(Gateway) 就是一个网络连接到另一个网络的“关口”。

​ 那什么是 API 网关呢?

​ 在微服务流行起来之前,api 网关就一直存在,最主要的应用场景就是开放平台, 也就是 open api; 这种场景大家接触的一定比较多,比如阿里的开放平台;当微服务流行起来以后,api 网关就成了上层应用集成的标配组件。

​ 比如说,支付包地址会有一个网关,做一个统一的路由转发。

为什么需要网关?

  • Kong 、Orange 、

对微服务组件地址进行统一抽象

​ API 网关意味着你要把 API 网关放到你的微服务的最前端,并且要让 API 网关变成由应用所发起的每个请求的入口。这样就可以简化实现客户端和微服务应用程序之间的沟通方式。

Backends for frontends

JavaGuide_Nginx_扩展_OpenRestry_多个GateWay网关服务.png

​ 当服务越来越多以后,我们需要考虑一个问题,就是对某些服务进行安全校验以及用户身份校验。甚至包括对流量进行控制。 我们会对需要做流控、需要做身份认证的服务单独提供认证功能,但是服务越来越多以后,会发现很多组件的校验是重复的。这些东西很明显不是每个微服务组件需要去关心的事情。微服务组件只需要负责接收请求以及返回响应即可。可以把身份认证、流控都放在 API 网关层来进行控制。

​ 针对于 app 或者 web 等分别做不同的网关。不同的客户端是不同的验证、不同的方式。

  • 按照服务组件进行统一的抽象
  • 针对不同的客户端来实现不同的 API 网关

网关的作用

  • 鉴权
  • 限流
  • 灰度发布
  • 分流
  • 日志记录

OpenResty 实现 API 网关限流及登录授权

OpenResty 为什么能做网关?

​ 前面我们了解到了网关的作用,通过网关,可以对 api 访问的前置操作进行统一的管理,比如鉴权、限流、负载均衡、日志收集、请求分片等。所以 API 网关的核心是所有客户端对接后端服务之前,都需要统一接入网关,通过网关层将所有非业务功能进行处理。

​ OpenResty 为什么能实现网关呢? OpenResty 有一个非常重要的因素是,对于每一个请求,Openresty 会把请求分为不同阶段,从而可以让第三方模块通过挂载行为来实现不同阶段的自定义行为。而这样的机制能够让我们非常方便地设计 api 网关。

JavaGuide_Nginx_扩展_OpenRestry_Lua插件图解.png

​ Nginx 本身在处理一个用户请求时,会按照不同的阶段进行处理,总共会分为 11 个阶段。而 openresty 的执行指令,就是在这 11 个步骤中挂载 lua 执行脚本实现扩展,我们分别看看每个指令的作用。

initbylua : 当 Nginx master 进程加载 nginx 配置文件时会运行这段 lua 脚本,一般用来注册全局变量或者预加载 lua 模块

initwokerby_lua: 每个 Nginx worker 进程启动时会执行的 lua 脚本,可以用来做健康检查

setbylua:设置一个变量

rewritebylua:在 rewrite 阶段执行,为每个请求执行指定的 lua 脚本

accessbylua:为每个请求在访问阶段调用 lua 脚本

contentbylua:前面演示过,通过 lua 脚本生成 content 输出给 http 响应

balancerbylua:实现动态负载均衡,如果不是走 contentbylua,则走 proxy_pass,再通过 upstream 进行转发

headerfilterby_lua: 通过 lua 来设置 headers 或者 cookie

bodyfilterby_lua:对响应数据进行过滤

logbylua : 在 log 阶段执行的脚本,一般用来做数据统计,将请求数据传输到后端进行分析

zuul 的 filter 也是类似的实现。

灰度发布

​ 在单一架构中,随着代码量和业务量不断扩大,版本迭代会逐步变成一个很困难的事情,哪怕是一点小的修改,都必须要对整个应用重新部署。 但是在微服务中, 各个模块是一个独立运行的组件,版本迭代会很方便,影响面很小。

​ 同时,微服务化的组件节点,对于我们去实现灰度发布(金丝雀发布:将一部分流量引导到新的版本)来说,也会变得很简单;

​ 还可以有白名单,比如说 QQ 的升级改造计划,针对你升级。

​ 所以通过 API 网关,可以对指定调用的微服务版本,通过版本来隔离。如下图所示。

JavaGuide_Nginx_扩展_OpenRestry_nginx做灰度切换.png

灰度发布的实现

  1. 文件目录, /data/program/openresty/gray [conf、logs、lua]

  2. 编写 Nginx 的配置文件 nginx.conf

[root@Darian1 openresty]# mkdir gray[root@Darian1 openresty]# cd gray[root@Darian1 gray]# mkdir lua[root@Darian1 gray]# mkdir logs[root@Darian1 gray]# mkdir conf[root@Darian1 gray]# cd conf/worker_processes 1;error_log      logs/error.log;events{worker_connections 1024;}http{lua_package_path "$prefix/lualib/?.lua;;";lua_package_cpath "$prefix/lualib/?.so;;";upstream prod {server 192.168.40.128:8080;}upstream pre {server 192.168.40.129:8080;}server {listen  80;server_name localhost;location /api {content_by_lua_file lua/gray.lua;}location @prod {proxy_pass http://prod;}location @pre {proxy_pass http://pre;}}}[root@Darian1 conf]# cd ../lua/[root@Darian1 lua]# vim gray.lua
  1. 编写 gray.lua 文件
local redis=require "resty.redis";local red=redis:new();red:set_timeout(1000);local ok,err=red:connect("192.168.40.128",6379);if not ok thenngx.say("failed to connect redis",err);return;endlocal headers=ngx.req.get_headers()local local_ip=headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addrlocal ip_lists=red:get("gray");if ip_lists ~= nil and  string.find(ip_lists,local_ip) == nil thenngx.exec("@prod");elsengx.exec("@pre");endlocal ok,err=red:close();
  1. 执行命令启动 nginx: [./nginx -p /data/program/openresty/gray]

  2. 启动 redis,并设置 set gray 192.168.11.160

  3. 通过浏览器运行: http://192.168.11.160/api 查看运行结果

    修改 redis gray 的值,将客户端的 ip 存储到 redis 中 。set gray 1 再次运行结果, 即可看到访问结果已经发生了变化。

[root@Darian1 sbin]# ./nginx  -p ../../gray[root@Darian1 bin]# ./redis-server ../../redis-3.2.8/redis.conf 
[root@Darian1 bin]# ./redis-cli 
127.0.0.1:6379> set gray 192.168.23.66
OK
127.0.0.1:6379> set gray 192.168.40.1
OK

JavaGuide_Nginx_扩展_OpenRestry_tomcat请求界面.png

高可用踩坑:

记得网卡配置正确。

JavaGuide_Nginx_扩展_OpenRestry_高可用配置正确.png

OpenResty/Demo/conf/nginx.conf 的配置文件

worker_processes 1;
error_log logs/error.log;
events {worker_connections 1024;
}
http {server {listen 8888;location / {default_type text/html;content_by_lua_block {local args = ngx.req.get_uri_args();ngx.say("Hello world");}}location /sub {content_by_lua_block{local args = ngx.req.get_uri_args();ngx.say(args.a-args.b);}}}
}
worker_processes 1; 
error_log      logs/error.log;events{  worker_connections 1024;
}
http{# 两个分号,表示默认路径下lua_package_path "$prefix/lualib/?.lua;;"; lua_package_cpath "$prefix/lualib/?.so;;"; upstream prod {server 192.168.11.156:8080;}upstream pre {server 192.168.11.156:8081;}server {listen  80;server_name localhost; location /api {content_by_lua_file lua/gray.lua;}location @prod { proxy_pass http://prod;}location @pre { proxy_pass http://pre;}}server {listen 8080;location / {content_by_lua_block { ngx.say("I'm prod env");}}}server {listen 8081; location / {content_by_lua_block { ngx.say("I'm pre env");}}}
}

百万架构师系列文章阅读体验感更佳

原文链接:https://javaguide.net

公众号:不止极客

来源于: https://javaguide.net

微信公众号:不止极客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/889834.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不用 ORM 会发生什么?

要弄清一个事物带来了什么,有种办法就是在场景中去掉它,当我们不使用 ORM 开发项目,需要补齐哪些能力,以表格形式列举要补齐的功能点和意义。我们仅能通过 SQL语句和数据库交互,在采用 ORM 项目开发的项目有时出于性能、数据库表达限制的原因而直接编写 SQL 语句,在脱离 …

事务中无法切换数据源?DataSourceSwitchInvoker:轻松实现多数据源切换执行工具类

背景: 在有标注为@Transactional的类或公共方法中(传播特性,如:NOT_SUPPORTED、SUPPORTS、REQUIRED【默认值】、REQUIRES_NEW)执行数据源切换可能不成功(比如:主从数据源切换,多数据源切换等,均会发现切换不成功,或“偶尔又切换成功”),导致本应该需要查主库却查了…

Java基础05(常用类)

匿名内部类 Object类 包装类 String类 BigDecimal类 Date类(特定时间)Calendar类(日历)SimpleDateFormat类(格式化时间)System类(系统类)Java基础05(常用类) 内部类 成员内部类在类的内部定义,与实例变量、实例方法同级别的类外部类的一个实例部分,创建内部类对象…

mysql表字段varchar(10)和varchar(255)测试文件占用

前言全局说明一、说明 1.1 环境: Windows 11 家庭版 23H2 22631.3737 MySQL: 服务器版本: 5.6.34 - MySQL Community Server (GPL) Navicat for MySQL: 10.1.71.2 测试样本 两个字段: id字段是 1~10位不等长度的随机数; num字段是 11~25位不等的随机数字;为了更好模拟实际使…

FreeRTOS高效应用实战

FreeRTOS高效应用实战 基于STM32CubeIDE生成对芯片移植好的FreeRTOS工程,使用HAL库编写FreeRTOS应用程序,实现FreeRTOS高效应用实战引入函数句柄的概念函数句柄(Function Handle)是编程中用于间接引用和操作函数的一种机制,其本质是将函数作为数据来传递和存储。以下是关于…

解决ZYNQ-7020开发板使用vitis编译uboot报错和无法正常调试的问题

整个学习过程是参考正点原子启明星开发板的2020.2版本嵌入式Linux开发指南,在学习uboot移植的时候遇到了问题。 新建工程和配置环境啥的和教程里都一样,就不罗嗦了,这里重点讲和教程不一样的地方(或者说教程里有问题的地方)。 新建工程后编译时遇到的报错 在按照教程新建ub…

markDown学习日记

标题 标题是通过#和一个空格来创建,标题的等级是通过#的个数来鉴别。 字体样式 进步进步进步 加粗效果由2个*前后包裹来实现 进步进步进步 斜体需要一个*来实现 (两者都实现需要三个*) 进步进步进步 删除需要两边都用波浪号 ~ 实现 引用明德新民 止于至善用>实…

LVM(Logical Volume Manager)

一. LVM概述 1. 什么是 LVM LVM(Logical Volume Manager,逻辑卷管理器)是 Linux 系统下的一种 存储管理 机制,能够灵活地管理磁盘分区。它提供了一种比传统分区管理(如fdisk、parted)更高级的存储管理方式,允许动态调整存储空间,方便扩展和缩减分区,而不会影响已有数据…

碎片

平板电脑和手机最大的区别就在于屏幕的大小, 一般手机屏幕的大小会在 3 英寸到 6 英寸之间, 而一般平板电脑屏幕的大小会在 7 英寸到 10 英寸之间。屏幕大小差距过大有可能会让同样的界面在视觉效果上有较大的差异, 比如一些界面在手机上看起来非常美观, 但在平板电脑上看起来就…

作业1 随笔

这个作业属于哪个课程 班级的链接这个作业要求在哪里 作业要求的链接这个作业的目标 学习使用markdown、博客园与GitHub,大致了解本学科1、自我介绍兴趣爱好:玩游戏,打羽毛球,喜欢拍照,喜欢理解学习软件工程专业的新东西 学习方面:主要学习Java,2、5个想弄懂的问题从事后…

NetPad:一个.NET开源、跨平台的C#编辑器

前言 今天大姚给大家分享一个基于.NET开源、跨平台的C#编辑器和游乐场:NetPad。 项目介绍 NetPad是一个基于.NET开源(MIT License)、跨平台的C#编辑器和游乐场,它允许用户立即运行C#代码,无需创建和管理项目。项目技术栈.NET:作为底层框架,提供强大的开发能力和跨平台支…