Nginx(五)

一、Nginx实现服务器端集群搭建

1.1NginxTomcat部署

我们都知道了 Nginx 在 高并发场景和处理静态资源是非常高性能的,但是在实际项目中除了静
态资源还有就是后台业务代码模块,一般后台业务都会被部署在
Tomcat weblogic 或者是 websphere web 服务器上。那么如何使用
Nginx 接收用户的请求并把请求转发到后台 web 服务器?
Nginx(五)_第1张图片

步骤分析 :
1. 准备 Tomcat 环境,并在 Tomcat 上部署一个 web 项目
2. 准备 Nginx 环境,使用 Nginx 接收请求,并把请求分发到 Tomat
环境准备 (Tomcat)
浏览器访问 :
http://192.168.200.146:8080/demo/index.html
Nginx(五)_第2张图片

获取动态资源的链接地址 :
http://192.168.200.146:8080/demo/getAddress
本次将采用 Tomcat 作为后台 web 服务器
(1)在 Centos 上准备一个 Tomcat
1.Tomcat 官网地址 :https://tomcat.apache.org/
  2. 下载 tomcat, 本次课程使用的是 apache-tomcat-8.5.59.tar.gz
  3. tomcat 进行解压缩
mkdir web_tomcat
tar -zxf apache-tomcat-8.5.59.tar.gz -C /web_tomcat
(2)准备一个 web 项目,将其打包为 war
1. 将资料中的 demo.war 上传到 tomcat8 目录下的 webapps 包下
2. tomcat 进行启动,进入 tomcat8 bin 目录下
  ./startup.sh
(3)启动 tomcat 进行访问测试。
静态资源 : http://192.168.200.146:8080/demo/index.html
  动态资源 : http://192.168.200.146:8080/demo/getAddress
环境准备 (Nginx)
(1)使用 Nginx 的反向代理,将请求转给 Tomcat 进行处理。
upstream webservice {
server 192.168.200.146:8080;
}
server{
listen 80;
server_name localhost;
location /demo {
proxy_pass http://webservice;
}
}
(2)启动访问测试
Nginx(五)_第3张图片

学习到这,可能大家会有一个困惑,明明直接通过 tomcat 就能访问,为
什么还需要多加一个 nginx ,这样不是反而是系统的复杂度变高了么 ?
接下来我们从两个方便给大家分析下这个问题,
第一个使用 Nginx 实现动静分离
第二个使用 Nginx 搭建 Tomcat 的集群

1.2Nginx实现动静分离

什么是动静分离 ?
: 后台应用程序的业务处理
: 网站的静态资源 (html,javaScript,css,images 等文件 )
分离 : 将两者进行分开部署访问,提供用户进行访问。举例说明就是以后
所有和静态资源相关的内容都交给 Nginx 来部署访问,非静态内容则交
个类似于 Tomcat 的服务器来部署访问。
为什么要动静分离 ?
前面我们介绍过 Nginx 在处理静态资源的时候,效率是非常高的,而且
Nginx 的并发访问量也是名列前茅,而 Tomcat 则相对比较弱一些,所以
把静态资源交个 Nginx 后,可以减轻 Tomcat 服务器的访问压力并提高静
态资源的访问速度。
动静分离以后,降低了动态资源和静态资源的耦合度。如动态资源宕机
了也不影响静态资源的展示。
如何实现动静分离 ?
实现动静分离的方式很多,比如静态资源可以部署到 CDN Nginx 等服
务器上,动态资源可以部署到 Tomcat,weblogic 或者 websphere 上。本
次课程只要使用 Nginx+Tomcat 来实现动静分离。
需求分析
Nginx(五)_第4张图片

动静分离实现步骤
1. demo.war 项目中的静态资源都删除掉,重新打包生成一个 war 包,
在资料中有提供。
2. war 包部署到 tomcat 中,把之前部署的内容删除掉
进入到 tomcat webapps 目录下,将之前的内容删除掉
将新的 war 包复制到 webapps
tomcat 启动
3. Nginx 所在服务器创建如下目录,并将对应的静态资源放入指定的位
Nginx(五)_第5张图片

其中 index.html 页面的内容如下 :
4. 配置 Nginx 的静态资源与动态资源的访问
  lang = "en" >
 
  charset = "UTF-8" >
</span> <span style="color:#333333;">Title</span> <span style="color:#117700;">
 
 
 
 
src = "images/logo.png" />
 

Nginx 如何将请求转发到后端服务器

 

id = "msg" >

  src = "images/mv.png" />
 
 
4. 配置 Nginx 的静态资源与动态资源的访问
  upstream webservice{
  server 192.168.200.146: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;
  }
  }
5. 启动测试,访问 http://192.168.200.133/index.html
Nginx(五)_第6张图片

