OpenResty简介及学习笔记

OpenResty简介及学习笔记

  • 摘要
  • 简介
  • 一、OpenResty综述
  • 二、指令说明:
    • *_by_lua
    • *_by_lua_block {lua_script}
    • *_by_lua_file
  • 三、登陆验证
    • IP防火墙
    • 操作头信息检测
    • 登陆缓存
    • 过滤参数
  • 四、输出过滤
    • header_filter_by_lua*
    • body_filter_by_lua*
  • 五、Redis

摘要

OpenResty系列文章之一。

前些日子做的一些研究,期间收获了一些对web后台应用的理解,nginx运行机理的部分理解。

本篇包括简介、部分模块的介绍。

简介

logo

OpenResty (也称为 ngx_openresty)是一个全功能的 Web 应用服务器。它打包了标准的 Nginx 核心,很多的常用的第三方模块,以及它们的大多数依赖项。

enter description here

通过众多进行良好设计的 Nginx 模块,OpenResty 有效地把 Nginx 服务器转变为一个强大的 Web 应用服务器,基于它开发人员可以使用Lua 编程语言对 Nginx 核心以及现有的各种Nginx C 模块进行脚本编程,构建出可以处理一万以上并发请求的极端高性能的 Web 应用。

OpenResty 致力于将你的服务器端应用完全运行于 Nginx 服务器中,充分利用 Nginx 的事件模型来进行非阻塞 I/O 通信。不仅仅是和 HTTP 客户端间的网络通信是非阻塞的,与MySQL、PostgreSQL、Memcached、以及 Redis 等众多远方后端之间的网络通信也是非阻塞的。

因为 OpenResty 软件包的维护者也是其中打包的许多 Nginx 模块的作者,所以 OpenResty 可以确保所包含的所有组件可以可靠地协同工作。

作者简介:

agentzh,本名章亦春,现任 CloudFare 系统工程师,主要是 Nginx 和 OpenResty 开发,是一名快乐的程序员,现定居美国旧金山。曾经在北京的时候供职于 Yahoo!中国以及淘宝(阿里巴巴)。

教程:agentzh 的 Nginx 教程

其他内容可以参看: OpenResty简介,OpenResty 作者章亦春访谈实录

一、OpenResty综述

 

 

 

 

OpenResty®通过 Lua 扩展 NGINX 实现的可伸缩的 Web 平台

先扔三个网站:

 

首先,OpenResty官网!

还有Github:OpenResty

为数不多的书籍:OpenResty最佳实践(Lua语言入门也可以全靠它)

下图给出的是Lua Nginx Module中各指令的执行顺序

alt

  • set_by_lua: 流程分支处理判断变量初始化
  • rewrite_by_lua: 转发、重定向、缓存等功能(例如特定请求代理到外网)
  • access_by_lua: IP 准入、接口权限等情况集中处理(例如配合 iptable 完成简单防火墙)
  • content_by_lua: 内容生成
  • header_filter_by_lua: 应答 HTTP 过滤处理(例如添加头部信息)
  • body_filter_by_lua: 应答 BODY 过滤处理(例如完成应答内容统一成大写)
  • log_by_lua: 会话完成后本地异步完成日志记录(日志可以记录在本地,还可以同步到其他机器)

实际上我们只使用其中一个阶段 content_by_lua,也可以完成所有的处理。但这样做,会让我们的代码比较臃肿,越到后期越发难以维护。把我们的逻辑放在不同阶段,分工明确,代码独立,后期发力可以有很多有意思的玩法。

二、指令说明:

*_by_lua < lua-script-str >

无后缀的指令,后加字符串型的lua程序。

如:


  • location / {
  • default_type text/html;
  • content_by_lua '
  • ngx.say("<p>hello, world</p>")
  • ';
  • }

nginx

*_by_lua_block {lua_script}

有block后缀,可以直接跟lua程序段。

如:


  • location / {
  • content_by_lua_block{
  • local test = 'Hello,world'
  • ngx.say(test)
  • }
  • }

*_by_lua_file < path-to-lua-script-file >

file后缀跟lua文件路径

如:


  • location ~ ^/api/([-_a-zA-Z0-9/]+) {
  • # 准入阶段完成参数验证
  • access_by_lua_file lua/access_check.lua;
  •  
  • #内容生成阶段
  • content_by_lua_file lua/$1.lua;
  •  
  • }

nginx

三、登陆验证

access_by_lua*中集中进行一些权限认证,防止恶意ip、非法行为进入到服务器中。


  • location / {
  • access_by_lua_block{
  • ngx.exit(ngx.HTTP_FORBIDDEN)
  • }
  • content_by_lua_block{
  • --内容生产阶段
  • }
  • }

