Skip to content

Commit

Permalink
Merge pull request #34 from story-x/main
Browse files Browse the repository at this point in the history
perf: 配置多个apiKey时负载均衡
  • Loading branch information
XasYer authored Jan 5, 2025
2 parents 1d7ea23 + ef70e06 commit a4ed463
Showing 1 changed file with 48 additions and 13 deletions.
61 changes: 48 additions & 13 deletions models/utils/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ import { Config } from '#components'
import { HttpProxyAgent } from 'http-proxy-agent'
import { HttpsProxyAgent } from 'https-proxy-agent'
import { logger, redis } from '#lib'
import _ from 'lodash'
import moment from 'moment'

const redisKey = 'steam-plugin'
// api请求次数前缀
const redisApiKey = `${redisKey}:api:`
// 429的key前缀
const redis429Key = `${redisKey}:429key:`
// key使用次数前缀
const redisUseKey = `${redisKey}:useKey:`

/**
* 通用请求方法
Expand All @@ -25,19 +30,33 @@ export default async function request (url, options = {}, retry = { count: 0, ke
return url
}
})()
// 最大重试次数
const maxRetry = Math.max(Math.ceil(Config.steam.apiKey.length * 1.5), 3)

const baseURL = options.baseURL ?? steamApi
logger.info(`开始请求api: ${url}`)
incr(url)

const now = moment().format('YYYY-MM-DD')
incr(`${redisApiKey}${now}:${url}`)

const start = Date.now()
const { key, keys } = await getKey(retry.keys)
let key = ''
let keys = []
const needKeyFlag = baseURL === steamApi && !options.params?.access_token
if (needKeyFlag) {
let { retKeys, retKey } = await getKey(retry.keys)
key = retKey
keys = retKeys
logger.info(`获取请求的key: ${key.slice(0, 5)}...${key.slice(-5)}`)
}
return await axios.request({
url,
baseURL,
httpAgent: Config.steam.proxy ? new HttpProxyAgent(Config.steam.proxy) : undefined,
httpsAgent: Config.steam.proxy ? new HttpsProxyAgent(Config.steam.proxy) : undefined,
...options,
params: {
key: (baseURL === steamApi && !options.params?.access_token) ? key : undefined,
key: key || undefined,
l: 'schinese',
cc: 'CN',
language: 'schinese',
Expand All @@ -46,11 +65,13 @@ export default async function request (url, options = {}, retry = { count: 0, ke
timeout: Config.steam.timeout * 1000
}).then(res => {
logger.info(`请求api成功: ${url}, 耗时: ${Date.now() - start}ms`)
// 缓存使用的key
if (key) { incr(`${redisUseKey}${now}:${key}`, 7) }
return res.data
}).catch(err => {
if (err.status === 429 && keys.length > 1) {
if (err.status === 429 && keys.length > 1 && key && retry.count < maxRetry) {
// 十分钟内不使用相同的key
redis.set(`${redisKey}:429key:${key}`, 1, { EX: 60 * 10 })
redis.set(`${redis429Key}${key}`, 1, { EX: 60 * 10 })
retry.count++
retry.keys = keys.filter(k => k !== key)
logger.error(`请求api失败: ${url}, 状态码: ${err.status}, 更换apiKey开始重试第${retry.count}次`)
Expand Down Expand Up @@ -88,24 +109,38 @@ export async function post (url, options = {}) {

async function getKey (keys = Config.steam.apiKey) {
const i = []
const now = moment().format('YYYY-MM-DD')
if (keys.length > 1) {
for (const key of keys) {
if (!await redis.exists(`${redisKey}:429key:${key}`)) {
if (!await redis.exists(`${redis429Key}${key}`)) {
i.push(key)
}
}
if (i.length === 0) {
i.push(...keys[0])
}
} else {
i.push(...keys)
}
return {
keys: i,
key: _.sample(i)
let key = i[0]
if (i.length === 1) {
return { retKeys: i, retKey: key }
}
let count = await redis.get(`${redisUseKey}${now}:${key}`) || 0
for (const k of i) {
if (k === key) {
continue
}
const c = await redis.get(`${redisUseKey}${now}:${k}`) || 0
if (c < count) {
key = k
count = c
}
}
return { retKeys: i, retKey: key }
}

function incr (url, day = 3) {
const now = moment().format('YYYY-MM-DD')
const key = `${redisKey}:api:${now}:${url}`
function incr (key, day = 3) {
redis.incr(key).then((i) => {
if (i == 1 && day > 0) {
redis.expire(key, 60 * 60 * 24 * day).catch(() => {})
Expand Down

0 comments on commit a4ed463

Please sign in to comment.