假如某个时间点,由于某个原因导致 Tomcat 后的服务器宕机了,我们再
次访问 Nginx, 会得到如下效果,用户还是能看到页面,只是缺失了访问
次数的统计,这就是前后端耦合度降低的效果,并且整个请求只和后的
服务器交互了一次, js images 都直接从 Nginx 返回,提供了效率,降
低了后的服务器的压力。
Nginx(五)_第7张图片

Nginx 实现 Tomcat 集群搭建
在使用 Nginx Tomcat 部署项目的时候,我们使用的是一台 Nginx 服务
器和一台 Tomcat 服务器,效果图如下 :
Nginx(五)_第8张图片

那么问题来了,如果 Tomcat 的真的宕机了,整个系统就会不完整,所以
如何解决上述问题,一台服务器容易宕机,那就多搭建几台 Tomcat 服务
器,这样的话就提升了后的服务器的可用性。这也就是我们常说的集
群,搭建 Tomcat 的集群需要用到了 Nginx 的反向代理和赋值均衡的知
识,具体如何来实现 ? 我们先来分析下原理
Nginx(五)_第9张图片

环境准备:
(1) 准备 3 tomcat, 使用端口进行区分 [ 实际环境应该是三台服务器 ] ,修
server.ml ,将端口修改分别修改为 8080,8180,8280
(2) 启动 tomcat 并访问测试,
Nginx(五)_第10张图片

Nginx(五)_第11张图片

(3) Nginx 对应的配置文件中添加如下内容 :
upstream webservice{
server 192.168.200.146:8080;
server 192.168.200.146:8180;
server 192.168.200.146:8280;
}
好了,完成了上述环境的部署,我们已经解决了 Tomcat 的高可用性,一
台服务器宕机,还有其他两条对外提供服务,同时也可以实现后台服务
器的不间断更新。但是新问题出现了,上述环境中,如果是 Nginx 宕机
了呢,那么整套系统都将服务对外提供服务了,这个如何解决?

1.3Nginx高可用解决方案

针对于上面提到的问题,我们来分析下要想解决上述问题,需要面临哪
些问题 ?
Nginx(五)_第12张图片

需要两台以上的 Nginx 服务器对外提供服务,这样的话就可以解决其中一
台宕机了,另外一台还能对外提供服务,但是如果是两台 Nginx 服务器的
话,会有两个 IP 地址,用户该访问哪台服务器,用户怎么知道哪台是好
的,哪台是宕机了的 ?
Keepalived
使用 Keepalived 来解决, Keepalived 软件由 C 编写的,最初是专为 LVS
负载均衡软件设计的, Keepalived 软件主要是通过 VRRP 协议实现高可
用功能。
VRRP 介绍
Nginx(五)_第13张图片

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 的状
用了 Keepalived 后,解决方案如下 :
Nginx(五)_第14张图片

环境搭建
环境准备
Nginx(五)_第15张图片

keepalived 的安装
步骤 1: 从官方网站下载 keepalived, 官网地址
https://keepalived.org/
步骤 2: 将下载的资源上传到服务器
keepalived-2.0.20.tar.gz
步骤 3: 创建 keepalived 目录,方便管理资源
mkdir keepalived
步骤 4: 将压缩文件进行解压缩,解压缩到指定的目录
tar -zxf keepalived-2.0.20.tar.gz -C keepalived/
步骤 5: keepalived 进行配置,编译和安装
cd keepalived/keepalived-2.0.20
./configure --sysconf=/etc --prefix=/usr/local
make && make install
安装完成后,有两个文件需要我们认识下,一个是
/etc/keepalived/keepalived.conf (keepalived 的系统配置文件,我
们主要操作的就是该文件 ) ,一个是 /usr/local/sbin 目录下的
keepalived , 是系统配置脚本,用来启动和关闭 keepalived
Keepalived 配置文件介绍
打开 keepalived.conf 配置文件
这里面会分三部,第一部分是 global 全局配置、第二部分是 vrrp 相关配
置、第三部分是 LVS 相关配置。 本次课程主要是使用 keepalived 实现高
可用部署,没有用到 LVS ,所以我们重点关注的是前两部分
global 全局部分:
global_defs {
# 通知邮件,当 keepalived 发送切换时需要发 email 给具体的邮箱
地址
  notification_email {
  }
  # 设置发件人的邮箱信息
  notification_email_from [email protected]
  # 指定 smpt 服务地址
  smtp_server 192.168.200.1
  # 指定 smpt 服务连接超时时间
  smtp_connect_timeout 30
  # 运行 keepalived 服务器的一个标识,可以用作发送邮件的主题信
  router_id LVS_DEVEL
  # 默认是不跳过检查。检查收到的 VRRP 通告中的所有地址可能会比较
耗时,设置此命令的意思是,如果通告与接收的上一个通告来自相同的
master 路由器,则不执行检查 ( 跳过检查 )
  vrrp_skip_check_adv_addr
  # 严格遵守 VRRP 协议。
  vrrp_strict
  # 在一个接口发送的两个免费 ARP 之间的延迟。可以精确到毫秒级。
默认是 0
  vrrp_garp_interval 0
  # 在一个网卡上每组 na 消息之间的延迟时间,默认为 0
  vrrp_gna_interval 0
  }
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-255
  priority 100 # 指定优先级,优先级高的将成为
