forked from erp-dev/erp
feat: sse && multi_merchant completed
This commit is contained in:
154
sse/simple_sse.py
Normal file
154
sse/simple_sse.py
Normal file
@@ -0,0 +1,154 @@
|
||||
"""
|
||||
简化的SSE实现,专门解决uvicorn无法退出的问题
|
||||
"""
|
||||
import json
|
||||
import time
|
||||
import threading
|
||||
from collections import defaultdict
|
||||
from django.http import HttpResponse
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
|
||||
# 全局状态
|
||||
_clients = {} # 存储客户端连接的状态
|
||||
_client_counter = 0
|
||||
_shutdown_flag = threading.Event()
|
||||
_lock = threading.Lock()
|
||||
|
||||
|
||||
def get_next_client_id():
|
||||
"""获取下一个客户端ID"""
|
||||
global _client_counter
|
||||
with _lock:
|
||||
_client_counter += 1
|
||||
return _client_counter
|
||||
|
||||
|
||||
def add_client(client_id):
|
||||
"""添加客户端"""
|
||||
with _lock:
|
||||
_clients[client_id] = {
|
||||
'active': True,
|
||||
'messages': [],
|
||||
'last_heartbeat': time.time()
|
||||
}
|
||||
|
||||
|
||||
def remove_client(client_id):
|
||||
"""移除客户端"""
|
||||
with _lock:
|
||||
_clients.pop(client_id, None)
|
||||
|
||||
|
||||
def broadcast_message(message):
|
||||
"""广播消息给所有客户端"""
|
||||
with _lock:
|
||||
for client_id, client_data in _clients.items():
|
||||
if client_data['active']:
|
||||
client_data['messages'].append(message)
|
||||
|
||||
|
||||
def get_client_messages(client_id):
|
||||
"""获取客户端的消息"""
|
||||
with _lock:
|
||||
if client_id in _clients:
|
||||
messages = _clients[client_id]['messages'][:]
|
||||
_clients[client_id]['messages'].clear()
|
||||
_clients[client_id]['last_heartbeat'] = time.time()
|
||||
return messages
|
||||
return []
|
||||
|
||||
|
||||
def cleanup_clients():
|
||||
"""清理所有客户端"""
|
||||
global _shutdown_flag
|
||||
_shutdown_flag.set()
|
||||
with _lock:
|
||||
for client_data in _clients.values():
|
||||
client_data['active'] = False
|
||||
_clients.clear()
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def simple_sse_view(request):
|
||||
"""简化的SSE视图"""
|
||||
if request.method == 'OPTIONS':
|
||||
response = HttpResponse()
|
||||
origin = request.META.get('HTTP_ORIGIN')
|
||||
if origin:
|
||||
response['Access-Control-Allow-Origin'] = origin
|
||||
response['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
|
||||
response['Access-Control-Allow-Headers'] = 'authorization, content-type, cache-control, accept'
|
||||
response['Access-Control-Allow-Credentials'] = 'true'
|
||||
response['Access-Control-Max-Age'] = '86400'
|
||||
return response
|
||||
|
||||
def event_generator():
|
||||
client_id = get_next_client_id()
|
||||
add_client(client_id)
|
||||
|
||||
try:
|
||||
# 发送连接成功消息
|
||||
yield f"data: {json.dumps({'type': 'connected', 'client_id': client_id})}\n\n"
|
||||
|
||||
# 主循环 - 使用更短的检查间隔
|
||||
iterations = 0
|
||||
while not _shutdown_flag.is_set():
|
||||
# 每5次迭代检查一次消息(约0.5秒)
|
||||
if iterations % 5 == 0:
|
||||
messages = get_client_messages(client_id)
|
||||
for message in messages:
|
||||
yield f"data: {json.dumps(message)}\n\n"
|
||||
|
||||
# 每50次迭代发送心跳(约5秒)
|
||||
if iterations % 50 == 0:
|
||||
yield f": heartbeat {time.time()}\n\n"
|
||||
|
||||
# 短暂睡眠,让出CPU并允许快速响应关闭信号
|
||||
time.sleep(0.1)
|
||||
iterations += 1
|
||||
|
||||
# 如果关闭标志被设置,退出循环
|
||||
if _shutdown_flag.is_set():
|
||||
break
|
||||
|
||||
# 发送关闭消息
|
||||
yield f"data: {json.dumps({'type': 'shutdown', 'message': 'Server shutting down'})}\n\n"
|
||||
|
||||
except Exception as e:
|
||||
print(f"SSE error for client {client_id}: {e}")
|
||||
finally:
|
||||
remove_client(client_id)
|
||||
print(f"Client {client_id} disconnected")
|
||||
|
||||
response = HttpResponse(event_generator(), content_type='text/event-stream')
|
||||
response['Cache-Control'] = 'no-cache'
|
||||
response['X-Accel-Buffering'] = 'no'
|
||||
|
||||
# CORS headers
|
||||
origin = request.META.get('HTTP_ORIGIN')
|
||||
if origin:
|
||||
response['Access-Control-Allow-Origin'] = origin
|
||||
response['Access-Control-Allow-Credentials'] = 'true'
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def broadcast_test_message():
|
||||
"""广播测试消息"""
|
||||
message = {
|
||||
'type': 'test',
|
||||
'message': 'Test message',
|
||||
'timestamp': time.time()
|
||||
}
|
||||
broadcast_message(message)
|
||||
return len(_clients)
|
||||
|
||||
|
||||
def get_status():
|
||||
"""获取状态"""
|
||||
with _lock:
|
||||
return {
|
||||
'clients': len(_clients),
|
||||
'shutdown': _shutdown_flag.is_set()
|
||||
}
|
||||
Reference in New Issue
Block a user