OpenAPI计费网关项目简介(技术篇)

引言

本文从技术和产品角度深入介绍我们开发的 OpenAPI 系统,这是一个基于Koa.js构建的高性能API计费解决方案,它不仅支持多种计费模式,还提供了强大的缓存机制、安全防护和灵活的请求处理能力。

系统架构

OpenAPI 系统采用了现代化的技术栈和架构设计:

  • 后端框架:基于Koa.js构建,利用其异步特性实现高并发处理
  • 数据存储:MongoDB用于持久化存储,Redis用于缓存和速率限制
  • 前端界面:Vue.js构建的管理界面,包含Monaco编辑器支持 请求模板 编辑

系统的核心组件包括:

  • API代理中间件
  • 计费服务
  • 速率限制器
  • 缓存系统
  • 安全认证模块

产品核心优势

1. 高性能设计

OpenAPI 系统在性能优化方面做了大量工作:

1.1 高效的HTTP连接管理

1
2
3
4
5
6
7
8
9
// 创建 keepAlive Agent
const httpsAgent = new https.Agent({
keepAlive: true,
keepAliveMsecs: 1000, // 连接保持时间
maxSockets: 100, // 并发连接数
maxFreeSockets: 10, // 允许的空闲连接数
timeout: TIMEOUT, // 超时时间
rejectUnauthorized: false // 忽略 SSL 证书错误
});

通过配置HTTP和HTTPS的Agent,系统实现了连接复用,大幅减少了TCP连接建立的开销。这在高并发场景下能显著提升性能,减少服务器资源消耗。

1.2 智能的重试机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 带重试的请求函数
async function retryableRequest(config, retries = RETRY_COUNT) {
try {
return await axiosInstance(config);
} catch (error) {
if (retries > 0 && (
error.code === 'ETIMEDOUT' ||
error.code === 'ECONNRESET' ||
error.code === 'ECONNREFUSED' ||
(error.response && error.response.status >= 500)
)) {
console.warn(`Retry attempt ${RETRY_COUNT - retries + 1} for ${config.url}`);
await delay(RETRY_DELAY * (RETRY_COUNT - retries));
return retryableRequest(config, retries - 1);
}
throw error;
}
}

系统实现了智能重试机制,可以自动处理临时性网络故障和上游服务暂时不可用的情况。重试策略采用了指数退避算法,避免了重试风暴,提高了系统的可靠性。

1.3 异步处理计费记录

系统将API调用和计费处理分离,API调用完成后异步处理计费记录,避免了计费处理对API响应时间的影响。

2. 高效缓存实现

缓存系统是 OpenAPI 的一大亮点,它极大地提升了系统性能和用户体验:

2.1 内存缓存实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MemoryCache {
constructor() {
this.cache = new Map();
this.timeouts = new Map();
}
set(key, value, ttlSeconds) {
this.cache.set(key, value);
if (ttlSeconds) {
// 清除旧的超时
if (this.timeouts.has(key)) {
clearTimeout(this.timeouts.get(key));
}
// 设置新的超时
const timeout = setTimeout(() => {
this.cache.delete(key);
this.timeouts.delete(key);
}, ttlSeconds * 1000);
this.timeouts.set(key, timeout);
}
}
// ...其他方法
}

系统实现了高效的内存缓存,使用Map数据结构存储缓存数据,并通过定时器实现缓存过期机制。这种设计比传统的数组遍历检查过期项更高效。

2.2 智能缓存策略

1
2
3
4
5
6
7
8
9
10
// 判断请求是否可缓存
function isCacheable(method, headers) {
// 只缓存GET请求,并且请求头没有指定no-cache
return method === 'GET' &&
(!headers['cache-control'] || !headers['cache-control'].includes('no-cache'));
}
// 获取缓存TTL(秒)
function getCacheTTL(resource) {
return resource.cacheTTL || 300; // 通过cacheTTL来自定义缓存时间, 默认5分钟
}