MASTER
  advert_int 1 # 指定发送 VRRP 通告的间隔,单位是秒
  authentication { #vrrp 之间通信的认证信息
  auth_type PASS # 指定认证方式。 PASS 简单密码认证 (
)
  auth_pass 1111 # 指定认证使用的密码,最多 8
  }
  virtual_ipaddress { # 虚拟 IP 地址设置虚拟 IP 地址,供用户
访问使用,可设置多个,一行一个
  192.168.200.222
  }
  }
配置内容如下 :
服务器 1
global_defs {
  notification_email {
  }
notification_email_from [email protected]
  smtp_server 192.168.200.1
  smtp_connect_timeout 30
  router_id keepalived1
  vrrp_skip_check_adv_addr
  vrrp_strict
  vrrp_garp_interval 0
  vrrp_gna_interval 0
  }
  vrrp_instance VI_1 {
state MASTER
  interface ens33
virtual_router_id 51
  priority 100
  advert_int 1
  authentication {
  auth_type PASS
  auth_pass 1111
  }
  virtual_ipaddress {
  192.168.200.222
  }
  }
服务器 2
! Configuration File for keepalived
  global_defs {
  notification_email {
}
notification_email_from [email protected]
smtp_server 192.168.200.1
smtp_connect_timeout 30
router_id keepalived2
vrrp_skip_check_adv_addr
vrrp_strict
vrrp_garp_interval 0
vrrp_gna_interval 0
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.200.222
}
}
访问测试
1. 启动 keepalived 之前,咱们先使用命令 ip a , 查看 192.168.200.133
192.168.200.122 这两台服务器的 IP 情况。
Nginx(五)_第16张图片

2. 分别启动两台服务器的 keepalived
cd /usr/local/sbin
  ./keepalived
再次通过 ip a 查看 ip
Nginx(五)_第17张图片

3. 当把 192.168.200.133 服务器上的 keepalived 关闭后,再次查看 ip
Nginx(五)_第18张图片

通过上述的测试,我们会发现,虚拟 IP(VIP) 会在 MASTER 节点上,当
MASTER 节点上的 keepalived 出问题以后,因为 BACKUP 无法收到
MASTER 发出的 VRRP 状态通过信息,就会直接升为 MASTER VIP
" 漂移 " 到新的 MASTER
上面测试和 Nginx 有什么关系 ?
我们把 192.168.200.133 服务器的 keepalived 再次启动下,由于它的优先
级高于服务器 192.168.200.122 的,所有它会再次成为 MASTER VIP
" 漂移 " 过去,然后我们再次通过浏览器访问 :
http://192.168.200.222/
Nginx(五)_第19张图片

如果把 192.168.200.133 服务器的 keepalived 关闭掉,再次访问相同的地
Nginx(五)_第20张图片

效果实现了以后, 我们会发现要想让 vip 进行切换,就必须要把服务器上
keepalived 进行关闭,而什么时候关闭 keepalived ? 应该是在
keepalived 所在服务器的 nginx 出现问题后,把 keepalived 关闭掉,就可
以让 VIP 执行另外一台服务器,但是现在这所有的操作都是通过手动来完
成的,我们如何能让系统自动判断当前服务器的 nginx 是否正确启动,如
果没有,要能让 VIP 自动进行 " 漂移 " ,这个问题该如何解决 ?
keepalived vrrp_script
keepalived 只能做到对网络故障和 keepalived 本身的监控,即当出现网
络故障或者 keepalived 本身出现问题时,进行切换。但是这些还不够,
我们还需要监控 keepalived 所在服务器上的其他业务,比如 Nginx, 如果
Nginx 出现异常了,仅仅 keepalived 保持正常,是无法完成系统的正常
工作的,因此需要根据业务进程的运行状态决定是否需要进行主备切
换,这个时候,我们可以通过编写脚本对业务进程进行检测监控。
实现步骤 :
1. keepalived 配置文件中添加对应的配置像
vrrp_script 脚本名称
{
script " 脚本位置 "
interval 3 # 执行时间间隔
weight -20 # 动态调整 vrrp_instance 的优先级
}
2. 编写脚本
ck_nginx.sh
#!/bin/bash
  num=`ps -C nginx --no-header | wc -l`
  if [ $num -eq 0 ];then
  /usr/local/nginx/sbin/nginx
  sleep 2
if [ `ps -C nginx --no-header | wc -l` -eq 0 ]; then
  killall keepalived
  fi
  fi
