记录学习笔记、分享资源工具、交流技术思想、提升工作效率

openresty到底是什么

运维 xiaomudk 1年前 (2020-03-24) 613次浏览 0个评论
文章目录[隐藏]

Resty 入门

简介

是一个基于 Nginx 与 Lua 的高性能 Web 平台,汇聚各种设计精良的 Nginx 模块,从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。
这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。

核心特性

  • 非阻塞IO模型
  • 集成Lua的开发环境,通过Lua扩展,使Nginx变成了一个web平台(框架)
  • 集成众多的Nginx模块

集成组件

集成组件
第三方组件 , 可以使用opm进行安装

目标

  • web服务
  • API网关
  • 负载均衡

Nginx 简介

为Web服务器而生, 为了解决C10K问题,其核心设计思想是基于事件的处理机制。
网上的架构图:

主要包括了Master和Worker。

Master:
Nginx的主进程,用来管理worker,充当与用户交互的接口。
不处理网络事件

Worker:
Nginx的工作进程

辅助进程(helper process):
缓存辅助进程

模块化

模块化的设计,使得Nginx很容易进行扩展。
常用模块

 1、ngx_http_core_module                #包括一些核心的http参数配置,对应Nginx的配置为HTTP区块部分
2、ngx_http_access_module              #访问控制模块,用来控制网站用户对Nginx的访问
3、ngx_http_gzip_module                #压缩模块,对Nginx返回的数据压缩,属于性能优化模块
4、ngx_http_fastcgi_module              #FastCGI模块,和 动态应用相关的模块,例如PHP
5、ngx_http_proxy_module                #Proxy代理模块
6、ngx_http_upstream_module             #负载均衡模块,可以实现网站的负载均衡功能及节点的健康检查
7、ngx_http_rewrite_module               #URL地址重写模块
8、ngx_http_limit_conn_module            #限制用户并发连接数及请求数模块
9、ngx_http_limit_req_module             #根据定义的key限制Nginx请求过程的速率
10、ngx_http_log_module                  #访问日志模块,以指定的格式记录Nginx客户访问日志等信息
11、ngx_http_auth_basic_module           #Web认证模块,设置Web用户通过账号、密码访问Nginx
12、ngx_http_ssl_module                   #ssl模块,用于加密的http连接,如https
13、ngx_http_stub_status_module         #记录Nginx基本访问状态信息等模块

处理阶段

每个请求对应Nginx相应11个处理阶段。

  1. post-read:读取请求内容
  2. server-rewrite: Server请求地址重写
  3. find-config:配置查找,不支持模块注册程序, 由Nginx核心完成当前请求与location配置块之间的配对工作
  4. rewrite:Location地址重写
  5. post-rewrite:请求地址重写提交阶段,
  6. preaccess:访问权限检查准备阶段,ngx_limit_req 控制请求的访问额度,ngx_limit_zone限制访问并发
  7. access:访问权限检查
    ngx_access, ngx_auth_request, ngx_lua, access_by_lua运行在该阶段
  8. post-access:访问权限检查提交, 配合access阶段实现标准,提供satify的功能。 satisfy all , satisfy any。
  9. try-files:配置项 try_files 的功能
  10. content:产生内容并输出HTTP相应, 是交互最多的阶段。
  11. log: 日志处理阶段,处理日志的输出

【待补充】

OpenRestry

应用场景:

  1. 高性能Web服务器
  2. 通过Lua扩展,构建web网关和应用防火墙(WAF)

最佳实践

部署安装

安装文档参考官网

内置模块

我本机下载的包中包括的模块

Hello World

作为一个直观的认识,以下是hello world的配置

worker_processes  1;        #nginx worker 数量
error_log logs/error.log;   #指定错误日志文件路径
events {
    worker_connections 1024;
}

http {
    server {
        #监听端口,若你的8000端口已经被占用,则需要修改
        listen 8000;
        location / {
            default_type text/html;

            content_by_lua_block {
                ngx.say("HelloWorld")
            }
        }
    }
}

content_by_lua_block , 执行lua的程序代码
content_by_lua_file /path/to/content.lua; 执行代码文件

Lua API

Nginx Lua API
通过该模块,用户可以编写Lua脚本与Nginx交互,处理客户端的请求。

注意: 代码中的I/O请求, 一定要使用Nginx Lua的API进行处理,因为所有API的处理方式为非阻塞调用, Lua官方标准的一些io库中,为阻塞的方式。

以下列出一些基本的API,给大家有个直观的认识

基本API

ngx.arg[index]:
上下文:set_by_lua , body_filter_by_lua
描述: 获取参数

ngx.null 表示nil值

ngx.var.VAR : 变量定义