系统根据HTTP方法和请求头智能判断请求是否可缓存,并支持通过API资源配置自定义缓存时间。这种灵活的缓存策略能够适应不同API的缓存需求。

2.3 缓存键生成

1
2
3
4
5
// 生成缓存键
function generateCacheKey(ctx, url) {
const data = `${ctx.method}:${url}:${JSON.stringify(ctx.request.body)}:${JSON.stringify(ctx.request.querystring)}`;
return crypto.createHash('md5').update(data).digest('hex');
}

缓存键的生成考虑了HTTP方法、URL、请求体和查询参数,确保了缓存的精确匹配。使用MD5哈希算法生成固定长度的缓存键,提高了缓存系统的效率。

3. 安全性设计

OpenAPI 系统在安全性方面做了全面考量:

3.1 API密钥认证

系统实现了基于API密钥的认证机制,每个用户都有唯一的API密钥,用于身份验证和访问控制。

3.2 速率限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
exports.rateLimiter = async (ctx, next) => {
const endpoint = ctx.path.replace('/api/', '');
const apiKey = ctx.state.apiKey;
if (!apiKey) {
ctx.status = 401;
ctx.body = { error: 'API key required' };
return;
}
const maxRequests = objLimit[endpoint] || DEFAULT_MAX_REQUESTS;
const key = `ratelimit:${endpoint}/${apiKey}`;
try {
const isAllowed = await cache.rateLimit(key, maxRequests, RATE_LIMIT_WINDOW);
const currentCount = cache.get(key) || 0;
ctx.set('X-RateLimit-Limit', maxRequests);
ctx.set('X-RateLimit-Remaining', Math.max(0, maxRequests - currentCount));
if (!isAllowed) {
ctx.status = 429;
ctx.body = { error: `访问频率超限(${maxRequests}req/m)...` };
return;
}
return next();
} catch (error) {
logger.error('Rate limiter error:', error);
return next(); // Proceed even if rate limiting fails
}
};

系统实现了基于内存缓存的速率限制器,可以针对不同的API端点设置不同的速率限制,有效防止了API滥用和DoS攻击。速率限制器还会自动从数据库加载配置并定期更新,确保限制策略的及时生效。

3.3 输入验证和错误处理

系统对所有输入进行严格验证,防止注入攻击和其他安全威胁。同时,系统实现了全面的错误处理机制,确保在出现异常情况时能够优雅地处理并返回适当的错误信息。

4. 灵活的请求处理

4.1 函数式请求模板

系统最近升级了请求模板功能,支持使用JavaScript函数定义请求转换逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 检查是否是函数字符串(以 function 开头)
if (typeof resource.requestTemplate === 'string' && resource.requestTemplate.trim().startsWith('function')) {
try {
// 将函数字符串转换为可执行函数
// 支持匿名函数和命名函数
const templateCode = resource.requestTemplate.trim();
let templateFunction = new Function('request', `
try {
return (${templateCode})(request);
} catch (err) {
console.error('Template function execution error:', err);
return {};
}
`);
// 执行函数并获取结果
const result = templateFunction(ctx.request);
if (result && typeof result === 'object') {
if (result.headers) {
headers = result.headers;
}
if (result.body) {
body = result.body
// 如果是FormData请求但模板返回了body,我们将转换为JSON请求
if (isMultipart) {
headers['content-type'] = 'application/json';
}
}
}
} catch (error) {
logger.error(`Error executing request template function: ${error.message}`);
}
}

这种函数式模板比传统的JSON模板更加灵活,可以实现复杂的请求转换逻辑,如条件判断、数据转换等。前端还配置了Monaco编辑器,提供JavaScript语法高亮,提升了模板编辑体验。

4.2 流式响应支持

