需求:
简单的说: 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;
}
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
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网关真的可以有很多玩法.
供参考