🔬
OpenResty 最佳实践
  • 序
  • 入门篇
  • Lua 入门
    • Lua 简介
    • Lua 环境搭建
    • Lua 编辑器选择
    • 基础数据类型
    • 表达式
    • 控制结构
      • if/else
      • while
      • repeat
      • for
      • break,return 和 goto
    • Lua 函数
      • 函数的定义
      • 函数的参数
      • 函数返回值
      • 全动态函数调用
    • 模块
    • String 库
    • Table 库
    • 日期时间函数
    • 数学库函数
    • 文件操作
  • Lua 高阶
    • 元表
    • 面向对象编程
    • 局部变量
    • 判断数组大小
    • 非空判断
    • 正则表达式
    • 虚变量
    • 抵制使用 module() 定义模块
    • 调用代码前先定义函数
    • 点号与冒号操作符的区别
    • module 是邪恶的
    • FFI
    • 什么是 JIT
  • Nginx
    • Nginx 新手起步
    • location 匹配规则
    • 静态文件服务
    • 日志
    • 反向代理
    • 负载均衡
    • 陷阱和常见错误
  • OpenResty
    • 环境搭建
      • Windows 平台
      • CentOS 平台
      • Ubuntu 平台
      • Mac OS X 平台
    • Hello World
    • 与其他 location 配合
    • 获取 uri 参数
    • 获取请求 body
    • 输出响应体
    • 日志输出
    • 简单 API Server 框架
    • 使用 Nginx 内置绑定变量
    • 子查询
    • 不同阶段共享变量
    • 防止 SQL 注入
    • 如何发起新 HTTP 请求
    • 如何完成 bit 操作
      • 一,复习二进制补码
      • 二,复习位运算
      • 三,LuaJIT 和 Lua BitOp Api
      • 四,位运算算法实例
      • 五,Lua BitOp 的安装
  • LuaRestyRedisLibrary
    • 访问有授权验证的 Redis
    • select+set_keepalive 组合操作引起的数据读写错误
    • redis 接口的二次封装(简化建连、拆连等细节)
    • redis 接口的二次封装(发布订阅)
    • pipeline 压缩请求数量
    • script 压缩复杂请求
    • 动态生成的 lua-resty-redis 模块方法
  • LuaCjsonLibrary
    • json 解析的异常捕获
    • 稀疏数组
    • 空 table 编码为 array 还是 object
  • PostgresNginxModule
    • 调用方式简介
    • 不支持事务
    • 超时
    • 健康监测
    • SQL 注入
  • LuaNginxModule
    • 执行阶段概念
    • 正确的记录日志
    • 热装载代码
    • 阻塞操作
    • 缓存
    • sleep
    • 定时任务
    • 禁止某些终端访问
    • 请求返回后继续执行
    • 调试
    • 请求中断后的处理
    • 我的 lua 代码需要调优么
    • 变量的共享范围
    • 动态限速
    • shared.dict 非队列性质
    • 正确使用长链接
    • 如何引用第三方 resty 库
    • 典型应用场景
    • 怎样理解 cosocket
    • 如何安全启动唯一实例的 timer
    • 如何正确的解析域名
  • LuaRestyDNSLibrary
    • 使用动态 DNS 来完成 HTTP 请求
  • LuaRestyLock
    • 缓存失效风暴
  • OpenResty 与 SSL
    • HTTPS 时代
    • 动态加载证书和 OCSP stapling
    • TLS session resumption
  • 测试
    • 代码静态分析
    • 单元测试
    • 代码覆盖率
    • API 测试
    • 性能测试
    • 持续集成
    • 灰度发布
      • 分流引擎设计
      • 控制台开发
      • 向运维平台发展
  • Web 服务
    • API 的设计
    • 数据合法性检测
    • 协议无痛升级
    • 代码规范
    • 连接池
    • C10K 编程
    • TIME_WAIT 问题
    • 与 Docker 使用的网络瓶颈
  • 火焰图
    • 什么是火焰图
    • 什么时候使用
    • 如何安装火焰图生成工具
    • 如何定位问题
    • 拓展阅读
    • FAQ
Powered by GitBook
On this page
  • 内部调用
  • 流水线方式跳转
  • 外部重定向
Edit on GitHub
  1. OpenResty

与其他 location 配合

Nginx 世界的 location 是异常强大的,毕竟 Nginx 的主要应用场景是在负载均衡、API Server,在不同 server、location 之间跳转更是家常便饭。利用不同 location 的功能组合,我们可以实现 内部调用、流水线方式跳转、外部重定向 等几种不同形式的服务,下面将给大家介绍几个主要应用,就当抛砖引玉。

内部调用

例如对数据库、内部公共函数的统一接口,可以把它们放到统一的 location 中。通常情况下,为了保护这些内部接口,都会把这些接口设置为 internal 。这么做的最主要好处就是可以让这个 内部接口相对独立,不受外界干扰。

示例代码:

location = /sum {
    # 只允许内部调用
    internal;

    # 这里做了一个求和运算只是一个例子,可以在这里完成一些数据库、
    # 缓存服务器的操作,达到基础模块和业务逻辑分离目的
    content_by_lua_block {
        local args = ngx.req.get_uri_args()
        ngx.say(tonumber(args.a) + tonumber(args.b))
    }
}

