How Weimob Boosted API Performance with APISIX: A Deep Dive
This article details Weimob's migration to APISIX, covering background, performance requirements, benchmark results, architectural analysis, Kubernetes deployment, custom plugin extensions for authentication and rate limiting, remaining challenges, and overall conclusions about the gateway's impact.
Weimob has been using APISIX for over a year, experiencing strong performance and rich plugins while encountering complex issues. This article summarizes the stage‑wise experience of APISIX on the Weimob Open Platform.
Background
The Weimob Open Platform provides three categories of open capabilities—API, message, SPI—each backed by a gateway. Rapid business growth required architecture and performance optimizations to improve developer experience, increase concurrency, and reduce response latency.
The API gateway evolved through three versions: Spring MVC 1.0, WebFlux 2.0, and the current APISIX‑based 3.0, delivering significant gains in both performance and functionality.
Performance Requirements
Before selecting APISIX, mainstream gateways were benchmarked for response time (RT) and QPS on an 8‑core 16 GB machine. The average RTs were 3.5 ms (no proxy), 10.3 ms (springcloudgateway), 23 ms (zuul 1.x), and 6.3 ms (APISIX), showing APISIX’s superior performance.
Performance Analysis
APISIX’s high‑performance stems from its foundation on OpenResty/Nginx, inheriting Nginx’s multi‑process event‑driven model and Sendfile mechanism. OpenResty’s LuaJIT and Resty libraries further accelerate Lua scripts by compiling them to machine code and providing optimized FFI bindings.
Resty also offers non‑blocking network optimizations, such as the lua‑resty‑redis library that uses cosocket API for asynchronous Redis I/O.
APISIX Deployment
Weimob deploys APISIX via a NodePort service in Kubernetes, deliberately bypassing the default Ingress to reduce an extra network hop. Although historical reasons prevented using APISIX as the default ingress, its cloud‑native capabilities now allow a unified gateway for internal services and the open platform.
Extended Function Requirements
The API gateway must support authentication, routing, parameter mapping, data encryption, protocol conversion (http → dubbo), circuit breaking, tenant‑level rate limiting, tracing, and detailed call logs. Some features are implemented with native plugins, while custom Lua plugins handle business‑specific logic.
Extension Implementation
1. Open API Authentication
A custom plugin
wm-auth.luavalidates the access token against Redis and, if missing, fetches token details via an HTTP call.
<code>local http_util = require("apisix.weimob.utils.http-util")
local redis_util = require("apisix.weimob.utils.redis-util")
-- first load from redis
local redis_key = "bidpid"..accesstoken
local red = redis_util:instance()
local cache, err = red:get(redis_key)
if err then
core.log.error("failed to get redis key,ignore this err: ", redis_key, ",", err)
end
if cache ~= nil then
local token_table = core.json.decode(cache)
if token_table == nil or next(token_table) == nil then
return 200, response_util.build_resp(err_constant.INVALID_ACCESS_TOKEN)
end
token_holder(ctx, token_table, conf)
return
end
-- request token from auth center
local analysis_token_url = conf.analysis_token_url
local token_res, token_err = http.post({uri = analysis_token_url.."?accesstoken="..accesstoken}, {})
if not token_res then
wlog.log(ctx, "open-b analysis token err,accesstoken:"..accesstoken, err_constant.PARSE_ACCESS_TOKEN_ERR)
return 200, response_util.build_resp(err_constant.SERVER_ERROR)
end
core.wmlog.warn(ctx, "analysis_token:"..accesstoken..",rs:", core.json.encode(token_res))
local token_table = core.json.decode(token_res)
if token_table == nil or next(token_table) == nil then
return 200, response_util.build_resp(err_constant.INVALID_ACCESS_TOKEN)
end
token_holder(ctx, token_table, conf)</code>2. Commercial Rate Limiting
The built‑in
limit-count-redis.luaplugin was extended to support API + application + merchant dimensions and both QPS and daily quotas.
<code>local script = [=[
if ARGV[1] and ARGV[1]~=-1 and ARGV[1]~='-1' then
if redis.call('ttl', KEYS[1]) < 0 then
redis.call('set', KEYS[1], 1, 'EX', 1)
else
local qt = redis.call('incrby', KEYS[1], 1)
local remain = ARGV[1]-tonumber(qt)
if remain<0 then
return -1;
end
end
end
if ARGV[2] and ARGV[2]~=-1 and ARGV[2]~='-1' then
if redis.call('ttl', KEYS[2]) < 0 then
redis.call('set', KEYS[2], 1, 'EX', 259200)
else
local dt = redis.call('incrby', KEYS[2], 1)
local remain = ARGV[2]-tonumber(dt)
if remain<0 then
return -2;
end
end
end
return 1;
]=]
function _M.access(conf, ctx)
local res, err = red:eval(script, 2, qps_cnt, qpd_cnt, s_limit, d_limit)
if err then
core.log.error("eval redis quota limit script err:", err)
return
end
if res == '1' or res == 1 then
ctx.var.execute_quota_limit = true
return
end
if res == '-1' or res == -1 then
ctx.var.execute_quota_limit = true
return 200, response_util.build_resp(err_constant.CALL_QPS_LIMIT)
end
if res == '-2' or res == -2 then
ctx.var.execute_quota_limit = true
return 200, response_util.build_resp(err_constant.CALL_LIMIT_EXCEED)
end
end</code>Redis Lua scripts ensure atomic counting and limit enforcement.
Other Issues
Remaining challenges include JSON serialization precision (cjson defaults to 14‑digit precision, insufficient for Java long values) and the prohibition of external I/O in the Content phase, which limits certain plugin capabilities.
Conclusion
APISIX has delivered impressive performance and a rich plugin ecosystem for Weimob, though some functional gaps remain. Ongoing development aims to further mature the cloud‑native API gateway and improve the open platform experience.
Weimob Technology Center
Official platform of the Weimob Technology Center
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.