Py学习  »  NGINX

Kong[nginx]-15 玩转ngx.location.capture

国服最坑开发 • 4 年前 • 521 次点击  

KONG专题目录


需求:
简单的说: API网关接收到客户端请求后, 要求立即返回结果.

具体案例: 客户端只负责把数据上报到服务器, 但并不关心处理结果.
如果这时候, 后端处理速度比较慢, 客户端会一直等在这里, 这个体验不是太好.

本文要展示的内容, 就是通过Kong来解决这个问题.

0x01 前提

本文涉及下述技术点, 如果有不太熟悉的地方, 请抱着看电影的心态观看本文.

  • nginx 基本配置
  • openresty lua 脚本
  • kong 的配置和使用

0x02 解决思路

本以为,在 access() 方法中, 调用kong.response.exit()方法给客户端返回结果后, 就可以执行后续调用操作, 但是这种方法并不可行.

This function interrupts the current processing and produces a response. It is typical to see plugins using it to produce a response before Kong has a chance to proxy the request (e.g. an authentication plugin rejecting a request, or a caching plugin serving a cached response).
此方法会中断当前请求, 并生成一个响应. 主要用于请求验证等行为.

那么, 也就是说 kong开头的对象(方法)已经不能解决问题了, 那么我们就要使用openresty中大量ngx开头的方法了.


经调查, 我们把目光集中到了 ngx.location.capture方法上.
官方介绍如下:

syntax: res = ngx.location.capture(uri, options?)
context: rewrite_by_lua, access_by_lua, content_by_lua*
Issues a synchronous but still non-blocking Nginx Subrequest using uri.
Nginx's subrequests provide a powerful way to make non-blocking internal requests to other locations configured with disk file directory or any other nginx C modules like ngx_proxy, ngx_fastcgi, ngx_memc, ngx_postgres, ngx_drizzle, and even ngx_lua itself and etc etc etc.
发起一个异步非阻塞子请求.
使用子请求时,支持OOXX种内部请求方式.

解释一下上面的意思 : 在使用上面的方法时, 第一个参数只能是内部请求.
这里的内部请求是特指nginx配置文件中的internal 路由.

可能不少小伙伴听的云里雾里, 那么,我们直接上代码吧..

这里介绍一下kong中的nginx.conf 文件修改方式:

千万不要直接去修改 /urs/local/kong/*.conf 文件, 因为 kong restart时会重新生成一套配置文件, 会把他们盖掉.

正确做法如下:

  • 如果是添加一个完整的 server节点的配置文件my.conf
    • 需要在 /etc/kong/kong.conf 中添加nginx_http_include=/etc/kong/my.conf
  • 如果想修改/usr/local/kong/nginx-kong.conf中部分内容
    • 请修改kong的模板文件 /usr/local/share/lua/5.1/kong/templates/nginx_kong.lua

OK, 我们在模板文件中, 添加一个内部请求定义:

cd /usr/local/share/lua/5.1/kong/templates/
vim nginx_kong.lua 

# 在 location = /kong_error_handler 这一行的上面添加下面内容
        location /internal_api {
              internal;
              set_by_lua $query_url 'return ngx.unescape_uri(ngx.var.arg_url);';
              proxy_pass $query_url;
        }   
修改nginx_kong.lua

0x03 测试用Java代码

我们准备一个测试用的SpringBoot应用, 并且写一个接口: 5秒延时后返回处理结果.

    @RequestMapping("/fast")
    public String getSlow() {
        System.out.println("start");
        try {
            Thread.sleep(5000);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end");
        return "Slow Done";
    }

留意一下, 接口执行时, 会在控制台 打印 start,end字样

0x04 配置Service

按照我们以前学过的知识 , 在 aaa.com上, 添加一个路由/slow

添加路由/slow

0x05 编写插件

直接上插件代码:

  • handler.lua
-- handler.lua
local BasePlugin   = require "kong.plugins.base_plugin"
local GGHandler    = BasePlugin:extend()

GGHandler.VERSION  = "1.0.0"
GGHandler.PRIORITY = 10

function GGHandler:access(config)
    kong.log("kong - start")

    -- 向客户端返回结果
    ngx.say([[ hello world]])
    -- 这里会结束与客户端的 socket连接
    ngx.eof()

    -- nginx内部接着执行子请求, 无视结果
    ngx.location.capture("/internal_api", {
        copy_all_vars = true,
        args          = { url = "http://127.0.0.1:9000/fast" },
        method        = ngx.HTTP_GET,
    })
    kong.log("kong - end")
end

return GGHandler

  • schema.lua
local typedefs     = require "kong.db.schema.typedefs"

return {
  name   = "fast-response",
  fields = {
    { protocols = typedefs.protocols_http },
    { config = {
      type   = "record",
      fields = { },
    },
    },
  },
}

0x06 验证

这种测试只有动图才有说服力, 一图胜千言 : )


测试效果

这一篇的应用场景有点冷门, 不过还是一句话: API网关真的可以有很多玩法.
供参考


KONG专题目录


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/37832
 
521 次点击