常量:

  • ngx.OK(0)
  • ngx.ERROR(-1)
  • ngx.AGAIN(-2)
  • ngx.DONE(-4)
  • ngx.DECLINED(-5)


  • ngx.HTTP_GET
  • ngx.HTTP_HEAD
  • ngx.HTTP_PUT
  • ngx.HTTP_POST
  • ngx.HTTP_DELETE
  • ngx.HTTP_OPTIONS (added in the v0.5.0rc24 release)
  • ngx.HTTP_MKCOL (added in the v0.8.2 release)
  • ngx.HTTP_COPY (added in the v0.8.2 release)
  • ngx.HTTP_MOVE (added in the v0.8.2 release)
  • ngx.HTTP_PROPFIND (added in the v0.8.2 release)
  • ngx.HTTP_PROPPATCH (added in the v0.8.2 release)
  • ngx.HTTP_LOCK (added in the v0.8.2 release)
  • ngx.HTTP_UNLOCK (added in the v0.8.2 release)
  • ngx.HTTP_PATCH (added in the v0.8.2 release)
  • ngx.HTTP_TRACE (added in the v0.8.2 release)

  • value = ngx.HTTP_CONTINUE (100) (first added in the v0.9.20 release)
  • value = ngx.HTTP_SWITCHING_PROTOCOLS (101) (first added in the v0.9.20 release)
  • value = ngx.HTTP_OK (200)
  • value = ngx.HTTP_CREATED (201)
  • value = ngx.HTTP_ACCEPTED (202) (first added in the v0.9.20 release)
  • value = ngx.HTTP_NO_CONTENT (204) (first added in the v0.9.20 release)
  • value = ngx.HTTP_PARTIAL_CONTENT (206) (first added in the v0.9.20 release)
  • value = ngx.HTTP_SPECIAL_RESPONSE (300)
  • value = ngx.HTTP_MOVED_PERMANENTLY (301)
  • value = ngx.HTTP_MOVED_TEMPORARILY (302)
  • value = ngx.HTTP_SEE_OTHER (303)
  • value = ngx.HTTP_NOT_MODIFIED (304)
  • value = ngx.HTTP_TEMPORARY_REDIRECT (307) (first added in the v0.9.20 release)
  • value = ngx.HTTP_BAD_REQUEST (400)
  • value = ngx.HTTP_UNAUTHORIZED (401)
  • value = ngx.HTTP_PAYMENT_REQUIRED (402) (first added in the v0.9.20 release)
  • value = ngx.HTTP_FORBIDDEN (403)
  • value = ngx.HTTP_NOT_FOUND (404)
  • value = ngx.HTTP_NOT_ALLOWED (405)
  • value = ngx.HTTP_NOT_ACCEPTABLE (406) (first added in the v0.9.20 release)
  • value = ngx.HTTP_REQUEST_TIMEOUT (408) (first added in the v0.9.20 release)
  • value = ngx.HTTP_CONFLICT (409) (first added in the v0.9.20 release)
  • value = ngx.HTTP_GONE (410)
  • value = ngx.HTTP_UPGRADE_REQUIRED (426) (first added in the v0.9.20 release)
  • value = ngx.HTTP_TOO_MANY_REQUESTS (429) (first added in the v0.9.20 release)
  • value = ngx.HTTP_CLOSE (444) (first added in the v0.9.20 release)
  • value = ngx.HTTP_ILLEGAL (451) (first added in the v0.9.20 release)
  • value = ngx.HTTP_INTERNAL_SERVER_ERROR (500)
  • value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501)
  • value = ngx.HTTP_BAD_GATEWAY (502) (first added in the v0.9.20 release)
  • value = ngx.HTTP_SERVICE_UNAVAILABLE (503)
  • value = ngx.HTTP_GATEWAY_TIMEOUT (504) (first added in the v0.3.1rc38 release)
  • value = ngx.HTTP_VERSION_NOT_SUPPORTED (505) (first added in the v0.9.20 release)
  • value = ngx.HTTP_INSUFFICIENT_STORAGE (507) (first added in the v0.9.20 release)

  • ngx.STDERR
  • ngx.EMERG
  • ngx.ALERT
  • ngx.CRIT
  • ngx.ERR
  • ngx.WARN
  • ngx.NOTICE
  • ngx.INFO
  • ngx.DEBUG

ngx.ctx: 表示每个请求的上下文数据

lacation

