支持茶服多个api地址的故障转移 (#301)

*  RequestError 新增 status_code 参数

*  新增支持故障转移的请求方法

*  支持茶服多个api地址的故障转移
This commit is contained in:
呵呵です
2024-04-30 01:52:41 +08:00
committed by GitHub
parent d037cf6d44
commit ec392ee384
4 changed files with 81 additions and 25 deletions

View File

@@ -1,4 +1,10 @@
from typing import Literal
GAME_TYPE: Literal['TOS'] = 'TOS'
BASE_URL = 'https://teatube.cn:8888/'
BASE_URL = {
'https://teatube.cn:8888/',
'http://cafuuchino1.studio26f.org:19970',
'http://cafuuchino2.studio26f.org:19970',
'http://cafuuchino3.studio26f.org:19970',
'http://cafuuchino4.studio26f.org:19970',
}

View File

@@ -3,6 +3,7 @@ from re import match
from typing import Literal
from urllib.parse import urlencode
from httpx import TimeoutException
from nonebot.compat import type_validate_json
from nonebot_plugin_orm import get_session
@@ -104,22 +105,30 @@ class Processor(ProcessorMeta):
"""获取用户信息"""
if self.processed_data.user_info is None:
if self.user.teaid is not None:
url = splice_url(
[
BASE_URL,
'getTeaIdInfo',
f'?{urlencode({"teaId":self.user.teaid})}',
]
)
url = [
splice_url(
[
i,
'getTeaIdInfo',
f'?{urlencode({"teaId":self.user.teaid})}',
]
)
for i in BASE_URL
]
else:
url = splice_url(
[
BASE_URL,
'getUsernameInfo',
f'?{urlencode({"username":self.user.name})}',
]
)
self.raw_response.user_info = await Request.request(url)
url = [
splice_url(
[
i,
'getUsernameInfo',
f'?{urlencode({"username":self.user.name})}',
]
)
for i in BASE_URL
]
self.raw_response.user_info = await Request.failover_request(
url, failover_code=[502], failover_exc=(TimeoutException,)
)
user_info: UserInfo = type_validate_json(UserInfo, self.raw_response.user_info) # type: ignore[arg-type]
if not isinstance(user_info, InfoSuccess):
raise RequestError(f'用户信息请求错误:\n{user_info.error}')
@@ -132,14 +141,19 @@ class Processor(ProcessorMeta):
other_parameter = {}
params = urlencode(dict(sorted(other_parameter.items())))
if self.processed_data.user_profile.get(params) is None:
self.raw_response.user_profile[params] = await Request.request(
splice_url(
[
BASE_URL,
'getProfile',
f'?{urlencode({"id":self.user.teaid or self.user.name,**other_parameter})}',
]
)
self.raw_response.user_profile[params] = await Request.failover_request(
[
splice_url(
[
i,
'getProfile',
f'?{urlencode({"id":self.user.teaid or self.user.name,**other_parameter})}',
]
)
for i in BASE_URL
],
failover_code=[502],
failover_exc=(TimeoutException,),
)
self.processed_data.user_profile[params] = UserProfile.model_validate_json(
self.raw_response.user_profile[params]

View File

@@ -18,6 +18,10 @@ class NeedCatchError(TetrisStatsError):
class RequestError(NeedCatchError):
"""请求错误"""
def __init__(self, message: str = '', *, status_code: int | None = None):
super().__init__(message)
self.status_code = status_code
class MessageFormatError(NeedCatchError):
"""用户发送的消息格式不正确"""

View File

@@ -1,3 +1,4 @@
from collections.abc import Sequence
from http import HTTPStatus
from urllib.parse import urljoin, urlparse
@@ -116,7 +117,8 @@ class Request:
response = await session.get(url, headers=cls._headers)
if response.status_code != HTTPStatus.OK:
raise RequestError(
f'请求错误 code: {response.status_code} {HTTPStatus(response.status_code).phrase}\n{response.text}'
f'请求错误 code: {response.status_code} {HTTPStatus(response.status_code).phrase}\n{response.text}',
status_code=response.status_code,
)
if is_json:
loads(response.content)
@@ -127,3 +129,33 @@ class Request:
if urlparse(url).netloc.lower().endswith('tetr.io'):
return await cls._anti_cloudflare(url)
raise
@classmethod
async def failover_request(
cls,
urls: Sequence[str],
*,
failover_code: Sequence[int],
failover_exc: tuple[type[BaseException], ...],
is_json: bool = True,
) -> bytes:
error_list: list[RequestError] = []
for i in urls:
logger.debug(f'尝试请求 {i}')
try:
return await cls.request(i, is_json=is_json)
except RequestError as e:
if e.status_code in failover_code: # 如果状态码在 failover_code 中, 则继续尝试下一个URL
error_list.append(e)
continue
# 如果状态码不在故障转移列表中, 则查找异常栈, 如果异常栈内有 failover_exc 内的异常类型, 则继续尝试下一个URL
tb = e.__traceback__
while tb is not None:
if isinstance(tb.tb_frame.f_locals.get('exc_value'), failover_exc):
error_list.append(e)
break
tb = tb.tb_next
else:
raise
continue
raise RequestError(f'所有地址皆不可用\n{error_list!r}')