From 7c8db759de17368af8b0b167b191bfc248d6cbbb Mon Sep 17 00:00:00 2001 From: xiaohao <932379959@qq.com> Date: Tue, 26 Aug 2025 07:35:40 +0800 Subject: [PATCH 01/15] [fit] add python nacos plugin. --- framework/fit/python/conf/application.yml | 3 +- framework/fit/python/conf/fit.yml | 8 +- framework/fit/python/fitframework/const.py | 14 +- .../heart_beat_agent.py | 2 +- .../conf/application.yml | 2 + .../fit_py_nacos_registry/conf/info.yml | 2 + .../entity.py | 11 + .../heartbeat/heartbeat_service.py | 31 + .../service/nacos_registry_server.py | 726 ++++++++++++++++++ .../fitable_address_service.py | 2 +- .../fitable_meta_service.py | 2 +- .../online_fitable_service.py | 2 +- .../test/fitable_address_service_test.py | 2 +- 13 files changed, 799 insertions(+), 8 deletions(-) create mode 100644 framework/fit/python/plugin/fit_py_nacos_registry/conf/application.yml create mode 100644 framework/fit/python/plugin/fit_py_nacos_registry/conf/info.yml rename framework/fit/python/plugin/{fit_py_registry_client => fit_py_nacos_registry}/entity.py (95%) create mode 100644 framework/fit/python/plugin/fit_py_nacos_registry/heartbeat/heartbeat_service.py create mode 100644 framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py diff --git a/framework/fit/python/conf/application.yml b/framework/fit/python/conf/application.yml index 1e581840..41b81e7e 100644 --- a/framework/fit/python/conf/application.yml +++ b/framework/fit/python/conf/application.yml @@ -17,4 +17,5 @@ debug-console: true terminate-main: enabled: false local_ip: "localhost" -context-path: "" \ No newline at end of file +context-path: "" + diff --git a/framework/fit/python/conf/fit.yml b/framework/fit/python/conf/fit.yml index 51f86858..74e454f2 100644 --- a/framework/fit/python/conf/fit.yml +++ b/framework/fit/python/conf/fit.yml @@ -155,11 +155,17 @@ fit.public.genericables.2ac926e6e40245b78b7bdda23bcb727b: route: default: "ONLINE_FIT_SERVICE_FITABLE_ID" fit.public.genericables.modelengine.fit.registry.registry-service.query-running-fitables: - name: "QUERY_FITABLE_METAS_GEN_ID" + name: "query_fitable_metas_gen_id" tags: - "nonTraceable" route: default: "query-running-fitables" +fit.public.genericables.modelengine.fit.registry.registry-service.register-fitables: + name: 'register_fitables_gen_id' + tags: + - 'nonTraceable' + route: + default: 'register-fitables' fit.public.genericables.GET_FITABLES_OF_GENERICABLE_GEN_ID: name: "get_fitables_of_genericable" tags: diff --git a/framework/fit/python/fitframework/const.py b/framework/fit/python/fitframework/const.py index 766edf53..c08b6b7b 100644 --- a/framework/fit/python/fitframework/const.py +++ b/framework/fit/python/fitframework/const.py @@ -231,12 +231,24 @@ # registry server QUERY_FIT_SERVICE_GEN_ID = 'modelengine.fit.registry.registry-service.query-fitables-addresses' +QUERY_FIT_SERVICE_FIT_ID = 'query-fitables-addresses' SUBSCRIBE_FIT_SERVICE_GEN_ID = 'modelengine.fit.registry.registry-service.subscribe-fitables' +SUBSCRIBE_FIT_SERVICE_FIT_ID = 'subscribe-fitables' +UNSUBSCRIBE_FIT_SERVICE_GEN_ID = 'modelengine.fit.registry.registry-service.unsubscribe-fitables' +UNSUBSCRIBE_FIT_SERVICE_FIT_ID = 'unsubscribe-fitables' REGISTER_FIT_SERVICE_GEN_ID = 'modelengine.fit.registry.registry-service.register-fitables' +REGISTER_FIT_SERVICE_FIT_ID = 'register-fitables' +UNREGISTER_FIT_SERVICE_GEN_ID = 'modelengine.fit.registry.registry-service.unregister-fitables' +UNREGISTER_FIT_SERVICE_FIT_ID = 'unregister-fitables' QUERY_FITABLE_METAS_GEN_ID = 'modelengine.fit.registry.registry-service.query-running-fitables' +QUERY_FITABLE_METAS_FIT_ID = 'query-running-fitables' # heartbeat server -HEART_BEAT_GEN_ID = 'modelengine.fit.heartbeat.send-heartbeat' +SEND_HEART_BEAT_GEN_ID = 'modelengine.fit.heartbeat.send-heartbeat' +STOP_HEART_BEAT_GEN_ID = 'modelengine.fit.heartbeat.stop-heartbeat' +SEND_HEART_BEAT_FIT_ID = 'send-heartbeat' +STOP_HEART_BEAT_FIT_ID = 'stop-heartbeat' + # debugger DEBUGGER_START_FIT_ID = 'debugger_start_fitable_id' diff --git a/framework/fit/python/plugin/fit_py_heart_beat_agent/heart_beat_agent.py b/framework/fit/python/plugin/fit_py_heart_beat_agent/heart_beat_agent.py index 2fd79eb7..7edb27a7 100644 --- a/framework/fit/python/plugin/fit_py_heart_beat_agent/heart_beat_agent.py +++ b/framework/fit/python/plugin/fit_py_heart_beat_agent/heart_beat_agent.py @@ -71,7 +71,7 @@ def get_runtime_worker_id() -> str: pass -@fit(const.HEART_BEAT_GEN_ID) +@fit(const.SEND_HEART_BEAT_GEN_ID) def heartbeat(beat_info: List[HeartBeatInfo], address: HeartBeatAddress) -> bool: """ 可能返回 false,也可能抛出异常,也可能超时 """ pass diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/conf/application.yml b/framework/fit/python/plugin/fit_py_nacos_registry/conf/application.yml new file mode 100644 index 00000000..49bad100 --- /dev/null +++ b/framework/fit/python/plugin/fit_py_nacos_registry/conf/application.yml @@ -0,0 +1,2 @@ +nacos: + serverAddr: 127.0.0.1:8848 diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/conf/info.yml b/framework/fit/python/plugin/fit_py_nacos_registry/conf/info.yml new file mode 100644 index 00000000..f3899853 --- /dev/null +++ b/framework/fit/python/plugin/fit_py_nacos_registry/conf/info.yml @@ -0,0 +1,2 @@ +category: "system" +level: 4 \ No newline at end of file diff --git a/framework/fit/python/plugin/fit_py_registry_client/entity.py b/framework/fit/python/plugin/fit_py_nacos_registry/entity.py similarity index 95% rename from framework/fit/python/plugin/fit_py_registry_client/entity.py rename to framework/fit/python/plugin/fit_py_nacos_registry/entity.py index ffffa438..fa585c40 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/entity.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/entity.py @@ -219,4 +219,15 @@ def __hash__(self): def __repr__(self): return str(tuple(self.__dict__.values())) +class HeartBeatInfo: + def __init__(self, sceneType: str, aliveTime: int, initDelay: int): + self.sceneType: str = sceneType + self.aliveTime: int = aliveTime + self.initDelay: int = initDelay + + +class HeartBeatAddress: + def __init__(self, id_: str): + self.id = id_ + diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/heartbeat/heartbeat_service.py b/framework/fit/python/plugin/fit_py_nacos_registry/heartbeat/heartbeat_service.py new file mode 100644 index 00000000..0c6348f2 --- /dev/null +++ b/framework/fit/python/plugin/fit_py_nacos_registry/heartbeat/heartbeat_service.py @@ -0,0 +1,31 @@ +# -- encoding: utf-8 -- +# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved. +# This file is a part of the ModelEngine Project. +# Licensed under the MIT License. See License.txt in the project root for license information. +# ====================================================================================================================== +""" +功 能:服务上线相关功能。 +""" +from typing import List +from fitframework import fitable, const +from ..entity import HeartBeatInfo, HeartBeatAddress + +@fitable(const.SEND_HEART_BEAT_GEN_ID, const.SEND_HEART_BEAT_FIT_ID) +def send_heartbeat(heartbeatInfo: List[HeartBeatInfo], address: HeartBeatAddress) -> bool: + """ + 发送心跳信息。 + + @param heartbeatInfo: 表示待停止心跳信息列表 + @param address: 表示待停止心跳信息列表。 + """ + return True + +@fitable(const.STOP_HEART_BEAT_GEN_ID, const.STOP_HEART_BEAT_FIT_ID) +def stop_Heartbeat(heartbeatInfo: List[HeartBeatInfo], address: HeartBeatAddress) -> bool: + """ + 发送停止心跳信息。 + + @param heartbeatInfo: 表示待停止心跳信息列表。 + @param address: 表示待停止心跳信息列表。 + """ + return True \ No newline at end of file diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py new file mode 100644 index 00000000..5d571dda --- /dev/null +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py @@ -0,0 +1,726 @@ +# -- encoding: utf-8 -- +# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved. +# This file is a part of the ModelEngine Project. +# Licensed under the MIT License. See License.txt in the project root for license information. +# ====================================================================================================================== +""" +Service for providing Nacos registry center functionality. + +@author 董智豪 +@since 2025-06-04 +""" +import logging +import asyncio +import json +import re +from concurrent.futures import ThreadPoolExecutor +from typing import List, Dict, Set + +from v2.nacos import NacosNamingService, ClientConfigBuilder, RegisterInstanceParam, ListInstanceParam, \ + DeregisterInstanceParam, SubscribeServiceParam, ClientConfig, Instance, ListServiceParam, ServiceList + +from fitframework import fitable, const, value +from fitframework.api.logging import plugin_logger +from fitframework.utils.json_serialize_utils import json_serialize, json_deserialize +from ..entity import Worker, FitableMeta, FitableInfo, Application, FitableAddressInstance, GenericableInfo, \ + FitableMetaInstance, ApplicationInstance, Address, Endpoint + +@value('nacos.serverAddr', default_value=None) +def _get_nacos_server_addr() -> str: + """ + 获取 Nacos 服务器地址。 + :return: Nacos 服务器地址。 + """ + pass + + +@value('nacos.username', default_value=None) +def _get_nacos_username() -> str: + """ + 获取 Nacos 用户名。 + :return: Nacos 用户名。 + """ + pass + + +@value('nacos.password', default_value=None) +def _get_nacos_password() -> str: + """ + 获取 Nacos 密码。 + :return: Nacos 密码。 + """ + pass + + +@value('nacos.accessKey', default_value=None) +def _get_nacos_access_key() -> str: + """ + 获取 Nacos Access Key。 + :return: Nacos Access Key。 + """ + pass + + +@value('nacos.secretKey', default_value=None) +def _get_nacos_secret_key() -> str: + """ + 获取 Nacos Secret Key。 + :return: Nacos Secret Key。 + """ + pass + + +@value('nacos.namespace', default_value="") +def _get_nacos_namespace() -> str: + """ + 获取 Nacos 命名空间。 + :return: Nacos 命名空间。 + """ + pass + + +@value('nacos.isEphemeral', default_value=True, converter=bool) +def _get_heartbeat_isEphemeral() -> bool: + """ + 获取 Nacos 心跳是否为临时实例。 + :return: 是否为临时实例。 + """ + pass + + +@value('nacos.heartBeatInterval', default_value=5000, converter=int) +def _get_heartbeat_interval() -> int: + """ + 获取 Nacos 心跳间隔时间。 + :return: 心跳间隔时间,单位为毫秒。 + """ + pass + + +@value('nacos.heartBeatTimeout', default_value=15000, converter=int) +def _get_heartbeat_timeout() -> int: + """ + 获取 Nacos 心跳超时时间。 + :return: 心跳超时时间,单位为毫秒。 + """ + pass + + +@value('nacos.weight', default_value=1.0, converter=float) +def _get_heartbeat_weight() -> float: + """ + 获取 Nacos 心跳权重。 + :return: 心跳权重。 + """ + pass + + +# 对async 第三方函数进行封装 +async def call_create_naming_service(config: ClientConfig) -> 'NacosNamingService': + return await NacosNamingService.create_naming_service(config) + + +async def call_list_instances(param: ListInstanceParam) -> List[Instance]: + return await _nacos_client.list_instances(param) + + +async def call_deregister_instance(param: DeregisterInstanceParam) -> bool: + return await _nacos_client.deregister_instance(param) + + +async def call_subscribe(param: SubscribeServiceParam) -> None: + await _nacos_client.subscribe(param) + + +async def call_unsubscribe(param: SubscribeServiceParam) -> None: + await _nacos_client.unsubscribe(param) + + +async def call_list_services(param: ListServiceParam) -> ServiceList: + return await _nacos_client.list_services(param) + + +# 初始化 Nacos 客户端 +config = (ClientConfigBuilder() + .server_address(_get_nacos_server_addr()) + .namespace_id(_get_nacos_namespace() or 'local') + .username(_get_nacos_username() or None) + .password(_get_nacos_password() or None) + .access_key(_get_nacos_access_key() or None) + .secret_key(_get_nacos_secret_key() or None) + .build()) + +_nacos_client = asyncio.run(call_create_naming_service(config)) + +# 常量 +CLUSTER_DOMAIN_KEY = "cluster.domain" +CLUSTER_PORT_PATTERN = re.compile(r"cluster\.(.*?)\.port") +WORKER_KEY = "worker" +APPLICATION_KEY = "application" +FITABLE_META_KEY = "fitable-meta" +SEPARATOR = "::" + +# 全局变量 +_service_subscriptions: Dict[str, any] = {} +_executor = ThreadPoolExecutor(max_workers=10) + +# 辅助函数 +def build_service_key(group_name: str, service_name: str) -> str: + """ + Builds a unique key in the format :: for service subscriptions. + + @param group_name: The group name as string. + @param service_name: The service name as string. + @return: A concatenated key like groupName::serviceName. + """ + return f"{group_name}{SEPARATOR}{service_name}" + + +def get_service_name(fitable: FitableInfo) -> str: + """获取服务名称""" + return f"{fitable.fitableId}{SEPARATOR}{fitable.fitableVersion}" + + +def get_group_name_from_fitable(fitable: FitableInfo) -> str: + """从FitableInfo获取组名""" + return f"{fitable.genericableId}{SEPARATOR}{fitable.genericableVersion}" + + +def get_group_name_from_genericable(genericable: GenericableInfo) -> str: + """从GenericableInfo获取组名""" + return f"{genericable.genericableId}{SEPARATOR}{genericable.genericableVersion}" + + +def create_instances(worker: Worker, application: Application, meta: FitableMeta) -> List[Dict]: + """ + 创建实例信息 + + @param worker: Worker node object. + @param application: Application object. + @param meta: FitableMeta metadata object. + @return: List of instance dictionaries. + """ + plugin_logger.debug(f"Creating instance for worker. [worker={worker.id}, application={application.nameVersion}, meta={meta}]") + instances = [] + + for address in worker.addresses: + for endpoint in address.endpoints: + # 准备元数据 + metadata = build_instance_metadata(worker, application, meta) + + # 构建实例 + instance = { + "ip": address.host, + "port": endpoint.port, + "weight": _get_heartbeat_weight(), + "ephemeral": _get_heartbeat_isEphemeral(), + "metadata": metadata + } + instances.append(instance) + + return instances + + +def build_instance_metadata(worker: Worker, application: Application, meta: FitableMeta) -> Dict[str, str]: + """ + Build metadata for service instance, including worker, application and FitableMeta information. + + @param worker: The worker node object. + @param application: The application object. + @param meta: The FitableMeta metadata object. + @return: A dict containing all serialized metadata. + """ + metadata = {} + + # 添加心跳配置 + metadata["preserved.heart.beat.interval"] = str(_get_heartbeat_interval()) + metadata["preserved.heart.beat.timeout"] = str(_get_heartbeat_timeout()) + + try: + metadata[WORKER_KEY] = json_serialize(worker) + metadata[APPLICATION_KEY] = json_serialize(application) + metadata[FITABLE_META_KEY] = json_serialize(meta) + except Exception as e: + plugin_logger.error(f"Failed to serialize metadata for worker: {e}") + + return metadata + + +def parse_fitable_meta(metadata: Dict) -> FitableMeta: + """解析FitableMeta""" + try: + meta_json = metadata.get(FITABLE_META_KEY) + if meta_json: + return json_deserialize(FitableMeta, meta_json) + except Exception as e: + plugin_logger.error(f"Failed to parse fitable meta for instance: {e}") + + # 返回默认值 + meta = FitableMeta() + meta.fitable = FitableInfo() + return meta + + +def parse_application(metadata: Dict) -> Application: + """解析Application""" + try: + app_json = metadata.get(APPLICATION_KEY) + if app_json: + return json_deserialize(Application, app_json) + except Exception as e: + plugin_logger.error(f"Failed to parse application metadata for instance: {e}") + + # 返回默认值 + app = Application() + app.nameVersion = "unknown" + return app + + +def parse_worker(instance_or_metadata) -> Worker: + """解析Worker""" + try: + # 处理不同的输入类型 + if hasattr(instance_or_metadata, 'metadata'): + metadata = instance_or_metadata.metadata + ip = getattr(instance_or_metadata, 'ip', 'unknown') + port = getattr(instance_or_metadata, 'port', 0) + else: + metadata = instance_or_metadata + ip = 'unknown' + port = 0 + + worker_json = metadata.get(WORKER_KEY) + if worker_json: + return json_deserialize(Worker, worker_json) + except Exception as e: + plugin_logger.error(f"Failed to parse worker metadata for instance: {e}") + + # 降级处理 - 创建基本worker信息 + worker = Worker() + worker.id = "unknown" + worker.environment = "" + worker.extensions = {} + + # 如果有IP和端口信息,创建基本地址 + if ip != 'unknown' and port != 0: + address = Address() + address.host = ip + + endpoint = Endpoint() + endpoint.port = port + endpoint.protocol = 1 # 默认协议 + + address.endpoints = [endpoint] + worker.addresses = [address] + else: + worker.addresses = [] + + return worker + + +def replace_addresses(workers: Set[Worker], application: Application) -> None: + """ + Extract all workers corresponding to instances and adjust addresses based on application extension information. + + @param workers: Set of workers to modify. + @param application: The application object. + """ + if not application.extensions or CLUSTER_DOMAIN_KEY not in application.extensions: + return + + cluster_domain = application.extensions.get(CLUSTER_DOMAIN_KEY) + if not cluster_domain: + return + + # 构建端点 + endpoints = build_endpoints(application.extensions) + + # 创建统一地址 + address = Address() + address.host = cluster_domain + address.endpoints = endpoints + + # 更新所有worker的地址 + for worker in workers: + worker.addresses = [address] + + +def build_endpoints(extensions: Dict[str, str]) -> List[Endpoint]: + """构建端点列表""" + endpoints = [] + + # 协议代码映射 + protocol_code_map = { + "rsocket": 0, + "socket": 1, + "http": 2, + "grpc": 3, + "uc": 10, + "shareMemory": 11 + } + + for key, value in extensions.items(): + match = CLUSTER_PORT_PATTERN.match(key) + if match: + protocol_name = match.group(1).lower() + if protocol_name in protocol_code_map: + endpoint = Endpoint() + endpoint.port = int(value) + endpoint.protocol = protocol_code_map[protocol_name] + endpoints.append(endpoint) + else: + plugin_logger.error(f"Unknown protocol: {protocol_name}") + + return endpoints + + +def on_service_changed(fitable_info: FitableInfo, worker_id: str) -> None: + """ + Handle service change events, query and notify updates to Fitables instance information. + + @param fitable_info: The changed Fitables information. + @param worker_id: The worker ID. + """ + try: + # 查询当前实例 + instances = query_fitable_addresses([fitable_info], worker_id) + # 这里需要实现通知机制 - 调用notify服务 + # notify_fitables(instances) + plugin_logger.debug(f"Service changed for fitable: {fitable_info}, instances: {len(instances)}") + except Exception as e: + plugin_logger.error(f"Service change handling failed: {e}") + + +def group_instances_by_application(instances: List[Instance]) -> Dict[Application, List[Instance]]: + """按应用分组实例""" + app_instances_map = {} + for instance in instances: + metadata = instance.metadata + app = parse_application(metadata) + app_instances_map.setdefault(app, []).append(instance) + return app_instances_map + + +def extract_workers(app_instances: List[Instance], application: Application) -> Set[Worker]: + """ + Extract all workers corresponding to instances and adjust addresses based on application extension information. + + @param app_instances: The list of application instances. + @param application: The application object. + @return: Set of workers. + """ + workers = set() + for instance in app_instances: + worker = parse_worker(instance) + workers.add(worker) + + # 如果应用有集群域名配置,替换地址 + if application.extensions and CLUSTER_DOMAIN_KEY in application.extensions: + replace_addresses(workers, application) + + return workers + + +@fitable(const.REGISTER_FIT_SERVICE_GEN_ID, const.REGISTER_FIT_SERVICE_FIT_ID) +def register_fitables(fitable_metas: List[FitableMeta], worker: Worker, application: Application) -> None: + """ + 注册中心所提供接口,注册泛服务实现的信息。 + + @param fitable_metas: 泛服务实现元数据列表。 + @param worker: 当前 FIT 进程信息。 + @param application: 当前应用信息。 + """ + try: + plugin_logger.debug(f"Registering fitables. [fitableMetas={fitable_metas}, worker={worker.id}, application={application.nameVersion}]") + + for meta in fitable_metas: + fitable = meta.fitable + group_name = get_group_name_from_fitable(fitable) + service_name = get_service_name(fitable) + + instances = create_instances(worker, application, meta) + for instance in instances: + param = RegisterInstanceParam( + service_name=service_name, + group_name=group_name, + ip=instance["ip"], + port=instance["port"], + weight=instance["weight"], + ephemeral=instance["ephemeral"], + metadata=instance["metadata"] + ) + asyncio.run(_nacos_client.register_instance(param)) + + plugin_logger.info(f"Successfully registered fitables for worker {worker.id}") + except Exception as e: + plugin_logger.error(f"Failed to register fitables due to registry error: {e}") + raise + +@fitable(const.UNREGISTER_FIT_SERVICE_GEN_ID, const.UNREGISTER_FIT_SERVICE_FIT_ID) +def unregister_fitables(fitables: List[FitableInfo], worker_id: str) -> None: + """ + 向注册中心服务端取消注册服务实现列表。 + + @param fitables: 表示待取消注册的服务实现列表。 + @param worker_id: 表示服务实现所在的进程的唯一标识。 + """ + plugin_logger.debug(f"Unregistering fitables for worker. [fitables={fitables}, workerId={worker_id}]") + + for fitable in fitables: + unregister_single_fitable(fitable, worker_id) + + +def unregister_single_fitable(fitable: FitableInfo, worker_id: str) -> None: + """取消注册单个Fitable""" + group_name = get_group_name_from_fitable(fitable) + service_name = get_service_name(fitable) + + try: + # 获取服务所有实例 + param = ListInstanceParam( + service_name=service_name, + group_name=group_name, + healthy_only=True + ) + instances = asyncio.run(call_list_instances(param)) + unregister_matching_instances(instances, worker_id, service_name, group_name) + except Exception as e: + plugin_logger.error(f"Failed to unregister fitable due to registry error: {e}") + + +def unregister_matching_instances(instances: List[Instance], worker_id: str, service_name: str, group_name: str) -> None: + """取消注册匹配的实例""" + for instance in instances: + try: + worker = parse_worker(instance) + if worker and worker.id == worker_id: + param = DeregisterInstanceParam( + service_name=service_name, + group_name=group_name, + ip=instance.ip, + port=instance.port + ) + asyncio.run(call_deregister_instance(param)) + plugin_logger.debug(f"Successfully deregistered instance {instance.ip}:{instance.port}") + except Exception as e: + plugin_logger.error(f"Failed to deregister instance: {e}") + + +@fitable(const.QUERY_FIT_SERVICE_GEN_ID, const.QUERY_FIT_SERVICE_FIT_ID) +def query_fitable_addresses(fitables: List[FitableInfo], worker_id: str) -> List[FitableAddressInstance]: + """ + 注册中心所提供接口,用于查询某个泛服务实现的实例信息,在拉模式下使用。 + + @param fitables: 泛服务实现信息列表。 + @param worker_id: 当前 FIT 进程标识。 + @return: 所获取的实例信息。 + """ + plugin_logger.debug(f"Querying fitables for worker. [fitables={fitables}, workerId={worker_id}]") + result_map = {} + + for fitable in fitables: + try: + instances = query_instances(fitable) + if not instances: + continue + process_application_instances(result_map, fitable, instances) + except Exception as e: + plugin_logger.error(f"Failed to query fitables for genericableId: {e}") + + return list(result_map.values()) + + +def query_instances(fitable: FitableInfo) -> List[Instance]: + """查询实例""" + group_name = get_group_name_from_fitable(fitable) + service_name = get_service_name(fitable) + + param = ListInstanceParam( + service_name=service_name, + group_name=group_name, + healthy_only=True + ) + return asyncio.run(call_list_instances(param)) + + +def process_application_instances(result_map: Dict, fitable: FitableInfo, instances: List[Instance]) -> None: + """处理应用实例""" + app_instances_map = group_instances_by_application(instances) + + for app, app_instances in app_instances_map.items(): + meta = parse_fitable_meta(app_instances[0].metadata) + workers = extract_workers(app_instances, app) + + fai = result_map.get(fitable) + if fai is None: + fai = FitableAddressInstance() + fai.fitable = fitable + fai.applicationInstances = [] + result_map[fitable] = fai + + app_instance = ApplicationInstance() + app_instance.application = app + app_instance.formats = meta.formats if meta.formats else [] + app_instance.workers = list(workers) + fai.applicationInstances.append(app_instance) + + +@fitable(const.SUBSCRIBE_FIT_SERVICE_GEN_ID, const.SUBSCRIBE_FIT_SERVICE_FIT_ID) +def subscribe_fit_service(fitables: List[FitableInfo], worker_id: str, callback_fitable_id: str) -> List[FitableAddressInstance]: + """ + 注册中心所提供接口,用于订阅某个泛服务实现的实例信息,并且也会返回查询到的实例信息,在推模式下使用。 + + @param fitables: 泛服务实现信息列表。 + @param worker_id: 当前 FIT 进程标识。 + @param callback_fitable_id: 用于回调的泛服务实现的标识。 + @return: 所查询到的实例信息。 + """ + plugin_logger.debug(f"Subscribing to fitables for worker. [fitables={fitables}, workerId={worker_id}, callbackFitableId={callback_fitable_id}]") + + # 注册订阅 + for fitable in fitables: + try: + group_name = get_group_name_from_fitable(fitable) + service_name = get_service_name(fitable) + service_key = build_service_key(group_name, service_name) + + if service_key in _service_subscriptions: + plugin_logger.debug(f"Already subscribed to service. [groupName={group_name}, serviceName={service_name}]") + continue + + # 创建事件监听器 + def create_event_listener(fitable_ref: FitableInfo, worker_id_ref: str): + def event_listener(event): + _executor.submit(on_service_changed, fitable_ref, worker_id_ref) + return event_listener + + event_listener = create_event_listener(fitable, worker_id) + _service_subscriptions[service_key] = event_listener + + # 注册订阅 + param = SubscribeServiceParam( + service_name=service_name, + group_name=group_name, + subscribe_callback=event_listener + ) + asyncio.run(call_subscribe(param)) + plugin_logger.debug(f"Subscribed to service. [groupName={group_name}, serviceName={service_name}]") + + except Exception as e: + plugin_logger.error(f"Failed to subscribe to Nacos service: {e}") + + return query_fitable_addresses(fitables, worker_id) + + +@fitable(const.UNSUBSCRIBE_FIT_SERVICE_GEN_ID, const.UNSUBSCRIBE_FIT_SERVICE_FIT_ID) +def unsubscribe_fitables(fitables: List[FitableInfo], worker_id: str, callback_fitable_id: str) -> None: + """ + 向注册中心服务端取消订阅指定服务实现的实例信息。 + + @param fitables: 表示指定服务实现列表的List。 + @param worker_id: 表示指定的进程的唯一标识的String。 + @param callback_fitable_id: 表示取消订阅回调服务实现的唯一标识的String。 + """ + plugin_logger.debug(f"Unsubscribing from fitables for worker. [fitables={fitables}, workerId={worker_id}, callbackFitableId={callback_fitable_id}]") + + for fitable in fitables: + try: + group_name = get_group_name_from_fitable(fitable) + service_name = get_service_name(fitable) + service_key = build_service_key(group_name, service_name) + + if service_key in _service_subscriptions: + listener = _service_subscriptions.pop(service_key) + + param = SubscribeServiceParam( + service_name=service_name, + group_name=group_name, + subscribe_callback=listener + ) + asyncio.run(call_unsubscribe(param)) + plugin_logger.debug(f"Unsubscribed from service. [groupName={group_name}, serviceName={service_name}]") + except Exception as e: + plugin_logger.error(f"Failed to unsubscribe from Nacos service: {e}") + + +@fitable(const.QUERY_FITABLE_METAS_GEN_ID, const.QUERY_FITABLE_METAS_FIT_ID) +def query_fitable_metas(genericable_infos: List[GenericableInfo]) -> List[FitableMetaInstance]: + """ + 注册中心所提供接口,用于查询泛服务的元数据。 + + @param genericable_infos: 泛服务信息列表。 + @return: 所查询到的泛服务元数据列表。 + """ + plugin_logger.debug(f"Querying fitable metas for genericables. [genericables={genericable_infos}]") + meta_environments = {} + + for genericable in genericable_infos: + process_genericable_services(genericable, meta_environments) + + return build_fitable_meta_instances(meta_environments) + + +def process_genericable_services(genericable: GenericableInfo, meta_environments: Dict) -> None: + """处理泛服务的服务列表""" + group_name = get_group_name_from_genericable(genericable) + + try: + # 获取分组下所有服务 + param = ListServiceParam( + namespace_id=_get_nacos_namespace(), + group_name=group_name, + page_no=1, + page_size=1000 # 假设一次获取足够多的服务 + ) + service_list = asyncio.run(call_list_services(param)) + + for service_name in service_list.data: + process_service_instances(service_name, group_name, meta_environments) + except Exception as e: + plugin_logger.error(f"Failed to query fitable metas: {e}") + + +def process_service_instances(service_name: str, group_name: str, meta_environments: Dict) -> None: + """处理服务实例""" + try: + # 获取服务实例 + param = ListInstanceParam( + service_name=service_name, + group_name=group_name, + healthy_only=True + ) + instances = asyncio.run(call_list_instances(param)) + + if not instances: + return + + meta = parse_fitable_meta(instances[0].metadata) + collect_environments_from_instances(instances, meta, meta_environments) + except Exception as e: + plugin_logger.error(f"Failed to select instances for service {service_name}: {e}") + + +def collect_environments_from_instances(instances: List[Instance], meta: FitableMeta, meta_environments: Dict) -> None: + """从实例中收集环境信息""" + for instance in instances: + try: + worker = parse_worker(instance) + if worker and worker.environment: + if meta not in meta_environments: + meta_environments[meta] = set() + meta_environments[meta].add(worker.environment) + except Exception as e: + plugin_logger.error(f"Failed to parse worker metadata: {e}") + + +def build_fitable_meta_instances(meta_environments: Dict) -> List[FitableMetaInstance]: + """构建FitableMetaInstance列表""" + results = [] + for meta, envs in meta_environments.items(): + instance = FitableMetaInstance() + instance.meta = meta + instance.environments = list(envs) + results.append(instance) + return results + + diff --git a/framework/fit/python/plugin/fit_py_registry_client/fitable_address_service.py b/framework/fit/python/plugin/fit_py_registry_client/fitable_address_service.py index 64d8b419..8f07b214 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/fitable_address_service.py +++ b/framework/fit/python/plugin/fit_py_registry_client/fitable_address_service.py @@ -14,7 +14,7 @@ from fitframework.api.decorators import fitable, fit, scheduled_executor, value from fitframework.api.logging import sys_plugin_logger from fitframework.utils import tools -from .entity import FitableAddressInstance, FitableInfo +from plugin.fit_py_nacos_registry.entity import FitableAddressInstance, FitableInfo from .registry_address_service import get_cache_aware_registry_address _PUSH_MODE = 'push' diff --git a/framework/fit/python/plugin/fit_py_registry_client/fitable_meta_service.py b/framework/fit/python/plugin/fit_py_registry_client/fitable_meta_service.py index f3934d17..e010ea96 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/fitable_meta_service.py +++ b/framework/fit/python/plugin/fit_py_registry_client/fitable_meta_service.py @@ -10,7 +10,7 @@ from fit_common_struct.core import Genericable from fitframework import fitable, const, fit -from .entity import FitableMetaInstance, GenericableInfo +from plugin.fit_py_nacos_registry.entity import FitableMetaInstance, GenericableInfo @fit(const.QUERY_FITABLE_METAS_GEN_ID) diff --git a/framework/fit/python/plugin/fit_py_registry_client/online_fitable_service.py b/framework/fit/python/plugin/fit_py_registry_client/online_fitable_service.py index 4a1395cc..8b9b461f 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/online_fitable_service.py +++ b/framework/fit/python/plugin/fit_py_registry_client/online_fitable_service.py @@ -17,7 +17,7 @@ from fitframework.api.decorators import scheduled_executor from fitframework.api.logging import sys_plugin_logger from fitframework.core.network.enums import SerializingStructureEnum -from .entity import Worker, FitableMeta, FitableInfo, Address as AddressRegistry, Endpoint, \ +from plugin.fit_py_nacos_registry.entity import Worker, FitableMeta, FitableInfo, Address as AddressRegistry, Endpoint, \ Application, Address diff --git a/framework/fit/python/plugin/fit_py_registry_client/test/fitable_address_service_test.py b/framework/fit/python/plugin/fit_py_registry_client/test/fitable_address_service_test.py index ae28acf6..079b6809 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/test/fitable_address_service_test.py +++ b/framework/fit/python/plugin/fit_py_registry_client/test/fitable_address_service_test.py @@ -21,7 +21,7 @@ class FitableAddressServiceTest(FitTestSupport): def setUpClass(cls): super(FitableAddressServiceTest, cls).setUpClass() from plugin.fit_py_registry_client import fitable_address_service - from plugin.fit_py_registry_client import entity + from plugin.fit_py_nacos_registry import entity global fitable_address_service global entity From c0756f988d03863b12f180b400c8afd65cac7351 Mon Sep 17 00:00:00 2001 From: xiaohao <932379959@qq.com> Date: Fri, 29 Aug 2025 20:47:07 +0800 Subject: [PATCH 02/15] [fit] Upgrade some plugin configurations to system configurations --- framework/fit/python/conf/application.yml | 32 +++++++++++++++ .../fit_py_http_client/conf/application.yml | 11 ------ .../conf/application.yml | 7 ---- .../registry_address_service.py | 39 ++++++++++++++++++- .../fit_py_server_http/conf/application.yml | 12 +----- 5 files changed, 71 insertions(+), 30 deletions(-) diff --git a/framework/fit/python/conf/application.yml b/framework/fit/python/conf/application.yml index 41b81e7e..36446220 100644 --- a/framework/fit/python/conf/application.yml +++ b/framework/fit/python/conf/application.yml @@ -19,3 +19,35 @@ terminate-main: local_ip: "localhost" context-path: "" +http: + server: + enabled: true + address: + use-random-port: false + port: 9666 + port-to-register: + protocol: 2 + formats: + - 1 + - 2 +https: + client: + verify_enabled: false # 是否需要验证服务端身份 + ca_path: "plugin/fit_py_http_client/resources/ca.crt" + assert_host_name: false # 是否在校验时校验主机名,仅当 verify_enabled 为 true 时有意义 + cert_enabled: false # 是否服务端对自身身份校验 + crt_path: "plugin/fit_py_http_client/resources/global.crt" + key_path: "plugin/fit_py_http_client/resources/global.key" + key_file_encrypted: false # 私钥是否被加密,仅当 cert_enabled 为 true 时有意义 + key_file_password: "" # 私钥的密码,仅当 cert_enabled 和 key_file_encrypted 为 true 时有意义 + key_file_password_encrypted: false # 私钥的密码是否被加密,仅当 cert_enabled 和 key_file_encrypted 为 true 时有意义 + +registry-center: + server: + mode: 'DIRECT' + addresses: + - "localhost:8090" + protocol: 2 + formats: + - 1 + context-path: "" \ No newline at end of file diff --git a/framework/fit/python/plugin/fit_py_http_client/conf/application.yml b/framework/fit/python/plugin/fit_py_http_client/conf/application.yml index 783c26c3..e69de29b 100644 --- a/framework/fit/python/plugin/fit_py_http_client/conf/application.yml +++ b/framework/fit/python/plugin/fit_py_http_client/conf/application.yml @@ -1,11 +0,0 @@ -https: - client: - verify_enabled: false # 是否需要验证服务端身份 - ca_path: "plugin/fit_py_http_client/resources/ca.crt" - assert_host_name: false # 是否在校验时校验主机名,仅当 verify_enabled 为 true 时有意义 - cert_enabled: false # 是否服务端对自身身份校验 - crt_path: "plugin/fit_py_http_client/resources/global.crt" - key_path: "plugin/fit_py_http_client/resources/global.key" - key_file_encrypted: false # 私钥是否被加密,仅当 cert_enabled 为 true 时有意义 - key_file_password: "" # 私钥的密码,仅当 cert_enabled 和 key_file_encrypted 为 true 时有意义 - key_file_password_encrypted: false # 私钥的密码是否被加密,仅当 cert_enabled 和 key_file_encrypted 为 true 时有意义 diff --git a/framework/fit/python/plugin/fit_py_registry_client/conf/application.yml b/framework/fit/python/plugin/fit_py_registry_client/conf/application.yml index 2151c63e..b14c85dc 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/conf/application.yml +++ b/framework/fit/python/plugin/fit_py_registry_client/conf/application.yml @@ -6,13 +6,6 @@ registry-center: registry_fitable_frequency: 15 registered-fitables-expire-interval: 30 # expected expire interval used by server invalid_address_ttl: 60 - server: - addresses: - - "localhost:8080" - protocol: 2 - formats: - - 1 - context-path: "" service_ids: - "modelengine.fit.registry.registry-service.register-fitables" - "modelengine.fit.registry.registry-service.query-fitables-addresses" diff --git a/framework/fit/python/plugin/fit_py_registry_client/registry_address_service.py b/framework/fit/python/plugin/fit_py_registry_client/registry_address_service.py index c9249eeb..f755359c 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/registry_address_service.py +++ b/framework/fit/python/plugin/fit_py_registry_client/registry_address_service.py @@ -9,6 +9,7 @@ from typing import List from fit_common_struct.core import Address +from fitframework import to_bool from fitframework.api.decorators import scheduled_executor, value from fitframework.api.logging import sys_plugin_logger from fitframework.utils import tools @@ -30,6 +31,10 @@ def _get_registry_pull_frequency(): def _get_registry_server_addresses() -> list: pass +@value('registry-center.server.mode',default_value='DIRECT') +def _get_registry_server_mode(): + pass + @value('registry-center.server.protocol', converter=int) def _get_registry_server_protocol(): @@ -57,9 +62,41 @@ def _build_address(addr: str) -> Address: return Address(host=ip, port=int(port), protocol=_get_registry_server_protocol(), environment=_get_worker_env(), formats=_get_registry_server_formats(), worker_id='', context_path=_get_registry_context_path()) +@value('http.server.enabled', False, converter=to_bool) +def _get_http_enabled(): + pass + +@value('http.server.address.port', converter=int) +def _get_http_server_port(): + pass + +@value('https.server.enabled', default_value=False, converter=to_bool) +def _get_https_enabled(): + pass + +@value('local_ip') +def _get_host(): + pass + +@value('https.server.address.port', converter=int) +def _get_https_server_port(): + pass + + +def _process_address_by_mode() -> list: + if _get_registry_server_mode() == 'PROXY': + if _get_http_enabled(): + port = _get_http_server_port() + elif _get_https_enabled(): + port = _get_https_server_port() + else: + port = "" + return [_get_host() + port] + + def _get_registry_addresses_from_configuration(): - return [_build_address(addr) for addr in _get_registry_server_addresses()] + return [_build_address(addr) for addr in _process_address_by_mode()] @scheduled_executor(_get_registry_pull_frequency()) diff --git a/framework/fit/python/plugin/fit_py_server_http/conf/application.yml b/framework/fit/python/plugin/fit_py_server_http/conf/application.yml index 12bb6f67..95bda7c9 100644 --- a/framework/fit/python/plugin/fit_py_server_http/conf/application.yml +++ b/framework/fit/python/plugin/fit_py_server_http/conf/application.yml @@ -1,15 +1,5 @@ server-thread-count: 8 -http: - server: - enabled: true - address: - use-random-port: false - port: 9666 - port-to-register: - protocol: 2 - formats: - - 1 - - 2 + https: server: enabled: false From 76cd52dc016567c6e55eae776a363f19f3343bd8 Mon Sep 17 00:00:00 2001 From: xiaohao <932379959@qq.com> Date: Fri, 29 Aug 2025 22:45:35 +0800 Subject: [PATCH 03/15] =?UTF-8?q?[fit]=20=E8=A7=A3=E5=86=B3=E5=9C=A8?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=87=BD=E6=95=B0=E4=B8=AD=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E5=BC=82=E6=AD=A5=E5=87=BD=E6=95=B0=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/fit_py_nacos_registry/entity.py | 16 +- .../service/nacos_registry_server.py | 187 +++++++++++++++--- 2 files changed, 180 insertions(+), 23 deletions(-) diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/entity.py b/framework/fit/python/plugin/fit_py_nacos_registry/entity.py index fa585c40..966adf66 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/entity.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/entity.py @@ -12,6 +12,19 @@ from fit_common_struct.core import Address as AddressInner +def safe_hash_dict(obj_dict): + """安全地计算包含列表的字典的哈希值""" + hashable_values = [] + for value in obj_dict.values(): + if isinstance(value, list): + hashable_values.append(tuple(value)) + elif isinstance(value, dict): + hashable_values.append(tuple(sorted(value.items()))) + else: + hashable_values.append(value) + return hash(tuple(hashable_values)) + + class FitableInfo(object): def __init__(self, genericableId: str, genericableVersion: str, fitableId: str, fitableVersion: str): @@ -68,7 +81,8 @@ def __eq__(self, other): return self.__dict__ == other.__dict__ def __hash__(self): - return hash(tuple(self.__dict__.values())) + # 使用安全的哈希函数处理包含列表的对象 + return safe_hash_dict(self.__dict__) def __repr__(self): return str(tuple(self.__dict__.values())) diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py index 5d571dda..2bda5f8c 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py @@ -13,7 +13,9 @@ import asyncio import json import re -from concurrent.futures import ThreadPoolExecutor +import threading +import queue +from concurrent.futures import ThreadPoolExecutor, Future from typing import List, Dict, Set from v2.nacos import NacosNamingService, ClientConfigBuilder, RegisterInstanceParam, ListInstanceParam, \ @@ -121,26 +123,36 @@ async def call_create_naming_service(config: ClientConfig) -> 'NacosNamingServic async def call_list_instances(param: ListInstanceParam) -> List[Instance]: - return await _nacos_client.list_instances(param) + client = _async_executor.get_nacos_client() + return await client.list_instances(param) async def call_deregister_instance(param: DeregisterInstanceParam) -> bool: - return await _nacos_client.deregister_instance(param) + client = _async_executor.get_nacos_client() + return await client.deregister_instance(param) async def call_subscribe(param: SubscribeServiceParam) -> None: - await _nacos_client.subscribe(param) + client = _async_executor.get_nacos_client() + await client.subscribe(param) async def call_unsubscribe(param: SubscribeServiceParam) -> None: - await _nacos_client.unsubscribe(param) + client = _async_executor.get_nacos_client() + await client.unsubscribe(param) async def call_list_services(param: ListServiceParam) -> ServiceList: - return await _nacos_client.list_services(param) + client = _async_executor.get_nacos_client() + return await client.list_services(param) -# 初始化 Nacos 客户端 +async def call_register_instance(param: RegisterInstanceParam) -> None: + client = _async_executor.get_nacos_client() + await client.register_instance(param) + + +# 初始化 Nacos 客户端配置 config = (ClientConfigBuilder() .server_address(_get_nacos_server_addr()) .namespace_id(_get_nacos_namespace() or 'local') @@ -150,7 +162,9 @@ async def call_list_services(param: ListServiceParam) -> ServiceList: .secret_key(_get_nacos_secret_key() or None) .build()) -_nacos_client = asyncio.run(call_create_naming_service(config)) +# 不在这里直接创建客户端,而是在需要时动态创建 +_nacos_client = None +_nacos_config = config # 常量 CLUSTER_DOMAIN_KEY = "cluster.domain" @@ -164,6 +178,123 @@ async def call_list_services(param: ListServiceParam) -> ServiceList: _service_subscriptions: Dict[str, any] = {} _executor = ThreadPoolExecutor(max_workers=10) + +# 线程池执行器,用于线程安全的异步调用 +_executor = ThreadPoolExecutor(max_workers=10) + + +class AsyncExecutor: + """专门用于处理异步操作的执行器,在后台线程中维护事件循环""" + + def __init__(self): + self._loop = None + self._thread = None + self._started = False + self._shutdown = False + self._nacos_client = None + self._init_complete = threading.Event() + + def start(self): + """启动后台事件循环线程""" + if self._started: + return + + self._thread = threading.Thread(target=self._run_event_loop, daemon=True, name="NacosAsyncThread") + self._thread.start() + + # 等待初始化完成 + if not self._init_complete.wait(timeout=10): # 最多等待10秒 + raise RuntimeError("Failed to initialize async executor within timeout") + + self._started = True + + def _run_event_loop(self): + """在后台线程中运行事件循环""" + try: + self._loop = asyncio.new_event_loop() + asyncio.set_event_loop(self._loop) + + # 在这个事件循环中创建 Nacos 客户端 + async def init_nacos_client(): + try: + self._nacos_client = await NacosNamingService.create_naming_service(_nacos_config) + plugin_logger.info("Nacos client initialized successfully") + except Exception as e: + plugin_logger.error(f"Failed to initialize Nacos client: {e}") + raise + finally: + # 标记初始化完成 + self._init_complete.set() + + self._loop.run_until_complete(init_nacos_client()) + + # 运行事件循环直到被关闭 + self._loop.run_forever() + except Exception as e: + plugin_logger.error(f"Error in async executor event loop: {e}") + self._init_complete.set() # 即使失败也要设置,避免无限等待 + finally: + try: + if self._nacos_client: + # 清理 Nacos 客户端 + pass + self._loop.close() + except: + pass + + def run_coroutine(self, coro): + """在后台事件循环中运行协程,返回结果""" + if not self._started: + self.start() + + if self._loop is None or self._nacos_client is None: + raise RuntimeError("Async executor not properly initialized") + + # 创建一个Future来获取结果 + result_future = Future() + + async def wrapped_coro(): + try: + result = await coro + result_future.set_result(result) + except Exception as e: + result_future.set_exception(e) + + # 在事件循环中调度协程 + self._loop.call_soon_threadsafe(asyncio.create_task, wrapped_coro()) + + # 等待结果 + return result_future.result(timeout=30) # 30秒超时 + + def get_nacos_client(self): + """获取 Nacos 客户端实例""" + if not self._started: + self.start() + return self._nacos_client + + def shutdown(self): + """关闭异步执行器""" + if self._loop and not self._loop.is_closed(): + self._loop.call_soon_threadsafe(self._loop.stop) + self._shutdown = True + + +# 全局异步执行器 +_async_executor = AsyncExecutor() + + +def _run_async_safely(coro): + """ + 线程安全地运行异步函数 + 使用专门的异步执行器在后台线程中处理 + """ + try: + return _async_executor.run_coroutine(coro) + except Exception as e: + plugin_logger.error(f"Error running async operation: {e}") + raise + + # 辅助函数 def build_service_key(group_name: str, service_name: str) -> str: """ @@ -256,8 +387,8 @@ def parse_fitable_meta(metadata: Dict) -> FitableMeta: plugin_logger.error(f"Failed to parse fitable meta for instance: {e}") # 返回默认值 - meta = FitableMeta() - meta.fitable = FitableInfo() + default_fitable = FitableInfo("unknown", "1.0", "unknown", "1.0") + meta = FitableMeta(default_fitable, [], []) return meta @@ -449,7 +580,7 @@ def register_fitables(fitable_metas: List[FitableMeta], worker: Worker, applicat ephemeral=instance["ephemeral"], metadata=instance["metadata"] ) - asyncio.run(_nacos_client.register_instance(param)) + _run_async_safely(call_register_instance(param)) plugin_logger.info(f"Successfully registered fitables for worker {worker.id}") except Exception as e: @@ -482,7 +613,7 @@ def unregister_single_fitable(fitable: FitableInfo, worker_id: str) -> None: group_name=group_name, healthy_only=True ) - instances = asyncio.run(call_list_instances(param)) + instances = _run_async_safely(call_list_instances(param)) unregister_matching_instances(instances, worker_id, service_name, group_name) except Exception as e: plugin_logger.error(f"Failed to unregister fitable due to registry error: {e}") @@ -500,7 +631,7 @@ def unregister_matching_instances(instances: List[Instance], worker_id: str, ser ip=instance.ip, port=instance.port ) - asyncio.run(call_deregister_instance(param)) + _run_async_safely(call_deregister_instance(param)) plugin_logger.debug(f"Successfully deregistered instance {instance.ip}:{instance.port}") except Exception as e: plugin_logger.error(f"Failed to deregister instance: {e}") @@ -540,7 +671,7 @@ def query_instances(fitable: FitableInfo) -> List[Instance]: group_name=group_name, healthy_only=True ) - return asyncio.run(call_list_instances(param)) + return _run_async_safely(call_list_instances(param)) def process_application_instances(result_map: Dict, fitable: FitableInfo, instances: List[Instance]) -> None: @@ -603,7 +734,7 @@ def event_listener(event): group_name=group_name, subscribe_callback=event_listener ) - asyncio.run(call_subscribe(param)) + _run_async_safely(call_subscribe(param)) plugin_logger.debug(f"Subscribed to service. [groupName={group_name}, serviceName={service_name}]") except Exception as e: @@ -637,7 +768,7 @@ def unsubscribe_fitables(fitables: List[FitableInfo], worker_id: str, callback_f group_name=group_name, subscribe_callback=listener ) - asyncio.run(call_unsubscribe(param)) + _run_async_safely(call_unsubscribe(param)) plugin_logger.debug(f"Unsubscribed from service. [groupName={group_name}, serviceName={service_name}]") except Exception as e: plugin_logger.error(f"Failed to unsubscribe from Nacos service: {e}") @@ -672,9 +803,9 @@ def process_genericable_services(genericable: GenericableInfo, meta_environments page_no=1, page_size=1000 # 假设一次获取足够多的服务 ) - service_list = asyncio.run(call_list_services(param)) + service_list = _run_async_safely(call_list_services(param)) - for service_name in service_list.data: + for service_name in service_list.services: process_service_instances(service_name, group_name, meta_environments) except Exception as e: plugin_logger.error(f"Failed to query fitable metas: {e}") @@ -689,7 +820,7 @@ def process_service_instances(service_name: str, group_name: str, meta_environme group_name=group_name, healthy_only=True ) - instances = asyncio.run(call_list_instances(param)) + instances = _run_async_safely(call_list_instances(param)) if not instances: return @@ -717,10 +848,22 @@ def build_fitable_meta_instances(meta_environments: Dict) -> List[FitableMetaIns """构建FitableMetaInstance列表""" results = [] for meta, envs in meta_environments.items(): - instance = FitableMetaInstance() - instance.meta = meta - instance.environments = list(envs) + instance = FitableMetaInstance(meta,list(envs)) results.append(instance) return results +# 模块清理函数 +import atexit + +def _cleanup_async_executor(): + """清理异步执行器""" + try: + _async_executor.shutdown() + except: + pass + +# 注册清理函数 +atexit.register(_cleanup_async_executor) + + From 5462b43522d241a3b72adfc3adab535169db9cb9 Mon Sep 17 00:00:00 2001 From: xiaohao <932379959@qq.com> Date: Sat, 30 Aug 2025 16:33:08 +0800 Subject: [PATCH 04/15] =?UTF-8?q?[fit]=20=E4=BF=AE=E6=94=B9=E4=BB=A3?= =?UTF-8?q?=E7=A0=81bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/fit_py_nacos_registry/entity.py | 10 +-- .../service/nacos_registry_server.py | 87 ++++++++----------- 2 files changed, 39 insertions(+), 58 deletions(-) diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/entity.py b/framework/fit/python/plugin/fit_py_nacos_registry/entity.py index 966adf66..4d6eb158 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/entity.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/entity.py @@ -145,7 +145,7 @@ def __eq__(self, other): return self.__dict__ == other.__dict__ def __hash__(self): - return hash(tuple(self.__dict__.values())) + return safe_hash_dict(self.__dict__) def __repr__(self): return str(tuple(self.__dict__.values())) @@ -168,7 +168,7 @@ def __eq__(self, other): return self.__dict__ == other.__dict__ def __hash__(self): - return hash(tuple(self.__dict__.values())) + return safe_hash_dict(self.__dict__) def __repr__(self): return str(tuple(self.__dict__.values())) @@ -192,7 +192,7 @@ def __eq__(self, other): return self.__dict__ == other.__dict__ def __hash__(self): - return hash(tuple(self.__dict__.values())) + return safe_hash_dict(self.__dict__) def __repr__(self): return str(tuple(self.__dict__.values())) @@ -210,7 +210,7 @@ def __eq__(self, other): return self.__dict__ == other.__dict__ def __hash__(self): - return hash(tuple(self.__dict__.values())) + return safe_hash_dict(self.__dict__) def __repr__(self): return str(tuple(self.__dict__.values())) @@ -228,7 +228,7 @@ def __eq__(self, other): return self.__dict__ == other.__dict__ def __hash__(self): - return hash(tuple(self.__dict__.values())) + return safe_hash_dict(self.__dict__) def __repr__(self): return str(tuple(self.__dict__.values())) diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py index 2bda5f8c..fa9fa4da 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py @@ -402,8 +402,7 @@ def parse_application(metadata: Dict) -> Application: plugin_logger.error(f"Failed to parse application metadata for instance: {e}") # 返回默认值 - app = Application() - app.nameVersion = "unknown" + app = Application("unknown", "unknown") return app @@ -427,53 +426,42 @@ def parse_worker(instance_or_metadata) -> Worker: plugin_logger.error(f"Failed to parse worker metadata for instance: {e}") # 降级处理 - 创建基本worker信息 - worker = Worker() - worker.id = "unknown" - worker.environment = "" - worker.extensions = {} + worker = Worker([], "unknown", "", {}) # 如果有IP和端口信息,创建基本地址 if ip != 'unknown' and port != 0: - address = Address() - address.host = ip - - endpoint = Endpoint() - endpoint.port = port - endpoint.protocol = 1 # 默认协议 - - address.endpoints = [endpoint] + endpoint = Endpoint(port, 1) # 默认协议 + address = Address(ip, [endpoint]) worker.addresses = [address] - else: - worker.addresses = [] return worker -def replace_addresses(workers: Set[Worker], application: Application) -> None: - """ - Extract all workers corresponding to instances and adjust addresses based on application extension information. - - @param workers: Set of workers to modify. - @param application: The application object. - """ - if not application.extensions or CLUSTER_DOMAIN_KEY not in application.extensions: - return - - cluster_domain = application.extensions.get(CLUSTER_DOMAIN_KEY) - if not cluster_domain: - return - - # 构建端点 - endpoints = build_endpoints(application.extensions) - - # 创建统一地址 - address = Address() - address.host = cluster_domain - address.endpoints = endpoints - - # 更新所有worker的地址 - for worker in workers: - worker.addresses = [address] +# def replace_addresses(workers: Set[Worker], application: Application) -> None: +# """ +# Extract all workers corresponding to instances and adjust addresses based on application extension information. +# +# @param workers: Set of workers to modify. +# @param application: The application object. +# """ +# if not application.extensions or CLUSTER_DOMAIN_KEY not in application.extensions: +# return +# +# cluster_domain = application.extensions.get(CLUSTER_DOMAIN_KEY) +# if not cluster_domain: +# return +# +# # 构建端点 +# endpoints = build_endpoints(application.extensions) +# +# # 创建统一地址 +# address = Address() +# address.host = cluster_domain +# address.endpoints = endpoints +# +# # 更新所有worker的地址 +# for worker in workers: +# worker.addresses = [address] def build_endpoints(extensions: Dict[str, str]) -> List[Endpoint]: @@ -495,9 +483,7 @@ def build_endpoints(extensions: Dict[str, str]) -> List[Endpoint]: if match: protocol_name = match.group(1).lower() if protocol_name in protocol_code_map: - endpoint = Endpoint() - endpoint.port = int(value) - endpoint.protocol = protocol_code_map[protocol_name] + endpoint = Endpoint(int(value), protocol_code_map[protocol_name]) endpoints.append(endpoint) else: plugin_logger.error(f"Unknown protocol: {protocol_name}") @@ -546,8 +532,8 @@ def extract_workers(app_instances: List[Instance], application: Application) -> workers.add(worker) # 如果应用有集群域名配置,替换地址 - if application.extensions and CLUSTER_DOMAIN_KEY in application.extensions: - replace_addresses(workers, application) + # if application.extensions and CLUSTER_DOMAIN_KEY in application.extensions: + # replace_addresses(workers, application) return workers @@ -684,15 +670,10 @@ def process_application_instances(result_map: Dict, fitable: FitableInfo, instan fai = result_map.get(fitable) if fai is None: - fai = FitableAddressInstance() - fai.fitable = fitable - fai.applicationInstances = [] + fai = FitableAddressInstance(applicationInstances=[], fitable=fitable) result_map[fitable] = fai - app_instance = ApplicationInstance() - app_instance.application = app - app_instance.formats = meta.formats if meta.formats else [] - app_instance.workers = list(workers) + app_instance = ApplicationInstance(workers=list(workers), application=app, formats=meta.formats if meta.formats else []) fai.applicationInstances.append(app_instance) From 85fff1a73ff2bfc8ba98dd669690e590cb90015a Mon Sep 17 00:00:00 2001 From: xiaohao <932379959@qq.com> Date: Sat, 30 Aug 2025 17:36:13 +0800 Subject: [PATCH 05/15] =?UTF-8?q?[fit]=20=E9=87=8D=E6=9E=84=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=85=AC=E5=85=B1=E7=B1=BB=E5=88=B0fit=5Fcommon=5Fstr?= =?UTF-8?q?uct?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entity.py | 0 .../fit_py_heart_beat_agent/heart_beat_agent.py | 2 +- .../fit_py_heart_beat_agent/heart_beat_utils.py | 12 ------------ .../heartbeat/heartbeat_service.py | 2 +- .../service/nacos_registry_server.py | 5 +---- .../fitable_address_service.py | 2 +- .../fit_py_registry_client/fitable_meta_service.py | 2 +- .../fit_py_registry_client/online_fitable_service.py | 2 +- .../test/fitable_address_service_test.py | 2 +- 9 files changed, 7 insertions(+), 22 deletions(-) rename framework/fit/python/{plugin/fit_py_nacos_registry => fit_common_struct}/entity.py (100%) diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/entity.py b/framework/fit/python/fit_common_struct/entity.py similarity index 100% rename from framework/fit/python/plugin/fit_py_nacos_registry/entity.py rename to framework/fit/python/fit_common_struct/entity.py diff --git a/framework/fit/python/plugin/fit_py_heart_beat_agent/heart_beat_agent.py b/framework/fit/python/plugin/fit_py_heart_beat_agent/heart_beat_agent.py index 7edb27a7..cd414704 100644 --- a/framework/fit/python/plugin/fit_py_heart_beat_agent/heart_beat_agent.py +++ b/framework/fit/python/plugin/fit_py_heart_beat_agent/heart_beat_agent.py @@ -26,7 +26,7 @@ from fitframework.api.decorators import register_event from fitframework.api.enums import FrameworkEvent as Fit_Event from fitframework.api.logging import sys_plugin_logger -from .heart_beat_utils import HeartBeatAddress, HeartBeatInfo +from fit_common_struct.entity import HeartBeatInfo, HeartBeatAddress # 用于控制心跳任务退出的队列 _HEART_BEAT_FINISH_QUEUE = multiprocessing.Queue() diff --git a/framework/fit/python/plugin/fit_py_heart_beat_agent/heart_beat_utils.py b/framework/fit/python/plugin/fit_py_heart_beat_agent/heart_beat_utils.py index 8da66bee..be3c2e46 100644 --- a/framework/fit/python/plugin/fit_py_heart_beat_agent/heart_beat_utils.py +++ b/framework/fit/python/plugin/fit_py_heart_beat_agent/heart_beat_utils.py @@ -12,18 +12,6 @@ from fitframework.api.logging import sys_plugin_logger -class HeartBeatInfo: - def __init__(self, sceneType: str, aliveTime: int, initDelay: int): - self.sceneType: str = sceneType - self.aliveTime: int = aliveTime - self.initDelay: int = initDelay - - -class HeartBeatAddress: - def __init__(self, id_: str): - self.id = id_ - - def timeout_or_exception_retry(timeout: int = 3, a_exception=Exception, max_retry: int = 1): """ diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/heartbeat/heartbeat_service.py b/framework/fit/python/plugin/fit_py_nacos_registry/heartbeat/heartbeat_service.py index 0c6348f2..9c6cd9a6 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/heartbeat/heartbeat_service.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/heartbeat/heartbeat_service.py @@ -8,7 +8,7 @@ """ from typing import List from fitframework import fitable, const -from ..entity import HeartBeatInfo, HeartBeatAddress +from fit_common_struct.entity import HeartBeatInfo, HeartBeatAddress @fitable(const.SEND_HEART_BEAT_GEN_ID, const.SEND_HEART_BEAT_FIT_ID) def send_heartbeat(heartbeatInfo: List[HeartBeatInfo], address: HeartBeatAddress) -> bool: diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py index fa9fa4da..8623dc46 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py @@ -9,12 +9,9 @@ @author 董智豪 @since 2025-06-04 """ -import logging import asyncio -import json import re import threading -import queue from concurrent.futures import ThreadPoolExecutor, Future from typing import List, Dict, Set @@ -24,7 +21,7 @@ from fitframework import fitable, const, value from fitframework.api.logging import plugin_logger from fitframework.utils.json_serialize_utils import json_serialize, json_deserialize -from ..entity import Worker, FitableMeta, FitableInfo, Application, FitableAddressInstance, GenericableInfo, \ +from fit_common_struct.entity import Worker, FitableMeta, FitableInfo, Application, FitableAddressInstance, GenericableInfo, \ FitableMetaInstance, ApplicationInstance, Address, Endpoint @value('nacos.serverAddr', default_value=None) diff --git a/framework/fit/python/plugin/fit_py_registry_client/fitable_address_service.py b/framework/fit/python/plugin/fit_py_registry_client/fitable_address_service.py index 8f07b214..1a33398b 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/fitable_address_service.py +++ b/framework/fit/python/plugin/fit_py_registry_client/fitable_address_service.py @@ -14,7 +14,7 @@ from fitframework.api.decorators import fitable, fit, scheduled_executor, value from fitframework.api.logging import sys_plugin_logger from fitframework.utils import tools -from plugin.fit_py_nacos_registry.entity import FitableAddressInstance, FitableInfo +from fit_common_struct.entity import FitableAddressInstance, FitableInfo from .registry_address_service import get_cache_aware_registry_address _PUSH_MODE = 'push' diff --git a/framework/fit/python/plugin/fit_py_registry_client/fitable_meta_service.py b/framework/fit/python/plugin/fit_py_registry_client/fitable_meta_service.py index e010ea96..0130fa23 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/fitable_meta_service.py +++ b/framework/fit/python/plugin/fit_py_registry_client/fitable_meta_service.py @@ -10,7 +10,7 @@ from fit_common_struct.core import Genericable from fitframework import fitable, const, fit -from plugin.fit_py_nacos_registry.entity import FitableMetaInstance, GenericableInfo +from fit_common_struct.entity import FitableMetaInstance, GenericableInfo @fit(const.QUERY_FITABLE_METAS_GEN_ID) diff --git a/framework/fit/python/plugin/fit_py_registry_client/online_fitable_service.py b/framework/fit/python/plugin/fit_py_registry_client/online_fitable_service.py index 8b9b461f..03bf9b3c 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/online_fitable_service.py +++ b/framework/fit/python/plugin/fit_py_registry_client/online_fitable_service.py @@ -17,7 +17,7 @@ from fitframework.api.decorators import scheduled_executor from fitframework.api.logging import sys_plugin_logger from fitframework.core.network.enums import SerializingStructureEnum -from plugin.fit_py_nacos_registry.entity import Worker, FitableMeta, FitableInfo, Address as AddressRegistry, Endpoint, \ +from fit_common_struct.entity import Worker, FitableMeta, FitableInfo, Address as AddressRegistry, Endpoint, \ Application, Address diff --git a/framework/fit/python/plugin/fit_py_registry_client/test/fitable_address_service_test.py b/framework/fit/python/plugin/fit_py_registry_client/test/fitable_address_service_test.py index 079b6809..69f4bfbd 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/test/fitable_address_service_test.py +++ b/framework/fit/python/plugin/fit_py_registry_client/test/fitable_address_service_test.py @@ -21,7 +21,7 @@ class FitableAddressServiceTest(FitTestSupport): def setUpClass(cls): super(FitableAddressServiceTest, cls).setUpClass() from plugin.fit_py_registry_client import fitable_address_service - from plugin.fit_py_nacos_registry import entity + from fit_common_struct import entity global fitable_address_service global entity From e92b6135968f85a4a5273183a975a0ef493d671c Mon Sep 17 00:00:00 2001 From: xiaohao <932379959@qq.com> Date: Sat, 30 Aug 2025 20:17:40 +0800 Subject: [PATCH 06/15] =?UTF-8?q?[fit]=20=E5=88=A0=E9=99=A4entity.py?= =?UTF-8?q?=E4=B8=AD=E7=9A=84GenericableInfo=E5=92=8CFitableInfo=EF=BC=8C?= =?UTF-8?q?=E4=BF=9D=E7=95=99core=E4=B8=AD=E7=9A=84Genericable=E5=92=8CFit?= =?UTF-8?q?able=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bootstrap/fit_py_service_db/service_db.py | 2 +- .../fit/python/fit_common_struct/core.py | 16 +++---- .../fit/python/fit_common_struct/entity.py | 43 ++----------------- .../core/broker/configure_based_brokerimpl.py | 20 ++++----- .../service/nacos_registry_server.py | 33 +++++++------- .../fitable_address_service.py | 20 ++++----- .../fitable_meta_service.py | 10 ++--- .../online_fitable_service.py | 13 +++--- .../test/fitable_address_service_test.py | 6 +-- 9 files changed, 64 insertions(+), 99 deletions(-) diff --git a/framework/fit/python/bootstrap/fit_py_service_db/service_db.py b/framework/fit/python/bootstrap/fit_py_service_db/service_db.py index f3110ecb..ce7d7500 100644 --- a/framework/fit/python/bootstrap/fit_py_service_db/service_db.py +++ b/framework/fit/python/bootstrap/fit_py_service_db/service_db.py @@ -127,7 +127,7 @@ def register_all_fit_services() -> None: fitable_aliases_infos = reduce(list.__add__, list(_plugin_fitable_dict.values())) local_fitable_aliases_infos = [] for fitable_aliases_info in fitable_aliases_infos: - if not _local_only_invoke(fitable_aliases_info.fitable.genericable_id): + if not _local_only_invoke(fitable_aliases_info.fitable.genericableId): local_fitable_aliases_infos.append(fitable_aliases_info) online_fit_services(local_fitable_aliases_infos) except FitException: diff --git a/framework/fit/python/fit_common_struct/core.py b/framework/fit/python/fit_common_struct/core.py index 5b193c3c..c20b1e03 100644 --- a/framework/fit/python/fit_common_struct/core.py +++ b/framework/fit/python/fit_common_struct/core.py @@ -10,9 +10,9 @@ class Genericable(object): - def __init__(self, genericable_id: str, genericable_version: str): - self.genericable_id = genericable_id - self.genericable_version = genericable_version + def __init__(self, genericableId: str, genericableVersion: str): + self.genericableId = genericableId + self.genericableVersion = genericableVersion def __eq__(self, other): if not isinstance(other, self.__class__): @@ -28,11 +28,11 @@ def __repr__(self): class Fitable(object): - def __init__(self, genericable_id: str, genericable_version: str, fitable_id: str, fitable_version: str): - self.genericable_id = genericable_id - self.genericable_version = genericable_version - self.fitable_id = fitable_id - self.fitable_version = fitable_version + def __init__(self, genericableId: str, genericableVersion: str, fitableId: str, fitableVersion: str): + self.genericableId = genericableId + self.genericableVersion = genericableVersion + self.fitableId = fitableId + self.fitableVersion = fitableVersion def __eq__(self, other): if not isinstance(other, self.__class__): diff --git a/framework/fit/python/fit_common_struct/entity.py b/framework/fit/python/fit_common_struct/entity.py index 4d6eb158..ebfa9fd1 100644 --- a/framework/fit/python/fit_common_struct/entity.py +++ b/framework/fit/python/fit_common_struct/entity.py @@ -10,6 +10,7 @@ from numpy import int32 from fit_common_struct.core import Address as AddressInner +from fit_common_struct.core import Fitable def safe_hash_dict(obj_dict): @@ -25,47 +26,9 @@ def safe_hash_dict(obj_dict): return hash(tuple(hashable_values)) -class FitableInfo(object): - - def __init__(self, genericableId: str, genericableVersion: str, fitableId: str, fitableVersion: str): - self.genericableId = genericableId - self.genericableVersion = genericableVersion - self.fitableId = fitableId - self.fitableVersion = fitableVersion - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __hash__(self): - return hash(tuple(self.__dict__.values())) - - def __repr__(self): - return str(tuple(self.__dict__.values())) - - -class GenericableInfo: - - def __init__(self, genericableId: str, genericableVersion: str): - self.genericableId = genericableId - self.genericableVersion = genericableVersion - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __hash__(self): - return hash(tuple(self.__dict__.values())) - - def __repr__(self): - return str(tuple(self.__dict__.values())) - - class FitableMeta(object): - def __init__(self, fitable: FitableInfo, aliases: List[str], formats: List[int32]): + def __init__(self, fitable: Fitable, aliases: List[str], formats: List[int32]): self.fitable = fitable self.aliases = aliases @@ -200,7 +163,7 @@ def __repr__(self): class FitableAddressInstance(object): - def __init__(self, applicationInstances: List[ApplicationInstance], fitable: FitableInfo): + def __init__(self, applicationInstances: List[ApplicationInstance], fitable: Fitable): self.applicationInstances = applicationInstances self.fitable = fitable diff --git a/framework/fit/python/fitframework/core/broker/configure_based_brokerimpl.py b/framework/fit/python/fitframework/core/broker/configure_based_brokerimpl.py index b42ce9fd..67da2b20 100644 --- a/framework/fit/python/fitframework/core/broker/configure_based_brokerimpl.py +++ b/framework/fit/python/fitframework/core/broker/configure_based_brokerimpl.py @@ -317,7 +317,7 @@ def default_load_balancing(self, generic_id, fitable_id, fitable: Fitable): f"addresses count: {len(addresses)}") if len(addresses) == 0: fit_logger.warning(f"cannot get any address can use in this worker. " - f"[genericable_id={fitable.genericable_id}, fitable_id={fitable.fitable_id}]") + f"[genericable_id={fitable.genericableId}, fitable_id={fitable.fitableId}]") return None # no choice! if len(addresses) == 1: @@ -339,13 +339,13 @@ def get_fit_service_addresses(self, fitable: Fitable) -> List[Address]: addresses: List[Address] = _get_fit_service_address_with_priorities(fitable) if not addresses: fit_logger.warning(f"cannot get any endpoint after checking format and protocol. " - f"[genericable_id={fitable.genericable_id}, fitable_id={fitable.fitable_id}]") + f"[genericable_id={fitable.genericableId}, fitable_id={fitable.fitableId}]") return [] addresses: List[Address] = _load_balance_env_filtering(addresses) if not addresses: fit_logger.warning(f"cannot get any endpoint after filtering by environment. " - f"[genericable_id={fitable.genericable_id}, fitable_id={fitable.fitable_id}]") + f"[genericable_id={fitable.genericableId}, fitable_id={fitable.fitableId}]") return [] return addresses @@ -398,7 +398,7 @@ def custom_load_balancing(self, address_filter: Callable[[Address], bool], addresses: List[Address] = self.get_fit_service_addresses(fitable) if len(addresses) == 0: fit_logger.warning(f"cannot get any address can use in this worker. " - f"[genericable_id={fitable.genericable_id}, fitable_id={fitable.fitable_id}]") + f"[genericable_id={fitable.genericableId}, fitable_id={fitable.fitableId}]") return None try: addresses = [address for address in addresses if address_filter(address)] @@ -407,15 +407,15 @@ def custom_load_balancing(self, address_filter: Callable[[Address], bool], return None if not addresses: fit_logger.warning(f"cannot get any address after custom load balancing. " - f"[genericable_id={fitable.genericable_id}, fitable_id={fitable.fitable_id}]") + f"[genericable_id={fitable.genericableId}, fitable_id={fitable.fitableId}]") return None if len(addresses) > 1: fit_logger.warning(f"get more than one address after custom load balancing. " - f"[genericable_id={fitable.genericable_id}, fitable_id={fitable.fitable_id}]") + f"[genericable_id={fitable.genericableId}, fitable_id={fitable.fitableId}]") return addresses[0] if addresses[0].id == _worker_id(): - return service_repo.get_fitable_ref(fitable.genericable_id, fitable.fitable_id) + return service_repo.get_fitable_ref(fitable.genericableId, fitable.fitableId) return addresses[0] @@ -466,9 +466,9 @@ def lb_call_template(fitable_info: Fitable, target_addresses: List[Address]) -> pass args = fitable, addresses - lb_fitable_id = get_fit_ffp_fitable_id(fitable.genericable_id, 'load_balance') + lb_fitable_id = get_fit_ffp_fitable_id(fitable.genericableId, 'load_balance') if lb_fitable_id: - fit_invoke_info = (fitable.genericable_id, lb_fitable_id, lb_call_template) + fit_invoke_info = (fitable.genericableId, lb_fitable_id, lb_call_template) return _ffp_invoke(fit_invoke_info, False, None, None, *args) else: fit_invoke_info = (const.LOAD_BALANCING_GEN_ID, const.LOAD_BALANCING_RANDOM_FIT_ID, lb_call_template) @@ -591,7 +591,7 @@ def _get_fit_service_address_with_priorities(fitable: Fitable) -> List[Address]: def _get_fit_service_address_and_convert(fitable: Fitable) -> List[Address]: addresses: List[Address] = get_fit_service_address_list(fitable) - fit_logger.debug(f"got address, gid: {fitable.genericable_id}, count: {len(addresses)}") + fit_logger.debug(f"got address, gid: {fitable.genericableId}, count: {len(addresses)}") return addresses diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py index 8623dc46..e6a483ad 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py @@ -21,8 +21,9 @@ from fitframework import fitable, const, value from fitframework.api.logging import plugin_logger from fitframework.utils.json_serialize_utils import json_serialize, json_deserialize -from fit_common_struct.entity import Worker, FitableMeta, FitableInfo, Application, FitableAddressInstance, GenericableInfo, \ +from fit_common_struct.entity import Worker, FitableMeta, Application, FitableAddressInstance, \ FitableMetaInstance, ApplicationInstance, Address, Endpoint +from fit_common_struct.core import Fitable,Genericable @value('nacos.serverAddr', default_value=None) def _get_nacos_server_addr() -> str: @@ -304,17 +305,17 @@ def build_service_key(group_name: str, service_name: str) -> str: return f"{group_name}{SEPARATOR}{service_name}" -def get_service_name(fitable: FitableInfo) -> str: +def get_service_name(fitable: Fitable) -> str: """获取服务名称""" return f"{fitable.fitableId}{SEPARATOR}{fitable.fitableVersion}" -def get_group_name_from_fitable(fitable: FitableInfo) -> str: +def get_group_name_from_fitable(fitable: Fitable) -> str: """从FitableInfo获取组名""" return f"{fitable.genericableId}{SEPARATOR}{fitable.genericableVersion}" -def get_group_name_from_genericable(genericable: GenericableInfo) -> str: +def get_group_name_from_genericable(genericable: Genericable) -> str: """从GenericableInfo获取组名""" return f"{genericable.genericableId}{SEPARATOR}{genericable.genericableVersion}" @@ -384,7 +385,7 @@ def parse_fitable_meta(metadata: Dict) -> FitableMeta: plugin_logger.error(f"Failed to parse fitable meta for instance: {e}") # 返回默认值 - default_fitable = FitableInfo("unknown", "1.0", "unknown", "1.0") + default_fitable = Fitable("unknown", "1.0", "unknown", "1.0") meta = FitableMeta(default_fitable, [], []) return meta @@ -488,7 +489,7 @@ def build_endpoints(extensions: Dict[str, str]) -> List[Endpoint]: return endpoints -def on_service_changed(fitable_info: FitableInfo, worker_id: str) -> None: +def on_service_changed(fitable_info: Fitable, worker_id: str) -> None: """ Handle service change events, query and notify updates to Fitables instance information. @@ -571,7 +572,7 @@ def register_fitables(fitable_metas: List[FitableMeta], worker: Worker, applicat raise @fitable(const.UNREGISTER_FIT_SERVICE_GEN_ID, const.UNREGISTER_FIT_SERVICE_FIT_ID) -def unregister_fitables(fitables: List[FitableInfo], worker_id: str) -> None: +def unregister_fitables(fitables: List[Fitable], worker_id: str) -> None: """ 向注册中心服务端取消注册服务实现列表。 @@ -584,7 +585,7 @@ def unregister_fitables(fitables: List[FitableInfo], worker_id: str) -> None: unregister_single_fitable(fitable, worker_id) -def unregister_single_fitable(fitable: FitableInfo, worker_id: str) -> None: +def unregister_single_fitable(fitable: Fitable, worker_id: str) -> None: """取消注册单个Fitable""" group_name = get_group_name_from_fitable(fitable) service_name = get_service_name(fitable) @@ -621,7 +622,7 @@ def unregister_matching_instances(instances: List[Instance], worker_id: str, ser @fitable(const.QUERY_FIT_SERVICE_GEN_ID, const.QUERY_FIT_SERVICE_FIT_ID) -def query_fitable_addresses(fitables: List[FitableInfo], worker_id: str) -> List[FitableAddressInstance]: +def query_fitable_addresses(fitables: List[Fitable], worker_id: str) -> List[FitableAddressInstance]: """ 注册中心所提供接口,用于查询某个泛服务实现的实例信息,在拉模式下使用。 @@ -644,7 +645,7 @@ def query_fitable_addresses(fitables: List[FitableInfo], worker_id: str) -> List return list(result_map.values()) -def query_instances(fitable: FitableInfo) -> List[Instance]: +def query_instances(fitable: Fitable) -> List[Instance]: """查询实例""" group_name = get_group_name_from_fitable(fitable) service_name = get_service_name(fitable) @@ -657,7 +658,7 @@ def query_instances(fitable: FitableInfo) -> List[Instance]: return _run_async_safely(call_list_instances(param)) -def process_application_instances(result_map: Dict, fitable: FitableInfo, instances: List[Instance]) -> None: +def process_application_instances(result_map: Dict, fitable: Fitable, instances: List[Instance]) -> None: """处理应用实例""" app_instances_map = group_instances_by_application(instances) @@ -675,7 +676,7 @@ def process_application_instances(result_map: Dict, fitable: FitableInfo, instan @fitable(const.SUBSCRIBE_FIT_SERVICE_GEN_ID, const.SUBSCRIBE_FIT_SERVICE_FIT_ID) -def subscribe_fit_service(fitables: List[FitableInfo], worker_id: str, callback_fitable_id: str) -> List[FitableAddressInstance]: +def subscribe_fit_service(fitables: List[Fitable], worker_id: str, callback_fitable_id: str) -> List[FitableAddressInstance]: """ 注册中心所提供接口,用于订阅某个泛服务实现的实例信息,并且也会返回查询到的实例信息,在推模式下使用。 @@ -698,7 +699,7 @@ def subscribe_fit_service(fitables: List[FitableInfo], worker_id: str, callback_ continue # 创建事件监听器 - def create_event_listener(fitable_ref: FitableInfo, worker_id_ref: str): + def create_event_listener(fitable_ref: Fitable, worker_id_ref: str): def event_listener(event): _executor.submit(on_service_changed, fitable_ref, worker_id_ref) return event_listener @@ -722,7 +723,7 @@ def event_listener(event): @fitable(const.UNSUBSCRIBE_FIT_SERVICE_GEN_ID, const.UNSUBSCRIBE_FIT_SERVICE_FIT_ID) -def unsubscribe_fitables(fitables: List[FitableInfo], worker_id: str, callback_fitable_id: str) -> None: +def unsubscribe_fitables(fitables: List[Fitable], worker_id: str, callback_fitable_id: str) -> None: """ 向注册中心服务端取消订阅指定服务实现的实例信息。 @@ -753,7 +754,7 @@ def unsubscribe_fitables(fitables: List[FitableInfo], worker_id: str, callback_f @fitable(const.QUERY_FITABLE_METAS_GEN_ID, const.QUERY_FITABLE_METAS_FIT_ID) -def query_fitable_metas(genericable_infos: List[GenericableInfo]) -> List[FitableMetaInstance]: +def query_fitable_metas(genericable_infos: List[Genericable]) -> List[FitableMetaInstance]: """ 注册中心所提供接口,用于查询泛服务的元数据。 @@ -769,7 +770,7 @@ def query_fitable_metas(genericable_infos: List[GenericableInfo]) -> List[Fitabl return build_fitable_meta_instances(meta_environments) -def process_genericable_services(genericable: GenericableInfo, meta_environments: Dict) -> None: +def process_genericable_services(genericable: Genericable, meta_environments: Dict) -> None: """处理泛服务的服务列表""" group_name = get_group_name_from_genericable(genericable) diff --git a/framework/fit/python/plugin/fit_py_registry_client/fitable_address_service.py b/framework/fit/python/plugin/fit_py_registry_client/fitable_address_service.py index 1a33398b..c9865be6 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/fitable_address_service.py +++ b/framework/fit/python/plugin/fit_py_registry_client/fitable_address_service.py @@ -14,7 +14,7 @@ from fitframework.api.decorators import fitable, fit, scheduled_executor, value from fitframework.api.logging import sys_plugin_logger from fitframework.utils import tools -from fit_common_struct.entity import FitableAddressInstance, FitableInfo +from fit_common_struct.entity import FitableAddressInstance from .registry_address_service import get_cache_aware_registry_address _PUSH_MODE = 'push' @@ -58,7 +58,7 @@ def get_runtime_worker_id() -> str: @fit(const.SUBSCRIBE_FIT_SERVICE_GEN_ID) -def subscribe_fit_service(fitables: List[FitableInfo], worker_id: str, callback_fitable_id: str) \ +def subscribe_fit_service(fitables: List[Fitable], worker_id: str, callback_fitable_id: str) \ -> List[FitableAddressInstance]: """ 注册中心所提供接口,用于订阅某个泛服务实现的实例信息,并且也会返回查询到的实例信息,在推模式下使用。 @@ -72,7 +72,7 @@ def subscribe_fit_service(fitables: List[FitableInfo], worker_id: str, callback_ @fit(const.QUERY_FIT_SERVICE_GEN_ID) -def query_fitable_addresses(fitables: List[FitableInfo], worker_id: str) -> List[FitableAddressInstance]: +def query_fitable_addresses(fitables: List[Fitable], worker_id: str) -> List[FitableAddressInstance]: """ 注册中心所提供接口,用于查询某个泛服务实现的实例信息,在拉模式下使用。 @@ -83,9 +83,9 @@ def query_fitable_addresses(fitables: List[FitableInfo], worker_id: str) -> List pass -def _convert_fitable_to_fitable_info(fitable_: Fitable) -> FitableInfo: - return FitableInfo(fitable_.genericable_id, fitable_.genericable_version, fitable_.fitable_id, - fitable_.fitable_version) +def _convert_fitable_to_fitable_info(fitable_: Fitable) -> Fitable: + return Fitable(fitable_.genericableId, fitable_.genericableVersion, fitable_.fitableId, + fitable_.fitableVersion) def _convert_fitable_address_instance_to_addresses(fitable_inst: FitableAddressInstance) -> \ @@ -120,7 +120,7 @@ def notify_fitable_changes(fitable_instances: List[FitableAddressInstance]) -> N _update_addresses_in_cache(fitable_, addresses) -def _get_fitable_address_instances(fitable_infos: List[FitableInfo]) -> List[FitableAddressInstance]: +def _get_fitable_address_instances(fitable_infos: List[Fitable]) -> List[FitableAddressInstance]: if _registry_client_mode() == _PULL_MODE: return query_fitable_addresses(fitable_infos, get_runtime_worker_id()) else: @@ -157,9 +157,9 @@ def get_fit_service_address_list(fitable_: Fitable) -> List[Address]: @param fitable_: 待查询的泛服务实例信息。 @return: 所查询到的该泛服务的实例列表。 """ - sys_plugin_logger.debug(f"get fit service address list: gid{fitable_.genericable_id}") - if fitable_.genericable_id in _get_registry_server_generic_ids(): - sys_plugin_logger.debug(f"get fit service address list: gid{fitable_.genericable_id}, is registry server api") + sys_plugin_logger.debug(f"get fit service address list: gid{fitable_.genericableId}") + if fitable_.genericableId in _get_registry_server_generic_ids(): + sys_plugin_logger.debug(f"get fit service address list: gid{fitable_.genericableId}, is registry server api") return get_cache_aware_registry_address() addresses = _get_addresses_from_cache(fitable_) if addresses: diff --git a/framework/fit/python/plugin/fit_py_registry_client/fitable_meta_service.py b/framework/fit/python/plugin/fit_py_registry_client/fitable_meta_service.py index 0130fa23..5a61eb67 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/fitable_meta_service.py +++ b/framework/fit/python/plugin/fit_py_registry_client/fitable_meta_service.py @@ -8,13 +8,13 @@ """ from typing import List -from fit_common_struct.core import Genericable from fitframework import fitable, const, fit -from fit_common_struct.entity import FitableMetaInstance, GenericableInfo +from fit_common_struct.entity import FitableMetaInstance +from fit_common_struct.core import Genericable @fit(const.QUERY_FITABLE_METAS_GEN_ID) -def query_fitable_metas(genericable_infos: List[GenericableInfo]) -> List[FitableMetaInstance]: +def query_fitable_metas(genericable_infos: List[Genericable]) -> List[FitableMetaInstance]: """ 注册中心所提供接口,用于查询泛服务的元数据。 @@ -27,6 +27,6 @@ def query_fitable_metas(genericable_infos: List[GenericableInfo]) -> List[Fitabl @fitable(const.GET_FITABLES_OF_GENERICABLE_GEN_ID, const.GET_FITABLES_OF_GENERICABLE_FIT_ID) def get_all_fitables_from_registry(genericable: Genericable) -> List[str]: fitable_meta_instances: List[FitableMetaInstance] = query_fitable_metas( - [GenericableInfo(genericable.genericable_id, genericable.genericable_version)]) + [Genericable(genericable.genericableId, genericable.genericableVersion)]) return [instance.meta.fitable.fitableId for instance in fitable_meta_instances if - instance.meta.fitable.genericableId == genericable.genericable_id] + instance.meta.fitable.genericableId == genericable.genericableId] diff --git a/framework/fit/python/plugin/fit_py_registry_client/online_fitable_service.py b/framework/fit/python/plugin/fit_py_registry_client/online_fitable_service.py index 03bf9b3c..3d2a2673 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/online_fitable_service.py +++ b/framework/fit/python/plugin/fit_py_registry_client/online_fitable_service.py @@ -17,8 +17,9 @@ from fitframework.api.decorators import scheduled_executor from fitframework.api.logging import sys_plugin_logger from fitframework.core.network.enums import SerializingStructureEnum -from fit_common_struct.entity import Worker, FitableMeta, FitableInfo, Address as AddressRegistry, Endpoint, \ +from fit_common_struct.entity import Worker, FitableMeta, Address as AddressRegistry, Endpoint, \ Application, Address +from fit_common_struct.core import Fitable @value('worker-environment.env') @@ -81,10 +82,10 @@ def register_fitables(fitable_metas: List[FitableMeta], worker: Worker, applicat def _convert_fitable_aliases_info_to_fitable_meta(fitable_aliases_info: FitableAliasesInfo) -> FitableMeta: local_formats = [each_format.value for each_format in get_registered_formats()] - fitable_info = FitableInfo(fitable_aliases_info.fitable.genericable_id, - fitable_aliases_info.fitable.genericable_version, - fitable_aliases_info.fitable.fitable_id, - fitable_aliases_info.fitable.fitable_version) + fitable_info = Fitable(fitable_aliases_info.fitable.genericableId, + fitable_aliases_info.fitable.genericableVersion, + fitable_aliases_info.fitable.fitableId, + fitable_aliases_info.fitable.fitableVersion) return FitableMeta(fitable_info, fitable_aliases_info.aliases, local_formats) @@ -100,7 +101,7 @@ def _fetch_all_addresses() -> List[AddressRegistry]: def _fetch_fitable_meta_info(fitable_meta: FitableMeta) -> tuple: - fitable_info: FitableInfo = fitable_meta.fitable + fitable_info: Fitable = fitable_meta.fitable return fitable_info.genericableId, fitable_info.fitableVersion, fitable_info.fitableId, fitable_info.fitableVersion diff --git a/framework/fit/python/plugin/fit_py_registry_client/test/fitable_address_service_test.py b/framework/fit/python/plugin/fit_py_registry_client/test/fitable_address_service_test.py index 69f4bfbd..272453be 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/test/fitable_address_service_test.py +++ b/framework/fit/python/plugin/fit_py_registry_client/test/fitable_address_service_test.py @@ -25,12 +25,12 @@ def setUpClass(cls): global fitable_address_service global entity - def query_fitable_addresses_side_effect(fitables: List[entity.FitableInfo], worker_id: str): + def query_fitable_addresses_side_effect(fitables: List[entity.Fitable], worker_id: str): if fitables[0].genericableId == "gid_ut": return [cls.build_fitable_address_instance("host_ut_1", 8000)] return [] - def subscribe_fit_service_side_effect(fitables: List[entity.FitableInfo], worker_id: str, + def subscribe_fit_service_side_effect(fitables: List[entity.Fitable], worker_id: str, callback_fitable_id: str): if fitables[0].genericableId == "gid_ut": return [cls.build_fitable_address_instance("host_ut_1", 8000)] @@ -59,7 +59,7 @@ def build_fitable_address_instance(cls, host: str, port: int): worker = entity.Worker([address], "worker_ut", "env_ut", {"http.context-path": "context-path-ut"}) application = entity.Application("name_ut", "name_version_ut") application_instance = entity.ApplicationInstance([worker], application, [1]) - fitable_info = entity.FitableInfo("gid_ut", "1.0.0", "fid_ut", "1.0.0") + fitable_info = entity.Fitable("gid_ut", "1.0.0", "fid_ut", "1.0.0") fitable_address_instance = entity.FitableAddressInstance([application_instance], fitable_info) return fitable_address_instance From a51c7f19c58f41f09bc3b92b4468c5ea6a0ba39c Mon Sep 17 00:00:00 2001 From: xiaohao <932379959@qq.com> Date: Wed, 3 Sep 2025 09:13:24 +0800 Subject: [PATCH 07/15] =?UTF-8?q?[fit]=20=E6=A0=BC=E5=BC=8F=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- framework/fit/python/conf/application.yml | 1 - framework/fit/python/fitframework/const.py | 2 +- .../python/plugin/fit_py_nacos_registry/conf/application.yml | 2 -- .../plugin/fit_py_nacos_registry/heartbeat/heartbeat_service.py | 1 + .../fit/python/plugin/fit_py_server_http/conf/application.yml | 1 - 5 files changed, 2 insertions(+), 5 deletions(-) diff --git a/framework/fit/python/conf/application.yml b/framework/fit/python/conf/application.yml index 36446220..f8f50945 100644 --- a/framework/fit/python/conf/application.yml +++ b/framework/fit/python/conf/application.yml @@ -18,7 +18,6 @@ terminate-main: enabled: false local_ip: "localhost" context-path: "" - http: server: enabled: true diff --git a/framework/fit/python/fitframework/const.py b/framework/fit/python/fitframework/const.py index c08b6b7b..a359fb7a 100644 --- a/framework/fit/python/fitframework/const.py +++ b/framework/fit/python/fitframework/const.py @@ -245,8 +245,8 @@ # heartbeat server SEND_HEART_BEAT_GEN_ID = 'modelengine.fit.heartbeat.send-heartbeat' -STOP_HEART_BEAT_GEN_ID = 'modelengine.fit.heartbeat.stop-heartbeat' SEND_HEART_BEAT_FIT_ID = 'send-heartbeat' +STOP_HEART_BEAT_GEN_ID = 'modelengine.fit.heartbeat.stop-heartbeat' STOP_HEART_BEAT_FIT_ID = 'stop-heartbeat' diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/conf/application.yml b/framework/fit/python/plugin/fit_py_nacos_registry/conf/application.yml index 49bad100..e69de29b 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/conf/application.yml +++ b/framework/fit/python/plugin/fit_py_nacos_registry/conf/application.yml @@ -1,2 +0,0 @@ -nacos: - serverAddr: 127.0.0.1:8848 diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/heartbeat/heartbeat_service.py b/framework/fit/python/plugin/fit_py_nacos_registry/heartbeat/heartbeat_service.py index 9c6cd9a6..7cd2351b 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/heartbeat/heartbeat_service.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/heartbeat/heartbeat_service.py @@ -6,6 +6,7 @@ """ 功 能:服务上线相关功能。 """ + from typing import List from fitframework import fitable, const from fit_common_struct.entity import HeartBeatInfo, HeartBeatAddress diff --git a/framework/fit/python/plugin/fit_py_server_http/conf/application.yml b/framework/fit/python/plugin/fit_py_server_http/conf/application.yml index 95bda7c9..2691b5be 100644 --- a/framework/fit/python/plugin/fit_py_server_http/conf/application.yml +++ b/framework/fit/python/plugin/fit_py_server_http/conf/application.yml @@ -1,5 +1,4 @@ server-thread-count: 8 - https: server: enabled: false From b826482310f28b47f952c1e4633734b6d3d110c1 Mon Sep 17 00:00:00 2001 From: xiaohao <932379959@qq.com> Date: Wed, 3 Sep 2025 09:41:06 +0800 Subject: [PATCH 08/15] =?UTF-8?q?[fit]=20=E6=A0=BC=E5=BC=8F=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- framework/fit/python/conf/application.yml | 3 +-- framework/fit/python/conf/fit.yml | 2 +- framework/fit/python/fitframework/const.py | 1 - .../service/nacos_registry_server.py | 11 ++++------- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/framework/fit/python/conf/application.yml b/framework/fit/python/conf/application.yml index f8f50945..344483c0 100644 --- a/framework/fit/python/conf/application.yml +++ b/framework/fit/python/conf/application.yml @@ -40,12 +40,11 @@ https: key_file_encrypted: false # 私钥是否被加密,仅当 cert_enabled 为 true 时有意义 key_file_password: "" # 私钥的密码,仅当 cert_enabled 和 key_file_encrypted 为 true 时有意义 key_file_password_encrypted: false # 私钥的密码是否被加密,仅当 cert_enabled 和 key_file_encrypted 为 true 时有意义 - registry-center: server: mode: 'DIRECT' addresses: - - "localhost:8090" + - "localhost:8848" protocol: 2 formats: - 1 diff --git a/framework/fit/python/conf/fit.yml b/framework/fit/python/conf/fit.yml index 74e454f2..64155ee8 100644 --- a/framework/fit/python/conf/fit.yml +++ b/framework/fit/python/conf/fit.yml @@ -505,4 +505,4 @@ fit.public.genericables.modelengine.fit.get.earliest.start.time: default: "local-worker" tags: - "localOnly" - - "nonTraceable" + - "nonTraceable" \ No newline at end of file diff --git a/framework/fit/python/fitframework/const.py b/framework/fit/python/fitframework/const.py index a359fb7a..645f4098 100644 --- a/framework/fit/python/fitframework/const.py +++ b/framework/fit/python/fitframework/const.py @@ -249,7 +249,6 @@ STOP_HEART_BEAT_GEN_ID = 'modelengine.fit.heartbeat.stop-heartbeat' STOP_HEART_BEAT_FIT_ID = 'stop-heartbeat' - # debugger DEBUGGER_START_FIT_ID = 'debugger_start_fitable_id' DEBUGGER_START_GEN_ID = 'debugger_start_gen_id' diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py index e6a483ad..5127258c 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py @@ -24,13 +24,10 @@ from fit_common_struct.entity import Worker, FitableMeta, Application, FitableAddressInstance, \ FitableMetaInstance, ApplicationInstance, Address, Endpoint from fit_common_struct.core import Fitable,Genericable +from fitframework.utils import tools -@value('nacos.serverAddr', default_value=None) -def _get_nacos_server_addr() -> str: - """ - 获取 Nacos 服务器地址。 - :return: Nacos 服务器地址。 - """ +@value('registry-center.server.addresses', converter=tools.to_list) +def _get_registry_server_addresses() -> list: pass @@ -152,7 +149,7 @@ async def call_register_instance(param: RegisterInstanceParam) -> None: # 初始化 Nacos 客户端配置 config = (ClientConfigBuilder() - .server_address(_get_nacos_server_addr()) + .server_address(_get_registry_server_addresses()[0]) .namespace_id(_get_nacos_namespace() or 'local') .username(_get_nacos_username() or None) .password(_get_nacos_password() or None) From 6e451836e794ad6bdf45029ee7a9ae3c43c039a6 Mon Sep 17 00:00:00 2001 From: xiaohao <932379959@qq.com> Date: Wed, 3 Sep 2025 20:46:42 +0800 Subject: [PATCH 09/15] =?UTF-8?q?[fit]=20=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- framework/fit/python/conf/application.yml | 2 +- .../fit_py_nacos_registry/service/__init__.py | 26 + .../service/async_executor.py | 212 +++++ .../fit_py_nacos_registry/service/config.py | 134 +++ .../service/constants.py | 29 + .../service/nacos_registry_server.py | 778 +++++------------- .../fit_py_nacos_registry/service/parsers.py | 137 +++ .../fit_py_nacos_registry/service/utils.py | 162 ++++ 8 files changed, 894 insertions(+), 586 deletions(-) create mode 100644 framework/fit/python/plugin/fit_py_nacos_registry/service/__init__.py create mode 100644 framework/fit/python/plugin/fit_py_nacos_registry/service/async_executor.py create mode 100644 framework/fit/python/plugin/fit_py_nacos_registry/service/config.py create mode 100644 framework/fit/python/plugin/fit_py_nacos_registry/service/constants.py create mode 100644 framework/fit/python/plugin/fit_py_nacos_registry/service/parsers.py create mode 100644 framework/fit/python/plugin/fit_py_nacos_registry/service/utils.py diff --git a/framework/fit/python/conf/application.yml b/framework/fit/python/conf/application.yml index 344483c0..b7274022 100644 --- a/framework/fit/python/conf/application.yml +++ b/framework/fit/python/conf/application.yml @@ -42,7 +42,7 @@ https: key_file_password_encrypted: false # 私钥的密码是否被加密,仅当 cert_enabled 和 key_file_encrypted 为 true 时有意义 registry-center: server: - mode: 'DIRECT' + mode: 'DIRECT' # DIRECT 表示直连,直接连接内存注册中心;PROXY 表示代理模式,通过本地代理服务连接 Nacos 注册中心 addresses: - "localhost:8848" protocol: 2 diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/__init__.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/__init__.py new file mode 100644 index 00000000..6f4ff757 --- /dev/null +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/__init__.py @@ -0,0 +1,26 @@ +# -- encoding: utf-8 -- +# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved. +# This file is a part of the ModelEngine Project. +# Licensed under the MIT License. See License.txt in the project root for license information. +# ====================================================================================================================== +""" +Nacos registry service package. +""" + +from .nacos_registry_server import ( + register_fitables, + unregister_fitables, + query_fitable_addresses, + subscribe_fit_service, + unsubscribe_fitables, + query_fitable_metas +) + +__all__ = [ + 'register_fitables', + 'unregister_fitables', + 'query_fitable_addresses', + 'subscribe_fit_service', + 'unsubscribe_fitables', + 'query_fitable_metas' +] diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/async_executor.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/async_executor.py new file mode 100644 index 00000000..e2c24cd4 --- /dev/null +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/async_executor.py @@ -0,0 +1,212 @@ +# -- encoding: utf-8 -- +# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved. +# This file is a part of the ModelEngine Project. +# Licensed under the MIT License. See License.txt in the project root for license information. +# ====================================================================================================================== +""" +Async executor for Nacos operations. + +This module provides an async executor for handling Nacos operations +in a background thread with proper event loop management. +""" +import asyncio +import atexit +import threading +from concurrent.futures import Future + +from v2.nacos import NacosNamingService, RegisterInstanceParam, ListInstanceParam, \ + DeregisterInstanceParam, SubscribeServiceParam, ListServiceParam + +from fitframework.api.logging import plugin_logger +from .config import build_nacos_config + + +class AsyncExecutor: + """Executor for handling asynchronous operations in a background thread.""" + + def __init__(self): + self._loop = None + self._thread = None + self._started = False + self._shutdown = False + self._nacos_client = None + self._init_complete = threading.Event() + + def start(self): + """Start the background event loop thread.""" + if self._started: + return + + self._thread = threading.Thread( + target=self._run_event_loop, + daemon=True, + name="NacosAsyncThread" + ) + self._thread.start() + + # Wait for initialization to complete + if not self._init_complete.wait(timeout=10): # Max wait 10 seconds + raise RuntimeError("Failed to initialize async executor within timeout") + + self._started = True + + def _run_event_loop(self): + """Run the event loop in the background thread.""" + try: + self._loop = asyncio.new_event_loop() + asyncio.set_event_loop(self._loop) + + # Create Nacos client in this event loop + async def init_nacos_client(): + try: + config = build_nacos_config() + self._nacos_client = await NacosNamingService.create_naming_service(config) + plugin_logger.info("Nacos client initialized successfully") + except Exception as e: + plugin_logger.error(f"Failed to initialize Nacos client: {e}") + raise + finally: + # Mark initialization complete + self._init_complete.set() + + self._loop.run_until_complete(init_nacos_client()) + + # Run event loop until shutdown + self._loop.run_forever() + except Exception as e: + plugin_logger.error(f"Error in async executor event loop: {e}") + self._init_complete.set() # Set even on failure to avoid infinite wait + finally: + try: + if self._nacos_client: + # Cleanup Nacos client if needed + pass + if self._loop: + self._loop.close() + except Exception as e: + plugin_logger.error(f"Error during cleanup: {e}") + + def run_coroutine(self, coro): + """ + Run a coroutine in the background event loop and return the result. + + Args: + coro: The coroutine to run. + + Returns: + The result of the coroutine. + + Raises: + RuntimeError: If the executor is not properly initialized. + """ + if not self._started: + self.start() + + if self._loop is None or self._nacos_client is None: + raise RuntimeError("Async executor not properly initialized") + + # Create a Future to get the result + result_future = Future() + + async def wrapped_coro(): + try: + result = await coro + result_future.set_result(result) + except Exception as e: + result_future.set_exception(e) + + # Schedule the coroutine in the event loop + self._loop.call_soon_threadsafe(asyncio.create_task, wrapped_coro()) + + # Wait for result + return result_future.result(timeout=30) # 30 second timeout + + def get_nacos_client(self): + """ + Get the Nacos client instance. + + Returns: + The Nacos client instance. + """ + if not self._started: + self.start() + return self._nacos_client + + def shutdown(self): + """Shutdown the async executor.""" + if self._loop and not self._loop.is_closed(): + self._loop.call_soon_threadsafe(self._loop.stop) + self._shutdown = True + + +# Global async executor +_async_executor = AsyncExecutor() + + +def run_async_safely(coro): + """ + Run an async operation safely using the dedicated executor. + + Args: + coro: The coroutine to run. + + Returns: + The result of the coroutine. + + Raises: + Exception: If the async operation fails. + """ + try: + return _async_executor.run_coroutine(coro) + except Exception as e: + plugin_logger.error(f"Error running async operation: {e}") + raise + + +# Async wrapper functions +async def call_list_instances(param: ListInstanceParam): + """List instances.""" + client = _async_executor.get_nacos_client() + return await client.list_instances(param) + + +async def call_deregister_instance(param: DeregisterInstanceParam) -> bool: + """Deregister instance.""" + client = _async_executor.get_nacos_client() + return await client.deregister_instance(param) + + +async def call_subscribe(param: SubscribeServiceParam) -> None: + """Subscribe to service.""" + client = _async_executor.get_nacos_client() + await client.subscribe(param) + + +async def call_unsubscribe(param: SubscribeServiceParam) -> None: + """Unsubscribe from service.""" + client = _async_executor.get_nacos_client() + await client.unsubscribe(param) + + +async def call_list_services(param: ListServiceParam): + """List services.""" + client = _async_executor.get_nacos_client() + return await client.list_services(param) + + +async def call_register_instance(param: RegisterInstanceParam) -> None: + """Register instance.""" + client = _async_executor.get_nacos_client() + await client.register_instance(param) + + +def _cleanup_async_executor(): + """Cleanup the async executor.""" + try: + _async_executor.shutdown() + except Exception as e: + plugin_logger.error(f"Error during async executor cleanup: {e}") + + +# Register cleanup function +atexit.register(_cleanup_async_executor) diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/config.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/config.py new file mode 100644 index 00000000..2542cad7 --- /dev/null +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/config.py @@ -0,0 +1,134 @@ +# -- encoding: utf-8 -- +# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved. +# This file is a part of the ModelEngine Project. +# Licensed under the MIT License. See License.txt in the project root for license information. +# ====================================================================================================================== +""" +Configuration module for Nacos registry server. +""" +from v2.nacos import ClientConfigBuilder + +from fitframework import value +from fitframework.utils import tools + + +@value('registry-center.server.addresses', converter=tools.to_list) +def get_registry_server_addresses() -> list: + """Get the list of registry server addresses.""" + pass + + +@value('nacos.username', default_value=None) +def get_nacos_username() -> str: + """ + Get the Nacos username. + + Returns: + Nacos username. + """ + pass + + +@value('nacos.password', default_value=None) +def get_nacos_password() -> str: + """ + Get the Nacos password. + + Returns: + Nacos password. + """ + pass + + +@value('nacos.accessKey', default_value=None) +def get_nacos_access_key() -> str: + """ + Get the Nacos access key. + + Returns: + Nacos access key. + """ + pass + + +@value('nacos.secretKey', default_value=None) +def get_nacos_secret_key() -> str: + """ + Get the Nacos secret key. + + Returns: + Nacos secret key. + """ + pass + + +@value('nacos.namespace', default_value="") +def get_nacos_namespace() -> str: + """ + Get the Nacos namespace. + + Returns: + Nacos namespace. + """ + pass + + +@value('nacos.isEphemeral', default_value=True, converter=bool) +def get_heartbeat_is_ephemeral() -> bool: + """ + Get whether the heartbeat is ephemeral. + + Returns: + Whether the heartbeat is ephemeral. + """ + pass + + +@value('nacos.heartBeatInterval', default_value=5000, converter=int) +def get_heartbeat_interval() -> int: + """ + Get the heartbeat interval in milliseconds. + + Returns: + Heartbeat interval in milliseconds. + """ + pass + + +@value('nacos.heartBeatTimeout', default_value=15000, converter=int) +def get_heartbeat_timeout() -> int: + """ + Get the heartbeat timeout in milliseconds. + + Returns: + Heartbeat timeout in milliseconds. + """ + pass + + +@value('nacos.weight', default_value=1.0, converter=float) +def get_heartbeat_weight() -> float: + """ + Get the heartbeat weight. + + Returns: + Heartbeat weight. + """ + pass + + +def build_nacos_config(): + """ + Build the Nacos client configuration. + + Returns: + Configured Nacos client config. + """ + return (ClientConfigBuilder() + .server_address(get_registry_server_addresses()[0]) + .namespace_id(get_nacos_namespace() or 'local') + .username(get_nacos_username()) + .password(get_nacos_password()) + .access_key(get_nacos_access_key()) + .secret_key(get_nacos_secret_key()) + .build()) diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/constants.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/constants.py new file mode 100644 index 00000000..0a8a2373 --- /dev/null +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/constants.py @@ -0,0 +1,29 @@ +# -- encoding: utf-8 -- +# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved. +# This file is a part of the ModelEngine Project. +# Licensed under the MIT License. See License.txt in the project root for license information. +# ====================================================================================================================== +""" +Constants for Nacos registry server. +""" +import re + +# Metadata keys +CLUSTER_DOMAIN_KEY = "cluster.domain" +WORKER_KEY = "worker" +APPLICATION_KEY = "application" +FITABLE_META_KEY = "fitable-meta" + +# Patterns and separators +CLUSTER_PORT_PATTERN = re.compile(r"cluster\.(.*?)\.port") +SEPARATOR = "::" + +# Protocol code mapping +PROTOCOL_CODE_MAP = { + "rsocket": 0, + "socket": 1, + "http": 2, + "grpc": 3, + "uc": 10, + "shareMemory": 11 +} diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py index 5127258c..b4206d32 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py @@ -5,551 +5,94 @@ # ====================================================================================================================== """ Service for providing Nacos registry center functionality. - -@author 董智豪 -@since 2025-06-04 """ -import asyncio -import re -import threading -from concurrent.futures import ThreadPoolExecutor, Future -from typing import List, Dict, Set +from concurrent.futures import ThreadPoolExecutor +from typing import List, Dict -from v2.nacos import NacosNamingService, ClientConfigBuilder, RegisterInstanceParam, ListInstanceParam, \ - DeregisterInstanceParam, SubscribeServiceParam, ClientConfig, Instance, ListServiceParam, ServiceList +from v2.nacos import RegisterInstanceParam, ListInstanceParam, \ + DeregisterInstanceParam, SubscribeServiceParam, Instance, ListServiceParam -from fitframework import fitable, const, value +from fitframework import fitable, const from fitframework.api.logging import plugin_logger -from fitframework.utils.json_serialize_utils import json_serialize, json_deserialize from fit_common_struct.entity import Worker, FitableMeta, Application, FitableAddressInstance, \ - FitableMetaInstance, ApplicationInstance, Address, Endpoint -from fit_common_struct.core import Fitable,Genericable -from fitframework.utils import tools - -@value('registry-center.server.addresses', converter=tools.to_list) -def _get_registry_server_addresses() -> list: - pass - - -@value('nacos.username', default_value=None) -def _get_nacos_username() -> str: - """ - 获取 Nacos 用户名。 - :return: Nacos 用户名。 - """ - pass - - -@value('nacos.password', default_value=None) -def _get_nacos_password() -> str: - """ - 获取 Nacos 密码。 - :return: Nacos 密码。 - """ - pass - - -@value('nacos.accessKey', default_value=None) -def _get_nacos_access_key() -> str: - """ - 获取 Nacos Access Key。 - :return: Nacos Access Key。 - """ - pass - - -@value('nacos.secretKey', default_value=None) -def _get_nacos_secret_key() -> str: - """ - 获取 Nacos Secret Key。 - :return: Nacos Secret Key。 - """ - pass - - -@value('nacos.namespace', default_value="") -def _get_nacos_namespace() -> str: - """ - 获取 Nacos 命名空间。 - :return: Nacos 命名空间。 - """ - pass - - -@value('nacos.isEphemeral', default_value=True, converter=bool) -def _get_heartbeat_isEphemeral() -> bool: - """ - 获取 Nacos 心跳是否为临时实例。 - :return: 是否为临时实例。 - """ - pass - - -@value('nacos.heartBeatInterval', default_value=5000, converter=int) -def _get_heartbeat_interval() -> int: - """ - 获取 Nacos 心跳间隔时间。 - :return: 心跳间隔时间,单位为毫秒。 - """ - pass - - -@value('nacos.heartBeatTimeout', default_value=15000, converter=int) -def _get_heartbeat_timeout() -> int: - """ - 获取 Nacos 心跳超时时间。 - :return: 心跳超时时间,单位为毫秒。 - """ - pass - - -@value('nacos.weight', default_value=1.0, converter=float) -def _get_heartbeat_weight() -> float: - """ - 获取 Nacos 心跳权重。 - :return: 心跳权重。 - """ - pass - - -# 对async 第三方函数进行封装 -async def call_create_naming_service(config: ClientConfig) -> 'NacosNamingService': - return await NacosNamingService.create_naming_service(config) - - -async def call_list_instances(param: ListInstanceParam) -> List[Instance]: - client = _async_executor.get_nacos_client() - return await client.list_instances(param) - - -async def call_deregister_instance(param: DeregisterInstanceParam) -> bool: - client = _async_executor.get_nacos_client() - return await client.deregister_instance(param) - - -async def call_subscribe(param: SubscribeServiceParam) -> None: - client = _async_executor.get_nacos_client() - await client.subscribe(param) - - -async def call_unsubscribe(param: SubscribeServiceParam) -> None: - client = _async_executor.get_nacos_client() - await client.unsubscribe(param) - - -async def call_list_services(param: ListServiceParam) -> ServiceList: - client = _async_executor.get_nacos_client() - return await client.list_services(param) - - -async def call_register_instance(param: RegisterInstanceParam) -> None: - client = _async_executor.get_nacos_client() - await client.register_instance(param) - - -# 初始化 Nacos 客户端配置 -config = (ClientConfigBuilder() - .server_address(_get_registry_server_addresses()[0]) - .namespace_id(_get_nacos_namespace() or 'local') - .username(_get_nacos_username() or None) - .password(_get_nacos_password() or None) - .access_key(_get_nacos_access_key() or None) - .secret_key(_get_nacos_secret_key() or None) - .build()) - -# 不在这里直接创建客户端,而是在需要时动态创建 -_nacos_client = None -_nacos_config = config - -# 常量 -CLUSTER_DOMAIN_KEY = "cluster.domain" -CLUSTER_PORT_PATTERN = re.compile(r"cluster\.(.*?)\.port") -WORKER_KEY = "worker" -APPLICATION_KEY = "application" -FITABLE_META_KEY = "fitable-meta" -SEPARATOR = "::" - -# 全局变量 + FitableMetaInstance, ApplicationInstance +from fit_common_struct.core import Fitable, Genericable + +from .config import get_nacos_namespace +from .utils import build_service_key, get_service_name, get_group_name_from_fitable, \ + get_group_name_from_genericable, create_instances +from .parsers import parse_fitable_meta, parse_application, parse_worker, \ + group_instances_by_application, extract_workers +from .async_executor import run_async_safely, call_list_instances, call_deregister_instance, \ + call_subscribe, call_unsubscribe, call_list_services, call_register_instance + +# Global variables _service_subscriptions: Dict[str, any] = {} _executor = ThreadPoolExecutor(max_workers=10) -# 线程池执行器,用于线程安全的异步调用 -_executor = ThreadPoolExecutor(max_workers=10) - - -class AsyncExecutor: - """专门用于处理异步操作的执行器,在后台线程中维护事件循环""" - - def __init__(self): - self._loop = None - self._thread = None - self._started = False - self._shutdown = False - self._nacos_client = None - self._init_complete = threading.Event() - - def start(self): - """启动后台事件循环线程""" - if self._started: - return - - self._thread = threading.Thread(target=self._run_event_loop, daemon=True, name="NacosAsyncThread") - self._thread.start() - - # 等待初始化完成 - if not self._init_complete.wait(timeout=10): # 最多等待10秒 - raise RuntimeError("Failed to initialize async executor within timeout") - - self._started = True - - def _run_event_loop(self): - """在后台线程中运行事件循环""" - try: - self._loop = asyncio.new_event_loop() - asyncio.set_event_loop(self._loop) - - # 在这个事件循环中创建 Nacos 客户端 - async def init_nacos_client(): - try: - self._nacos_client = await NacosNamingService.create_naming_service(_nacos_config) - plugin_logger.info("Nacos client initialized successfully") - except Exception as e: - plugin_logger.error(f"Failed to initialize Nacos client: {e}") - raise - finally: - # 标记初始化完成 - self._init_complete.set() - - self._loop.run_until_complete(init_nacos_client()) - - # 运行事件循环直到被关闭 - self._loop.run_forever() - except Exception as e: - plugin_logger.error(f"Error in async executor event loop: {e}") - self._init_complete.set() # 即使失败也要设置,避免无限等待 - finally: - try: - if self._nacos_client: - # 清理 Nacos 客户端 - pass - self._loop.close() - except: - pass - - def run_coroutine(self, coro): - """在后台事件循环中运行协程,返回结果""" - if not self._started: - self.start() - - if self._loop is None or self._nacos_client is None: - raise RuntimeError("Async executor not properly initialized") - - # 创建一个Future来获取结果 - result_future = Future() - - async def wrapped_coro(): - try: - result = await coro - result_future.set_result(result) - except Exception as e: - result_future.set_exception(e) - - # 在事件循环中调度协程 - self._loop.call_soon_threadsafe(asyncio.create_task, wrapped_coro()) - - # 等待结果 - return result_future.result(timeout=30) # 30秒超时 - - def get_nacos_client(self): - """获取 Nacos 客户端实例""" - if not self._started: - self.start() - return self._nacos_client - - def shutdown(self): - """关闭异步执行器""" - if self._loop and not self._loop.is_closed(): - self._loop.call_soon_threadsafe(self._loop.stop) - self._shutdown = True - - -# 全局异步执行器 -_async_executor = AsyncExecutor() - - -def _run_async_safely(coro): - """ - 线程安全地运行异步函数 - 使用专门的异步执行器在后台线程中处理 - """ - try: - return _async_executor.run_coroutine(coro) - except Exception as e: - plugin_logger.error(f"Error running async operation: {e}") - raise - - -# 辅助函数 -def build_service_key(group_name: str, service_name: str) -> str: - """ - Builds a unique key in the format :: for service subscriptions. - - @param group_name: The group name as string. - @param service_name: The service name as string. - @return: A concatenated key like groupName::serviceName. - """ - return f"{group_name}{SEPARATOR}{service_name}" - - -def get_service_name(fitable: Fitable) -> str: - """获取服务名称""" - return f"{fitable.fitableId}{SEPARATOR}{fitable.fitableVersion}" - - -def get_group_name_from_fitable(fitable: Fitable) -> str: - """从FitableInfo获取组名""" - return f"{fitable.genericableId}{SEPARATOR}{fitable.genericableVersion}" - - -def get_group_name_from_genericable(genericable: Genericable) -> str: - """从GenericableInfo获取组名""" - return f"{genericable.genericableId}{SEPARATOR}{genericable.genericableVersion}" - - -def create_instances(worker: Worker, application: Application, meta: FitableMeta) -> List[Dict]: - """ - 创建实例信息 - - @param worker: Worker node object. - @param application: Application object. - @param meta: FitableMeta metadata object. - @return: List of instance dictionaries. - """ - plugin_logger.debug(f"Creating instance for worker. [worker={worker.id}, application={application.nameVersion}, meta={meta}]") - instances = [] - - for address in worker.addresses: - for endpoint in address.endpoints: - # 准备元数据 - metadata = build_instance_metadata(worker, application, meta) - - # 构建实例 - instance = { - "ip": address.host, - "port": endpoint.port, - "weight": _get_heartbeat_weight(), - "ephemeral": _get_heartbeat_isEphemeral(), - "metadata": metadata - } - instances.append(instance) - - return instances - - -def build_instance_metadata(worker: Worker, application: Application, meta: FitableMeta) -> Dict[str, str]: - """ - Build metadata for service instance, including worker, application and FitableMeta information. - - @param worker: The worker node object. - @param application: The application object. - @param meta: The FitableMeta metadata object. - @return: A dict containing all serialized metadata. - """ - metadata = {} - - # 添加心跳配置 - metadata["preserved.heart.beat.interval"] = str(_get_heartbeat_interval()) - metadata["preserved.heart.beat.timeout"] = str(_get_heartbeat_timeout()) - - try: - metadata[WORKER_KEY] = json_serialize(worker) - metadata[APPLICATION_KEY] = json_serialize(application) - metadata[FITABLE_META_KEY] = json_serialize(meta) - except Exception as e: - plugin_logger.error(f"Failed to serialize metadata for worker: {e}") - - return metadata - - -def parse_fitable_meta(metadata: Dict) -> FitableMeta: - """解析FitableMeta""" - try: - meta_json = metadata.get(FITABLE_META_KEY) - if meta_json: - return json_deserialize(FitableMeta, meta_json) - except Exception as e: - plugin_logger.error(f"Failed to parse fitable meta for instance: {e}") - - # 返回默认值 - default_fitable = Fitable("unknown", "1.0", "unknown", "1.0") - meta = FitableMeta(default_fitable, [], []) - return meta - - -def parse_application(metadata: Dict) -> Application: - """解析Application""" - try: - app_json = metadata.get(APPLICATION_KEY) - if app_json: - return json_deserialize(Application, app_json) - except Exception as e: - plugin_logger.error(f"Failed to parse application metadata for instance: {e}") - - # 返回默认值 - app = Application("unknown", "unknown") - return app - - -def parse_worker(instance_or_metadata) -> Worker: - """解析Worker""" - try: - # 处理不同的输入类型 - if hasattr(instance_or_metadata, 'metadata'): - metadata = instance_or_metadata.metadata - ip = getattr(instance_or_metadata, 'ip', 'unknown') - port = getattr(instance_or_metadata, 'port', 0) - else: - metadata = instance_or_metadata - ip = 'unknown' - port = 0 - - worker_json = metadata.get(WORKER_KEY) - if worker_json: - return json_deserialize(Worker, worker_json) - except Exception as e: - plugin_logger.error(f"Failed to parse worker metadata for instance: {e}") - - # 降级处理 - 创建基本worker信息 - worker = Worker([], "unknown", "", {}) - - # 如果有IP和端口信息,创建基本地址 - if ip != 'unknown' and port != 0: - endpoint = Endpoint(port, 1) # 默认协议 - address = Address(ip, [endpoint]) - worker.addresses = [address] - - return worker - - -# def replace_addresses(workers: Set[Worker], application: Application) -> None: -# """ -# Extract all workers corresponding to instances and adjust addresses based on application extension information. -# -# @param workers: Set of workers to modify. -# @param application: The application object. -# """ -# if not application.extensions or CLUSTER_DOMAIN_KEY not in application.extensions: -# return -# -# cluster_domain = application.extensions.get(CLUSTER_DOMAIN_KEY) -# if not cluster_domain: -# return -# -# # 构建端点 -# endpoints = build_endpoints(application.extensions) -# -# # 创建统一地址 -# address = Address() -# address.host = cluster_domain -# address.endpoints = endpoints -# -# # 更新所有worker的地址 -# for worker in workers: -# worker.addresses = [address] - - -def build_endpoints(extensions: Dict[str, str]) -> List[Endpoint]: - """构建端点列表""" - endpoints = [] - - # 协议代码映射 - protocol_code_map = { - "rsocket": 0, - "socket": 1, - "http": 2, - "grpc": 3, - "uc": 10, - "shareMemory": 11 - } - - for key, value in extensions.items(): - match = CLUSTER_PORT_PATTERN.match(key) - if match: - protocol_name = match.group(1).lower() - if protocol_name in protocol_code_map: - endpoint = Endpoint(int(value), protocol_code_map[protocol_name]) - endpoints.append(endpoint) - else: - plugin_logger.error(f"Unknown protocol: {protocol_name}") - - return endpoints - - def on_service_changed(fitable_info: Fitable, worker_id: str) -> None: """ Handle service change events, query and notify updates to Fitables instance information. - - @param fitable_info: The changed Fitables information. - @param worker_id: The worker ID. + + Args: + fitable_info: The changed Fitables information. + worker_id: The worker ID. """ try: - # 查询当前实例 + # Query current instances instances = query_fitable_addresses([fitable_info], worker_id) - # 这里需要实现通知机制 - 调用notify服务 # notify_fitables(instances) - plugin_logger.debug(f"Service changed for fitable: {fitable_info}, instances: {len(instances)}") + plugin_logger.debug( + f"Service changed for fitable: {fitable_info}, instances: {len(instances)}" + ) except Exception as e: plugin_logger.error(f"Service change handling failed: {e}") -def group_instances_by_application(instances: List[Instance]) -> Dict[Application, List[Instance]]: - """按应用分组实例""" - app_instances_map = {} - for instance in instances: - metadata = instance.metadata - app = parse_application(metadata) - app_instances_map.setdefault(app, []).append(instance) - return app_instances_map +def extract_workers(app_instances: List[Instance], application: Application) -> List[Worker]: + """ + Extract all workers corresponding to instances. + Args: + app_instances: The list of application instances. + application: The application object. -def extract_workers(app_instances: List[Instance], application: Application) -> Set[Worker]: - """ - Extract all workers corresponding to instances and adjust addresses based on application extension information. - - @param app_instances: The list of application instances. - @param application: The application object. - @return: Set of workers. + Returns: + List of workers. """ - workers = set() + workers = [] for instance in app_instances: worker = parse_worker(instance) - workers.add(worker) - - # 如果应用有集群域名配置,替换地址 - # if application.extensions and CLUSTER_DOMAIN_KEY in application.extensions: - # replace_addresses(workers, application) - + workers.append(worker) + return workers @fitable(const.REGISTER_FIT_SERVICE_GEN_ID, const.REGISTER_FIT_SERVICE_FIT_ID) def register_fitables(fitable_metas: List[FitableMeta], worker: Worker, application: Application) -> None: """ - 注册中心所提供接口,注册泛服务实现的信息。 + Register Fitable service implementations to the registry center. - @param fitable_metas: 泛服务实现元数据列表。 - @param worker: 当前 FIT 进程信息。 - @param application: 当前应用信息。 + Args: + fitable_metas: List of Fitable metadata to register. + worker: Current FIT process information. + application: Current application information. + + Raises: + Exception: If registration fails due to registry error. """ try: - plugin_logger.debug(f"Registering fitables. [fitableMetas={fitable_metas}, worker={worker.id}, application={application.nameVersion}]") - + plugin_logger.debug( + f"Registering fitables. [fitableMetas={fitable_metas}, " + f"worker={worker.id}, application={application.nameVersion}]" + ) + for meta in fitable_metas: fitable = meta.fitable group_name = get_group_name_from_fitable(fitable) service_name = get_service_name(fitable) - + instances = create_instances(worker, application, meta) for instance in instances: param = RegisterInstanceParam( @@ -561,7 +104,7 @@ def register_fitables(fitable_metas: List[FitableMeta], worker: Worker, applicat ephemeral=instance["ephemeral"], metadata=instance["metadata"] ) - _run_async_safely(call_register_instance(param)) + run_async_safely(call_register_instance(param)) plugin_logger.info(f"Successfully registered fitables for worker {worker.id}") except Exception as e: @@ -571,37 +114,54 @@ def register_fitables(fitable_metas: List[FitableMeta], worker: Worker, applicat @fitable(const.UNREGISTER_FIT_SERVICE_GEN_ID, const.UNREGISTER_FIT_SERVICE_FIT_ID) def unregister_fitables(fitables: List[Fitable], worker_id: str) -> None: """ - 向注册中心服务端取消注册服务实现列表。 + Unregister service implementations from the registry center. - @param fitables: 表示待取消注册的服务实现列表。 - @param worker_id: 表示服务实现所在的进程的唯一标识。 + Args: + fitables: List of Fitable implementations to unregister. + worker_id: Unique identifier of the process where service implementations reside. """ - plugin_logger.debug(f"Unregistering fitables for worker. [fitables={fitables}, workerId={worker_id}]") - + plugin_logger.debug( + f"Unregistering fitables for worker. [fitables={fitables}, workerId={worker_id}]" + ) + for fitable in fitables: unregister_single_fitable(fitable, worker_id) def unregister_single_fitable(fitable: Fitable, worker_id: str) -> None: - """取消注册单个Fitable""" + """ + Unregister a single Fitable implementation. + + Args: + fitable: The Fitable implementation to unregister. + worker_id: The worker ID. + """ group_name = get_group_name_from_fitable(fitable) service_name = get_service_name(fitable) - + try: - # 获取服务所有实例 + # Get all instances for the service param = ListInstanceParam( service_name=service_name, group_name=group_name, healthy_only=True ) - instances = _run_async_safely(call_list_instances(param)) + instances = run_async_safely(call_list_instances(param)) unregister_matching_instances(instances, worker_id, service_name, group_name) except Exception as e: plugin_logger.error(f"Failed to unregister fitable due to registry error: {e}") def unregister_matching_instances(instances: List[Instance], worker_id: str, service_name: str, group_name: str) -> None: - """取消注册匹配的实例""" + """ + Unregister matching instances. + + Args: + instances: List of instances to check. + worker_id: The worker ID to match. + service_name: The service name. + group_name: The group name. + """ for instance in instances: try: worker = parse_worker(instance) @@ -612,7 +172,7 @@ def unregister_matching_instances(instances: List[Instance], worker_id: str, ser ip=instance.ip, port=instance.port ) - _run_async_safely(call_deregister_instance(param)) + run_async_safely(call_deregister_instance(param)) plugin_logger.debug(f"Successfully deregistered instance {instance.ip}:{instance.port}") except Exception as e: plugin_logger.error(f"Failed to deregister instance: {e}") @@ -621,13 +181,18 @@ def unregister_matching_instances(instances: List[Instance], worker_id: str, ser @fitable(const.QUERY_FIT_SERVICE_GEN_ID, const.QUERY_FIT_SERVICE_FIT_ID) def query_fitable_addresses(fitables: List[Fitable], worker_id: str) -> List[FitableAddressInstance]: """ - 注册中心所提供接口,用于查询某个泛服务实现的实例信息,在拉模式下使用。 + Query instance information for Fitable implementations (pull mode). + + Args: + fitables: List of Fitable implementation information. + worker_id: Current FIT process identifier. - @param fitables: 泛服务实现信息列表。 - @param worker_id: 当前 FIT 进程标识。 - @return: 所获取的实例信息。 + Returns: + List of obtained instance information. """ - plugin_logger.debug(f"Querying fitables for worker. [fitables={fitables}, workerId={worker_id}]") + plugin_logger.debug( + f"Querying fitables for worker. [fitables={fitables}, workerId={worker_id}]" + ) result_map = {} for fitable in fitables: @@ -643,20 +208,35 @@ def query_fitable_addresses(fitables: List[Fitable], worker_id: str) -> List[Fit def query_instances(fitable: Fitable) -> List[Instance]: - """查询实例""" + """ + Query instances for a specific Fitable. + + Args: + fitable: The Fitable to query instances for. + + Returns: + List of instances. + """ group_name = get_group_name_from_fitable(fitable) service_name = get_service_name(fitable) - + param = ListInstanceParam( service_name=service_name, group_name=group_name, healthy_only=True ) - return _run_async_safely(call_list_instances(param)) + return run_async_safely(call_list_instances(param)) def process_application_instances(result_map: Dict, fitable: Fitable, instances: List[Instance]) -> None: - """处理应用实例""" + """ + Process application instances and group them. + + Args: + result_map: Dictionary to store results. + fitable: The Fitable being processed. + instances: List of instances to process. + """ app_instances_map = group_instances_by_application(instances) for app, app_instances in app_instances_map.items(): @@ -675,44 +255,54 @@ def process_application_instances(result_map: Dict, fitable: Fitable, instances: @fitable(const.SUBSCRIBE_FIT_SERVICE_GEN_ID, const.SUBSCRIBE_FIT_SERVICE_FIT_ID) def subscribe_fit_service(fitables: List[Fitable], worker_id: str, callback_fitable_id: str) -> List[FitableAddressInstance]: """ - 注册中心所提供接口,用于订阅某个泛服务实现的实例信息,并且也会返回查询到的实例信息,在推模式下使用。 + Subscribe to Fitable service instance information (push mode). + + Args: + fitables: List of Fitable implementation information. + worker_id: Current FIT process identifier. + callback_fitable_id: Identifier for callback Fitable implementation. - @param fitables: 泛服务实现信息列表。 - @param worker_id: 当前 FIT 进程标识。 - @param callback_fitable_id: 用于回调的泛服务实现的标识。 - @return: 所查询到的实例信息。 + Returns: + Queried instance information. """ - plugin_logger.debug(f"Subscribing to fitables for worker. [fitables={fitables}, workerId={worker_id}, callbackFitableId={callback_fitable_id}]") - - # 注册订阅 + plugin_logger.debug( + f"Subscribing to fitables for worker. [fitables={fitables}, " + f"workerId={worker_id}, callbackFitableId={callback_fitable_id}]" + ) + + # Register subscriptions for fitable in fitables: try: group_name = get_group_name_from_fitable(fitable) service_name = get_service_name(fitable) service_key = build_service_key(group_name, service_name) - + if service_key in _service_subscriptions: - plugin_logger.debug(f"Already subscribed to service. [groupName={group_name}, serviceName={service_name}]") + plugin_logger.debug( + f"Already subscribed to service. [groupName={group_name}, serviceName={service_name}]" + ) continue - - # 创建事件监听器 + + # Create event listener def create_event_listener(fitable_ref: Fitable, worker_id_ref: str): def event_listener(event): _executor.submit(on_service_changed, fitable_ref, worker_id_ref) return event_listener - + event_listener = create_event_listener(fitable, worker_id) _service_subscriptions[service_key] = event_listener - - # 注册订阅 + + # Register subscription param = SubscribeServiceParam( service_name=service_name, group_name=group_name, subscribe_callback=event_listener ) - _run_async_safely(call_subscribe(param)) - plugin_logger.debug(f"Subscribed to service. [groupName={group_name}, serviceName={service_name}]") - + run_async_safely(call_subscribe(param)) + plugin_logger.debug( + f"Subscribed to service. [groupName={group_name}, serviceName={service_name}]" + ) + except Exception as e: plugin_logger.error(f"Failed to subscribe to Nacos service: {e}") @@ -722,11 +312,12 @@ def event_listener(event): @fitable(const.UNSUBSCRIBE_FIT_SERVICE_GEN_ID, const.UNSUBSCRIBE_FIT_SERVICE_FIT_ID) def unsubscribe_fitables(fitables: List[Fitable], worker_id: str, callback_fitable_id: str) -> None: """ - 向注册中心服务端取消订阅指定服务实现的实例信息。 + Unsubscribe from specified Fitable service instance information. - @param fitables: 表示指定服务实现列表的List。 - @param worker_id: 表示指定的进程的唯一标识的String。 - @param callback_fitable_id: 表示取消订阅回调服务实现的唯一标识的String。 + Args: + fitables: List of specified Fitable implementations. + worker_id: Unique identifier of the specified process. + callback_fitable_id: Unique identifier for unsubscribe callback Fitable implementation. """ plugin_logger.debug(f"Unsubscribing from fitables for worker. [fitables={fitables}, workerId={worker_id}, callbackFitableId={callback_fitable_id}]") @@ -744,7 +335,7 @@ def unsubscribe_fitables(fitables: List[Fitable], worker_id: str, callback_fitab group_name=group_name, subscribe_callback=listener ) - _run_async_safely(call_unsubscribe(param)) + run_async_safely(call_unsubscribe(param)) plugin_logger.debug(f"Unsubscribed from service. [groupName={group_name}, serviceName={service_name}]") except Exception as e: plugin_logger.error(f"Failed to unsubscribe from Nacos service: {e}") @@ -753,12 +344,17 @@ def unsubscribe_fitables(fitables: List[Fitable], worker_id: str, callback_fitab @fitable(const.QUERY_FITABLE_METAS_GEN_ID, const.QUERY_FITABLE_METAS_FIT_ID) def query_fitable_metas(genericable_infos: List[Genericable]) -> List[FitableMetaInstance]: """ - 注册中心所提供接口,用于查询泛服务的元数据。 + Query Fitable metadata from the registry center. + + Args: + genericable_infos: List of Genericable information. - @param genericable_infos: 泛服务信息列表。 - @return: 所查询到的泛服务元数据列表。 + Returns: + List of queried Fitable metadata. """ - plugin_logger.debug(f"Querying fitable metas for genericables. [genericables={genericable_infos}]") + plugin_logger.debug( + f"Querying fitable metas for genericables. [genericables={genericable_infos}]" + ) meta_environments = {} for genericable in genericable_infos: @@ -768,19 +364,25 @@ def query_fitable_metas(genericable_infos: List[Genericable]) -> List[FitableMet def process_genericable_services(genericable: Genericable, meta_environments: Dict) -> None: - """处理泛服务的服务列表""" + """ + Process service list for a Genericable. + + Args: + genericable: The Genericable to process. + meta_environments: Dictionary to collect metadata environments. + """ group_name = get_group_name_from_genericable(genericable) - + try: - # 获取分组下所有服务 + # Get all services under the group param = ListServiceParam( - namespace_id=_get_nacos_namespace(), + namespace_id=get_nacos_namespace(), group_name=group_name, page_no=1, - page_size=1000 # 假设一次获取足够多的服务 + page_size=1000 # Assume fetching enough services at once ) - service_list = _run_async_safely(call_list_services(param)) - + service_list = run_async_safely(call_list_services(param)) + for service_name in service_list.services: process_service_instances(service_name, group_name, meta_environments) except Exception as e: @@ -788,19 +390,26 @@ def process_genericable_services(genericable: Genericable, meta_environments: Di def process_service_instances(service_name: str, group_name: str, meta_environments: Dict) -> None: - """处理服务实例""" + """ + Process service instances. + + Args: + service_name: The service name. + group_name: The group name. + meta_environments: Dictionary to collect metadata environments. + """ try: - # 获取服务实例 + # Get service instances param = ListInstanceParam( service_name=service_name, group_name=group_name, healthy_only=True ) - instances = _run_async_safely(call_list_instances(param)) + instances = run_async_safely(call_list_instances(param)) if not instances: return - + meta = parse_fitable_meta(instances[0].metadata) collect_environments_from_instances(instances, meta, meta_environments) except Exception as e: @@ -808,7 +417,14 @@ def process_service_instances(service_name: str, group_name: str, meta_environme def collect_environments_from_instances(instances: List[Instance], meta: FitableMeta, meta_environments: Dict) -> None: - """从实例中收集环境信息""" + """ + Collect environment information from instances. + + Args: + instances: List of instances to process. + meta: FitableMeta object. + meta_environments: Dictionary to collect environments. + """ for instance in instances: try: worker = parse_worker(instance) @@ -821,25 +437,17 @@ def collect_environments_from_instances(instances: List[Instance], meta: Fitable def build_fitable_meta_instances(meta_environments: Dict) -> List[FitableMetaInstance]: - """构建FitableMetaInstance列表""" + """ + Build FitableMetaInstance list. + + Args: + meta_environments: Dictionary mapping metadata to environments. + + Returns: + List of FitableMetaInstance objects. + """ results = [] for meta, envs in meta_environments.items(): - instance = FitableMetaInstance(meta,list(envs)) + instance = FitableMetaInstance(meta, list(envs)) results.append(instance) return results - - -# 模块清理函数 -import atexit - -def _cleanup_async_executor(): - """清理异步执行器""" - try: - _async_executor.shutdown() - except: - pass - -# 注册清理函数 -atexit.register(_cleanup_async_executor) - - diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/parsers.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/parsers.py new file mode 100644 index 00000000..a54e8c40 --- /dev/null +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/parsers.py @@ -0,0 +1,137 @@ +# -- encoding: utf-8 -- +# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved. +# This file is a part of the ModelEngine Project. +# Licensed under the MIT License. See License.txt in the project root for license information. +# ====================================================================================================================== +""" +Parsing functions for Nacos registry server. +""" +from typing import Dict, List, Set + +from v2.nacos import Instance + +from fit_common_struct.entity import Worker, Application, FitableMeta, Endpoint, Address +from fit_common_struct.core import Fitable +from fitframework.utils.json_serialize_utils import json_deserialize +from fitframework.api.logging import plugin_logger + +from .constants import WORKER_KEY, APPLICATION_KEY, FITABLE_META_KEY + + +def parse_fitable_meta(metadata: Dict) -> FitableMeta: + """ + Parse FitableMeta from metadata. + + Args: + metadata: The metadata dictionary. + + Returns: + Parsed FitableMeta object or default if parsing fails. + """ + try: + meta_json = metadata.get(FITABLE_META_KEY) + if meta_json: + return json_deserialize(FitableMeta, meta_json) + except Exception as e: + plugin_logger.error(f"Failed to parse fitable meta for instance: {e}") + + # Return default value + default_fitable = Fitable("unknown", "1.0", "unknown", "1.0") + meta = FitableMeta(default_fitable, [], []) + return meta + + +def parse_application(metadata: Dict) -> Application: + """ + Parse Application from metadata. + + Args: + metadata: The metadata dictionary. + + Returns: + Parsed Application object or default if parsing fails. + """ + try: + app_json = metadata.get(APPLICATION_KEY) + if app_json: + return json_deserialize(Application, app_json) + except Exception as e: + plugin_logger.error(f"Failed to parse application metadata for instance: {e}") + + # Return default value + return Application("unknown", "unknown") + + +def parse_worker(instance_or_metadata) -> Worker: + """ + Parse Worker from instance or metadata. + + Args: + instance_or_metadata: Either an Instance object or metadata dictionary. + + Returns: + Parsed Worker object or default if parsing fails. + """ + try: + # Handle different input types + if hasattr(instance_or_metadata, 'metadata'): + metadata = instance_or_metadata.metadata + ip = getattr(instance_or_metadata, 'ip', 'unknown') + port = getattr(instance_or_metadata, 'port', 0) + else: + metadata = instance_or_metadata + ip = 'unknown' + port = 0 + + worker_json = metadata.get(WORKER_KEY) + if worker_json: + return json_deserialize(Worker, worker_json) + except Exception as e: + plugin_logger.error(f"Failed to parse worker metadata for instance: {e}") + + # Fallback - create basic worker information + worker = Worker([], "unknown", "", {}) + + # If IP and port info available, create basic address + if ip != 'unknown' and port != 0: + endpoint = Endpoint(port, 1) # Default protocol + address = Address(ip, [endpoint]) + worker.addresses = [address] + + return worker + + +def group_instances_by_application(instances: List[Instance]) -> Dict[Application, List[Instance]]: + """ + Group instances by application. + + Args: + instances: List of instances to group. + + Returns: + Dictionary mapping applications to their instances. + """ + app_instances_map = {} + for instance in instances: + metadata = instance.metadata + app = parse_application(metadata) + app_instances_map.setdefault(app, []).append(instance) + return app_instances_map + + +def extract_workers(app_instances: List[Instance], application: Application) -> Set[Worker]: + """ + Extract all workers corresponding to instances. + + Args: + app_instances: The list of application instances. + application: The application object. + + Returns: + Set of workers. + """ + workers = set() + for instance in app_instances: + worker = parse_worker(instance) + workers.add(worker) + return workers diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/utils.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/utils.py new file mode 100644 index 00000000..5d144d9f --- /dev/null +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/utils.py @@ -0,0 +1,162 @@ +# -- encoding: utf-8 -- +# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved. +# This file is a part of the ModelEngine Project. +# Licensed under the MIT License. See License.txt in the project root for license information. +# ====================================================================================================================== +""" +Utility functions for Nacos registry server. +""" +from typing import List, Dict + +from fit_common_struct.core import Fitable, Genericable +from fit_common_struct.entity import Worker, Application, FitableMeta, Endpoint + +from fitframework.api.logging import plugin_logger +from fitframework.utils.json_serialize_utils import json_serialize + +from .constants import SEPARATOR, CLUSTER_PORT_PATTERN, PROTOCOL_CODE_MAP, \ + WORKER_KEY, APPLICATION_KEY, FITABLE_META_KEY +from .config import get_heartbeat_weight, get_heartbeat_is_ephemeral, \ + get_heartbeat_interval, get_heartbeat_timeout + + +def build_service_key(group_name: str, service_name: str) -> str: + """ + Build a unique key in the format :: for service subscriptions. + + Args: + group_name: The group name as string. + service_name: The service name as string. + + Returns: + A concatenated key like groupName::serviceName. + """ + return f"{group_name}{SEPARATOR}{service_name}" + + +def get_service_name(fitable: Fitable) -> str: + """ + Get the service name from Fitable. + + Args: + fitable: The Fitable object. + + Returns: + The service name. + """ + return f"{fitable.fitableId}{SEPARATOR}{fitable.fitableVersion}" + + +def get_group_name_from_fitable(fitable: Fitable) -> str: + """ + Get the group name from Fitable. + + Args: + fitable: The Fitable object. + + Returns: + The group name. + """ + return f"{fitable.genericableId}{SEPARATOR}{fitable.genericableVersion}" + + +def get_group_name_from_genericable(genericable: Genericable) -> str: + """ + Get the group name from Genericable. + + Args: + genericable: The Genericable object. + + Returns: + The group name. + """ + return f"{genericable.genericableId}{SEPARATOR}{genericable.genericableVersion}" + + +def create_instances(worker: Worker, application: Application, meta: FitableMeta) -> List[Dict]: + """ + Create instance information. + + Args: + worker: Worker node object. + application: Application object. + meta: FitableMeta metadata object. + + Returns: + List of instance dictionaries. + """ + plugin_logger.debug( + f"Creating instance for worker. [worker={worker.id}, " + f"application={application.nameVersion}, meta={meta}]" + ) + instances = [] + + for address in worker.addresses: + for endpoint in address.endpoints: + # Prepare metadata + metadata = build_instance_metadata(worker, application, meta) + + # Build instance + instance = { + "ip": address.host, + "port": endpoint.port, + "weight": get_heartbeat_weight(), + "ephemeral": get_heartbeat_is_ephemeral(), + "metadata": metadata + } + instances.append(instance) + + return instances + + +def build_instance_metadata(worker: Worker, application: Application, meta: FitableMeta) -> Dict[str, str]: + """ + Build metadata for service instance, including worker, application and FitableMeta information. + + Args: + worker: The worker node object. + application: The application object. + meta: The FitableMeta metadata object. + + Returns: + A dict containing all serialized metadata. + """ + metadata = {} + + # Add heartbeat configuration + metadata["preserved.heart.beat.interval"] = str(get_heartbeat_interval()) + metadata["preserved.heart.beat.timeout"] = str(get_heartbeat_timeout()) + + try: + metadata[WORKER_KEY] = json_serialize(worker) + metadata[APPLICATION_KEY] = json_serialize(application) + metadata[FITABLE_META_KEY] = json_serialize(meta) + except Exception as e: + plugin_logger.error(f"Failed to serialize metadata for worker: {e}") + + return metadata + + +def build_endpoints(extensions: Dict[str, str]) -> List[Endpoint]: + """ + Build endpoint list from extensions. + + Args: + extensions: Extension configuration dictionary. + + Returns: + List of endpoints. + """ + endpoints = [] + + for key, value in extensions.items(): + match = CLUSTER_PORT_PATTERN.match(key) + if match: + protocol_name = match.group(1).lower() + if protocol_name in PROTOCOL_CODE_MAP: + endpoint = Endpoint(int(value), PROTOCOL_CODE_MAP[protocol_name]) + endpoints.append(endpoint) + else: + plugin_logger.error(f"Unknown protocol: {protocol_name}") + + return endpoints From c3716388b544aae231a754d99af3b0d1e3a6ee00 Mon Sep 17 00:00:00 2001 From: xiaohao <932379959@qq.com> Date: Thu, 4 Sep 2025 09:42:24 +0800 Subject: [PATCH 10/15] =?UTF-8?q?[fit]=20=E8=BF=9E=E6=8E=A5=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/fit_py_registry_client/registry_address_service.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/fit/python/plugin/fit_py_registry_client/registry_address_service.py b/framework/fit/python/plugin/fit_py_registry_client/registry_address_service.py index f755359c..b70078a9 100644 --- a/framework/fit/python/plugin/fit_py_registry_client/registry_address_service.py +++ b/framework/fit/python/plugin/fit_py_registry_client/registry_address_service.py @@ -92,6 +92,10 @@ def _process_address_by_mode() -> list: else: port = "" return [_get_host() + port] + elif _get_registry_server_mode() == 'DIRECT': + return _get_registry_server_addresses() + else: + raise RuntimeError(f"unsupported registry server mode: {_get_registry_server_mode()}") From 68f3ea8c0653adef624dc6b3e9e0861cc82c5398 Mon Sep 17 00:00:00 2001 From: xiaohao <932379959@qq.com> Date: Tue, 9 Sep 2025 14:14:16 +0800 Subject: [PATCH 11/15] =?UTF-8?q?[fit]=20=E5=A2=9E=E5=8A=A0entity.py?= =?UTF-8?q?=E4=B8=AD=E7=B1=BB=E7=9A=84=E6=96=B9=E6=B3=95=E3=80=81=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E9=BB=98=E8=AE=A4=E6=B3=A8=E5=86=8C=E4=B8=AD=E5=BF=83?= =?UTF-8?q?=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- framework/fit/python/conf/application.yml | 2 +- .../fit/python/fit_common_struct/entity.py | 27 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/framework/fit/python/conf/application.yml b/framework/fit/python/conf/application.yml index b7274022..85450471 100644 --- a/framework/fit/python/conf/application.yml +++ b/framework/fit/python/conf/application.yml @@ -44,7 +44,7 @@ registry-center: server: mode: 'DIRECT' # DIRECT 表示直连,直接连接内存注册中心;PROXY 表示代理模式,通过本地代理服务连接 Nacos 注册中心 addresses: - - "localhost:8848" + - "localhost:8080" protocol: 2 formats: - 1 diff --git a/framework/fit/python/fit_common_struct/entity.py b/framework/fit/python/fit_common_struct/entity.py index ebfa9fd1..fbb90efc 100644 --- a/framework/fit/python/fit_common_struct/entity.py +++ b/framework/fit/python/fit_common_struct/entity.py @@ -196,15 +196,38 @@ def __hash__(self): def __repr__(self): return str(tuple(self.__dict__.values())) -class HeartBeatInfo: +class HeartBeatInfo(object): + def __init__(self, sceneType: str, aliveTime: int, initDelay: int): self.sceneType: str = sceneType self.aliveTime: int = aliveTime self.initDelay: int = initDelay + def __eq__(self, other): + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __hash__(self): + return safe_hash_dict(self.__dict__) + + def __repr__(self): + return str(tuple(self.__dict__.values())) + -class HeartBeatAddress: +class HeartBeatAddress(object): def __init__(self, id_: str): self.id = id_ + def __eq__(self, other): + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __hash__(self): + return safe_hash_dict(self.__dict__) + + def __repr__(self): + return str(tuple(self.__dict__.values())) + From 1dd7fe3ad8795489b722b80dea024b5fed3086ed Mon Sep 17 00:00:00 2001 From: xiaohao <932379959@qq.com> Date: Tue, 9 Sep 2025 15:37:44 +0800 Subject: [PATCH 12/15] =?UTF-8?q?[fit]=20=E4=BF=AE=E6=94=B9=E5=B0=8F?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/fit_py_nacos_registry/conf/application.yml | 3 +++ .../plugin/fit_py_nacos_registry/service/async_executor.py | 7 +++++-- .../plugin/fit_py_nacos_registry/service/constants.py | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/conf/application.yml b/framework/fit/python/plugin/fit_py_nacos_registry/conf/application.yml index e69de29b..902e5c97 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/conf/application.yml +++ b/framework/fit/python/plugin/fit_py_nacos_registry/conf/application.yml @@ -0,0 +1,3 @@ +nacos: + async: + timeout: 30 \ No newline at end of file diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/async_executor.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/async_executor.py index e2c24cd4..b3ebab79 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/service/async_executor.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/async_executor.py @@ -13,13 +13,16 @@ import atexit import threading from concurrent.futures import Future +from fitframework import value from v2.nacos import NacosNamingService, RegisterInstanceParam, ListInstanceParam, \ DeregisterInstanceParam, SubscribeServiceParam, ListServiceParam from fitframework.api.logging import plugin_logger from .config import build_nacos_config - +@value("nacos.async.timeout",default_value=10, converter=int) +def get_nacos_async_timeout(): + pass class AsyncExecutor: """Executor for handling asynchronous operations in a background thread.""" @@ -119,7 +122,7 @@ async def wrapped_coro(): self._loop.call_soon_threadsafe(asyncio.create_task, wrapped_coro()) # Wait for result - return result_future.result(timeout=30) # 30 second timeout + return result_future.result(timeout=get_nacos_async_timeout()) # 30 second timeout def get_nacos_client(self): """ diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/constants.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/constants.py index 0a8a2373..ef504c58 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/service/constants.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/constants.py @@ -25,5 +25,5 @@ "http": 2, "grpc": 3, "uc": 10, - "shareMemory": 11 + "share_memory": 11 } From a9f653e7cc9d12f7cbdb6b2fdfce286ecd7e6ae7 Mon Sep 17 00:00:00 2001 From: xiaohao <932379959@qq.com> Date: Tue, 9 Sep 2025 16:34:12 +0800 Subject: [PATCH 13/15] =?UTF-8?q?[fit]=20=E4=BF=AE=E6=94=B9=E5=B0=8F?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/async_executor.py | 27 ++++++----- .../service/nacos_registry_server.py | 46 +++++++++---------- .../fit_py_nacos_registry/service/parsers.py | 8 ++-- .../fit_py_nacos_registry/service/utils.py | 8 ++-- 4 files changed, 47 insertions(+), 42 deletions(-) diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/async_executor.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/async_executor.py index b3ebab79..5bee3a4d 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/service/async_executor.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/async_executor.py @@ -18,7 +18,7 @@ from v2.nacos import NacosNamingService, RegisterInstanceParam, ListInstanceParam, \ DeregisterInstanceParam, SubscribeServiceParam, ListServiceParam -from fitframework.api.logging import plugin_logger +from fitframework.api.logging import sys_plugin_logger from .config import build_nacos_config @value("nacos.async.timeout",default_value=10, converter=int) def get_nacos_async_timeout(): @@ -64,9 +64,9 @@ async def init_nacos_client(): try: config = build_nacos_config() self._nacos_client = await NacosNamingService.create_naming_service(config) - plugin_logger.info("Nacos client initialized successfully") + sys_plugin_logger.info("Nacos client initialized successfully") except Exception as e: - plugin_logger.error(f"Failed to initialize Nacos client: {e}") + sys_plugin_logger.error(f"Failed to initialize Nacos client: {e}") raise finally: # Mark initialization complete @@ -77,17 +77,22 @@ async def init_nacos_client(): # Run event loop until shutdown self._loop.run_forever() except Exception as e: - plugin_logger.error(f"Error in async executor event loop: {e}") + sys_plugin_logger.error(f"Error in async executor event loop: {e}") self._init_complete.set() # Set even on failure to avoid infinite wait finally: try: if self._nacos_client: - # Cleanup Nacos client if needed - pass - if self._loop: + # Try to close the client if it has a close method + try: + self._nacos_client.shutdown() + sys_plugin_logger.info("Nacos client cleaned up") + except Exception as cleanup_error: + sys_plugin_logger.error(f"Error cleaning up Nacos client: {cleanup_error}") + + if self._loop and not self._loop.is_closed(): self._loop.close() except Exception as e: - plugin_logger.error(f"Error during cleanup: {e}") + sys_plugin_logger.error(f"Error during cleanup: {e}") def run_coroutine(self, coro): """ @@ -122,7 +127,7 @@ async def wrapped_coro(): self._loop.call_soon_threadsafe(asyncio.create_task, wrapped_coro()) # Wait for result - return result_future.result(timeout=get_nacos_async_timeout()) # 30 second timeout + return result_future.result(timeout=get_nacos_async_timeout()) def get_nacos_client(self): """ @@ -162,7 +167,7 @@ def run_async_safely(coro): try: return _async_executor.run_coroutine(coro) except Exception as e: - plugin_logger.error(f"Error running async operation: {e}") + sys_plugin_logger.error(f"Error running async operation: {e}") raise @@ -208,7 +213,7 @@ def _cleanup_async_executor(): try: _async_executor.shutdown() except Exception as e: - plugin_logger.error(f"Error during async executor cleanup: {e}") + sys_plugin_logger.error(f"Error during async executor cleanup: {e}") # Register cleanup function diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py index b4206d32..d119fc98 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py @@ -13,7 +13,7 @@ DeregisterInstanceParam, SubscribeServiceParam, Instance, ListServiceParam from fitframework import fitable, const -from fitframework.api.logging import plugin_logger +from fitframework.api.logging import sys_plugin_logger from fit_common_struct.entity import Worker, FitableMeta, Application, FitableAddressInstance, \ FitableMetaInstance, ApplicationInstance from fit_common_struct.core import Fitable, Genericable @@ -43,11 +43,11 @@ def on_service_changed(fitable_info: Fitable, worker_id: str) -> None: # Query current instances instances = query_fitable_addresses([fitable_info], worker_id) # notify_fitables(instances) - plugin_logger.debug( + sys_plugin_logger.debug( f"Service changed for fitable: {fitable_info}, instances: {len(instances)}" ) except Exception as e: - plugin_logger.error(f"Service change handling failed: {e}") + sys_plugin_logger.error(f"Service change handling failed: {e}") def extract_workers(app_instances: List[Instance], application: Application) -> List[Worker]: @@ -83,7 +83,7 @@ def register_fitables(fitable_metas: List[FitableMeta], worker: Worker, applicat Exception: If registration fails due to registry error. """ try: - plugin_logger.debug( + sys_plugin_logger.debug( f"Registering fitables. [fitableMetas={fitable_metas}, " f"worker={worker.id}, application={application.nameVersion}]" ) @@ -106,9 +106,9 @@ def register_fitables(fitable_metas: List[FitableMeta], worker: Worker, applicat ) run_async_safely(call_register_instance(param)) - plugin_logger.info(f"Successfully registered fitables for worker {worker.id}") + sys_plugin_logger.info(f"Successfully registered fitables for worker {worker.id}") except Exception as e: - plugin_logger.error(f"Failed to register fitables due to registry error: {e}") + sys_plugin_logger.error(f"Failed to register fitables due to registry error: {e}") raise @fitable(const.UNREGISTER_FIT_SERVICE_GEN_ID, const.UNREGISTER_FIT_SERVICE_FIT_ID) @@ -120,7 +120,7 @@ def unregister_fitables(fitables: List[Fitable], worker_id: str) -> None: fitables: List of Fitable implementations to unregister. worker_id: Unique identifier of the process where service implementations reside. """ - plugin_logger.debug( + sys_plugin_logger.debug( f"Unregistering fitables for worker. [fitables={fitables}, workerId={worker_id}]" ) @@ -149,7 +149,7 @@ def unregister_single_fitable(fitable: Fitable, worker_id: str) -> None: instances = run_async_safely(call_list_instances(param)) unregister_matching_instances(instances, worker_id, service_name, group_name) except Exception as e: - plugin_logger.error(f"Failed to unregister fitable due to registry error: {e}") + sys_plugin_logger.error(f"Failed to unregister fitable due to registry error: {e}") def unregister_matching_instances(instances: List[Instance], worker_id: str, service_name: str, group_name: str) -> None: @@ -173,9 +173,9 @@ def unregister_matching_instances(instances: List[Instance], worker_id: str, ser port=instance.port ) run_async_safely(call_deregister_instance(param)) - plugin_logger.debug(f"Successfully deregistered instance {instance.ip}:{instance.port}") + sys_plugin_logger.debug(f"Successfully deregistered instance {instance.ip}:{instance.port}") except Exception as e: - plugin_logger.error(f"Failed to deregister instance: {e}") + sys_plugin_logger.error(f"Failed to deregister instance: {e}") @fitable(const.QUERY_FIT_SERVICE_GEN_ID, const.QUERY_FIT_SERVICE_FIT_ID) @@ -190,7 +190,7 @@ def query_fitable_addresses(fitables: List[Fitable], worker_id: str) -> List[Fit Returns: List of obtained instance information. """ - plugin_logger.debug( + sys_plugin_logger.debug( f"Querying fitables for worker. [fitables={fitables}, workerId={worker_id}]" ) result_map = {} @@ -202,7 +202,7 @@ def query_fitable_addresses(fitables: List[Fitable], worker_id: str) -> List[Fit continue process_application_instances(result_map, fitable, instances) except Exception as e: - plugin_logger.error(f"Failed to query fitables for genericableId: {e}") + sys_plugin_logger.error(f"Failed to query fitables for genericableId: {e}") return list(result_map.values()) @@ -265,7 +265,7 @@ def subscribe_fit_service(fitables: List[Fitable], worker_id: str, callback_fita Returns: Queried instance information. """ - plugin_logger.debug( + sys_plugin_logger.debug( f"Subscribing to fitables for worker. [fitables={fitables}, " f"workerId={worker_id}, callbackFitableId={callback_fitable_id}]" ) @@ -278,7 +278,7 @@ def subscribe_fit_service(fitables: List[Fitable], worker_id: str, callback_fita service_key = build_service_key(group_name, service_name) if service_key in _service_subscriptions: - plugin_logger.debug( + sys_plugin_logger.debug( f"Already subscribed to service. [groupName={group_name}, serviceName={service_name}]" ) continue @@ -299,12 +299,12 @@ def event_listener(event): subscribe_callback=event_listener ) run_async_safely(call_subscribe(param)) - plugin_logger.debug( + sys_plugin_logger.debug( f"Subscribed to service. [groupName={group_name}, serviceName={service_name}]" ) except Exception as e: - plugin_logger.error(f"Failed to subscribe to Nacos service: {e}") + sys_plugin_logger.error(f"Failed to subscribe to Nacos service: {e}") return query_fitable_addresses(fitables, worker_id) @@ -319,7 +319,7 @@ def unsubscribe_fitables(fitables: List[Fitable], worker_id: str, callback_fitab worker_id: Unique identifier of the specified process. callback_fitable_id: Unique identifier for unsubscribe callback Fitable implementation. """ - plugin_logger.debug(f"Unsubscribing from fitables for worker. [fitables={fitables}, workerId={worker_id}, callbackFitableId={callback_fitable_id}]") + sys_plugin_logger.debug(f"Unsubscribing from fitables for worker. [fitables={fitables}, workerId={worker_id}, callbackFitableId={callback_fitable_id}]") for fitable in fitables: try: @@ -336,9 +336,9 @@ def unsubscribe_fitables(fitables: List[Fitable], worker_id: str, callback_fitab subscribe_callback=listener ) run_async_safely(call_unsubscribe(param)) - plugin_logger.debug(f"Unsubscribed from service. [groupName={group_name}, serviceName={service_name}]") + sys_plugin_logger.debug(f"Unsubscribed from service. [groupName={group_name}, serviceName={service_name}]") except Exception as e: - plugin_logger.error(f"Failed to unsubscribe from Nacos service: {e}") + sys_plugin_logger.error(f"Failed to unsubscribe from Nacos service: {e}") @fitable(const.QUERY_FITABLE_METAS_GEN_ID, const.QUERY_FITABLE_METAS_FIT_ID) @@ -352,7 +352,7 @@ def query_fitable_metas(genericable_infos: List[Genericable]) -> List[FitableMet Returns: List of queried Fitable metadata. """ - plugin_logger.debug( + sys_plugin_logger.debug( f"Querying fitable metas for genericables. [genericables={genericable_infos}]" ) meta_environments = {} @@ -386,7 +386,7 @@ def process_genericable_services(genericable: Genericable, meta_environments: Di for service_name in service_list.services: process_service_instances(service_name, group_name, meta_environments) except Exception as e: - plugin_logger.error(f"Failed to query fitable metas: {e}") + sys_plugin_logger.error(f"Failed to query fitable metas: {e}") def process_service_instances(service_name: str, group_name: str, meta_environments: Dict) -> None: @@ -413,7 +413,7 @@ def process_service_instances(service_name: str, group_name: str, meta_environme meta = parse_fitable_meta(instances[0].metadata) collect_environments_from_instances(instances, meta, meta_environments) except Exception as e: - plugin_logger.error(f"Failed to select instances for service {service_name}: {e}") + sys_plugin_logger.error(f"Failed to select instances for service {service_name}: {e}") def collect_environments_from_instances(instances: List[Instance], meta: FitableMeta, meta_environments: Dict) -> None: @@ -433,7 +433,7 @@ def collect_environments_from_instances(instances: List[Instance], meta: Fitable meta_environments[meta] = set() meta_environments[meta].add(worker.environment) except Exception as e: - plugin_logger.error(f"Failed to parse worker metadata: {e}") + sys_plugin_logger.error(f"Failed to parse worker metadata: {e}") def build_fitable_meta_instances(meta_environments: Dict) -> List[FitableMetaInstance]: diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/parsers.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/parsers.py index a54e8c40..89aaf83e 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/service/parsers.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/parsers.py @@ -13,7 +13,7 @@ from fit_common_struct.entity import Worker, Application, FitableMeta, Endpoint, Address from fit_common_struct.core import Fitable from fitframework.utils.json_serialize_utils import json_deserialize -from fitframework.api.logging import plugin_logger +from fitframework.api.logging import sys_plugin_logger from .constants import WORKER_KEY, APPLICATION_KEY, FITABLE_META_KEY @@ -33,7 +33,7 @@ def parse_fitable_meta(metadata: Dict) -> FitableMeta: if meta_json: return json_deserialize(FitableMeta, meta_json) except Exception as e: - plugin_logger.error(f"Failed to parse fitable meta for instance: {e}") + sys_plugin_logger.error(f"Failed to parse fitable meta for instance: {e}") # Return default value default_fitable = Fitable("unknown", "1.0", "unknown", "1.0") @@ -56,7 +56,7 @@ def parse_application(metadata: Dict) -> Application: if app_json: return json_deserialize(Application, app_json) except Exception as e: - plugin_logger.error(f"Failed to parse application metadata for instance: {e}") + sys_plugin_logger.error(f"Failed to parse application metadata for instance: {e}") # Return default value return Application("unknown", "unknown") @@ -87,7 +87,7 @@ def parse_worker(instance_or_metadata) -> Worker: if worker_json: return json_deserialize(Worker, worker_json) except Exception as e: - plugin_logger.error(f"Failed to parse worker metadata for instance: {e}") + sys_plugin_logger.error(f"Failed to parse worker metadata for instance: {e}") # Fallback - create basic worker information worker = Worker([], "unknown", "", {}) diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/utils.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/utils.py index 5d144d9f..cc8ca40b 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/service/utils.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/utils.py @@ -11,7 +11,7 @@ from fit_common_struct.core import Fitable, Genericable from fit_common_struct.entity import Worker, Application, FitableMeta, Endpoint -from fitframework.api.logging import plugin_logger +from fitframework.api.logging import sys_plugin_logger from fitframework.utils.json_serialize_utils import json_serialize from .constants import SEPARATOR, CLUSTER_PORT_PATTERN, PROTOCOL_CODE_MAP, \ @@ -85,7 +85,7 @@ def create_instances(worker: Worker, application: Application, meta: FitableMeta Returns: List of instance dictionaries. """ - plugin_logger.debug( + sys_plugin_logger.debug( f"Creating instance for worker. [worker={worker.id}, " f"application={application.nameVersion}, meta={meta}]" ) @@ -132,7 +132,7 @@ def build_instance_metadata(worker: Worker, application: Application, meta: Fita metadata[APPLICATION_KEY] = json_serialize(application) metadata[FITABLE_META_KEY] = json_serialize(meta) except Exception as e: - plugin_logger.error(f"Failed to serialize metadata for worker: {e}") + sys_plugin_logger.error(f"Failed to serialize metadata for worker: {e}") return metadata @@ -157,6 +157,6 @@ def build_endpoints(extensions: Dict[str, str]) -> List[Endpoint]: endpoint = Endpoint(int(value), PROTOCOL_CODE_MAP[protocol_name]) endpoints.append(endpoint) else: - plugin_logger.error(f"Unknown protocol: {protocol_name}") + sys_plugin_logger.error(f"Unknown protocol: {protocol_name}") return endpoints From 4b443a71ab8ac85b65c3a0016a65800d1b693530 Mon Sep 17 00:00:00 2001 From: xiaohao <932379959@qq.com> Date: Tue, 9 Sep 2025 17:55:01 +0800 Subject: [PATCH 14/15] =?UTF-8?q?[fit]=20=E5=A2=9E=E5=8A=A0=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E6=B1=A0=E6=B8=85=E7=90=86=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/nacos_registry_server.py | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py index d119fc98..6314c673 100644 --- a/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py +++ b/framework/fit/python/plugin/fit_py_nacos_registry/service/nacos_registry_server.py @@ -8,6 +8,8 @@ """ from concurrent.futures import ThreadPoolExecutor from typing import List, Dict +import weakref +import atexit from v2.nacos import RegisterInstanceParam, ListInstanceParam, \ DeregisterInstanceParam, SubscribeServiceParam, Instance, ListServiceParam @@ -27,10 +29,24 @@ call_subscribe, call_unsubscribe, call_list_services, call_register_instance # Global variables -_service_subscriptions: Dict[str, any] = {} +_service_subscriptions: weakref.WeakValueDictionary = weakref.WeakValueDictionary() _executor = ThreadPoolExecutor(max_workers=10) +def _cleanup_executor(): + """Cleanup the thread pool executor.""" + try: + if _executor: + _executor.shutdown(wait=True) + sys_plugin_logger.info("Thread pool executor shut down successfully") + except Exception as e: + sys_plugin_logger.error(f"Error shutting down thread pool executor: {e}") + + +# Register cleanup function to ensure executor is properly closed +atexit.register(_cleanup_executor) + + def on_service_changed(fitable_info: Fitable, worker_id: str) -> None: """ Handle service change events, query and notify updates to Fitables instance information. @@ -327,9 +343,9 @@ def unsubscribe_fitables(fitables: List[Fitable], worker_id: str, callback_fitab service_name = get_service_name(fitable) service_key = build_service_key(group_name, service_name) - if service_key in _service_subscriptions: - listener = _service_subscriptions.pop(service_key) - + # Use pop with default to avoid KeyError if listener was garbage collected + listener = _service_subscriptions.pop(service_key, None) + if listener is not None: param = SubscribeServiceParam( service_name=service_name, group_name=group_name, @@ -337,6 +353,8 @@ def unsubscribe_fitables(fitables: List[Fitable], worker_id: str, callback_fitab ) run_async_safely(call_unsubscribe(param)) sys_plugin_logger.debug(f"Unsubscribed from service. [groupName={group_name}, serviceName={service_name}]") + else: + sys_plugin_logger.debug(f"Listener already cleaned up for service. [groupName={group_name}, serviceName={service_name}]") except Exception as e: sys_plugin_logger.error(f"Failed to unsubscribe from Nacos service: {e}") From 809d70b2257ce601257cb5bd79ced44e413b6d15 Mon Sep 17 00:00:00 2001 From: xiaohao <932379959@qq.com> Date: Tue, 9 Sep 2025 18:15:54 +0800 Subject: [PATCH 15/15] =?UTF-8?q?[fit]=20=E7=A7=BB=E5=8A=A8=E9=83=A8?= =?UTF-8?q?=E5=88=86=E9=85=8D=E7=BD=AE=E5=88=B0=E6=8F=92=E4=BB=B6=E5=86=85?= =?UTF-8?q?=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- framework/fit/python/conf/application.yml | 11 ----------- .../plugin/fit_py_http_client/conf/application.yml | 11 +++++++++++ .../plugin/fit_py_server_http/conf/application.yml | 1 - 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/framework/fit/python/conf/application.yml b/framework/fit/python/conf/application.yml index 85450471..446d1fb8 100644 --- a/framework/fit/python/conf/application.yml +++ b/framework/fit/python/conf/application.yml @@ -29,17 +29,6 @@ http: formats: - 1 - 2 -https: - client: - verify_enabled: false # 是否需要验证服务端身份 - ca_path: "plugin/fit_py_http_client/resources/ca.crt" - assert_host_name: false # 是否在校验时校验主机名,仅当 verify_enabled 为 true 时有意义 - cert_enabled: false # 是否服务端对自身身份校验 - crt_path: "plugin/fit_py_http_client/resources/global.crt" - key_path: "plugin/fit_py_http_client/resources/global.key" - key_file_encrypted: false # 私钥是否被加密,仅当 cert_enabled 为 true 时有意义 - key_file_password: "" # 私钥的密码,仅当 cert_enabled 和 key_file_encrypted 为 true 时有意义 - key_file_password_encrypted: false # 私钥的密码是否被加密,仅当 cert_enabled 和 key_file_encrypted 为 true 时有意义 registry-center: server: mode: 'DIRECT' # DIRECT 表示直连,直接连接内存注册中心;PROXY 表示代理模式,通过本地代理服务连接 Nacos 注册中心 diff --git a/framework/fit/python/plugin/fit_py_http_client/conf/application.yml b/framework/fit/python/plugin/fit_py_http_client/conf/application.yml index e69de29b..e6baccab 100644 --- a/framework/fit/python/plugin/fit_py_http_client/conf/application.yml +++ b/framework/fit/python/plugin/fit_py_http_client/conf/application.yml @@ -0,0 +1,11 @@ +https: + client: + verify_enabled: false # 是否需要验证服务端身份 + ca_path: "plugin/fit_py_http_client/resources/ca.crt" + assert_host_name: false # 是否在校验时校验主机名,仅当 verify_enabled 为 true 时有意义 + cert_enabled: false # 是否服务端对自身身份校验 + crt_path: "plugin/fit_py_http_client/resources/global.crt" + key_path: "plugin/fit_py_http_client/resources/global.key" + key_file_encrypted: false # 私钥是否被加密,仅当 cert_enabled 为 true 时有意义 + key_file_password: "" # 私钥的密码,仅当 cert_enabled 和 key_file_encrypted 为 true 时有意义 + key_file_password_encrypted: false # 私钥的密码是否被加密,仅当 cert_enabled 和 key_file_encrypted 为 true 时有意义 \ No newline at end of file diff --git a/framework/fit/python/plugin/fit_py_server_http/conf/application.yml b/framework/fit/python/plugin/fit_py_server_http/conf/application.yml index 2691b5be..4dbac3fd 100644 --- a/framework/fit/python/plugin/fit_py_server_http/conf/application.yml +++ b/framework/fit/python/plugin/fit_py_server_http/conf/application.yml @@ -23,7 +23,6 @@ https: tls_protocol: "" ciphers: - async: task-count-limit: 1000 result-save-duration: 300