funciton 含义 备注
ngx.req.get_uri_args 获取参数
ngx.req.read_body 读取body
ngx.req.get_post_args 获取post参数
ngx.location.capture
ngx.location.capture_multi 子请求 local res1, res2 = ngx.location.capture_multi({ {"/sum", {args={a=3, b=8}}}
ngx.exec 内部跳转
ngx.redirect 外部重定向
ngx.encode_args 转义
ngx.req.get_body_data 获取body lua_need_request_body on;
ngx.req.read_body 非全局获取body
ngx.req.get_body_file 已存入临时文件时,通过该方法获取

内置变量

变量列表
引用变量

API Server

OpenResty的主要目的之一,是用来作为API的一个server,通过lua脚本对基础功能进行扩充,使之具备限流,熔断,降级的功能

简单的API 框架

抄自Resty最佳实践

实现一个最最简单的数学计算:加、减、乘、除,给大家演示如何搭建简单的 API Server。

nginx conf 的例子

worker_processes  1;        #nginx worker 数量
error_log logs/error.log;   #指定错误日志文件路径
events {
    worker_connections 1024;
}
http {
    server {
        listen 80;

        # 在代码路径中使用nginx变量
        # 注意: nginx var 的变量一定要谨慎,否则将会带来非常大的风险
        location ~ ^/api/([-_a-zA-Z0-9/]+) {
            access_by_lua_file  lua/access_check.lua;
            content_by_lua_file lua/$1.lua;
        }
    }
}

lua 脚本例子

--========== {$prefix}/lua/comm/param.lua
local _M = {}

-- 对输入参数逐个进行校验,只要有一个不是数字类型,则返回 false
function _M.is_number(...)
    local arg = {...}

    local num
    for _,v in ipairs(arg) do
        num = tonumber(v)
        if nil == num then
            return false
        end
    end

    return true
end

return _M

--========== {$prefix}/lua/access_check.lua
local param= require("comm.param")
local args = ngx.req.get_uri_args()

if not args.a or not args.b or not param.is_number(args.a, args.b) then
    ngx.exit(ngx.HTTP_BAD_REQUEST)
    return
end

子查询

子查询是常用的功能,可以发起非阻塞的内部请求访问目标Location。

注意:
子查询与HTTP 301/302 完全不同。
子查询与内部的重定向(ngx.exec)完全不同。
子查询只是模拟HTTP接口的形式,没有额外的HTTP/TCP流量,也没有IPC 调用,所有工作都是在C语言的内部完成。

--发起一个请求
res = ngx.location.capture('/foo/bar?a=3&b=4')

--发起一个POST请求
 res = ngx.location.capture(
     '/foo/bar',
     { method = ngx.HTTP_POST, body = 'hello, world' }
 )
--arg可以附带参数
 ngx.location.capture('/foo?a=1',
     { args = { b = 3, c = ':' } }
 )
 --等同于
  ngx.location.capture('/foo?a=1&b=3&c=%3a')
 --或者
  ngx.location.capture('/foo?a=1',
     { args = 'b=3&c=%3a' } }
 )

HTTP 调用

引用 resty.http 库资源,它来自 github
参考 resty-http 官方 wiki 说明,我们可以知道 request_uri 函数完成了连接池、HTTP 请求等一系列动作。

题外话,为什么这么简单的方法我们还要求助外部开源组件呢?其实我也觉得这个功能太基础了,真的应该集成到 OpenResty 官方包里面,只不过目前官方默认包里还没有。

如果你的内部请求比较少,使用 ngx.location.capture+proxy_pass 的方式还没什么问题。但如果你的请求数量比较多,或者需要频繁的修改上游地址,那么 resty.http就更适合你。

示例:

location /test {
            content_by_lua_block {
                ngx.req.read_body()
                local args, err = ngx.req.get_uri_args()

                -- http库
                local http = require "resty.http"
                local httpc = http.new()
                local res, err = httpc:request_uri(
                    "http://127.0.0.1:81/spe_md5",
                        {
                        method = "POST",
                        body = args.data,
                      }
                )

                if 200 ~= res.status then
                    ngx.exit(res.status)
                end

                if args.key == res.body then
                    ngx.say("valid request")
                else
                    ngx.say("invalid request")
                end
            }

连接池

从我们应用最多的 HTTP 连接、数据库连接、消息推送、日志存储等,所有点到点之间,都需要花样繁多的各色连接。为了传输数据,我们需要完成创建连接、收发数据、拆除连接。对并发量不高的场景,我们为每个请求都完整走这三步(短连接),开发工作基本只考虑业务即可,基本上也不会有什么问题。一旦挪到高并发应用场景,那么可能我们就要郁闷了。

思考:为什么要使用连接池?
如果没有连接池,你将会碰到下面几个常见问题:

    性能普遍上不去
    CPU 大量资源被系统消耗
    网络一旦抖动,会有大量 TIME_WAIT 产生,不得不定期重启服务或定期重启机器
    服务器工作不稳定,QPS 忽高忽低

Resty在使用连接池时,如果不注意,很容易产生数据奇怪的错误。
通过使用短连接可以规避该问题, 但当高并发的场景,每次都为一个请求建立连接,是对企业内部防火墙巨大的考验。

在 OpenResty 中,所有具备 set_keepalive 的类、库函数,说明他都是支持连接池的。

在连接memcache时,如果没有设置keepalive,使用的就是短连接。
resty可以通过设置keepalive, 将已经建立的正确连接放入连接池;
此处需要注意的是,需要把已建立的连接放入池子,千万不要把失败的连接也放进去。

示例

local redis = require "resty.redis"
local red = redis:new()
local ok, err = red:connect("192.168.1.123", 6379)
if not ok then
        ngx.log(ngx.ERR, err)
        return
end
red:set_keepalive(10000, 100)

cache

srcache
【待补充】

缓存失效

缓存失效的瞬间, 请求会打向数据库,dop-pile effect;
使用lua-resty-lock来解决缓存失效问题

HTTPS

【待补充】
参考

灰度发布

【待补充】
参考

限流

【待补充】
参考

Kong

一个基于OpenResty的网关,有开源版本,有企业版本。


本网站采用知识共享署名-相同方式共享 4.0 国际许可协议进行授权
转载请注明原文链接:openresty到底是什么
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址