IP防火墙

OpenResty最佳实践提到:禁止某些终端访问

上面文档下方推荐第三方包:Github,Lua-resty-iputils

操作头信息检测

通过access_by_lua*可以对请求进行过滤,比如可以在这里检查有没有带authorization头部信息,若没有带可以用ngx.exit()直接退出。

登陆缓存

Github: lua-resty-jwt模块进行jwt校验

结合lua-resty-jwtlua-resty-redis甚至可以将服务器登陆缓存移植到nginx上。

过滤参数

还未尝试。

防止 SQL 注入

四、输出过滤

通常是header_filter_by_lua*body_filter_by_lua*配合实现。

【文档来源官方】:

注意下列API函数现在还不能在set_by_lua*header_filter_by_lua*body_filter_by_lua*log_by_lua*四个环境中使用:

  • 输出API 函数 (e.g., ngx.say and ngx.send_headers)
  • 控制API 函数 (e.g., ngx.redirect and ngx.exec)
  • 子请求API 函数 (e.g., ngx.location.capture and ngx.location.capture_multi)
  • Cosocket API 函数 (e.g., ngx.socket.tcp and ngx.req.socket).

header_filter_by_lua*

使用Lua代码在lua块中定义输出头信息。

example1:


  • location / {
  • proxy_pass http://mybackend;
  • header_filter_by_lua 'ngx.header.Foo = "blah"';
  • }

nginx

example2:


  • header_filter_by_lua_block {
  • ngx.header["content-length"] = nil
  • }

body_filter_by_lua*

参考:

谈谈 OpenResty 中的 body_filter_by_lua*

输入数据块chunks要经过ngx.arg1 (Lua 字符串)的过滤,"eof"标志指示响应体数据流的末端通过ngx.arg2 (Lua bollean值)显示。

这个情景下,"eof"标志就是主请求的last_buf或子请求的 last_in_chain。"eof" == "true" 表示此次nginx请求的响应结束了。

由于响应体可能会分多块发送,body_filter_by_lua*可能会被多次调用。详细机理参考上面文献有提到。


  • location /t {
  • echo hello world;
  • echo hiya globe;
  •  
  • body_filter_by_lua '
  • ......
  • ';
  • }

nginx

比如上面,body_filter_by_lua*首次调用时ngx.arg1 的值只是hello world,不包括下面的hiya globe

我验证了一下:


  • location = /test_bf {
  • echo hello world;
  • echo this;
  • echo is;
  • echo world;
  • header_filter_by_lua_block {
  • ngx.header.content_length = nil
  • }
  • body_filter_by_lua_block {
  • if string.match(ngx.arg[1], "this") then
  • ngx.arg[2] = true
  • ngx.arg[1] = "end"
  • return
  • end
  • }
  • }

上述location发送四块数据,匹配到this便设置"eof"不再发送下去了。


  • $ curl

结果:


  • $ hello world
  • $ end

要点:

① ngx.arg[2] = true设置新的"eof"标志使截断响应,设置"eof"依旧是有效的响应。

② ngx.arg[1] = "end"修改ngx.arg[1] 的值,即为修改输出响应。而当需要替换一些信息,仅需一行代码即可实现。


  • ngx.arg[1] = ngx.re.gsub(ngx.arg[1], "o", "*")

上文也提到。上述即为将所有 ' o ' 字符替换为 ' * ' 字符

若该location作为其他location的子请求,而作为子请求不想被过滤数据。需要通过ngx.is_subrequest判断。


  • body_filter_by_lua_block {
  • if ngx.arg[1] and not ngx.is_subrequest then
  • ......
  • end
  • }

nginx

还有一点,如果在body_filter_by_lua*中的Lua代码会修改响应体的长度,这就需要去把Content-Length的响应头去除掉。我之前测试与前端联调时,就是发现body过滤必须要字符数相同才会显示,很头疼不知道什么原因。其实只要去掉这个头就行了。


  • location = / {
  • proxy_pass ......
  • header_filter_by_lua_block {
  • ngx.header.content_length = nil
  • }
  • body_filter_by_lua_block {
  • ngx.arg[1] = ngx.re.gsub(ngx.arg[1], "o", "**")
  • }
  • }

五、Redis

官方github:lua-resty-redis

OpenResty最佳实践:访问有授权验证的 Redis

官方包封装了一些Redis的方法,使用起来还是挺舒适的。

本文链接:https://blog.maozhiting.com/post/openresty_notes.html

— EOF —

作者 毛毛 发表于 2017-11-05 15:11:55 ,并被添加「 OpenResty 」标签 ,最后修改于 2017-11-05 15:47:30

Published by

风君子

独自遨游何稽首 揭天掀地慰生平