Skip to content

Commit

Permalink
feat: add 'radmon_id' algorithm for 'request-id' plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
jiangfucheng committed Jan 4, 2023
1 parent 403e4c5 commit 312ad1b
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 7 deletions.
46 changes: 41 additions & 5 deletions apisix/plugins/request-id.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ local tostring = tostring
local math_pow = math.pow
local math_ceil = math.ceil
local math_floor = math.floor
local math_random = math.random
local str_byte = string.byte
local ffi = require "ffi"

local plugin_name = "request-id"

Expand All @@ -40,7 +43,27 @@ local schema = {
properties = {
header_name = {type = "string", default = "X-Request-Id"},
include_in_response = {type = "boolean", default = true},
algorithm = {type = "string", enum = {"uuid", "snowflake", "nanoid"}, default = "uuid"}
algorithm = {
type = "string",
enum = {"uuid", "snowflake", "nanoid", "range_id"},
default = "uuid"
},
range_id = {
type = "object",
properties = {
length = {
type = "integer",
minimum = 6,
default = 16
},
char_set = {
type = "string",
-- The Length is set to 6 just avoid too short length, it may repeat
minLength = 6,
default = "abcdefghijklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ0123456789"
}
}
}
}
}

Expand Down Expand Up @@ -202,14 +225,27 @@ local function next_id()
return snowflake:next_id()
end

-- generate range_id
local function get_range_id(range_id)
local res = ffi.new("unsigned char[?]", range_id.length)
for i = 0, range_id.length - 1 do
res[i] = str_byte(range_id.char_set, math_random(#range_id.char_set))
end
return ffi.string(res, range_id.length)
end

local function get_request_id(algorithm)
if algorithm == "uuid" then
local function get_request_id(conf)
if conf.algorithm == "uuid" then
return uuid()
end
if algorithm == "nanoid" then
if conf.algorithm == "nanoid" then
return nanoid.safe_simple()
end

if conf.algorithm == "range_id" then
return get_range_id(conf.range_id)
end

return next_id()
end

Expand All @@ -218,7 +254,7 @@ function _M.rewrite(conf, ctx)
local headers = ngx.req.get_headers()
local uuid_val
if not headers[conf.header_name] then
uuid_val = get_request_id(conf.algorithm)
uuid_val = get_request_id(conf)
core.request.set_header(ctx, conf.header_name, uuid_val)
else
uuid_val = headers[conf.header_name]
Expand Down
4 changes: 3 additions & 1 deletion docs/en/latest/plugins/request-id.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ The Plugin will not add a unique ID if the request already has a header with the
| ------------------- | ------- | -------- | -------------- | ------------------------------- | ---------------------------------------------------------------------- |
| header_name | string | False | "X-Request-Id" | | Header name for the unique request ID. |
| include_in_response | boolean | False | true | | When set to `true`, adds the unique request ID in the response header. |
| algorithm | string | False | "uuid" | ["uuid", "snowflake", "nanoid"] | Algorithm to use for generating the unique request ID. |
| algorithm | string | False | "uuid" | ["uuid", "snowflake", "nanoid", "range_id"] | Algorithm to use for generating the unique request ID. |
| range_id.char_set | string | False | "abcdefghijklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ0123456789| The minimum string length is 6 | Character set for range_id |
| range_id.length | integer | False | 16 | Minimum 6 | Id length for range_id algorithm |

### Using snowflake algorithm to generate unique ID

Expand Down
4 changes: 3 additions & 1 deletion docs/zh/latest/plugins/request-id.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ description: 本文介绍了 Apache APISIX request-id 插件的相关操作,
| ------------------- | ------- | -------- | -------------- | ------ | ------------------------------ |
| header_name | string || "X-Request-Id" | | unique ID 的请求头的名称。 |
| include_in_response | boolean || true | | 当设置为 `true` 时,将 unique ID 加入返回头。 |
| algorithm | string || "uuid" | ["uuid", "snowflake", "nanoid"] | 指定的 unique ID 生成算法。 |
| algorithm | string || "uuid" | ["uuid", "snowflake", "nanoid", "range_id"] | 指定的 unique ID 生成算法。 |
| range_id.char_set | string || "abcdefghijklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ0123456789| 字符串长度最小为 6 | range_id 算法的字符集 |
| range_id.length | integer || 16 | 最小值为 6 | range_id 算法的 id 长度 |

### 使用 snowflake 算法生成 unique ID

Expand Down
184 changes: 184 additions & 0 deletions t/plugin/request-id2.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';

worker_connections(1024);
repeat_each(1);
no_long_string();
no_root_location();

add_block_preprocessor(sub {
my ($block) = @_;

if (!$block->request) {
$block->set_value("request", "GET /t");
}
});

run_tests;

__DATA__
=== TEST 1: check config with algorithm range_id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"request-id": {
"algorithm": "range_id",
"range_id": {
"char_set": "abcdefg",
"length": 20
}
}
},
"upstream": {
"nodes": {
"127.0.0.1:1982": 1
},
"type": "roundrobin"
},
"uri": "/opentracing"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 2: add plugin with algorithm range_id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"request-id": {
"algorithm": "range_id"
}
},
"upstream": {
"nodes": {
"127.0.0.1:1982": 1
},
"type": "roundrobin"
},
"uri": "/opentracing"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 3: add plugin with algorithm range_id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local http = require "resty.http"
local v = {}
local ids = {}
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"request-id": {
"algorithm": "range_id",
"range_id": {}
}
},
"upstream": {
"nodes": {
"127.0.0.1:1982": 1
},
"type": "roundrobin"
},
"uri": "/opentracing"
}]]
)
if code >= 300 then
ngx.say("algorithm range_id is error")
end
for i = 1, 180 do
local th = assert(ngx.thread.spawn(function()
local httpc = http.new()
local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/opentracing"
local res, err = httpc:request_uri(uri,
{
method = "GET",
headers = {
["Content-Type"] = "application/json",
}
}
)
if not res then
ngx.log(ngx.ERR, err)
return
end
local id = res.headers["X-Request-Id"]
if not id then
return -- ignore if the data is not synced yet.
end
if #id ~= 16 then
ngx.say(id)
ngx.say("incorrect length for id")
return
end
for j = 1, #id do
local start, en = string.find(id, '[a-zA-Z0-9]', j)
if start ~= j or en ~= j then
ngx.say("incorrect char set for id")
ngx.say(id)
return
end
end
if ids[id] == true then
ngx.say("ids not unique")
return
end
ids[id] = true
end, i))
table.insert(v, th)
end
for i, th in ipairs(v) do
ngx.thread.wait(th)
end
ngx.say("true")
}
}
--- wait: 5
--- response_body
true

0 comments on commit 312ad1b

Please sign in to comment.