Skip to content

Commit e80daaf

Browse files
authored
feat: 新增多worker运行支持 (#21)
* feat: 定时任务新增redis分布式锁以在多worker下正常运行 * refactor: 重构日志系统以支持多worker * fix: 修复lint错误 * chore: 更新.gitignore规则
1 parent 26cc9c4 commit e80daaf

File tree

26 files changed

+1834
-216
lines changed

26 files changed

+1834
-216
lines changed

.gitignore

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# Byte-compiled / optimized / DLL files
22
__pycache__/
3-
.idea/
4-
.vscode/
53
*.py[cod]
64
*$py.class
75

@@ -138,3 +136,19 @@ dmypy.json
138136

139137
# Cython debug symbols
140138
cython_debug/
139+
140+
# PyCharm
141+
.idea/
142+
143+
# VSCode
144+
.vscode/
145+
146+
# AI Editor
147+
.agent/
148+
.claude/
149+
.codebuddy/
150+
.codex/
151+
.cursor/
152+
.opencode/
153+
.qoder/
154+
.trae/

ruoyi-fastapi-backend/.env.dev

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ APP_PORT = 9099
1313
APP_VERSION= '1.8.1'
1414
# 应用是否开启热重载
1515
APP_RELOAD = true
16+
# 应用工作进程数
17+
APP_WORKERS = 1
1618
# 应用是否开启IP归属区域查询
1719
APP_IP_LOCATION_QUERY = true
1820
# 应用是否允许账号同时登录
@@ -67,4 +69,50 @@ REDIS_USERNAME = ''
6769
# Redis密码
6870
REDIS_PASSWORD = ''
6971
# Redis数据库
70-
REDIS_DATABASE = 2
72+
REDIS_DATABASE = 2
73+
74+
# -------- 日志配置 --------
75+
# Redis Stream Key
76+
LOG_STREAM_KEY = 'log:stream'
77+
# Redis Stream 消费组名称
78+
LOG_STREAM_GROUP = 'log_aggregator'
79+
# Redis Stream 消费者名称前缀
80+
LOG_STREAM_CONSUMER_PREFIX = 'worker'
81+
# 每次读取的最大消息数量
82+
LOG_STREAM_BATCH_SIZE = 100
83+
# 阻塞读取等待时间(毫秒)
84+
LOG_STREAM_BLOCK_MS = 2000
85+
# Stream 最大长度(近似裁剪)
86+
LOG_STREAM_MAXLEN = 100000
87+
# Pending 回收最小空闲时间(毫秒)
88+
LOG_STREAM_CLAIM_IDLE_MS = 60000
89+
# Pending 回收检查间隔(毫秒)
90+
LOG_STREAM_CLAIM_INTERVAL_MS = 5000
91+
# 每次回收的最大消息数量
92+
LOG_STREAM_CLAIM_BATCH_SIZE = 100
93+
# 去重 Key 过期时间(秒)
94+
LOG_STREAM_DEDUP_TTL = 3600
95+
# 去重 Key 前缀
96+
LOG_STREAM_DEDUP_PREFIX = 'log:dedup'
97+
# stdout 输出是否为 JSON
98+
LOGURU_JSON = false
99+
# Loguru 最低输出级别
100+
LOGURU_LEVEL = 'INFO'
101+
# 是否输出到 stdout
102+
LOGURU_STDOUT = true
103+
# 是否启用文件日志
104+
LOG_FILE_ENABLED = true
105+
# 文件日志根目录
106+
LOG_FILE_BASE_DIR = 'logs'
107+
# 文件滚动策略
108+
LOGURU_ROTATION = '50MB'
109+
# 文件保留策略
110+
LOGURU_RETENTION = '30 days'
111+
# 文件压缩格式
112+
LOGURU_COMPRESSION = 'zip'
113+
# 实例标识(用于区分实例)
114+
LOG_INSTANCE_ID = 'dev'
115+
# 服务名称(用于统一标识服务)
116+
LOG_SERVICE_NAME = 'ruoyi-fastapi-backend'
117+
# Worker 标识(auto 自动生成)
118+
LOG_WORKER_ID = 'auto'

ruoyi-fastapi-backend/.env.dockermy

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ APP_PORT = 9099
1313
APP_VERSION= '1.8.1'
1414
# 应用是否开启热重载
1515
APP_RELOAD = false
16+
# 应用工作进程数
17+
APP_WORKERS = 1
1618
# 应用是否开启IP归属区域查询
1719
APP_IP_LOCATION_QUERY = true
1820
# 应用是否允许账号同时登录
@@ -67,4 +69,50 @@ REDIS_USERNAME = ''
6769
# Redis密码
6870
REDIS_PASSWORD = ''
6971
# Redis数据库
70-
REDIS_DATABASE = 2
72+
REDIS_DATABASE = 2
73+
74+
# -------- 日志配置 --------
75+
# Redis Stream Key
76+
LOG_STREAM_KEY = 'log:stream'
77+
# Redis Stream 消费组名称
78+
LOG_STREAM_GROUP = 'log_aggregator'
79+
# Redis Stream 消费者名称前缀
80+
LOG_STREAM_CONSUMER_PREFIX = 'worker'
81+
# 每次读取的最大消息数量
82+
LOG_STREAM_BATCH_SIZE = 100
83+
# 阻塞读取等待时间(毫秒)
84+
LOG_STREAM_BLOCK_MS = 2000
85+
# Stream 最大长度(近似裁剪)
86+
LOG_STREAM_MAXLEN = 100000
87+
# Pending 回收最小空闲时间(毫秒)
88+
LOG_STREAM_CLAIM_IDLE_MS = 60000
89+
# Pending 回收检查间隔(毫秒)
90+
LOG_STREAM_CLAIM_INTERVAL_MS = 5000
91+
# 每次回收的最大消息数量
92+
LOG_STREAM_CLAIM_BATCH_SIZE = 100
93+
# 去重 Key 过期时间(秒)
94+
LOG_STREAM_DEDUP_TTL = 3600
95+
# 去重 Key 前缀
96+
LOG_STREAM_DEDUP_PREFIX = 'log:dedup'
97+
# stdout 输出是否为 JSON
98+
LOGURU_JSON = false
99+
# Loguru 最低输出级别
100+
LOGURU_LEVEL = 'INFO'
101+
# 是否输出到 stdout
102+
LOGURU_STDOUT = true
103+
# 是否启用文件日志
104+
LOG_FILE_ENABLED = true
105+
# 文件日志根目录
106+
LOG_FILE_BASE_DIR = 'logs'
107+
# 文件滚动策略
108+
LOGURU_ROTATION = '50MB'
109+
# 文件保留策略
110+
LOGURU_RETENTION = '30 days'
111+
# 文件压缩格式
112+
LOGURU_COMPRESSION = 'zip'
113+
# 实例标识(用于区分实例)
114+
LOG_INSTANCE_ID = 'dockermy'
115+
# 服务名称(用于统一标识服务)
116+
LOG_SERVICE_NAME = 'ruoyi-fastapi-backend'
117+
# Worker 标识(auto 自动生成)
118+
LOG_WORKER_ID = 'auto'

ruoyi-fastapi-backend/.env.dockerpg

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ APP_PORT = 9099
1313
APP_VERSION= '1.8.1'
1414
# 应用是否开启热重载
1515
APP_RELOAD = false
16+
# 应用工作进程数
17+
APP_WORKERS = 1
1618
# 应用是否开启IP归属区域查询
1719
APP_IP_LOCATION_QUERY = true
1820
# 应用是否允许账号同时登录
@@ -67,4 +69,50 @@ REDIS_USERNAME = ''
6769
# Redis密码
6870
REDIS_PASSWORD = ''
6971
# Redis数据库
70-
REDIS_DATABASE = 2
72+
REDIS_DATABASE = 2
73+
74+
# -------- 日志配置 --------
75+
# Redis Stream Key
76+
LOG_STREAM_KEY = 'log:stream'
77+
# Redis Stream 消费组名称
78+
LOG_STREAM_GROUP = 'log_aggregator'
79+
# Redis Stream 消费者名称前缀
80+
LOG_STREAM_CONSUMER_PREFIX = 'worker'
81+
# 每次读取的最大消息数量
82+
LOG_STREAM_BATCH_SIZE = 100
83+
# 阻塞读取等待时间(毫秒)
84+
LOG_STREAM_BLOCK_MS = 2000
85+
# Stream 最大长度(近似裁剪)
86+
LOG_STREAM_MAXLEN = 100000
87+
# Pending 回收最小空闲时间(毫秒)
88+
LOG_STREAM_CLAIM_IDLE_MS = 60000
89+
# Pending 回收检查间隔(毫秒)
90+
LOG_STREAM_CLAIM_INTERVAL_MS = 5000
91+
# 每次回收的最大消息数量
92+
LOG_STREAM_CLAIM_BATCH_SIZE = 100
93+
# 去重 Key 过期时间(秒)
94+
LOG_STREAM_DEDUP_TTL = 3600
95+
# 去重 Key 前缀
96+
LOG_STREAM_DEDUP_PREFIX = 'log:dedup'
97+
# stdout 输出是否为 JSON
98+
LOGURU_JSON = false
99+
# Loguru 最低输出级别
100+
LOGURU_LEVEL = 'INFO'
101+
# 是否输出到 stdout
102+
LOGURU_STDOUT = true
103+
# 是否启用文件日志
104+
LOG_FILE_ENABLED = true
105+
# 文件日志根目录
106+
LOG_FILE_BASE_DIR = 'logs'
107+
# 文件滚动策略
108+
LOGURU_ROTATION = '50MB'
109+
# 文件保留策略
110+
LOGURU_RETENTION = '30 days'
111+
# 文件压缩格式
112+
LOGURU_COMPRESSION = 'zip'
113+
# 实例标识(用于区分实例)
114+
LOG_INSTANCE_ID = 'dockerpg'
115+
# 服务名称(用于统一标识服务)
116+
LOG_SERVICE_NAME = 'ruoyi-fastapi-backend'
117+
# Worker 标识(auto 自动生成)
118+
LOG_WORKER_ID = 'auto'

ruoyi-fastapi-backend/.env.prod

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ APP_PORT = 9099
1313
APP_VERSION= '1.8.1'
1414
# 应用是否开启热重载
1515
APP_RELOAD = false
16+
# 应用工作进程数
17+
APP_WORKERS = 1
1618
# 应用是否开启IP归属区域查询
1719
APP_IP_LOCATION_QUERY = true
1820
# 应用是否允许账号同时登录
@@ -67,4 +69,50 @@ REDIS_USERNAME = ''
6769
# Redis密码
6870
REDIS_PASSWORD = ''
6971
# Redis数据库
70-
REDIS_DATABASE = 2
72+
REDIS_DATABASE = 2
73+
74+
# -------- 日志配置 --------
75+
# Redis Stream Key
76+
LOG_STREAM_KEY = 'log:stream'
77+
# Redis Stream 消费组名称
78+
LOG_STREAM_GROUP = 'log_aggregator'
79+
# Redis Stream 消费者名称前缀
80+
LOG_STREAM_CONSUMER_PREFIX = 'worker'
81+
# 每次读取的最大消息数量
82+
LOG_STREAM_BATCH_SIZE = 100
83+
# 阻塞读取等待时间(毫秒)
84+
LOG_STREAM_BLOCK_MS = 2000
85+
# Stream 最大长度(近似裁剪)
86+
LOG_STREAM_MAXLEN = 100000
87+
# Pending 回收最小空闲时间(毫秒)
88+
LOG_STREAM_CLAIM_IDLE_MS = 60000
89+
# Pending 回收检查间隔(毫秒)
90+
LOG_STREAM_CLAIM_INTERVAL_MS = 5000
91+
# 每次回收的最大消息数量
92+
LOG_STREAM_CLAIM_BATCH_SIZE = 100
93+
# 去重 Key 过期时间(秒)
94+
LOG_STREAM_DEDUP_TTL = 3600
95+
# 去重 Key 前缀
96+
LOG_STREAM_DEDUP_PREFIX = 'log:dedup'
97+
# stdout 输出是否为 JSON
98+
LOGURU_JSON = false
99+
# Loguru 最低输出级别
100+
LOGURU_LEVEL = 'INFO'
101+
# 是否输出到 stdout
102+
LOGURU_STDOUT = true
103+
# 是否启用文件日志
104+
LOG_FILE_ENABLED = true
105+
# 文件日志根目录
106+
LOG_FILE_BASE_DIR = 'logs'
107+
# 文件滚动策略
108+
LOGURU_ROTATION = '50MB'
109+
# 文件保留策略
110+
LOGURU_RETENTION = '30 days'
111+
# 文件压缩格式
112+
LOGURU_COMPRESSION = 'zip'
113+
# 实例标识(用于区分实例)
114+
LOG_INSTANCE_ID = 'prod'
115+
# 服务名称(用于统一标识服务)
116+
LOG_SERVICE_NAME = 'ruoyi-fastapi-backend'
117+
# Worker 标识(auto 自动生成)
118+
LOG_WORKER_ID = 'auto'

ruoyi-fastapi-backend/app.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@
1212
port=AppConfig.app_port,
1313
root_path=AppConfig.app_root_path,
1414
reload=AppConfig.app_reload,
15+
workers=AppConfig.app_workers,
1516
)