Linux ps 命令用于显示当前进程 (process) 的状态。
-C(command) : 指定命令的所有进程
--no-header 排除标题
3. 为脚本文件设置权限
chmod 755 ck_nginx.sh
4. 将脚本添加到
vrrp_script ck_nginx {
  script "/etc/keepalived/ck_nginx.sh" # 执行脚本的位置
interval 2 # 执行脚本的周期,秒为单位
  weight -20 # 权重的计算方式
  }
  vrrp_instance VI_1 {
  state MASTER
  interface ens33
  virtual_router_id 10
  priority 100
  advert_int 1
authentication {
  auth_type PASS
  auth_pass 1111
  }
  virtual_ipaddress {
  192.168.200.111
  }
  track_script {
  ck_nginx
  }
  }
5. 如果效果没有出来,可以使用 tail - f /var/log/messages 查看日
志信息,找对应的错误信息。
6. 测试
问题思考 :
通常如果 master 服务死掉后 backup 会变成 master ,但是当 master 服务
又好了的时候 master 此时会抢占 VIP ,这样就会发生两次切换对业务繁
忙的网站来说是不好的。所以我们要在配置文件加入 nopreempt 非抢
占,但是这个参数只能用于 state backup ,故我们在用 HA 的时候最好
master backup state 都设置成 backup 让其通过 priority 来竞争。

二、Nginx制作下载站点

首先我们先要清楚什么是下载站点 ?
我们先来看一个网站 http://nginx.org/download/ 这个我们刚开始学
Nginx 的时候给大家看过这样的网站,该网站主要就是用来提供用户
来下载相关资源的网站,就叫做下载网站。
1583825943945
如何制作一个下载站点 :
nginx 使用的是模块 ngx_http_autoindex_module 来实现的,该模块处
理以斜杠 ("/") 结尾的请求,并生成目录列表。
nginx 编译的时候会自动加载该模块,但是该模块默认是关闭的,我们需
要使用下来指令来完成对应的配置
(1) autoindex: 启用或禁用目录列表输出
Nginx(五)_第21张图片

(2) autoindex_exact_size: 对应 HTLM 格式,指定是否在目录列表展示
文件的详细大小
默认为 on ,显示出文件的确切大小,单位是 bytes 。 改为 offff 后,显示出
文件的大概大小,单位是 kB 或者 MB 或者 GB
Nginx(五)_第22张图片

(3) autoindex_format :设置目录列表的格式
Nginx(五)_第23张图片

注意 : 该指令在 1.7.9 及以后版本中出现
(4) autoindex_localtime: 对应 HTML 格式,是否在目录列表上显示时
间。
默认为 off ,显示的文件时间为 GMT 时间。 改为 on 后,显示的文件时间
为文件的服务器时间
Nginx(五)_第24张图片

配置方式如下 :
location /download{
root /usr/local;
autoindex on;
autoindex_exact_size on;
autoindex_format html;
autoindex_localtime on;
}
XML/JSON 格式 [ 一般不用这两种方式 ]

三、Nginx的用户认证模块

对应系统资源的访问,我们往往需要限制谁能访问,谁不能访问。这块
就是我们通常所说的认证部分,认证需要做的就是根据用户输入的用户
名和密码来判定用户是否为合法用户,如果是则放行访问,如果不是则
拒绝访问。
Nginx 对应用户认证这块是通过 ngx_http_auth_basic_module 模块来实
现的,它允许通过使用 "HTTP 基本身份验证 " 协议验证用户名和密码来限
制对资源的访问。默认情况下 nginx 是已经安装了该模块,如果不需要则
使用 --without-http_auth_basic_module
该模块的指令比较简单,
(1) auth_basic: 使用 “ HTTP 基本认证 协议启用用户名和密码的验证
Nginx(五)_第25张图片

开启后,服务端会返回 401 ,指定的字符串会返回到客户端,给用户以
提示信息,但是不同的浏览器对内容的展示不一致。
(2) auth_basic_user_fifile: 指定用户名和密码所在文件
Nginx(五)_第26张图片

指定文件路径,该文件中的用户名和密码的设置,密码需要进行加密。
可以采用工具自动生成
实现步骤 :
1.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;
}
2. 我们需要使用 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 //
验证用户名和密码是否正确
上述方式虽然能实现用户名和密码的验证,但是大家也看到了,所有的
用户名和密码信息都记录在文件里面,如果用户量过大的话,这种方式
就显得有点麻烦了,这时候我们就得通过后台业务代码来进行用户权限
的校验了。

四、Nginx的扩展模块

Nginx 是可扩展的,可用于处理各种使用场景。本节中,我们将探讨使
Lua 扩展 Nginx 的功能。

4.1Lua

概念

