Nginx进阶篇【五】
- 八、Nginx实现服务器端集群搭建
- 8.1.Nginx与Tomcat部署
- 8.1.1.环境准备(Tomcat)
- 8.1.1.1.浏览器访问:
- 8.1.1.2.获取动态资源的链接地址:
- 8.1.1.3.在Centos上准备一个Tomcat作为后台web服务器
- 8.1.1.4.准备一个web项目,将其打包为war
- 8.1.1.5.启动tomcat进行访问测试。
- 8.1.2.环境准备(Nginx)
- 8.1.2.1.使用Nginx的反向代理,将请求转给Tomcat进行处理。
- 8.1.2.2.启动访问测试
- 8.2.Nginx+Tomcat来实现动静分离
- 8.2.1.动静分离概念
- 8.2.2.需求分析
- 8.2.3.动静分离实现步骤
- 8.2.3.1.将demo.war项目中的静态资源都删除掉,重新打包生成一个war包,在资料中有提供。
- 8.2.3.2.将war包部署到tomcat中,把之前部署的内容删除掉
- 8.2.3.3.在Nginx所在服务器创建如下目录,并将对应的静态资源放入指定的位置
- 8.2.3.4.配置Nginx的静态资源与动态资源的访问
- 8.2.3.5.启动测试,访问http://192.168.229.136/index.html
- 8.3.Nginx实现Tomcat集群搭建
- 8.3.1.分析下原理与环境准备
- 8.3.2.准备3台tomcat,使用端口进行区分[实际环境应该是三台服务器],修改server.ml,将端口修改分别修改为8080,8180,8280
- 8.3.3.启动tomcat并访问测试,
- 8.3.4.在Nginx对应的配置文件中添加如下内容:
- 8.4.Nginx高可用解决方案
- 8.4.1.Keepalived
- 8.4.2.VRRP介绍
- 8.4.3.使用Keepalived的解决方案
- 8.4.4.环境搭建
- 8.4.4.1.环境准备
- 8.4.4.2.keepalived的安装
- 8.4.4.2.1.步骤1:从官方网站下载keepalived
- 8.4.4.2.2.步骤2:将压缩文件进行解压缩
- 8.4.4.2.3.步骤3:对keepalived进行配置,编译和安装
- 8.4.4.2.4.报错解决
- 8.4.4.3.keepalived的系统配置文件和系统配置脚本
- 8.4.5.Keepalived配置文件介绍
- 8.4.5.1.打开keepalived.conf配置文件
- 8.4.5.2.global全局配置
- 8.4.5.3.vrrp相关配置
- 8.4.5.4.服务器1配置内容如下:
- 8.4.5.5.服务器2配置内容如下:
- 8.4.5.6.访问测试
- 8.4.5.6.1. 启动keepalived之前,咱们先使用命令 `ip a`,查看192.168.229.136和192.168.229.137这两台服务器的IP情况。
- 8.4.5.6.2. 分别启动两台服务器的keepalived
- 8.4.5.6.3. 当把192.168.229.136服务器上的keepalived关闭后,再次查看ip
- 8.4.5.6.4.上面测试和Nginx有什么关系?
- 8.4.5.6.5.解决问题:如何在服务器上让keepalived 的虚拟IP可以网络访问
- 8.4.5.6.6.如果把192.168.229.136服务器的keepalived关闭掉,再次访问相同的地址
- 8.4.6.Keepalived之自动切换脚本实现【keepalived之vrrp_script】
- 8.4.6.1. 在keepalived配置文件中添加对应的配置像
- 8.4.6.2. 编写脚本
- 8.4.6.3. 为脚本文件设置权限
- 8.4.6.4. 将脚本添加到
- 8.4.6.5. 如果效果没有出来,可以使用 `tail -f /var/log/messages`查看日志信息,找对应的错误信息。
- 8.4.6.6. 测试
- 8.4.6.7. 模拟136Nginx服务器宕机效果测试
- 8.4.6.8.问题思考:
- 九、Nginx制作在线下载站点【模块ngx_http_autoindex_module】
- 9.1.下载站点
- 9.2.如何制作一个下载站点:
- 9.2.1.autoindex:启用或禁用目录列表输出
- 9.2.2.autoindex_exact_size:对应HTLM格式,指定是否在目录列表展示文件的详细大小
- 9.2.3.autoindex_format:设置目录列表的格式
- 9.2.4.autoindex_localtime:对应HTML格式,是否在目录列表上显示时间。
- 9.3.资源准备
- 9.4.Nginx配置方式如下:
- 9.5.下载站点测试成功
- 十、Nginx的用户认证模块【ngx_http_auth_basic_module模块】
- 10.1.auth_basic:使用“ HTTP基本认证”协议启用用户名和密码的验证
- 10.2.auth_basic_user_file:指定用户名和密码所在文件
- 10.3.实现步骤:nginx.conf添加如下内容
- 10.4.使用`htpasswd`工具生成用户名和密码
- 10.5.测试
- 十一、Nginx的扩展模块【ngx_lua模块环境准备】
- 11.1.方式一:lua-nginx-module
- 11.1.1. LuaJIT是采用C语言编写的Lua代表的解释器
- 11.1.2.下载lua-nginx-module
- 11.1.2.1.在centos上使用wget来下载:
- 11.1.2.2.将下载的资源进行解压:
- 11.1.2.3.更改目录名:
- 11.1.2.4.导入环境变量
- 11.1.2.5.进入Nginx的目录执行如下命令:
- 11.1.2.6.安装
- 11.1.2.7.如果启动Nginx出现如下错误:
- 11.1.2.8.如果启动Nginx出现以下错误信息
- 11.1.2.9.干脆直接用openresty
- 11.1.2.10.make报错修改ngx_http_lua_headers_in.c文件
- 11.1.2.11.测试
- 11.2.方式二:OpenRestry
- 11.2.1.概述
- 11.2.2.下载OpenResty:
- 11.2.3.使用wget下载:
- 11.2.4.解压缩:
- 11.2.5.进入OpenResty目录:
- 11.2.6.编译和安装
- 11.2.7.进入OpenResty的目录,找到nginx:
- 11.2.8.在conf目录下的nginx.conf添加如下内容
- 11.2.9.在sbin目录下启动nginx
- 11.2.10.通过浏览器访问测试
- 11.2.11.设置环境变量
- 11.2.12.openresty安装成系统服务
- 11.2.13.openresty常用命令
- 十二、ngx_lua的使用
- 指令执行的顺序
- 解释下*的作用
- init_by_lua*
- init_worker_by_lua*
- set_by_lua*
- rewrite_by_lua*
- access_by_lua*
- content_by_lua*
- header_filter_by_lua*
- body_filter_by_lua*
- log_by_lua*
- balancer_by_lua*
- ssl_certificate_by_*
- 需求:
- 实现代码
- 测试
- 十三、ngx_lua操作Redis
- 13.1.介绍
- 13.2.lua-resty-redis环境准备
- 13.2.1.准备一个Redis环境【安装Redis】
- 13.2.2.准备对应的API
- 13.2.3.步骤
- 13.2.4.效果实现
- 13.2.5.运行测试效果
- 十四、ngx_lua操作Mysql
- 14.1.介绍
- 14.2.lua-resty-mysql
- 14.3.使用lua-resty-mysql实现数据库的查询
- 14.3.1.准备一个Mysql环境【安装Mysql】
- 14.3.2.创建一个数据库表及表中的数据
- 14.3.3.数据库连接四要素:
- 14.4.API学习
- 14.5.步骤
- 14.6.效果实现--查询单个数据
- 14.7.测试
- 14.8.查询多行数据并显示
- 14.8.问题
- 14.9.使用lua-cjson处理查询结果--返回JSON字符串
- 14.9.1.引入cjson
- 14.9.2.调用cjson的encode方法进行类型转换
- 14.9.3.使用
- 14.9.4.测试
- 14.9.5.cjson定义为全局变量--防止重复代码
- 14.10.lua-resty-mysql实现数据库的增删改
- 14.10.1.优化send_query和read_result
- 14.10.2.优化代码
- 14.10.3.新增
- 14.10.4.修改
- 14.10.5.删除
- 14.11.lua_mysql_redis_nginx.conf
- 十五、综合小案例---使用ngx_lua模块完成Redis缓存预热。
- 15.1.准备一张表(users)【上面】
- 15.2.浏览器输入如下地址
- 15.3.从表中查询出符合条件的记录,此时获取的结果为table类型
- 15.4.使用cjson将table数据转换成json字符串
- 15.5.将查询的结果数据存入Redis中
- 15.6.代码
- 15.7.测试结果
- 15.8.修改某个值测试redis
- 15.9.完整代码
- endl
八、Nginx实现服务器端集群搭建
8.1.Nginx与Tomcat部署
Nginx在高并发场景和处理静态资源是非常高性能的,但是在实际项目中除了静态资源还有就是后台业务代码模块,一般后台业务都会被部署在Tomcat,weblogic或者是websphere等web服务器上。
那么如何使用Nginx接收用户的请求并把请求转发到后台web服务器?
步骤分析:
1.准备Tomcat环境,并在Tomcat上部署一个web项目
2.准备Nginx环境,使用Nginx接收请求,并把请求分发到Tomat上
8.1.1.环境准备(Tomcat)
8.1.1.1.浏览器访问:
http://192.168.229.131:8080/demo/index.html
8.1.1.2.获取动态资源的链接地址:
http://192.168.229.131:8080/demo/getAddress
8.1.1.3.在Centos上准备一个Tomcat作为后台web服务器
- Tomcat官网地址:https://tomcat.apache.org/
- 下载tomcat,apache-tomcat-8.5.98.tar.gz
wget https://dlcdn.apache.org/tomcat/tomcat-8/v8.5.98/bin/apache-tomcat-8.5.98.tar.gz
- 将tomcat进行解压缩
tar -zxf apache-tomcat-8.5.98.tar.gz
8.1.1.4.准备一个web项目,将其打包为war
1.将资料中的demo.war上传到tomcat8目录下的webapps包下
2.将tomcat进行启动,进入tomcat8的bin目录下
./startup.sh
8.1.1.5.启动tomcat进行访问测试。
静态资源: http://192.168.229.131:8080/demo/index.html
动态资源: http://192.168.229.131:8080/demo/getAddress
8.1.2.环境准备(Nginx)
8.1.2.1.使用Nginx的反向代理,将请求转给Tomcat进行处理。
upstream webservice {server 192.168.229.131:8080;
}
server{listen 80;server_name localhost;location /demo {proxy_pass http://webservice;}
}
user www;
worker_processes 2;events {accept_mutex on; #设置Nginx网络连接序列化,防止多个进程对连接的争抢multi_accept on; #设置Nginx的worker进程是否可以同时接收多个网络请求worker_connections 1024; # 设置Nginx单个worker进程最大的连接数use epoll; #设置Nginx使用的事件驱动模型,使用epoll函数来优化Ngin
}http {include mime.types;default_type application/octet-stream;sendfile on;tcp_nopush on; # 主要是用来提升网络包的传输效率tcp_nodelay on; # 提高网络包传输的实时性keepalive_timeout 65;upstream webservice {server 192.168.229.131:8080;}server {listen 80;server_name localhost;location /demo {proxy_pass http://webservice;}location / {root html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}}}
8.1.2.2.启动访问测试
8.2.Nginx+Tomcat来实现动静分离
8.2.1.动静分离概念
动静分离
-
动:后台应用程序的业务处理
-
静:网站的静态资源(html,javaScript,css,images等文件)
-
分离:将两者进行分开部署访问,提供用户进行访问。举例说明就是以后所有和静态资源相关的内容都交给Nginx来部署访问,非静态内容则交个类似于Tomcat的服务器来部署访问。
为什么要动静分离?
-
Nginx在处理静态资源的时候,效率是非常高的,而且Nginx的并发访问量也是名列前茅,而Tomcat则相对比较弱一些,所以把静态资源交个Nginx后,可以减轻Tomcat服务器的访问压力并提高静态资源的访问速度。
-
动静分离以后,降低了动态资源和静态资源的耦合度。如动态资源宕机了也不影响静态资源的展示。
如何实现动静分离?
- 实现动静分离的方式很多,比如静态资源可以部署到CDN、Nginx等服务器上,动态资源可以部署到Tomcat,weblogic或者websphere上。
8.2.2.需求分析
8.2.3.动静分离实现步骤
8.2.3.1.将demo.war项目中的静态资源都删除掉,重新打包生成一个war包,在资料中有提供。
8.2.3.2.将war包部署到tomcat中,把之前部署的内容删除掉
进入到tomcat的webapps目录下,将之前的内容删除掉
将新的war包复制到webapps下
将tomcat启动
8.2.3.3.在Nginx所在服务器创建如下目录,并将对应的静态资源放入指定的位置
其中index.html页面的内容如下:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script src="js/jquery.min.js"></script><script>$(function(){$.get('http://192.168.229.136/demo/getAddress',function(data){$("#msg").html(data);});});</script>
</head>
<body><img src="images/logo.png"/><h1>Nginx如何将请求转发到后端服务器</h1><h3 id="msg"></h3><img src="images/mv.png"/>
</body>
</html>
8.2.3.4.配置Nginx的静态资源与动态资源的访问
upstream webservice{server 192.168.229.131:8080;
}
server {listen 80;server_name localhost;#动态资源访问配置location /demo {proxy_pass http://webservice;}#静态资源访问配置location ~/.*\.(png|jpg|gif|js){root html/web;gzip on;}location / {root html/web;index index.html index.htm;}
}
8.2.3.5.启动测试,访问http://192.168.229.136/index.html
假如某个时间点,由于某个原因导致Tomcat后的服务器宕机了,我们再次访问Nginx,会得到如下效果,用户还是能看到页面,只是缺失了访问次数的统计,这就是前后端耦合度降低的效果,并且整个请求只和后的服务器交互了一次,js和images都直接从Nginx返回,提供了效率,降低了后的服务器的压力。
8.3.Nginx实现Tomcat集群搭建
在使用Nginx和Tomcat部署项目的时候,我们使用的是一台Nginx服务器和一台Tomcat服务器,效果图如下:
那么问题来了,如果Tomcat的真的宕机了,整个系统就会不完整,所以如何解决上述问题,一台服务器容易宕机,那就多搭建几台Tomcat服务器,这样的话就提升了后的服务器的可用性。这也就是我们常说的集群,搭建Tomcat的集群需要用到了Nginx的反向代理和赋值均衡的知识,具体如何来实现?
8.3.1.分析下原理与环境准备
8.3.2.准备3台tomcat,使用端口进行区分[实际环境应该是三台服务器],修改server.ml,将端口修改分别修改为8080,8180,8280
vim tomcat02/conf/server.xml
vim tomcat03/conf/server.xml
8.3.3.启动tomcat并访问测试,
http://192.168.229.131:8080/demo/getAddress
http://192.168.229.131:8180/demo/getAddress
http://192.168.229.131:8280/demo/getAddress
8.3.4.在Nginx对应的配置文件中添加如下内容:
upstream webservice{server 192.168.229.131:8080;server 192.168.229.131:8180;server 192.168.229.131:8280;
}
完成了上述环境的部署,我们已经解决了Tomcat的高可用性,一台服务器宕机,还有其他两条对外提供服务,同时也可以实现后台服务器的不间断更新。
如果是Nginx宕机了呢,那么整套系统都将服务对外提供服务了,这个如何解决?
8.4.Nginx高可用解决方案
针对于上面提到的问题,我们来分析下要想解决上述问题,需要面临哪些问题?
需要两台以上的Nginx服务器对外提供服务,这样的话就可以解决其中一台宕机了,另外一台还能对外提供服务,
但是如果是两台Nginx服务器的话,会有两个IP地址,用户该访问哪台服务器,用户怎么知道哪台是好的,哪台是宕机了的?
8.4.1.Keepalived
使用Keepalived来解决,Keepalived 软件
由 C 编写的,最初是专为 LVS 负载均衡
软件设计的,Keepalived 软件主要是通过 VRRP 协议
实现高可用功能
。
8.4.2.VRRP介绍
VRRP
(Virtual Route Redundancy Protocol)协议,翻译过来为虚拟路由冗余协议
。
VRRP协议将两台或多台路由器设备虚拟成一个设备,对外提供虚拟路由器IP,而在路由器组内部,如果实际拥有这个对外IP的路由器如果工作正常的话就是MASTER,MASTER实现针对虚拟路由器IP的各种网络功能。其他设备不拥有该虚拟IP,状态为BACKUP,处了接收MASTER的VRRP状态通告信息以外,不执行对外的网络功能。当主机失效
时,BACKUP
将接管
原先MASTER
的网络功能。
从上面的介绍信息获取到的内容就是VRRP是一种协议,那这个协议是用来干什么的?
1.选择协议
VRRP可以把一个虚拟路由器的责任动态分配到局域网上的 VRRP 路由器中的一台。
其中的虚拟路由即Virtual路由是由VRRP路由群组创建的一个不真实存在的路由,这个虚拟路由也是有对应的IP地址。
而且VRRP路由1和VRRP路由2之间会有竞争选择,通过选择会产生一个Master路由和一个Backup路由。
2.路由容错协议
Master路由和Backup路由之间会有一个心跳检测,Master会定时告知Backup自己的状态,如果在指定的时间内,
Backup没有接收到这个通知内容,Backup就会替代Master成为新的Master。
Master路由有一个特权就是虚拟路由和后端服务器都是通过Master进行数据传递交互的,
而备份节点则会直接丢弃这些请求和数据,不做处理,只是去监听Master的状态
8.4.3.使用Keepalived的解决方案
8.4.4.环境搭建
8.4.4.1.环境准备
VIP | IP | 主机名 | 主/从 |
---|---|---|---|
192.168.229.136 | keepalived1 | Master | |
192.168.229.222 | |||
192.168.229.137 | keepalived2 | Backup |
8.4.4.2.keepalived的安装
keepalived官网地址:https://keepalived.org/
8.4.4.2.1.步骤1:从官方网站下载keepalived
# 创建keepalived目录,方便管理资源
mkdir -p /keepalived/corecd /keepalived/corewget https://keepalived.org/software/keepalived-2.0.20.tar.gz
8.4.4.2.2.步骤2:将压缩文件进行解压缩
tar -zxf keepalived-2.0.20.tar.gz
8.4.4.2.3.步骤3:对keepalived进行配置,编译和安装
cd /keepalived/core/keepalived-2.0.20# 解决报错
yum -y install libnl libnl-devel./configure --sysconf=/etc --prefix=/usr/localmake && make install
8.4.4.2.4.报错解决
Use IPv4 devconf : No
Use iptables : Yes
Use libiptc : No
Use libipset : No
Use nftables : No
init type : systemd
Strict config checks : No
Build genhash : Yes
Build documentation : No*** WARNING - this build will not support IPVS with IPv6. Please install libnl/libnl-3 dev libraries to support IPv6 with IPVS.
# 解决报错
yum -y install libnl libnl-devel
8.4.4.3.keepalived的系统配置文件和系统配置脚本
/etc/keepalived/keepalived.conf
(keepalived的系统配置文件
,我们主要操作的就是该文件)
/usr/local/sbin
目录下的keepalived
,是系统配置脚本
,用来启动和关闭keepalived
cd /etc/keepalived
cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.bak
vi /etc/keepalived/keepalived.confcd /usr/local/sbin
./keepalived -v
8.4.5.Keepalived配置文件介绍
8.4.5.1.打开keepalived.conf配置文件
这里面会分三部,第一部分是global全局配置
、第二部分是vrrp相关配置
、第三部分是LVS相关配置
。
本次主要是使用keepalived实现高可用部署,没有用到LVS
cd /etc/keepalived
vi /etc/keepalived/keepalived.confcd /usr/local/sbin
8.4.5.2.global全局配置
global全局部分:
global_defs {#通知邮件,当keepalived发送切换时需要发email给具体的邮箱地址notification_email {acassen@firewall.locfailover@firewall.locsysadmin@firewall.loc}#设置发件人的邮箱信息notification_email_from Alexandre.Cassen@firewall.loc#指定smpt服务地址smtp_server 192.168.229.1#指定smpt服务连接超时时间smtp_connect_timeout 30#运行keepalived服务器的一个标识,可以用作发送邮件的主题信息router_id LVS_DEVEL#默认是不跳过检查。检查收到的VRRP通告中的所有地址可能会比较耗时,设置此命令的意思是,如果通告与接收的上一个通告来自相同的master路由器,则不执行检查(跳过检查)vrrp_skip_check_adv_addr#严格遵守VRRP协议。vrrp_strict#在一个接口发送的两个免费ARP之间的延迟。可以精确到毫秒级。默认是0vrrp_garp_interval 0#在一个网卡上每组na消息之间的延迟时间,默认为0vrrp_gna_interval 0
}
8.4.5.3.vrrp相关配置
VRRP部分,该部分可以包含以下四个子模块
1. vrrp_script
2. vrrp_sync_group
3. garp_group
4. vrrp_instance
我们会用到第一个和第四个,
#设置keepalived实例的相关信息,VI_1为VRRP实例名称
vrrp_instance VI_1 {state MASTER #有两个值可选MASTER主 BACKUP备interface ens33 #vrrp实例绑定的接口,用于发送VRRP包[当前服务器使用的网卡名称]virtual_router_id 51#指定VRRP实例ID,范围是0-255priority 100 #指定优先级,优先级高的将成为MASTERadvert_int 1 #指定发送VRRP通告的间隔,单位是秒authentication { #vrrp之间通信的认证信息auth_type PASS #指定认证方式。PASS简单密码认证(推荐)auth_pass 1111 #指定认证使用的密码,最多8位}virtual_ipaddress { #虚拟IP地址设置虚拟IP地址,供用户访问使用,可设置多个,一行一个192.168.229.222 #指定虚拟ip,自己定义的虚拟ip}
}
8.4.5.4.服务器1配置内容如下:
global_defs {notification_email {tom@itcast.cnjerry@itcast.cn}notification_email_from zhaomin@itcast.cnsmtp_server 192.168.229.1smtp_connect_timeout 30router_id keepalived1vrrp_skip_check_adv_addrvrrp_strictvrrp_garp_interval 0vrrp_gna_interval 0
}vrrp_instance VI_1 {state MASTERinterface ens33virtual_router_id 51priority 100advert_int 1authentication {auth_type PASSauth_pass 1111}virtual_ipaddress {192.168.229.222}
}
8.4.5.5.服务器2配置内容如下:
! Configuration File for keepalivedglobal_defs {notification_email {tom@itcast.cnjerry@itcast.cn}notification_email_from zhaomin@itcast.cnsmtp_server 192.168.229.1smtp_connect_timeout 30router_id keepalived2vrrp_skip_check_adv_addrvrrp_strictvrrp_garp_interval 0vrrp_gna_interval 0
}vrrp_instance VI_1 {state BACKUPinterface ens33virtual_router_id 51priority 90advert_int 1authentication {auth_type PASSauth_pass 1111}virtual_ipaddress {192.168.229.222}
}
8.4.5.6.访问测试
8.4.5.6.1. 启动keepalived之前,咱们先使用命令 ip a
,查看192.168.229.136和192.168.229.137这两台服务器的IP情况。
ip a
8.4.5.6.2. 分别启动两台服务器的keepalived
cd /usr/local/sbin./keepalived
再次通过 ip a
查看ip
ip a
8.4.5.6.3. 当把192.168.229.136服务器上的keepalived关闭后,再次查看ip
ps -ef | grep keepalived
通过上述的测试,我们会发现,虚拟IP(VIP)会在MASTER节点上
,当MASTER节点上的keepalived出问题以后
,因为BACKUP
无法收到MASTER发出的VRRP状态通过信息
,就会直接升为MASTER
。VIP也会"漂移"到新的MASTER
。
8.4.5.6.4.上面测试和Nginx有什么关系?
我们把192.168.229.136
服务器的keepalived再次启动
下,由于它的优先级高
于服务器192.168.229.122的,所有它会再次成为MASTER
,VIP也会"漂移"过去,然后我们再次通过浏览器访问:
http://192.168.229.222/
8.4.5.6.5.解决问题:如何在服务器上让keepalived 的虚拟IP可以网络访问
在宿主机上配置keepalived 启动成功后,创建虚拟IP访问不了
确保防火墙关闭还是访问不了虚拟IP 192.168.229.222
# 增加一条iptables策略,iptables对虚拟服务ip彻底放行
iptables -t nat -A PREROUTING -p tcp -d 虚拟服务ip --dport 80 -j REDIRECT
ip asystemctl stop firewalldsystemctl status firewalldiptables -t nat -A PREROUTING -p tcp -d 192.168.229.222 --dport 80 -j REDIRECT
8.4.5.6.6.如果把192.168.229.136服务器的keepalived关闭掉,再次访问相同的地址
效果实现了以后, 我们会发现要想让vip进行切换,就必须要把服务器上的keepalived进行关闭,而什么时候关闭keepalived呢?应该是在keepalived所在服务器的nginx出现问题后,把keepalived关闭掉,就可以让VIP执行另外一台服务器,但是现在这所有的操作都是通过手动来完成的,我们如何能让系统自动判断当前服务器的nginx是否正确启动,如果没有,要能让VIP自动进行"漂移",这个问题该如何解决?
8.4.6.Keepalived之自动切换脚本实现【keepalived之vrrp_script】
keepalived
只能做到对网络故障和keepalived本身的监控
,即当出现网络故障或者keepalived本身出现问题时,进行切换。但是这些还不够,我们还需要监控keepalived所在服务器上的其他业务,比如Nginx,如果Nginx出现异常
了,仅仅keepalived保持正常,是无法完成系统的正常工作的,因此需要根据业务进程的运行状态决定是否需要进行主备切换,这个时候,我们可以通过编写脚本对业务进程进行检测监控
。
实现步骤:
8.4.6.1. 在keepalived配置文件中添加对应的配置像
vrrp_script 脚本名称
{script "脚本位置"interval 3 #执行时间间隔3秒weight -20 #动态调整vrrp_instance的优先级
}
8.4.6.2. 编写脚本
ck_nginx.sh
#!/bin/bash
num=`ps -C nginx --no-header | wc -l` # 计算nginx进程行数
if [ $num -eq 0 ];then # 判断nginx进程行数是否等于0/usr/local/nginx/sbin/nginx # 等于0启动nginxsleep 2 # 睡上2秒if [ `ps -C nginx --no-header | wc -l` -eq 0 ]; then # 再次判断nginx进程行数是否等于0killall keepalived # 如果报错 杀死keepalived进程fi
fi
Linux ps命令用于显示当前进程 (process) 的状态。
-C(command) :指定命令的所有进程
–no-header 排除标题
8.4.6.3. 为脚本文件设置权限
chmod 755 ck_nginx.sh
8.4.6.4. 将脚本添加到
vrrp_script ck_nginx {script "/etc/keepalived/ck_nginx.sh" #执行脚本的位置interval 2 #执行脚本的周期,秒为单位weight -20 #权重的计算方式
}
vrrp_instance VI_1 {state MASTERinterface ens33virtual_router_id 10priority 100advert_int 1authentication {auth_type PASSauth_pass 1111}virtual_ipaddress {192.168.229.111}track_script {ck_nginx}
}
8.4.6.5. 如果效果没有出来,可以使用 tail -f /var/log/messages
查看日志信息,找对应的错误信息。
8.4.6.6. 测试
8.4.6.7. 模拟136Nginx服务器宕机效果测试
8.4.6.8.问题思考:
通常如果master服务死掉后backup会变成master,但是当master服务又好了的时候 master此时会抢占VIP,这样就会发生两次切换对业务繁忙的网站来说是不好的
。所以我们要在配置文件加入 nopreempt 非抢占,但是这个参数只能用于state 为backup,故我们在用HA的时候最好master 和backup的state都设置成backup 让其通过priority来竞争。
九、Nginx制作在线下载站点【模块ngx_http_autoindex_module】
9.1.下载站点
我们先来看一个网站http://nginx.org/download/
这个我们刚开始学习Nginx的时候给大家看过这样的网站,该网站主要就是用来提供用户来下载相关资源的网站
,就叫做下载网站
。
9.2.如何制作一个下载站点:
nginx使用的是模块ngx_http_autoindex_module
来实现的,该模块处理以斜杠(“/”)结尾的请求,并生成目录列表。
nginx编译的时候会自动加载该模块,但是该模块默认是关闭的
,我们需要使用下来指令来完成对应的配置
9.2.1.autoindex:启用或禁用目录列表输出
语法 | autoindex on|off; |
---|---|
默认值 | autoindex off; |
位置 | http、server、location |
9.2.2.autoindex_exact_size:对应HTLM格式,指定是否在目录列表展示文件的详细大小
默认为on,显示出文件的确切大小,单位是bytes。
改为off后,显示出文件的大概大小,单位是kB或者MB或者GB
语法 | autoindex_exact_size on|off; |
---|---|
默认值 | autoindex_exact_size on; |
位置 | http、server、location |
9.2.3.autoindex_format:设置目录列表的格式
语法 | autoindex_format html|xml|json|jsonp; |
---|---|
默认值 | autoindex_format html; |
位置 | http、server、location |
注意:该指令在1.7.9及以后版本中出现
9.2.4.autoindex_localtime:对应HTML格式,是否在目录列表上显示时间。
默认为off,显示的文件时间为GMT时间。
改为on后,显示的文件时间为文件的服务器时间
语法 | autoindex_localtime on | off; |
---|---|
默认值 | autoindex_localtime off; |
位置 | http、server、location |
9.3.资源准备
9.4.Nginx配置方式如下:
location /download{root /usr/local;autoindex on;autoindex_exact_size on;autoindex_format html;autoindex_localtime on;
}
XML/JSON格式[一般不用这两种方式]
9.5.下载站点测试成功
十、Nginx的用户认证模块【ngx_http_auth_basic_module模块】
对应系统资源的访问,我们往往需要限制谁能访问,谁不能访问。这块就是我们通常所说的认证部分,认证需要做的就是根据用户输入的用户名和密码来判定用户是否为合法用户,如果是则放行访问,如果不是则拒绝访问。
Nginx对应用户认证
这块是通过ngx_http_auth_basic_module模块
来实现的,它允许通过使用"HTTP基本身份验证"协议验证用户名和密码来限制对资源的访问。默认
情况下nginx是已经安装了该模块
,如果不需要则使用--without-http_auth_basic_module
。
10.1.auth_basic:使用“ HTTP基本认证”协议启用用户名和密码的验证
语法 | auth_basic string|off; |
---|---|
默认值 | auth_basic off; |
位置 | http,server,location,limit_except |
开启后,服务端会返回401
,指定的字符串会返回到客户端,给用户以提示信息,但是不同的浏览器对内容的展示不一致。
10.2.auth_basic_user_file:指定用户名和密码所在文件
语法 | auth_basic_user_file file; |
---|---|
默认值 | — |
位置 | http,server,location,limit_except |
指定文件路径,该文件中的用户名和密码的设置,密码需要进行加密。可以采用工具自动生成
10.3.实现步骤:nginx.conf添加如下内容
location /download{root /usr/local;autoindex on;autoindex_exact_size on;autoindex_format html;autoindex_localtime on;auth_basic 'please input your auth';auth_basic_user_file htpasswd;
}
10.4.使用htpasswd
工具生成用户名和密码
yum install -y httpd-tools
htpasswd -c /usr/local/nginx/conf/htpasswd username //创建一个新文件记录用户名和密码
htpasswd -b /usr/local/nginx/conf/htpasswd username password //在指定文件新增一个用户名和密码
htpasswd -D /usr/local/nginx/conf/htpasswd username //从指定文件删除一个用户信息
htpasswd -v /usr/local/nginx/conf/htpasswd username //验证用户名和密码是否正确
10.5.测试
上述方式虽然能实现用户名和密码的验证,但是所有的用户名和密码信息都记录在文件里面,如果用户量过大的话,这种方式就显得有点麻烦,这时候我们就得通过后台业务代码来进行用户权限的校验。
十一、Nginx的扩展模块【ngx_lua模块环境准备】
Nginx是可扩展的,可用于处理各种使用场景。使用Lua扩展Nginx的功能。
ngx_lua模块概念
:淘宝开发的ngx_lua模块通过将lua解释器集成进Nginx,可以采用lua脚本实现业务逻辑,由于lua的紧凑、快速以及内建协程,所以在保证高并发服务能力的同时极大地降低了业务逻辑实现成本。
11.1.方式一:lua-nginx-module
11.1.1. LuaJIT是采用C语言编写的Lua代表的解释器
官网地址为:http://luajit.org/
git下载:https://github.com/LuaJIT/LuaJIT/tags
在官网上找到对应的下载地址:http://luajit.org/download/LuaJIT-2.0.5.tar.gz
在centos上使用wget来下载:
mkdir -p /lua/corecd /lua/corewget http://luajit.org/download/LuaJIT-2.0.5.tar.gz
将下载的资源进行解压:
tar -zxf LuaJIT-2.0.5.tar.gz
进入解压的目录:
cd LuaJIT-2.0.5
执行编译和安装:
make PREFIX=/usr/local/luajitmake install
测试:
luajit -v
11.1.2.下载lua-nginx-module
下载地址:https://github.com/openresty/lua-nginx-module/tags
11.1.2.1.在centos上使用wget来下载:
wget https://github.com/openresty/lua-nginx-module/archive/refs/tags/v0.10.14.tar.gz
11.1.2.2.将下载的资源进行解压:
tar -zxf lua-nginx-module-0.10.14.tar.gz
11.1.2.3.更改目录名:
mv lua-nginx-module-0.10.14 lua-nginx-module
11.1.2.4.导入环境变量
告诉Nginx去哪里找luajit
vi /etc/profileexport LUAJIT_LIB=/usr/local/lib
export LUAJIT_INC=/usr/local/include/luajit-2.0source /etc/profile
11.1.2.5.进入Nginx的目录执行如下命令:
./configure --prefix=/usr/local/nginx \
--sbin-path=/usr/local/nginx/sbin/nginx \
--modules-path=/usr/local/nginx/modules \
--conf-path=/usr/local/nginx/conf/nginx.conf \
--error-log-path=/usr/local/nginx/logs/error.log \
--http-log-path=/usr/local/nginx/logs/access.log \
--pid-path=/usr/local/nginx/logs/nginx.pid \
--lock-path=/usr/local/nginx/logs/nginx.lock \
--with-http_gzip_static_module \
--with-http_ssl_module \
--with-stream \
--add-module=/nginx/module/fair \
--add-module=/nginx/module/purge \
--add-module=/nginx/module/lua-nginx-module
11.1.2.6.安装
make && make install
11.1.2.7.如果启动Nginx出现如下错误:
error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory
解决方案:
设置软链接,使用如下命令
ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2
11.1.2.8.如果启动Nginx出现以下错误信息
[root@localhost sbin]# ./nginx
nginx: [warn] lua_load_resty_core is deprecated (the lua-resty-core library is required since ngx_lua v0.10.16) in /usr/local/nginx/conf/nginx.conf:13
nginx: [alert] detected a LuaJIT version which is not OpenResty's; many optimizations will be disabled and performance will be compromised (see https://github.com/openresty/luajit2 for OpenResty's LuaJIT or, even better, consider using the OpenResty releases from https://openresty.org/en/download.html)
nginx: [alert] failed to load the 'resty.core' module (https://github.com/openresty/lua-resty-core); ensure you are using an OpenResty release from https://openresty.org/en/download.html (reason: module 'resty.core' not found:no field package.preload['resty.core']no file './resty/core.lua'no file '/usr/local/luajit/share/luajit-2.0.5/resty/core.lua'no file '/usr/local/share/lua/5.1/resty/core.lua'no file '/usr/local/share/lua/5.1/resty/core/init.lua'no file '/usr/local/luajit/share/lua/5.1/resty/core.lua'no file '/usr/local/luajit/share/lua/5.1/resty/core/init.lua'no file './resty/core.so'no file '/usr/local/lib/lua/5.1/resty/core.so'no file '/usr/local/luajit/lib/lua/5.1/resty/core.so'no file '/usr/local/lib/lua/5.1/loadall.so'no file './resty.so'no file '/usr/local/lib/lua/5.1/resty.so'no file '/usr/local/luajit/lib/lua/5.1/resty.so'no file '/usr/local/lib/lua/5.1/loadall.so') in /usr/local/nginx/conf/nginx.conf:71
这个问题到git上面看了 ,https://github.com/openresty/lua-nginx-module/issues/1509
就是在nginx.conf 中的 http{}模块中加入下面这行代码:lua_load_resty_core off;但是检查的时候发现这命令已经废弃
分析原因:因为lua-nginx-module是来自openrestry,错误中提示的resty.core是openrestry的核心模块,对其下的很多函数进行了优化等工作。以前的版本默认不会把该模块编译进去,所以需要使用的话,我们得手动安装,或者禁用就可以。但是最新的lua-nginx-module模块已经强制性安装了该模块,所以此处因为缺少resty模块导致的报错信息。使用低版本lua-nginx-module-0.10.14方可解决
解决方案有两个:一种是下载对应的模块,另一种则是禁用掉restry模块
,禁用的方式为:在nginx.conf
的http模块设置 lua_load_resty_core off来禁用
http{lua_load_resty_core off;
}
11.1.2.9.干脆直接用openresty
nginx: [alert] detected a LuaJIT version which is not OpenResty's;
many optimizations will be disabled and performance will be compromised
(see https://github.com/openresty/luajit2 for OpenResty's LuaJIT or, even better,
consider using the OpenResty releases from https://openresty.org/en/download.html)
让我不要用这个luajit版本,可以用openresty提供的luajit优化版本,或者干脆直接用openresty
卸载luajit官网版本,下载openresty提供的luajit优化版本
11.1.2.10.make报错修改ngx_http_lua_headers_in.c文件
‘ngx_http_headers_in_t’没有名为‘cookies’的成员
In file included from /usr/include/dlfcn.h:25:0,from src/os/unix/ngx_linux_config.h:58,from src/core/ngx_config.h:26,from /nginx/module/lua-nginx-module/src/ddebug.h:11,from /nginx/module/lua-nginx-module/src/ngx_http_lua_headers_in.c:10:
/nginx/module/lua-nginx-module/src/ngx_http_lua_headers_in.c:162:18: 错误:‘ngx_http_headers_in_t’没有名为‘cookies’的成员offsetof(ngx_http_headers_in_t, cookies),^
make[1]: *** [objs/addon/src/ngx_http_lua_headers_in.o] 错误 1
make[1]: 离开目录“/nginx/core/nginx-1.24.0”
make: *** [build] 错误 2
vi /nginx/module/lua-nginx-module/src/ngx_http_lua_headers_in.c
11.1.2.11.测试
在nginx.conf下配置如下内容:
location /lua{default_type 'text/html';content_by_lua 'ngx.say("<h1>HELLO,LUA</h1>")';
}
配置成功后,启动nginx,通过浏览器进行访问,如果获取到如下结果,则证明安装成功。
11.2.方式二:OpenRestry
11.2.1.概述
OpenResty是由淘宝工程师开发的,所以其官方网站(http://openresty.org/)我们读起来是非常的方便。OpenResty是一个基于Nginx与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。所以本身OpenResty内部就已经集成了Nginx和Lua,所以我们使用起来会更加方便。
11.2.2.下载OpenResty:
https://openresty.org/download/openresty-1.25.3.1.tar.gz
11.2.3.使用wget下载:
mkdir -p /openresty/corecd /openresty/corewget https://openresty.org/download/openresty-1.25.3.1.tar.gz
11.2.4.解压缩:
tar -zxf openresty-1.25.3.1.tar.gz
11.2.5.进入OpenResty目录:
cd openresty-1.25.3.1
11.2.6.编译和安装
./configuremake && make install
cd nginx-1.25.3
sh ./configure --prefix=/usr/local/openresty/nginx \--with-cc-opt='-O2' \--add-module=../ngx_devel_kit-0.3.3 \--add-module=../echo-nginx-module-0.63 \--add-module=../xss-nginx-module-0.06 \--add-module=../ngx_coolkit-0.2 \--add-module=../set-misc-nginx-module-0.33 \--add-module=../form-input-nginx-module-0.12 \--add-module=../encrypted-session-nginx-module-0.09 \--add-module=../srcache-nginx-module-0.33 \--add-module=../ngx_lua-0.10.26 \--add-module=../ngx_lua_upstream-0.07 \--add-module=../headers-more-nginx-module-0.37 \--add-module=../array-var-nginx-module-0.06 \--add-module=../memc-nginx-module-0.20 \--add-module=../redis2-nginx-module-0.15 \--add-module=../redis-nginx-module-0.3.9 \--add-module=../rds-json-nginx-module-0.16 \--add-module=../rds-csv-nginx-module-0.09 \--add-module=../ngx_stream_lua-0.0.14 \--with-ld-opt='-Wl,-rpath,/usr/local/openresty/luajit/lib' \--with-stream --without-pcre2 --with-stream_ssl_module --with-stream_ssl_preread_module --with-http_ssl_module
11.2.7.进入OpenResty的目录,找到nginx:
cd /usr/local/openresty/nginx/
11.2.8.在conf目录下的nginx.conf添加如下内容
location /lua{default_type 'text/html';content_by_lua 'ngx.say("<h1>HELLO,OpenRestry</h1>")';
}
11.2.9.在sbin目录下启动nginx
11.2.10.通过浏览器访问测试
11.2.11.设置环境变量
vim /etc/profile# openresty
export PATH=$PATH:/usr/local/openresty/nginx/sbin# 输入下面命令让设置的环境变量生效
source /etc/profile# 查看版本信息
nginx -V
11.2.12.openresty安装成系统服务
# 创建脚本
vi /usr/lib/systemd/system/openresty.service# 重新加载系统服务
systemctl daemon-reload
服务脚本内容
[Unit]
Description=openresty web service
Documentation=https://openresty.org/cn/
After=network.target[Service]
Type=forking
PIDFile=/usr/local/openresty/nginx/logs/nginx.pid
ExecStartPre=/usr/local/openresty/nginx/sbin/nginx -t -c /usr/local/openresty/nginx/conf/nginx.conf
ExecStart=/usr/local/openresty/nginx/sbin/nginx
ExecReload=/usr/local/openresty/nginx/sbin/nginx -s reload
ExecStop=/usr/local/openresty/nginx/sbin/nginx -s stop
PrivateTmp=true[Install]
WantedBy=default.target
[Unit]:服务的说明
Description:描述服务
After:描述服务类别
[Service]服务运行参数的设置
Type=forking是后台运行的形式ExecStartpre是检查配置文件
ExecStart为服务的具体运行命令
ExecReload为重启命令
ExecStop为停止命令
PrivateTmp=True表示给服务分配独立的临时空间
注意:[Service]的启动、重启、停止命令全部要求使用绝对路径
[Install]运行级别下服务安装的相关设置,可设置为多用户,即系统运行级别为3
11.2.13.openresty常用命令
# 启动服务
systemctl start openresty#停止
systemctl stop openresty#重启
systemctl restart openresty#重新加载配置文件
systemctl reload openresty# 查看nginx状态
systemctl status openresty# 开机启动
systemctl enable openresty.service# 查看nginx是否启动
ps -ef | grep openresty
十二、ngx_lua的使用
指令执行的顺序
使用Lua编写Nginx脚本的基本构建块是指令。指令用于指定何时运行用户Lua代码以及如何使用结果。下图显示了执行指令的顺序
。
解释下*的作用
*:无 , 即 xxx_by_lua ,指令后面跟的是 lua指令
*:_file,即 xxx_by_lua_file 指令后面跟的是 lua文件
*:_block,即 xxx_by_lua_block 在0.9.17版后替换init_by_lua_file
init_by_lua*
该指令在每次Nginx重新加载配置时执行,可以用来完成一些耗时模块的加载,或者初始化一些全局配置。
init_worker_by_lua*
该指令用于启动一些定时任务,如心跳检查、定时拉取服务器配置等。
set_by_lua*
该指令只要用来做变量赋值,这个指令一次只能返回一个值,并将结果赋值给Nginx中指定的变量。
rewrite_by_lua*
该指令用于执行内部URL重写或者外部重定向,典型的如伪静态化URL重写,本阶段在rewrite处理阶段的最后默认执行。
access_by_lua*
该指令用于访问控制。例如,如果只允许内网IP访问。
content_by_lua*
该指令是应用最多的指令,大部分任务是在这个阶段完成的,其他的过程往往为这个阶段准备数据,正式处理基本都在本阶段。
header_filter_by_lua*
该指令用于设置应答消息的头部信息。
body_filter_by_lua*
该指令是对响应数据进行过滤,如截断、替换。
log_by_lua*
该指令用于在log请求处理阶段,用Lua代码处理日志,但并不替换原有log处理。
balancer_by_lua*
该指令主要的作用是用来实现上游服务器的负载均衡器算法
ssl_certificate_by_*
该指令作用在Nginx和下游服务开始一个SSL握手操作时将允许本配置项的Lua代码。
需求:
http://192.168.229.136?name=张三&gender=1
Nginx接收到请求后,根据gender传入的值,如果gender传入的是1,则在页面上展示
张三先生,如果gender传入的是0,则在页面上展示张三女士,如果未传或者传入的不是1和2则在页面上展示张三。
实现代码
location /getByGender {default_type 'text/html';#set_by_luaset_by_lua $param "-- 获取请求url上的参数对应值 name genderlocal uri_args = ngx.req.get_uri_args()local name = uri_args['name']local gender = uri_args['gender']-- 条件判断 if gender 1 先生 0 女生if gender == '1' thenreturn name..'先生'elseif gender == '0' thenreturn name..'女士'elsereturn nameend";header_filter_by_lua "ngx.header.aaa='bbb'";charset utf-8;return 200 $param;}
worker_processes 1;
events {worker_connections 1024;
}http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;server {listen 80;server_name localhost;location /getByGender {default_type 'text/html';#set_by_luaset_by_lua $param "-- 获取请求url上的参数对应值 name genderlocal uri_args = ngx.req.get_uri_args()local name = uri_args['name']local gender = uri_args['gender']-- 条件判断 if gender 1 先生 0 女生if gender == '1' thenreturn name..'先生'elseif gender == '0' thenreturn name..'女士'elsereturn nameend";charset utf-8;return 200 $param;}location /lua{default_type 'text/html';content_by_lua 'ngx.say("<h1>HELLO,OpenRestry</h1>")';}location / {root html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}}}
测试
http://192.168.229.136/getByGender?name=TOM&gender=0
http://192.168.229.136/getByGender?name=TOM&gender=1
十三、ngx_lua操作Redis
13.1.介绍
Redis
在系统中经常作为数据缓存
、内存数据库使用
,在大型系统中扮演着非常重要的作用。
在Nginx核心系统中,Redis是常备组件。
Nginx支持3种方法访问Redis,分别是HttpRedis模块
、HttpRedis2Module
、lua-resty-redis库
。
这三种方式中HttpRedis模块提供的指令少,功能单一,适合做简单缓存,
HttpRedis2Module模块
比HttpRedis模块操作更灵活,功能更强大。
而Lua-resty-redis库
是OpenResty
提供的一个操作Redis的接口库,可根据自己的业务情况做一些逻辑处理,适合做复杂的业务逻辑。
13.2.lua-resty-redis环境准备
13.2.1.准备一个Redis环境【安装Redis】
连接地址
host= 192.168.229.137
port=6379
13.2.2.准备对应的API
lua-resty-redis提供了访问Redis的详细API,包括创建对接、连接、操作、数据处理等。这些API基本上与Redis的操作一一对应。
(1)redis = require "resty.redis"
(2)new创建一个Redis对象,遇到错误时,redis为nil,err为错误描述信息语法: redis,err = redis:new(),创建一个Redis对象。
(3)connect语法:ok,err=redis:connect(host,port[,options_table]),设置连接Redis的连接信息。ok:连接成功返回 1,连接失败返回nilerr:返回对应的错误信息
(4)set_timeout语法: redis:set_timeout(time) ,设置请求操作Redis的超时时间。
(5)close语法: ok,err = redis:close(),关闭当前连接,成功返回1,失败返回nil和错误信息
(6)redis命令对应的方法在lua-resty-redis中,所有的Redis命令都有自己的方法,方法名字和命令名字相同,只是全部为小写。
13.2.3.步骤
-- 引入Redis对应的接口对象
-- 创建一个Redis对象
-- 设置超时时间
-- 获取连接
-- 往Redis中存储数据
-- 从Redis中获取数据
-- 关闭连接
13.2.4.效果实现
location / {default_type "text/html";content_by_lua_block{local redis = require "resty.redis" -- 引入Redis对应的接口对象local redisObj = redis:new() -- 创建一个Redis对象redisObj:set_timeout(1000) -- 设置超时时间1slocal ok,err = redisObj:connect("192.168.229.137",6379) -- 设置redis连接信息if not ok then -- 判断是否连接成功ngx.say("failed to connection redis",err)returnendok,err = redisObj:set("username","TOM")-- 往Redis中存储数据if not ok then -- 判断是否存入成功ngx.say("failed to set username",err)returnendlocal res,err = redisObj:get("username") -- 从redis中获取数据ngx.say(res) -- 将数据写会消息体中redisObj:close() -- 关闭连接}
}
13.2.5.运行测试效果
http://192.168.229.136/testRedis
十四、ngx_lua操作Mysql
14.1.介绍
MySQL是一个使用广泛的关系型数据库
。在ngx_lua中,MySQL有两种访问模式,分别是使
(1)用ngx_lua模块和lua-resty-mysql
模块:这两个模块是安装OpenResty时默认安装
的。
(2)使用drizzle_nginx_module(HttpDrizzleModule)模块:需要单独安装,这个库现不在OpenResty中。
14.2.lua-resty-mysql
lua-resty-mysql是OpenResty开发的模块,使用灵活、功能强大,适合复杂的业务场景,同时支持存储过程的访问。
14.3.使用lua-resty-mysql实现数据库的查询
14.3.1.准备一个Mysql环境【安装Mysql】
host: 192.168.229.137
port: 3306
username:root
password:123456
14.3.2.创建一个数据库表及表中的数据
create database nginx_db;use nginx_db;create table users(id int primary key auto_increment,username varchar(30),birthday date,salary double
);insert into users(id,username,birthday,salary) values(null,"TOM","1988-11-11",10000.0);
insert into users(id,username,birthday,salary) values(null,"JERRY","1989-11-11",20000.0);
insert into users(id,username,birthday,salary) values(null,"ROWS","1990-11-11",30000.0);
insert into users(id,username,birthday,salary) values(null,"LUCY","1991-11-11",40000.0);
insert into users(id,username,birthday,salary) values(null,"JACK","1992-11-11",50000.0);
14.3.3.数据库连接四要素:
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.200.111:3306/nginx_db
username=root
password=123456
14.4.API学习
(1)引入"resty.mysql"模块local mysql = require "resty.mysql"
(2)new创建一个MySQL连接对象,遇到错误时,db为nil,err为错误描述信息语法: db,err = mysql:new()
(3)connect尝试连接到一个MySQL服务器语法:ok,err=db:connect(options),options是一个参数的Lua表结构,里面包含数据库连接的相关信息host:服务器主机名或IP地址port:服务器监听端口,默认为3306user:登录的用户名password:登录密码database:使用的数据库名
(4)set_timeout设置子请求的超时时间(ms),包括connect方法语法:db:set_timeout(time)
(5)close关闭当前MySQL连接并返回状态。如果成功,则返回1;如果出现任何错误,则将返回nil和错误描述。语法:db:close()
(6)send_query异步向远程MySQL发送一个查询。如果成功则返回成功发送的字节数;如果错误,则返回nil和错误描述语法:bytes,err=db:send_query(sql)
(7)read_result从MySQL服务器返回结果中读取一行数据。res返回一个描述OK包或结果集包的Lua表,语法:res, err, errcode, sqlstate = db:read_result() res, err, errcode, sqlstate = db:read_result(rows) :rows指定返回结果集的最大值,默认为4如果是查询,则返回一个容纳多行的数组。每行是一个数据列的key-value对,如{{id=1,username="TOM",birthday="1988-11-11",salary=10000.0},{id=2,username="JERRY",birthday="1989-11-11",salary=20000.0}}如果是增删改,则返回类上如下数据{insert_id = 0,server_status=2,warning_count=1,affected_rows=2,message=nil}返回值:res:操作的结果集err:错误信息errcode:MySQL的错误码,比如1064sqlstate:返回由5个字符组成的标准SQL错误码,比如42000
14.5.步骤
-- 引入resty.mysql
-- 创建连接对象 new
-- 建立连接 传入数据库连接的相关信息
-- 判断是否连接成功
-- 设置超时时间 set_timeout
-- 发送SQL语句
-- 读取返回的结果,并且把结果输出到页面
-- 关闭连接
14.6.效果实现–查询单个数据
location /{content_by_lua_block{local mysql = require "resty.mysql"local db = mysql:new()local ok,err = db:connect{host="192.168.200.111",port=3306,user="root",password="123456",database="nginx_db"}db:set_timeout(1000)db:send_query("select * from users where id =1")local res,err,errcode,sqlstate = db:read_result()ngx.say(res[1].id..","..res[1].username..","..res[1].birthday..","..res[1].salary)db:close()}}
14.7.测试
http://192.168.229.136/testMysql
14.8.查询多行数据并显示
db:send_query("select * from users")
-- 读取返回的结果,并且把结果输出到页面
local res,err,errcode,sqlstate = db:read_result()for i,v in ipairs(res) dongx.say(v.id..","..v.username..","..v.birthday..","..v.salary.."<br>")
end
14.8.问题
1.如何获取返回数据的内容
2.如何实现查询多条数据
3.如何实现数据库的增删改操作
14.9.使用lua-cjson处理查询结果–返回JSON字符串
read_result()得到的结果res都是table类型,要想在页面上展示,就必须知道table的具体数据结构才能进行遍历获取。处理起来比较麻烦,接下来我们介绍一种简单方式cjson,使用它就可以将table类型的数据转换成json字符串,把json字符串展示在页面上即可。具体如何使用?
14.9.1.引入cjson
local cjson = require "cjson"
14.9.2.调用cjson的encode方法进行类型转换
cjson.encode(res)
14.9.3.使用
location /{content_by_lua_block{local mysql = require "resty.mysql"local cjson = require "cjson"local db = mysql:new()local ok,err = db:connect{host="192.168.200.111",port=3306,user="root",password="123456",database="nginx_db"}db:set_timeout(1000)db:send_query("select * from users")local res,err,errcode,sqlstate = db:read_result()ngx.say(cjson.encode(res))db:close()}}
14.9.4.测试
14.9.5.cjson定义为全局变量–防止重复代码
init_by_lua_block{cjson = require "cjson" -- 引入cjson}server {listen 80;server_name localhost;location /testMysql {default_type "text/html";content_by_lua_block{-- 引入cjson-- local cjson = require "cjson"-- 引入resty.mysqllocal mysql = require "resty.mysql"-- 创建连接对象 newlocal db = mysql:new()-- 建立连接 传入数据库连接的相关信息local ok,err = db:connect{host = "192.168.229.137",port = 3306,user = "root",password = "123456",database = "nginx_db"}if not ok then -- 判断是否连接成功ngx.say("faild to connect mysql",err)returnend-- 设置超时时间 set_timeoutdb:set_timeout(1000)-- 发送SQL语句db:send_query("select * from users")-- 读取返回的结果,并且把结果输出到页面local res,err,errcode,sqlstate = db:read_result()-- ngx.say(res[1].id..","..res[1].username..","..res[1].birthday..","..res[1].salary)--[[for i,v in ipairs(res) dongx.say(v.id..","..v.username..","..v.birthday..","..v.salary.."<br>")end--]]ngx.say(cjson.encode(res))-- 关闭连接db:close()}}
14.10.lua-resty-mysql实现数据库的增删改
14.10.1.优化send_query和read_result
本方法是send_query和read_result组合的快捷方法。
语法:
res, err, errcode, sqlstate = db:query(sql[,rows])
14.10.2.优化代码
location /{content_by_lua_block{local mysql = require "resty.mysql"local db = mysql:new()local ok,err = db:connect{host="192.168.229.137",port=3306,user="root",password="123456",database="nginx_db",max_packet_size=1024,compact_arrays=false}db:set_timeout(1000)local res,err,errcode,sqlstate = db:query("select * from users")--local res,err,errcode,sqlstate = db:query("insert into users(id,username,birthday,salary) values(null,'zhangsan','2020-11-11',32222.0)")--local res,err,errcode,sqlstate = db:query("update users set username='lisi' where id = 6")--local res,err,errcode,sqlstate = db:query("delete from users where id = 6")db:close()}
}
14.10.3.新增
14.10.4.修改
14.10.5.删除
14.11.lua_mysql_redis_nginx.conf
worker_processes 1;
events {worker_connections 1024;
}http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;init_by_lua_block{cjson = require "cjson" -- 引入cjson}server {listen 80;server_name localhost;location /testMysql {default_type "text/html";content_by_lua_block{-- 引入cjson-- local cjson = require "cjson"-- 引入resty.mysqllocal mysql = require "resty.mysql"-- 创建连接对象 newlocal db = mysql:new()-- 建立连接 传入数据库连接的相关信息local ok,err = db:connect{host = "192.168.229.137",port = 3306,user = "root",password = "123456",database = "nginx_db"}if not ok then -- 判断是否连接成功ngx.say("faild to connect mysql",err)returnend-- 设置超时时间 set_timeoutdb:set_timeout(1000)-- 发送SQL语句-- db:send_query("select * from users")-- 读取返回的结果,并且把结果输出到页面-- local res,err,errcode,sqlstate = db:read_result()-- 查询-- local sql = "select * from users"-- 新增-- local sql = "insert into users(username,birthday,salary) values ('zhangsan','2024-12-12',3334)"-- 修改-- local sql = "update users set username = 'lisi' where id = 6"-- 删除local sql = "delete from users where id = 6"local res,err,errcode,sqlstate = db:query(sql)-- ngx.say(res[1].id..","..res[1].username..","..res[1].birthday..","..res[1].salary)--[[for i,v in ipairs(res) dongx.say(v.id..","..v.username..","..v.birthday..","..v.salary.."<br>")end--]]ngx.say(cjson.encode(res))-- 关闭连接db:close()}}location /testRedis {default_type "text/html";content_by_lua_block{-- 引入Redis对应的接口对象local redis = require "resty.redis"-- 创建一个Redis对象local redisObj = redis:new()-- 设置超时时间redisObj:set_timeout(1000)-- 获取连接local ok,err = redisObj:connect("192.168.229.137",6379)if not ok thenngx.say("faild to connect redis",err)returnend-- 往Redis中存储数据ok,err = redisObj:set("username","ROSE")if not ok thenngx.say("faild to connect username",err)returnend-- 从Redis中获取数据local res,err = redisObj:get("username")ngx.say(res)-- 关闭连接redisObj:close()}}location /getByGender {default_type 'text/html';#set_by_luaset_by_lua $param "-- 获取请求url上的参数对应值 name genderlocal uri_args = ngx.req.get_uri_args()local name = uri_args['name']local gender = uri_args['gender']-- 条件判断 if gender 1 先生 0 女生if gender == '1' thenreturn name..'先生'elseif gender == '0' thenreturn name..'女士'elsereturn nameend";header_filter_by_lua "ngx.header.aaa='bbb'";charset utf-8;return 200 $param;}location /lua{default_type 'text/html';content_by_lua 'ngx.say("<h1>HELLO,OpenRestry</h1>")';}location / {root html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}}}
十五、综合小案例—使用ngx_lua模块完成Redis缓存预热。
15.1.准备一张表(users)【上面】
15.2.浏览器输入如下地址
http://191.168.229.137?username=TOM
15.3.从表中查询出符合条件的记录,此时获取的结果为table类型
15.4.使用cjson将table数据转换成json字符串
15.5.将查询的结果数据存入Redis中
15.6.代码
init_by_lua_block{redis = require "resty.redis"mysql = require "resty.mysql"cjson = require "cjson"
}
location /{default_type "text/html";content_by_lua_block{--获取请求的参数usernamelocal param = ngx.req.get_uri_args()["username"]--建立mysql数据库的连接local db = mysql:new()local ok,err = db:connect{host="192.168.200.111",port=3306,user="root",password="123456",database="nginx_db"}if not ok thenngx.say("failed connect to mysql:",err)returnend--设置连接超时时间db:set_timeout(1000)--查询数据local sql = ""if not param thensql="select * from users"elsesql="select * from users where username=".."'"..param.."'"endlocal res,err,errcode,sqlstate=db:query(sql)if not res thenngx.say("failed to query from mysql:",err)returnend--连接redislocal rd = redis:new()ok,err = rd:connect("192.168.200.111",6379)if not ok thenngx.say("failed to connect to redis:",err)returnendrd:set_timeout(1000)--循环遍历数据for i,v in ipairs(res) dord:set("user_"..v.username,cjson.encode(v))endngx.say("success")rd:close()db:close()}}
15.7.测试结果
15.8.修改某个值测试redis
15.9.完整代码
worker_processes 1;
events {worker_connections 1024;
}http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;init_by_lua_block{mysql = require "resty.mysql"redis = require "resty.redis"cjson = require "cjson"}server {listen 80;server_name localhost;location / {default_type "text/html";content_by_lua_block{-- 获取传入的参数local param = ngx.req.get_uri_args()["username"] -- 获取请求的参数username-- 建立mysql数据库的连接local db = mysql:new()local ok,err = db:connect{host="192.168.229.137",port=3306,user="root",password="123456",database="nginx_db"}if not ok thenngx.say("faild connect to mysql:",err)returnenddb:set_timeout(1000) --设置连接超时时间local sql = "" --查询数据if not param thensql = "select * from users"elsesql = "select * from users where username =".."'"..param.."'"endlocal res,err,errcode,sqlstate = db:query(sql)if not res thenngx.say("faild to query from mysql:",err)returnend-- 连接redislocal rd = redis:new()ok,err = rd:connect("192.168.229.137",6379)if not ok thenngx.say("faild connect to redis:",err)returnendrd:set_timeout(1000)for i,v in ipairs(res) dord:set("user_"..v.username,cjson.encode(v)) -- cjson转换为JSON字符串存储到redisendngx.say("success")rd:close()db:close()}}error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}}}