ruoyi-fastapi-backend/common/annotation/log_annotation.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from async_lru import alru_cache
1111
from fastapi import Request
1212
from fastapi.responses import JSONResponse, ORJSONResponse, UJSONResponse
13-
from sqlalchemy.ext.asyncio import AsyncSession
1413
from starlette.status import HTTP_200_OK
1514
from typing_extensions import ParamSpec
1615
from user_agents import parse
@@ -20,7 +19,7 @@
2019
from config.env import AppConfig
2120
from exceptions.exception import LoginException, ServiceException, ServiceWarning
2221
from module_admin.entity.vo.log_vo import LogininforModel, OperLogModel
23-
from module_admin.service.log_service import LoginLogService, OperationLogService
22+
from module_admin.service.log_service import LogQueueService
2423
from utils.dependency_util import DependencyUtil
2524
from utils.log_util import logger
2625
from utils.response_util import ResponseUtil
@@ -63,8 +62,6 @@ async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
6362
request_name_list = get_function_parameters_name_by_type(func, Request)
6463
request = get_function_parameters_value_by_name(func, request_name_list[0], *args, **kwargs)
6564
DependencyUtil.check_exclude_routes(request, err_msg='当前路由不在认证规则内,不可使用Log装饰器')
66-
session_name_list = get_function_parameters_name_by_type(func, AsyncSession)
67-
query_db = get_function_parameters_value_by_name(func, session_name_list[0], *args, **kwargs)
6865
request_method = request.method
6966
user_agent = request.headers.get('User-Agent')
7067
# 获取操作类型
@@ -122,7 +119,7 @@ async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
122119
}
123120
)
124121

125-
await LoginLogService.add_login_log_services(query_db, LogininforModel(**login_log))
122+
await LogQueueService.enqueue_login_log(request, LogininforModel(**login_log), func_path)
126123
else:
127124
current_user = RequestContext.get_current_user()
128125
oper_name = current_user.user.user_name
@@ -145,7 +142,7 @@ async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
145142
operTime=oper_time,
146143
costTime=int(cost_time),
147144
)
148-
await OperationLogService.add_operation_log_services(query_db, operation_log)
145+
await LogQueueService.enqueue_operation_log(request, operation_log, func_path)
149146

150147
return result
151148

ruoyi-fastapi-backend/common/constant.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,16 @@ class JobConstant:
133133
JOB_WHITE_LIST = ['module_task']
134134

135135

136+
class LockConstant:
137+
"""
138+
分布式锁常量
139+
"""
140+
141+
APP_STARTUP_LOCK_KEY = 'app:startup:lock'
142+
LOCK_EXPIRE_SECONDS = 60
143+
LOCK_RENEWAL_INTERVAL = 20
144+
145+
136146
class MenuConstant:
137147
"""
138148
菜单常量

0 commit comments

Comments
 (0)