Lua 是一种轻量、小巧的脚本语言,用标准 C 语言编写并以源代码形式开
发。设计的目的是为了嵌入到其他应用程序中,从而为应用程序提供灵
活的扩展和定制功能。
特性
跟其他语言进行比较, Lua 有其自身的特点:
(1)轻量级
Lua 用标准 C 语言编写并以源代码形式开发,编译后仅仅一百余千字节,可
以很方便的嵌入到其他程序中。
(2)可扩展
Lua 提供非常丰富易于使用的扩展接口和机制,由宿主语言 ( 通常是 C
C++) 提供功能, Lua 可以使用它们,就像内置的功能一样。
(3)支持面向过程编程和函数式编程
应用场景
Lua 在不同的系统中得到大量应用,场景的应用场景如下 :
游戏开发、独立应用脚本、 web 应用脚本、扩展和数据库插件、系统安
全上。
Lua 的安装
linux 上安装 Lua 非常简单,只需要下载源码包并在终端解压、编译即
可使用。
Lua 的官网地址为 : https://www.lua.org
Nginx(五)_第27张图片

1. 点击 download 可以找到对应版本的下载地址,我们本次课程采用的
lua-5.3.5, 其对应的资源链接地址为 https://www.lua.org/ftp/lua-5.
4.1.tar.gz, 也可以使用 wget 命令直接下载 :
wget https://www.lua.org/ftp/lua-5.4.1.tar.gz
2. 编译安装
cd lua-5.4.1
  make linux test
  make install
如果在执行 make linux test 失败,报如下错误 :

说明当前系统缺少 libreadline-dev 依赖包,需要通过命令来进行安装
yum install -y readline-devel
验证是否安装成功
lua -v
Lua 的语法
Lua C/C++ 语法非常相似,整体上比较清晰,简洁。条件语句、循环语
句、函数调用都与 C/C++ 基本一致。如果对 C/C++ 不太熟悉的同学来说,
也没关系,因为天下语言是一家,基本上理解起来都不会太困难。我们
一点点来讲。
第一个 Lua 程序
大家需要知道的是, Lua 有两种交互方式,分别是 : 交互式和脚本式,这
两者的区别,下面我们分别来讲解下:
交互式之 HELLOWORLD
交互式是指可以在命令行输入程序,然后回车就可以看到运行的效果。
Lua 交互式编程模式可以通过命令 lua -i lua 来启用 :

在命令行中 key 输入如下命令,并按回车 , 会有输出在控制台:

脚本式之 HELLOWORLD
脚本式是将代码保存到一个以 lua 为扩展名的文件中并执行的方式。
方式一 :
我们需要一个文件名为 hello.lua, 在文件中添加要执行的代码,然后通过
命令 lua hello.lua 来执行,会在控制台输出对应的结果。
hello.lua
print("Hello World!!")

方式二 :
hello.lua 做如下修改
#!/usr/local/bin/lua
  print("Hello World!!!")
第一行用来指定 Lua 解释器所在位置为 /usr/local/bin/lua ,加上 # 号标记
解释器会忽略它。一般情况下 #! 就是用来指定用哪个程序来运行本文
件。但是 hello.lua 并不是一个可执行文件,需要通过 chmod 来设置可执
行权限,最简单的方式为 :
chmod 755 hello.lua
然后执行该文件
./hello.lua

补充一点,如果想在交互式中运行脚本式的 hello.lua 中的内容,我们可
以使用一个 dofile 函数,如:
dofile("lua_demo/hello.lua")
注意 : Lua 语言中,连续语句之间的分隔符并不是必须的,也就是说后
面不需要加分号,当然加上也不会报错,
Lua 语言中,表达式之间的换行也起不到任何作用。如以下四个写
法,其实都是等效的
写法一
a=1
b=a+2
写法二
a=1;
b=a+2;
写法三
a=1; b=a+2;
写法四
a=1 b=a+2
不建议使用第四种方式,可读性太差。
Lua 的注释
关于 Lua 的注释要分两种,第一种是单行注释,第二种是多行注释。
单行注释的语法为:
-- 注释内容
多行注释的语法为 :
--[[
注释内容
注释内容
--]]
如果想取消多行注释,只需要在第一个 -- 之前在加一个 - 即可,如:
---[[
注释内容
注释内容
--]]
标识符
换句话说标识符就是我们的变量名, Lua 定义变量名以一个字母 A Z
a z 或下划线 _ 开头后加上 0 个或多个字母,下划线,数字(0到 9)。这块建议大家最好不要使用下划线加大写字母的标识符,因为 Lua 的保留字也是这样定义的,容易发生冲突。注意Lua 是区分大小写字母 的。
A0
关键字
下列是 Lua 的关键字,大家在定义常量、变量或其他用户自定义标识符
都要避免使用以下这些关键字:
Nginx(五)_第28张图片

一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION
被保留用于 Lua 内部全局变量。这个也是上面我们不建议这么定义标识
符的原因。
运算符
Lua 中支持的运算符有算术运算符、关系运算符、逻辑运算符、其他运
算符。
算术运算符 :
+ 加法
- 减法
* 乘法
/ 除法
% 取余
^ 乘幂
- 负号
例如 :
10+20 -->30
20-10 -->10
10*20 -->200
20/10 -->2
3%2 -->1
10^2 -->100
-10 -->-10
关系运算符
== 等于
~= 不等于
> 大于
  < 小于
  >= 大于等于