location = /app/test {
    content_by_lua_block {
        local res = ngx.location.capture(
                        "/sum", {args={a=3, b=8}}
                        )
        ngx.say("status:", res.status, " response:", res.body)
    }
}

紧接着,稍微扩充一下,并行请求的效果,示例如下:

location = /sum {
    internal;
    content_by_lua_block {
        ngx.sleep(0.1)
        local args = ngx.req.get_uri_args()
        ngx.print(tonumber(args.a) + tonumber(args.b))
    }
}

location = /subduction {
    internal;
    content_by_lua_block {
        ngx.sleep(0.1)
        local args = ngx.req.get_uri_args()
        ngx.print(tonumber(args.a) - tonumber(args.b))
    }
}

location = /app/test_parallels {
    content_by_lua_block {
        local start_time = ngx.now()
        local res1, res2 = ngx.location.capture_multi( {
                        {"/sum", {args={a=3, b=8}}},
                        {"/subduction", {args={a=3, b=8}}}
                    })
        ngx.say("status:", res1.status, " response:", res1.body)
        ngx.say("status:", res2.status, " response:", res2.body)
        ngx.say("time used:", ngx.now() - start_time)
    }
}

location = /app/test_queue {
    content_by_lua_block {
        local start_time = ngx.now()
        local res1 = ngx.location.capture_multi( {
                        {"/sum", {args={a=3, b=8}}}
                    })
        local res2 = ngx.location.capture_multi( {
                        {"/subduction", {args={a=3, b=8}}}
                    })
        ngx.say("status:", res1.status, " response:", res1.body)
        ngx.say("status:", res2.status, " response:", res2.body)
        ngx.say("time used:", ngx.now() - start_time)
    }
}

测试结果:

➜  ~ curl 127.0.0.1/app/test_parallels
status:200 response:11
status:200 response:-5
time used:0.10099983215332
➜  ~ curl 127.0.0.1/app/test_queue
status:200 response:11
status:200 response:-5
time used:0.20199990272522

利用 ngx.location.capture_multi 函数,直接完成了两个子请求并行执行。当两个请求没有相互依赖时,这种方法可以极大地提高查询效率。 两个无依赖请求,各自是 100ms,顺序执行需要 200ms,但通过并行执行它们可以在 100ms 全部完成。实际生产中查询时间可能没这么规整,但思想大同小异,这个特性是很有用的。

该方法,可以被广泛应用于广告系统(1:N 模型,一个请求,后端从 N 家供应商中获取条件最优广告)、高并发前端页面展示(并行无依赖界面、降级开关等)。

流水线方式跳转

现在的网络请求,已经变得越来越拥挤。各种不同 API 、下载请求混杂在一起,这就要求不同厂商针对下载的动态调整有不同的定制策略。而这些策略在一天的不同时间段,执行规则可能还不一样。这时候我们就可以效仿工厂的流水线模式,逐层过滤、处理。

示例代码:

location ~ ^/static/([-_a-zA-Z0-9/]+).jpg {
    set $image_name $1;
    content_by_lua_block {
        ngx.exec("/download_internal/images/"
                .. ngx.var.image_name .. ".jpg");
    };
}

location /download_internal {
    internal;
    # 这里还可以有其他统一的 download 下载设置,例如限速等
    alias ../download;
}

注意,ngx.exec 方法与 ngx.redirect 是完全不同的,前者是个 纯粹的内部跳转 并且 没有引入任何额外 HTTP 信号。

这里的两个 location 更像是流水线上工人之间的协作关系。第一环节的工人完成自己的处理部分后,直接交给第二环节处理人(实际上可以有更多环节),它们之间的 数据流是定向 的。

外部重定向

不知道大家什么时候开始注意的,百度的首页已经不再是 HTTP 协议,它已经全面修改到了 HTTPS 协议上。但是对于大家的输入习惯,估计还是在地址栏里面输入 baidu.com ,回车后发现它会自动跳转到 https://www.baidu.com ,这时候就需要的外部重定向了。

location = /foo {
    content_by_lua_block {
        ngx.say([[I am foo]])
    }
}

location = / {
    rewrite_by_lua_block {
        return ngx.redirect('/foo');
    }
}

执行测试,结果如下:

➜  ~  curl 127.0.0.1 -i
HTTP/1.1 302 Moved Temporarily
Server: openresty/1.9.3.2rc3
Date: Sun, 22 Nov 2015 11:04:03 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: /foo

<html>
<head><title>302 Found</title></head>
<body bgcolor="white">
<center><h1>302 Found</h1></center>
<hr><center>openresty/1.9.3.2rc3</center>
</body>
</html>

➜  ~  curl 127.0.0.1/foo -i
HTTP/1.1 200 OK
Server: openresty/1.9.3.2rc3
Date: Sun, 22 Nov 2015 10:43:51 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive

I am foo

当我们使用浏览器访问页面 http://127.0.0.1 就可以发现浏览器会自动跳转到 http://127.0.0.1/foo 。

与之前两个应用实例不同的,外部重定向是可以跨域名的。例如从 A 网站跳转到 B 网站是绝对允许的。在 CDN 场景的大量下载应用中,一般分为调度、存储两个重要环节。调度就是通过根据请求方 IP 、下载文件等信息寻找最近、最快节点,应答跳转给请求方完成下载。

PreviousHello WorldNext获取 uri 参数

Last updated 2 years ago

图例
图例