系统支持流式响应,特别适用于处理大模型响应或需要实时数据的场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
async function handleStreamProxy(ctx, apiResource, upstream, opt) {
const stream = new PassThrough();
ctx.body = stream;
// 设置 SSE headers
ctx.set({
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
try {
// 处理特殊请求头
const headers = {
...opt.headers,
'api-key': opt.headers['api-key'] || '',
'Content-Type': 'application/json',
'Accept': 'text/event-stream'
};
// 移除不需要的头
delete headers.host;
delete headers['x-api-key'];
delete headers['content-length'];
const optA = {
method: ctx.method,
url: upstream,
data: {
...opt.body,
stream: true
},
headers,
responseType: 'stream'
};
// 转发请求到上游服务
const response = await axiosInstance(optA);
// 直接转发响应
response.data.pipe(stream);
} catch (error) {
console.error('Stream proxy error:', error);
stream.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
stream.end();
}
}

流式响应处理使系统能够支持现代AI API的流式输出,如OpenAI的流式聊天完成API。

5. 精确的Token计费

系统最近升级了token计费功能,支持更精确的计费方式:

  • 输入输出分离计费:系统可以分别计算输入和输出token的费用,并设置不同的价格
  • 智能识别输入token:对于常见的LLM API格式(如包含prompt或messages字段的请求),系统能够智能识别并计算输入token
  • 灵活的计费模式:系统支持根据API资源的billingMode属性决定使用哪种计费模式

这种精确的token计费方式特别适合AI API提供商,可以更公平地反映API使用成本。

产品设计考量

1. 用户体验至上

OpenAPI 系统的设计始终以用户体验为中心:

  • 简洁的API设计:遵循RESTful原则,API接口简洁明了,支持浏览器调用调试
  • 详细的使用统计:提供实时使用量统计,帮助用户了解API使用情况
  • 灵活的计费策略:支持多种计费模式,满足不同用户的需求

2. 可扩展性设计

系统在设计时充分考虑了可扩展性:

  • 模块化架构:系统分为多个独立模块,如认证、代理、计费等,便于扩展和维护
  • 插件化设计:关键功能如缓存、速率限制等都设计为可插拔的组件
  • 配置驱动:大部分功能通过配置驱动,无需修改代码即可调整系统行为

3. 运维友好

系统设计了完善的日志和监控机制,便于运维人员排查问题和优化系统性能。同时,系统支持通过环境变量配置关键参数,便于在不同环境中部署。

技术实现对比

缓存实现对比

实现方式 优点 缺点
内存缓存 (当前实现) 速度快,实现简单 不支持分布式,重启后缓存丢失
Redis缓存 支持分布式,持久化 需要额外维护Redis服务
文件缓存 简单,持久化 I/O开销大,性能较低

我们选择内存缓存作为主要实现,同时为分布式部署场景预留了Redis缓存的接口。

速率限制实现对比

实现方式 优点 缺点
内存计数器 (当前实现) 速度快,实现简单 不支持分布式
滑动窗口 更精确的限流 实现复杂,资源消耗较大
令牌桶 允许突发流量 实现复杂

我们选择内存计数器作为速率限制的实现,在单机部署场景下提供了良好的性能和足够的准确性。

未来发展方向

  • 支持多种货币:计划添加多币种支持,满足国际用户需求
  • 计费预警功能:当用户接近月度限额时发送通知
  • 自定义计费规则:允许用户定义更复杂的计费规则
  • 计费报表导出:支持导出详细的计费报表
  • 集成支付网关:直接在系统中完成充值和支付

结论

OpenAPI 系统通过高性能设计、高效缓存实现、全面的安全考量和灵活的请求处理能力,独立开发者提供了一个强大的计费解决方案。系统不仅满足了基本的计费需求,还通过精确的token计费、函数式请求模板等创新功能,提升了用户体验和系统灵活性。

作为独立开发者,更应追求技术卓越和产品完美, OpenAPI 系统正是这种追求的体现。我们相信,随着系统的不断发展和完善,它将为更多的API提供者创造价值。

本文由 OpenAPI 系统开发团队撰写,旨在分享我们在API计费领域的技术实践和产品思考。

分享到:

评论完整模式加载中...如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理