<= 小于等于
例如 :
10==10 -->true
10~=10 -->false
  20>10 -->true
  20<10 -->false
  20>=10 -->true
  20<=10 -->false
逻辑运算符
and 逻辑与 A and B &&
  or 逻辑或 A or B ||
not 逻辑非 取反,如果为 true, 则返回 false !
逻辑运算符可以作为 if 的判断条件,返回的结果如下 :
A = true
  B = true
  A and B -->true
  A or B -->true
not A -->false
  A = true
B = false
A and B -->false
A or B -->true
not A -->false
A = false
B = true
A and B -->false
A or B -->true
not A -->true
其他运算符
.. 连接两个字符串
# 一元预算法,返回字符串或表的长度
例如 :
> "HELLO ".."WORLD" -->HELLO WORLD
> #"HELLO" -->5
全局变量 & 局部变量
Lua 语言中,全局变量无须声明即可使用。在默认情况下,变量总是
认为是全局的,如果未提前赋值,默认为 nil:

要想声明一个局部变量,需要使用 local 来声明

Lua 数据类型
Lua 8 个数据类型
nil( 空,无效值 )
boolean( 布尔, true/false)
number( 数值 )
string( 字符串 )
function( 函数 )
table (表)
thread( 线程 )
userdata (用户数据)
可以使用 type 函数测试给定变量或者的类型:
print(type(nil)) -->nil
print(type(true)) --> boolean
print(type(1.1*1.1)) --> number
print(type("Hello world")) --> string
print(type(io.stdin)) -->userdata
print(type(print)) --> function
print(type(type)) -->function
print(type{}) -->table
print(type(type(X))) --> string
nil
nil 是一种只有一个 nil 值的类型,它的作用可以用来与其他所有值进行区
分,也可以当想要移除一个变量时,只需要将该变量名赋值为 nil, 垃圾回
收就会会释放该变量所占用的内存。
boolean
boolean 类型具有两个值, true false boolean 类型一般被用来做条
件判断的真与假。在 Lua 语言中,只会将 false nil 视为假,其他的都视
为真,特别是在条件检测中 0 和空字符串都会认为是真,这个和我们熟悉
的大多数语言不太一样。
number
Lua5.3 版本开始, Lua 语言为数值格式提供了两种选择 :integer( 整型 )
float( 双精度浮点型 )[ 和其他语言不太一样, float 不代表单精度类型 ]
数值常量的表示方式 :
>4 -->4
>0.4 -->0.4
>4.75e-3 -->0.00475
>4.75e3 -->4750
不管是整型还是双精度浮点型,使用 type() 函数来取其类型,都会返回的
number
>type(3) -->number
>type(3.3) -->number
所以它们之间是可以相互转换的,同时,具有相同算术值的整型值和浮
点型值在 Lua 语言中是相等的
string
Lua 语言中的字符串即可以表示单个字符,也可以表示一整本书籍。在
Lua 语言中,操作 100K 或者 1M 个字母组成的字符串的程序很常见。
可以使用单引号或双引号来声明字符串
>a = "hello"
>b = 'world'
>print(a) -->hello
>print(b) -->world
如果声明的字符串比较长或者有多行,则可以使用如下方式进行声明
html = [[
Lua-string
Lua
]]
table
table Lua 语言中最主要和强大的数据结构。使用表, Lua 语言可以以
一种简单、统一且高效的方式表示数组、集合、记录和其他很多数据结
构。 Lua 语言中的表本质上是一种辅助数组。这种数组比 Java 中的数组
更加灵活,可以使用数值做索引,也可以使用字符串或其他任意类型的
值作索引 ( nil )
创建表的最简单方式 :
  > a = {}
创建数组 :
我们都知道数组就是相同数据类型的元素按照一定顺序排列的集合,那
么使用 table 如何创建一个数组呢 ?
  >arr = {"TOM","JERRY","ROSE"}
要想获取数组中的值,我们可以通过如下内容来获取 :
print(arr[0]) nil
  print(arr[1]) TOM
  print(arr[2]) JERRY
  print(arr[3]) ROSE
从上面的结果可以看出来,数组的下标默认是从 1 开始的。所以上述创建
数组,也可以通过如下方式来创建
  >arr = {}
  >arr[1] = "TOM"
>arr[2] = "JERRY"
  >arr[3] = "ROSE"
上面我们说过了,表的索引即可以是数字,也可以是字符串等其他的内
容,所以我们也可以将索引更改为字符串来创建
>arr = {}
>arr["X"] = 10
  >arr["Y"] = 20
  >arr["Z"] = 30
