Squashed 'paste-framework/' content from commit 34e8684

git-subtree-dir: paste-framework
git-subtree-split: 34e8684c4bc3cebbe177509f42ab4ef5b5425a7a
This commit is contained in:
zwf
2026-06-02 19:09:22 +08:00
commit 4729698049
107 changed files with 21484 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
{
"app_name": "Redis Stream Demo",
"redis_desc": "Redis 数据库连接配置及相关描述",
"redis": {
"connection": {
"url": "redis://:HaitenRedis@20250703@100.64.0.1:3379/2",
"max_connections": 1000,
"encoding": "utf-8",
"decode_responses": true
},
"streams": {
"demo": {
"group": "DEMO_PROCESSORS",
"consumer": "demo_worker"
}
}
},
"logger_desc": "用于日志输出的配置,各服务可以有自己的配置,但要使用独立配置时,必须编写额外代码",
"logger": {
"default": {
"desc": "默认日志配置,该配置小节的名称已经配置在 PASTE 框架中",
"basic": {
"filename": "logs/root.log",
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
"level": 20
},
"filename": "logs/default.log",
"name": "Demo",
"max_bytes": 20971520,
"backup_count": 40
}
},
"tornado_desc": "用于 Tornado 服务的配置,每一项后面允许设置多个服务",
"tornado": {
"demo": {
"autoreload": false,
"handlers_pkg": "examples.03_redis_stream",
"port": 9000,
"static_path": "static",
"template_path": "templates",
"swagger_title": "DemoAPI",
"swagger_description": "Demo API",
"swagger_api_version": "1.0.1",
"swagger_contact": "email@qq.com"
}
},
"version": "1.0.1"
}
+69
View File
@@ -0,0 +1,69 @@
import datetime
import json
import logging
from paste.core.logging import echo_log
from paste.db.redis import StreamActor
from paste.web.decorators import route
from paste.web.handler import RequestHandler
@route("/stream")
class MessageHandler(RequestHandler):
"""
演示请求发布 Redis Stream 消息。
"""
# 从配置中加载 Stream 配置路径
stream_config_path = "redis.streams.demo"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 初始化 StreamActor 实例(按配置创建)
self.actor = StreamActor.new_actor(self.stream_config_path)
async def post(self):
"""
接收前端 POST 请求,发布消息到 Redis Stream,立即响应。
请求体格式:
{
"user_id": "123",
"event": "login",
"data": {"ip": "192.168.1.1"}
}
"""
try:
# 1. 获取请求参数
body = self.request_arguments()
user_id = body.get("user_id")
event = body.get("event")
data = body.get("data", {})
if not user_id or not event:
self.response_error(
Exception("参数缺失:必须提供 user_id 和 event"),
status_code=400,
api_status_code=400
)
return
# 2. 构造消息数据
message_data = {
"user_id": user_id,
"event": event,
"timestamp": datetime.datetime.now(datetime.timezone.utc).isoformat() + 'Z',
"data": json.dumps(data)
}
# 3. 异步发布消息(立即返回,不等待消费)
msg_id = await self.actor.publish(message_data)
# 4. 响应成功
self.response_ok(
message="消息已成功发布",
message_id=msg_id,
stream=self.stream_config_path
)
except Exception as e:
echo_log('异常', logging.ERROR, True)
self.response_error(e, status_code=500, api_status_code=500)
+20
View File
@@ -0,0 +1,20 @@
from tornado.ioloop import IOLoop
from paste.core import config
from paste.core.logging import set_logger_config
from paste.web.application import Application
if __name__ == "__main__":
# 日志配置
logger_config_name = 'logger.default'
set_logger_config(logger_config_name)
# 应用配置
demo_config: dict = config.get_config('tornado.demo', {})
port = config.get_config('tornado.demo.port', 9000)
# 创建应用
app = Application(**demo_config)
app.listen(port)
handlers_pkg = config.get_config('tornado.demo.handlers_pkg')
print(f"App {handlers_pkg} is running at http://localhost:{port}")
# 启动监听
IOLoop.current().start()
+152
View File
@@ -0,0 +1,152 @@
"""
演示 Redis Stream 消息队列服务。
"""
import asyncio
import logging
import os
import socket
import sys
from typing import Optional
import redis
from paste.core import aio_pool
from paste.core.logging import set_logger_config, echo_log, get_logger
from paste.db.redis import StreamActor
from paste.service.daemonize import DaemonizeService
logger_config_name = 'logger.default'
"""
配置文件中日志配置字段名称。
"""
current_event_loop = None
"""
事件循环对象。
"""
pid_file = os.path.join(os.path.curdir, 'stream_service.pid')
"""
PID 文件路径。
"""
service_name = 'Redis Stream 消息队列服务'
"""
服务名称。
"""
# 配置路径:从 config.json 中读取
stream_config_path = "redis.streams.demo"
# 创建 StreamActor 实例
stream_actor: Optional[StreamActor] = None
async def process_message(data: dict):
"""
业务回调:处理每条消息
"""
user_id = data.get("user_id", "unknown")
event = data.get("event", "")
stream_data = data.get("data", "")
timestamp = data.get("timestamp", "")
echo_log(f"消费消息: user_id={user_id}, event='{event}', stream_data='{stream_data}', time={timestamp}")
# 模拟处理:写入数据库、发送邮件、更新缓存...
# 示例:记录日志 + 模拟耗时
for i in range(10):
echo_log(f"后台任务开始执行-{i}...")
await asyncio.sleep(0.8)
echo_log(f"消息处理完成: {user_id}")
return True
def current_loop() -> asyncio.AbstractEventLoop:
"""
这里必须采用方法,在适当的时间点创建事件循环对象,否则会导致服务无法启动。
:return: 事件循环对象
"""
global current_event_loop
if current_event_loop is None:
current_event_loop = asyncio.new_event_loop()
return current_event_loop
def start_service():
"""
控制台服务方式启动。
"""
set_logger_config(logger_config_name)
echo_log(f"正在启动{service_name}...")
try:
# 检测 Redis 连接
echo_log('检测 Redis 服务...')
_runner = aio_pool.get_aio_runner()
_runner(StreamActor.ping())
echo_log('Redis 服务正常.')
# 创建 StreamActor 监听服务
global stream_actor
stream_actor = StreamActor.new_actor(stream_config_path)
echo_log(f"{service_name}已启动,正在监听{service_name}...")
_runner(stream_actor.run_forever(process_message, is_delete=True))
except (redis.exceptions.TimeoutError, socket.timeout):
echo_log('Redis 服务异常.', level=logging.ERROR, is_log_exc=True)
echo_log(f"{service_name}启动失败.")
except KeyboardInterrupt:
echo_log(msg='KeyboardInterrupt')
stop_service()
except Exception as e:
echo_log(msg=e, level=logging.ERROR, is_log_exc=True)
echo_log(f"{service_name}因未知异常启动失败.")
def stop_service():
"""
停止服务。
"""
echo_log(f"正在停止{service_name}...")
# 停止监听
stream_actor.subscribe_stop()
# 停止事件循环
current_loop().stop()
echo_log(f"{service_name}已停止.")
def start():
"""
驻内存服务方式启动。
"""
set_logger_config(logger_config_name)
get_logger()
ds = DaemonizeService(pid_file=pid_file, name=service_name)
ds.set_start_callback(start_service)
ds.set_term_callback(stop_service)
ds.start()
def stop():
"""
驻内存服务方式停止。
"""
set_logger_config(logger_config_name)
get_logger()
ds = DaemonizeService(pid_file=pid_file, name=service_name)
ds.set_start_callback(start_service)
ds.set_term_callback(stop_service)
ds.stop()
if __name__ == "__main__":
if len(sys.argv) > 1:
if sys.argv[1] == "start":
start_service()
elif sys.argv[1] == "stop":
stop_service()
else:
print("用法: python service/stream_service.py start")
else:
start_service()