as工程sync慢

博客提到,某些情况下与电脑连接的网络有关,通过切换网络可实现秒sync完成,强调了网络对sync操作的影响。

有时是跟电脑连接的网络有关,切换网络秒sync完

import asyncio from functools import partial from anyio import Event, fail_after, run, sleep from collections import deque from src.websocket.utils import YWSServerManager, _server_lock # 在全局创建队列 - 添加内存限制和背压控制 MAX_QUEUE_SIZE = 1000 # 最大队列大小 MEMORY_THRESHOLD = 0.8 # 内存使用率阈值(80%) processing_queue = asyncio.Queue(maxsize=MAX_QUEUE_SIZE) # 兼容包内/脚本直接运行两种方式的导入 try: from .utils import create_yws_server, create_yws_provider # 导入布局数据服务(包内相对导入) from ..base.layout_data_service import LayoutDataService # 导入YJS事件处理器 from .yjs_event_processor import YJSEventProcessor, YJSOperationExecutor, YJSEventItem # 导入自定义日志模块 from ..base.logger_config import log_yjs_operation except ImportError: import os, sys _current_dir = os.path.dirname(__file__) _src_dir = os.path.abspath(os.path.join(_current_dir, "..")) if _src_dir not in sys.path: sys.path.insert(0, _src_dir) # 绝对导入(将 src 加入 sys.path 后) from websocket.utils import create_yws_server, create_yws_provider from base.layout_data_service import LayoutDataService from websocket.yjs_event_processor import YJSEventProcessor, YJSOperationExecutor, YJSEventItem # 导入自定义日志模块 from ..base.logger_config import log_yjs_operation from pycrdt import Array, Map, Decoder, read_message, Doc, TransactionEvent, handle_sync_message, create_sync_message from typing import Optional, Any, Tuple, AsyncGenerator # 初始化布局数据服务(实例在每次回调前确保绑定到正确工程的连接) layout_service = LayoutDataService() # 初始化YJS事件处理器 event_processor = YJSEventProcessor() operation_executor = YJSOperationExecutor(layout_service) # 导入堆损坏检测器 try: # 延迟导入,避免启动时循环依赖 from .yjs_store import YJSStore except Exception: YJSStore = None def _activate_project_db(project_name: str): """在处理回调前,确保切换到对应工程的数据库连接池。 注意:这里假定 BaseDAO 访问的是全局 DBpool; 我们通过 MultiProjectManager 切换当前工程来影响后续 DAO 使用的连接。 """ try: from src.base.multi_project_manager import project_manager # 确保目标工程连接有效,并切换全局 DBpool if project_name in [p.name for p in project_manager.get_all_projects()]: if not project_manager.get_project_db_pool(project_name): import asyncio loop = asyncio.get_event_loop() loop.create_task(project_manager.connect_to_database(project_name)) project_manager.activate_project_db_pool(project_name) except Exception: pass # 旧的复杂处理函数已被新的事件处理器替代 def callback(event, key=None, project_name: str = None): """YJS数据更新回调 - 使用新的事件处理器""" # log_yjs_operation( # operation_type="callback_start", # entity_type="websocket", # entity_id="callback", # project_name=project_name, # details="=== YJS数据更新回调 ===", # level="INFO" # ) for item in event: # log_yjs_operation( # operation_type="event_item", # entity_type="websocket", # entity_id="item", # project_name=project_name, # details=f"Keys: {item.keys}, Payload: {item.path}", # level="DEBUG" # ) try: # 创建YJS事件项 # print(f"item.keys: {item.keys}") # print(f"item.target: {item.target}") # print(f"item.path: {item.path}") # 过滤空路径的全量/初始化消息,避免写入数据库 if not hasattr(item, 'path') or not item.path: log_yjs_operation( operation_type="skip_empty_path", entity_type="websocket", entity_id="filter", project_name=project_name, details="检测到空 path 的初始化/快照消息,已跳过持久化处理", level="INFO" ) continue event_item = YJSEventItem( keys=item.keys if hasattr(item, 'keys') else {}, target=item.target, path=item.path if hasattr(item, 'path') else [] ) asyncio.run_coroutine_threadsafe( processing_queue.put((event_item, project_name)), asyncio.get_event_loop() ) # 使用事件处理器解析事件 # operations = event_processor.process_event(event_item) # if operations: # # 将操作放入队列(生产者) # asyncio.run_coroutine_threadsafe( # processing_queue.put((event_item, project_name)), # asyncio.get_event_loop() # ) # # # 异步执行重IO操作,避免阻塞事件循环 # # async def _execute_and_log_in_background(proj_name): # # try: # # event_item = YJSEventItem( # # keys=item.keys if hasattr(item, 'keys') else {}, # # target=item.target, # # path=item.path if hasattr(item, 'path') else [] # # ) # # # ops = await asyncio.to_thread( # # # event_processor.process_event, # # # event_item # # # ) # # ops = event_processor.process_event(event_item) # # log_yjs_operation( # # operation_type="event_parsing", # # entity_type="websocket", # # entity_id="parsing", # # project_name=project_name, # # details=f"解析到 {len(ops)} 个操作", # # level="INFO" # # ) # # # 1. 在后台线程中执行数据库操作 # # results = await asyncio.to_thread( # # operation_executor.execute_operations, ops, proj_name # # ) # # log_yjs_operation( # # operation_type="operation_execution", # # entity_type="websocket", # # entity_id="execution", # # project_name=proj_name, # # details=f"后台操作执行结果: {results}", # # level="INFO" # # ) # # # # 2. 在后台线程中记录到 ystore # # if YJSStore is not None and proj_name: # # ystore = YJSStore(proj_name) # # op_dicts = [op.to_dict() for op in ops] # # count = await asyncio.to_thread( # # ystore.log_operations, op_dicts, source_project=proj_name # # ) # # log_yjs_operation( # # operation_type="operation_logged", # # entity_type="websocket", # # entity_id="logging", # # project_name=proj_name, # # details=f"成功后台记录 {count} 条操作到 yjs_operation_logs", # # level="INFO" # # ) # # except Exception as e: # # log_yjs_operation( # # operation_type="background_operation_error", # # entity_type="websocket", # # entity_id="error", # # project_name=proj_name, # # details=f"后台执行或记录操作时出错: {e}", # # level="ERROR" # # ) # # # 创建一个后台任务来执行,不阻塞当前回调 # #asyncio.create_task(_execute_and_log_in_background(project_name)) except Exception as e: log_yjs_operation( operation_type="callback_error", entity_type="websocket", entity_id="error", project_name=project_name, details=f"处理YJS数据时发生错误: {e}", level="ERROR" ) import traceback traceback.print_exc() async def consumer_task_wrapper(): """ 一个包装器,通过在发生崩溃时自动重启来确保 consumer_task 始终在运行。 """ while True: try: # 运行核心的消费者任务 await consumer_task() except asyncio.CancelledError: # 如果是正常的取消操作(如程序关闭),则记录日志并退出 log_yjs_operation( operation_type="consumer_shutdown", entity_type="system", entity_id="lifecycle", details="Consumer task wrapper was cancelled, shutting down gracefully.", level="INFO" ) break except Exception as e: # 如果发生任何其他意外崩溃 import traceback # 记录严重错误日志,包括完整的堆栈信息 log_yjs_operation( operation_type="consumer_crash", entity_type="system", entity_id="critical_error", details=f"""CRITICAL: consumer_task crashed unexpectedly and will be restarted: {e} {traceback.format_exc()}""", level="CRITICAL" ) # 等待5秒后重启,防止因连续崩溃导致CPU占用过高 await asyncio.sleep(5) async def consumer_task(): """消费者协程:从队列中读取并处理数据,添加内存清理和性能监控""" from src.base.multi_project_manager import project_manager import gc import time # 性能监控变量 processed_count = 0 start_time = time.time() last_gc_time = start_time while True: operations, project_name = None, None try: # 定期内存清理(每处理100个事件或5分钟执行一次GC) current_time = time.time() if processed_count % 100 == 0 or current_time - last_gc_time > 300: gc.collect() # 强制垃圾回收 last_gc_time = current_time # 记录内存使用情况 try: import psutil memory_info = psutil.virtual_memory() log_yjs_operation( operation_type="memory_cleanup", entity_type="websocket", entity_id="memory", project_name="system", details=f"内存使用: {memory_info.percent}%, 已处理 {processed_count} 个事件", level="DEBUG" ) except Exception: pass # 从队列获取任务(消费者)- 添加超时机制 try: event_item, project_name = await asyncio.wait_for( processing_queue.get(), timeout=30.0 # 30秒超时 ) except asyncio.TimeoutError: # 队列长时间为空,记录状态 log_yjs_operation( operation_type="consumer_idle", entity_type="websocket", entity_id="idle", project_name="system", details="消费者任务空闲超过30秒", level="DEBUG" ) continue # 处理事件 operations = event_processor.process_event(event_item) # 性能统计 processed_count += 1 if processed_count % 50 == 0: # 每处理50个事件记录一次性能 elapsed_time = time.time() - start_time events_per_second = processed_count / elapsed_time if elapsed_time > 0 else 0 log_yjs_operation( operation_type="performance_stats", entity_type="websocket", entity_id="performance", project_name="system", details=f"处理速度: {events_per_second:.2f} 事件/秒, 总处理: {processed_count}", level="INFO" ) results = operation_executor.execute_operations(operations, project_name) # 记录到ystore - 使用实例缓存和连接池优化 if YJSStore is not None and project_name and operations: try: # 分批处理大操作列表,避免内存峰值 batch_size = 100 # 每批处理100个操作 total_count = 0 # 使用实例缓存获取YJSStore实例,避免重复创建 ystore = YJSStore.get_instance(project_name) # 分批转换和记录操作 for i in range(0, len(operations), batch_size): batch_operations = operations[i:i + batch_size] # 安全转换操作到字典格式 operation_dicts = [] for op in batch_operations: try: op_dict = op.to_dict() operation_dicts.append(op_dict) except Exception as conv_error: log_yjs_operation( operation_type="operation_conversion_error", entity_type="websocket", entity_id="conversion", project_name=project_name, details=f"操作转换失败: {conv_error},跳过该操作", level="ERROR" ) continue if operation_dicts: # 异步记录到ystore,添加超时机制 try: count = await asyncio.wait_for( asyncio.to_thread( ystore.log_operations, operation_dicts, source_project=project_name ), timeout=30.0 # 30秒超时 ) total_count += count log_yjs_operation( operation_type="batch_operation_logged", entity_type="websocket", entity_id="batch_logging", project_name=project_name, details=f"成功记录批次 {i//batch_size + 1},本批 {count} 条操作", level="DEBUG" ) except asyncio.TimeoutError: log_yjs_operation( operation_type="ystore_log_timeout", entity_type="websocket", entity_id="timeout", project_name=project_name, details=f"ystore记录操作超时,批次 {i//batch_size + 1} 被跳过", level="WARNING" ) except Exception as log_error: log_yjs_operation( operation_type="ystore_log_error", entity_type="websocket", entity_id="log_error", project_name=project_name, details=f"ystore记录操作失败: {log_error},批次 {i//batch_size + 1} 被跳过", level="ERROR" ) if total_count > 0: log_yjs_operation( operation_type="operations_logged_summary", entity_type="websocket", entity_id="summary", project_name=project_name, details=f"总计记录 {total_count}/{len(operations)} 条操作到 yjs_operation_logs", level="INFO" ) else: log_yjs_operation( operation_type="no_operations_logged", entity_type="websocket", entity_id="no_log", project_name=project_name, details="所有操作记录尝试均失败", level="WARNING" ) except Exception as e: log_yjs_operation( operation_type="ystore_setup_error", entity_type="websocket", entity_id="setup_error", project_name=project_name, details=f"ystore初始化或处理失败: {e}", level="ERROR" ) # 标记任务完成 processing_queue.task_done() # 动态调整处理间隔,根据队列长度调整处理速度 queue_size = processing_queue.qsize() if queue_size > MAX_QUEUE_SIZE * 0.5: # 队列超过50%容量时加速处理 await asyncio.sleep(0.01) # 快速处理 elif queue_size > MAX_QUEUE_SIZE * 0.2: # 队列超过20%容量时正常处理 await asyncio.sleep(0.05) else: # 队列较空时速处理,节省CPU await asyncio.sleep(0.1) except Exception as e: log_yjs_operation( operation_type="consumer_error", entity_type="websocket", entity_id="error", project_name=project_name or "Unknown", details=f"消费者处理时出错: {e}", level="ERROR" ) # 错误情况下确保任务完成,避免阻塞队列 try: processing_queue.task_done() except ValueError: # task_done() raises ValueError if called more than once pass except Exception: pass # Ignore other errors during error handling # 错误后短暂休息,避免连续错误导致CPU占用过高 await asyncio.sleep(1.0) def doc_callback(event: TransactionEvent, project_name: str = None): """文档变更回调函数""" try: log_yjs_operation( operation_type="doc_callback_start", entity_type="websocket", entity_id="doc_callback", project_name=project_name, details="=== 文档变更回调 ===", level="INFO" ) y = Doc() y.apply_update(event.update) list_ = list(y.keys()) log_yjs_operation( operation_type="doc_keys", entity_type="websocket", entity_id="doc_keys", project_name=project_name, details=f"Document keys: {list_}", level="DEBUG" ) for item in list_: try: log_yjs_operation( operation_type="doc_item_processing", entity_type="websocket", entity_id="doc_item", project_name=project_name, details=f"Processing document item: {item}", level="DEBUG" ) map_ = y.get(item, type=Map) if hasattr(map_, 'to_py'): data = map_.to_py() # 如果数据是有效的布局数据,尝试更新数据库 if isinstance(data, dict) and any(key in data for key in ['nodes', 'edges', 'layers', 'ssios', 'loadtypes', 'agvtypes']): log_yjs_operation( operation_type="doc_layout_detected", entity_type="websocket", entity_id="layout_detection", project_name=project_name, details="检测到布局数据,开始更新数据库...", level="INFO" ) result = layout_service.process_yjs_update(data, project_name=project_name) log_yjs_operation( operation_type="doc_db_update_result", entity_type="websocket", entity_id="db_result", project_name=project_name, details=f"数据库更新结果: {result}", level="INFO" ) # 记录文档级别的操作到 yjs_operation_logs try: if YJSStore is not None and project_name: ystore = YJSStore(project_name) # 为每个检测到的实体类型创建操作记录 operations = [] for entity_key in ['nodes', 'edges', 'layers', 'ssios', 'loadtypes', 'agvtypes', 'stations', 'locations', 'actions', 'wops']: if entity_key in data and data[entity_key]: operation = { 'entity_type': entity_key, 'operation_type': 'update', 'entity_id': f'doc_{entity_key}', 'data': data[entity_key], 'old_data': None } operations.append(operation) if operations: count = ystore.log_operations(operations, source_project=project_name) log_yjs_operation( operation_type="doc_operation_logged", entity_type="websocket", entity_id="doc_logging", project_name=project_name, details=f"成功记录 {count} 条文档操作到 yjs_operation_logs", level="INFO" ) except Exception as e: log_yjs_operation( operation_type="doc_operation_logging_error", entity_type="websocket", entity_id="doc_logging_error", project_name=project_name, details=f"记录文档操作到 yjs_operation_logs 失败: {e}", level="ERROR" ) else: log_yjs_operation( operation_type="doc_invalid_format", entity_type="websocket", entity_id="invalid_format", project_name=project_name, details=f"数据不是有效的布局数据格式: {data}", level="WARNING" ) else: log_yjs_operation( operation_type="doc_no_to_py", entity_type="websocket", entity_id="no_to_py", project_name=project_name, details=f"Map 对象没有 to_py 方法: {map_}", level="WARNING" ) except Exception as item_e: log_yjs_operation( operation_type="doc_item_error", entity_type="websocket", entity_id="item_error", project_name=project_name, details=f"处理文档项 {item} 时发生错误: {item_e}", level="ERROR" ) import traceback traceback.print_exc() except Exception as e: log_yjs_operation( operation_type="doc_callback_error", entity_type="websocket", entity_id="callback_error", project_name=project_name, details=f"文档回调处理失败: {e}", level="ERROR" ) import traceback traceback.print_exc() _global_server_manager: Optional['YWSServerManager'] = None async def get_global_server_manager() -> YWSServerManager: """ 获取全局服务器管理器实例 """ global _global_server_manager async with _server_lock: if _global_server_manager is None: _global_server_manager = YWSServerManager() return _global_server_manager async def main(sync_port=12345, project_name: str = None, room_name: str = None): """ 主函数,启动指定项目的 YJS 服务 """ global _global_server_manager async with _server_lock: if _global_server_manager is None: _global_server_manager = YWSServerManager() server_manager = _global_server_manager try: # 为项目启动服务器(如果尚未运行) if sync_port not in server_manager.servers: await server_manager.start_server(project_name, sync_port) # 添加 provider ydoc, server_websocket = await server_manager.add_provider(project_name, room_name) # ydoc是与websocket连接的实例,preloaded_ydoc是导入时预加载了数据的实例 from .yjs_factory import yjs_manager # preloaded_ydoc = yjs_manager.get_document(project_name) log_yjs_operation( operation_type="client_connected", entity_type="websocket", entity_id="client", project_name=project_name, details='客户端创建成功,准备加载数据', level="INFO" ) # 核心修复:直接将预加载文档的内容完整地复制到服务器文档中 # 避免使用 get_update/apply_update,此方法更直接可靠 # with ydoc.transaction(): # # 1. 清空当前服务器文档,确保是干净状态 # for key in list(ydoc.keys()): # del ydoc[key] # # 2. 遍历预加载文档的所有顶层项目,并复制到服务器文档 # for key, value in preloaded_ydoc.items(): # ydoc[key] = value # # log_yjs_operation( # operation_type="data_copy_complete", # entity_type="websocket", # entity_id="copy", # project_name=project_name, # details='已通过直接复制,将预加载YDoc的内容同步到服务器YDoc', # level="INFO" # ) # 用同步后的、与websocket连接的ydoc,替换掉管理器中的旧ydoc实例 # 这样后续所有操作都将基于这个与客户端连接的ydoc yjs_manager.set_document(project_name, ydoc) # 从YDoc中获取 'elements' Map,如果不存在则创建 if 'elements' in ydoc: ymap2 = ydoc['elements'] else: ydoc['elements'] = ymap2 = Map() # 确保所有必要的子Map都存在,以防快照不完整 entity_types = ['edges', 'nodes', 'stations', 'locations', 'agvtypes', 'loadtypes', 'actions', 'wops', 'ssios', 'layers', 'angleNodes'] for entity_type in entity_types: if entity_type not in ymap2: ymap2[entity_type] = Map() # 从 yjs_operation_logs 表中读取原来工程的数据并写入共享数据结构 try: if YJSStore is not None and project_name: log_yjs_operation( operation_type="load_operation_logs", entity_type="websocket", entity_id="data_loading", project_name=project_name, details="开始从 yjs_operation_logs 加载工程数据...", level="INFO" ) ystore = YJSStore(project_name) # 从 yjs_operation_logs 表中获取所有操作记录 operation_logs = ystore.iter_operation_logs(since_timestamp=0.0) if operation_logs: log_yjs_operation( operation_type="logs_found", entity_type="websocket", entity_id="data_loading", project_name=project_name, details=f"找到 {len(operation_logs)} 条操作记录,开始重建数据...", level="INFO" ) # 按实体类型组织数据 entity_data = {} for log in operation_logs: entity_type = log.get('entity_type') entity_id = log.get('entity_id') operation_type = log.get('operation_type') data = log.get('data') # 跳过无效数据 if not entity_type or not entity_id: if not data and operation_type=='delete': pass else: continue # 初始化实体类型字典 if entity_type not in entity_data: entity_data[entity_type] = {} # 根据操作类型处理数据 if operation_type == 'delete': # 删除操作,从字典中移除 if entity_id in entity_data[entity_type]: del entity_data[entity_type][entity_id] else: # 添加或更新操作,写入最新数据 entity_data[entity_type][entity_id] = data # 将重建的数据写入共享数据结构 for entity_type, entities in entity_data.items(): if entity_type in ymap2.keys() and entities: entity_map = ymap2[entity_type] for entity_id, entity_data in entities.items(): entity_map[entity_id] = entity_data log_yjs_operation( operation_type="data_loaded", entity_type="websocket", entity_id=f"load_{entity_type}", project_name=project_name, details=f"已加载 {len(entities)} 个 {entity_type} 数据项", level="INFO" ) else: log_yjs_operation( operation_type="no_operation_logs", entity_type="websocket", entity_id="data_loading", project_name=project_name, details="未找到操作记录,尝试加载最新状态...", level="INFO" ) # 如果没有操作记录,尝试获取最新状态 try: # 这里可以添加获取最新状态的备用方法 # 例如从数据库直接读取当前状态 pass except Exception as state_error: log_yjs_operation( operation_type="state_loading_error", entity_type="websocket", entity_id="data_loading", project_name=project_name, details=f"加载最新状态失败: {state_error}", level="WARNING" ) except Exception as e: log_yjs_operation( operation_type="data_loading_error", entity_type="websocket", entity_id="data_loading", project_name=project_name, details=f"从操作日志加载数据时发生错误: {e}", level="ERROR" ) import traceback traceback.print_exc() # ymap2 = ymap7 = Map({'123':123}) # ydoc["map2"] = ymap2 # ymap2['key1'] = "value1" # ymap2['key2'] = ymap7 # ydoc.on('update') # 订阅变更事件 def on_change(event): # 在每次事件处理前激活对应工程的 DB 连接 if project_name: _activate_project_db(project_name) callback(event, "test_key", project_name=project_name) # loop = asyncio.get_running_loop() # loop.create_task(ystore.write(event.update)) # log_yjs_operation( operation_type="map_content_updated", entity_type="websocket", entity_id="map_update", project_name=project_name, details="Map content updated (full snapshot logging is disabled for performance)", level="DEBUG" ) # def on_doc_change(event): # if project_name: # _activate_project_db(project_name) # doc_callback(event, project_name=project_name) # subscription = ymap.observe_deep(on_change) sub2 = ymap2.observe_deep(on_change) # sub2 = ydoc.observe(on_doc_change) # 新增:挂载原始更新(raw update)观察器,用于调用 ystore.write # 这将把所有二进制更新数据持久化到 yjs_updates 表,确保数据完整性 if YJSStore is not None and project_name: ystore = YJSStore(project_name) # pycrdt的observe回调是同步的,但我们的write是异步的 # 因此定义一个同步包装器,在其中创建异步任务 def raw_update_callback(event): # 从同步函数中安全地调用异步代码 try: #ystore.write(event.update) loop = asyncio.get_running_loop() loop.create_task(ystore.write(event.update)) except Exception as e: log_yjs_operation( operation_type="raw_update_callback_error", entity_type="websocket", entity_id="observer", project_name=project_name, details=f'原始更新回调中创建任务失败: {e}', level="ERROR" ) # 订阅整个文档的原始二进制更新事件 # ydoc.observe(raw_update_callback) #ymap2.observe_deep(raw_update_callback) log_yjs_operation( operation_type="raw_update_observer_attached", entity_type="websocket", entity_id="observer", project_name=project_name, details='已挂载原始更新(raw update)观察器,将自动持久化到yjs_updates表', level="INFO" ) await consumer_task_wrapper() # consumer = asyncio.create_task() # sub3 = ydoc.get_update() # try: # while True: # await asyncio.sleep(1) # 保持事件循环运行 # finally: # # 优雅关停:当 main 任务被取消时,这个 finally 块会被执行 # log_yjs_operation( # operation_type="shutdown_start", # entity_type="websocket", # entity_id="main", # project_name=project_name, # details='开始关停YJS服务实例...', # level="INFO" # ) # # # 给 consumer 一些时间来处理剩余的任务 # try: # # 等待最多 30 秒让消费者完成工作 # await asyncio.wait_for(consumer, timeout=60.0) # except asyncio.TimeoutError: # log_yjs_operation( # operation_type="consumer_shutdown_timeout", # entity_type="websocket", # entity_id="consumer", # project_name=project_name, # details='消费者任务关闭超时,正在强制取消...', # level="WARNING" # ) # if not consumer.done(): # consumer.cancel() # try: # await consumer # except asyncio.CancelledError: # pass # except asyncio.CancelledError: # print('消费者任务已成功关闭') # log_yjs_operation( # operation_type="consumer_shutdown_success", # entity_type="websocket", # entity_id="consumer", # project_name=project_name, # details='消费者任务已成功关闭。', # level="INFO" # ) # except Exception as e: log_yjs_operation( operation_type="main_error", entity_type="websocket", entity_id="main", project_name=project_name, details=f"项目 {project_name} 发生错误: {e}", level="ERROR" ) import traceback traceback.print_exc() raise # finally: # if sub2: # sub2.unobserve() async def run_server(): """运行服务器并保持连接""" try: await main() except KeyboardInterrupt: log_yjs_operation( operation_type="server_interrupted", entity_type="websocket", entity_id="server", project_name=None, details="服务器被中断", level="INFO" ) if __name__ == "__main__": from anyio import run run(run_server) # ========== 多实例启动/停止接口(供工厂调用) ========== def start(project_name: str, port: int, room_name: str = None) -> asyncio.Task: """启动一个YJS服务实例,返回运行任务。 - project_name: 工程名,用于日志/房间隔离 - port: 监听端口(每个工程独立端口) - room_name: 房间名(默认等于 project_name) """ return asyncio.create_task(main(project_name=project_name, room_name=room_name)) async def stop(task: asyncio.Task) -> None: """停止由 start 返回的任务。""" if task and not task.done(): task.cancel() try: await task except asyncio.CancelledError: pass 这个里面的cusumer处理队列里面的数据未完成的时候,又触发了callback往队列里面塞数据,,会有什么问题吗
最新发布
11-01
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值