当然,如果想要获取这些数组中的值,可以使用下面的方式
方式一
>print(arr["X"])
>print(arr["Y"])
>print(arr["Z"])
方式二
>print(arr.X)
>print(arr.Y)
>print(arr.Z)
当前 table 的灵活不进于此,还有更灵活的声明方式
>arr = {"TOM",X=10,"JERRY",Y=20,"ROSE",Z=30}
如何获取上面的值 ?
TOM : arr[1]
10 : arr["X"] | arr.X
JERRY: arr[2]
20 : arr["Y"] | arr.Y
ROESE?
function
Lua 语言中,函数( Function )是对语句和表达式进行抽象的主要方
式。
定义函数的语法为 :
function functionName(params)
end
函数被调用的时候,传入的参数个数与定义函数时使用的参数个数不一
致的时候, Lua 语言会通过 抛弃多余参数和将不足的参数设为 nil 的方
式来调整参数的个数。
function f(a,b)
  print(a,b)
  end
  f() --> nil nil
  f(2) --> 2 nil
  f(2,6) --> 2 6
  f(2.6.8) --> 2 6 (8 被丢弃 )
可变长参数函数
function add(...)
  a,b,c=...
  print(a)
  print(b)
  print(c)
  end
  add(1,2,3) --> 1 2 3
函数返回值可以有多个,这点和 Java 不太一样
function f(a,b)
  return a,b
  end
  x,y=f(11,22) --> x=11,y=22
thread
thread 翻译过来是线程的意思,在 Lua 中, thread 用来表示执行的独立
线路,用来执行协同程序。
userdata
userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++
言库所创建的类型。
Lua 控制结构
Lua 语言提供了一组精简且常用的控制结构,包括用于条件执行的证 以 及用于循环的 while、 repeat for 。 所有的控制结构语法上都有一个 显式的终结符: end 用于终结 if for while 结构, until 用于终结 repeat 结构。
if then elseif else
if 语句先测试其条件,并根据条件是否满足执行相应的 then 部分或 else
部分。 else 部分 是可选的。
function testif(a)
  if a>0 then
  print("a 是正数 ")
  end
  end
  function testif(a)
  if a>0 then
  print("a 是正数 ")
  else
  print("a 是负数 ")
  end
  end
如果要编写嵌套的 if 语句,可以使用 elseif 。 它类似于在 else 后面紧跟
一个 if 。根据传入的年龄返回不同的结果,如
age<=18 青少年,
  age>18 , age <=45 青年
  age>45 , age<=60 中年人
  age>60 老年人
  function show(age)
  if age<=18 then
  return " 青少年 "
  elseif age>18 and age<=45 then
  return " 青年 "
  elseif age>45 and age<=60 then
  return " 中年人 "
  elseif age>60 then
  return " 老年人 "
  end
end
while 循环
顾名思义,当条件为真时 while 循环会重复执行其循环体。 Lua 语言先
测试 while 语句 的条件,若条件为假则循环结束;否则, Lua 会执行循
环体并不断地重复这个过程。
语法:
while 条件 do
循环体
end
例子 : 实现数组的循环
function testWhile()
local i = 1
while i<=10 do
print(i)
i=i+1
end
end
repeat 循环
顾名思义, repeat-until 语句会重复执行其循环体直到条件为真时结
束。 由于条件测试在循环体之后执行,所以循环体至少会执行一次。
语法
repeat
循环体
until 条件
function testRepeat()
local i = 10
repeat
print(i)
i=i-1
until i < 1
end
for 循环
数值型 for 循环
语法
for param=exp1,exp2,exp3 do
循环体
end
param 的值从 exp1 变化到 exp2 之前的每次循环会执行 循环体,并在每
次循环结束后将步长 (step)exp3 增加到 param 上。 exp3 可选,如果不设
置默认为 1
for i = 1,100,10 do
print(i)
end
泛型 for 循环
泛型 for 循环通过一个迭代器函数来遍历所有值,类似于 java 中的
foreach 语句。
语法
for i,v in ipairs(x) do
  循环体
  end
i 是数组索引值, v 是对应索引的数组元素值, ipairs Lua 提供的一个迭
代器函数,用来迭代数组, x 是要遍历的数组。
例如 :
arr = {"TOME","JERRY","ROWS","LUCY"}
  for i,v in ipairs(arr) do
  print(i,v)
end
上述实例输出的结果为
1 TOM
2 JERRY
3 ROWS
4 LUCY
但是如果将 arr 的值进行修改为
arr = {"TOME","JERRY","ROWS",x="JACK","LUCY"}
同样的代码在执行的时候,就只能看到和之前一样的结果,而其中的 x
JACK 就无法遍历出来,缺失了数据,如果解决呢 ?
我们可以将迭代器函数变成 pairs,
for i,v in pairs(arr) do
print(i,v)
end
上述实例就输出的结果为
1 TOM
2 JERRY
3 ROWS
4 LUCY
x JACK

4.2ngx_lua模块概念

淘宝开发的 ngx_lua 模块通过将 lua 解释器集成进 Nginx ,可以采用 lua
本实现业务逻辑,由于 lua 的紧凑、快速以及内建协程,所以在保证高并
发服务能力的同时极大地降低了业务逻辑实现成本。

4.3ngx_lua模块环境准备

方式一:lua-nginx-module

1. LuaJIT是采用C语言编写的Lua代表的解释器。

官网地址为 : http://luajit.org/
在官网上找到对应的下载地址 : http://luajit.org/download/LuaJIT-2.0.5.t
ar.gz
方式二 :OpenRestry
概述
前面我们提到过, OpenResty 是由淘宝工程师开发的,所以其官方网站
( http://openresty.org/ ) 我们读起来是非常的方便。 OpenResty 是一个基
Nginx Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua
库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并
发、扩展性极高的动态 Web 应用、 Web 服务和动态网关。所以本身
OpenResty 内部就已经集成了 Nginx Lua ,所以我们使用起来会更加方
便。

4.4ngx_lua的使用

使用Lua编写Nginx脚本的基本构建块是指令。指令用于指定何时运行用

Lua 代码以及如何使用结果。下图显示了执行指令的顺序。

4.5ngx_lua操作Redis

Redis 在系统中经常作为数据缓存、内存数据库使用,在大型系统中扮演
着非常重要的作用。在 Nginx 核心系统中, Redis 是常备组件。 Nginx
3 种方法访问 Redis, 分别是 HttpRedis 模块、 HttpRedis2Module lua
resty-redis 库。这三种方式中 HttpRedis 模块提供的指令少,功能单一,
适合做简单缓存, HttpRedis2Module 模块比 HttpRedis 模块操作更灵
活,功能更强大。而 Lua-resty-redis 库是 OpenResty 提供的一个操作
Redis 的接口库,可根据自己的业务情况做一些逻辑处理,适合做复杂的
业务逻辑。所以本次课程将主要以 Lua-resty-redis 来进行讲解。
lua-resty-redis 环境准备
步骤一 : 准备一个 Redis 环境

4.6ngx_lua操作Mysql

MySQL 是一个使用广泛的关系型数据库。在 ngx_lua 中, MySQL 有两种
访问模式 , 分别是使
(1)用 ngx_lua 模块和 lua-resty-mysql 模块:这两个模块是安装
OpenResty 时默认安装的。
(2)使用 drizzle_nginx_module(HttpDrizzleModule) 模块:需要单独
安装,这个库现不在 OpenResty 中。
lua-resty-mysql
lua-resty-mysql OpenResty 开发的模块,使用灵活、功能强大,适合
复杂的业务场景,同时支持存储过程的访问。
使用 lua-resty-mysql 实现数据库的查询

使用 lua-cjson 处理查询结果
通过上述的案例学习, read_result() 得到的结果 res 都是 table 类型,要想
在页面上展示,就必须知道 table 的具体数据结构才能进行遍历获取。处
理起来比较麻烦,接下来我们介绍一种简单方式 cjson ,使用它就可以将
table 类型的数据转换成 json 字符串,把 json 字符串展示在页面上即可。
具体如何使用 ?
步骤一:引入 cjson
lua-resty-mysql 实现数据库的增删改
优化 send_query read_result
本方法是 send_query read_result 组合的快捷方法。
语法 :
有了该 API ,上面的代码我们就可以进行对应的优化,如下 :
综合小案例
使用 ngx_lua 模块完成 Redis 缓存预热。
分析 :
(1)先得有一张表 (users)
(2)浏览器输入如下地址
http://191.168.200.133?username=TOM
(3)从表中查询出符合条件的记录,此时获取的结果为 table 类型
(4)使用 cjson table 数据转换成 json 字符串
(5)将查询的结果数据存入 Redis
init_by_lua_block{
  redis = require "resty.redis"
  mysql = require "resty.mysql"
  cjson = require "cjson"
  }
  location /{
  default_type "text/html";
  content_by_lua_block{
  -- 获取请求的参数 username
  local 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 then
  ngx.say("failed connect to
mysql:",err)
  return
  end
  -- 设置连接超时时间
db:set_timeout(1000)
  -- 查询数据
  local sql = "";
  if not param then 31 sql="select * from users"
  else
  sql="select * from users where
username=".."'"..param.."'"
  end
  local
res,err,errcode,sqlstate=db:query(sql)
  if not res then
  ngx.say("failed to query from
mysql:",err)
return
  end
  -- 连接 redis
  local rd = redis:new()
  ok,err =
rd:connect("192.168.200.111",6379)
  if not ok then
  ngx.say("failed to connect to
redis:",err)
return
  end
  rd:set_timeout(1000)
  -- 循环遍历数据
  for i,v in ipairs(res) do
rd:set("user_"..v.username,cjson.encode(v))
  end
  ngx.say("success")
  rd:close()
  db:close()
  }
  }

你可能感兴趣的:(java,#,运维,nginx,服务器,运维)