isa-common 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. isa_common-0.1.0/PKG-INFO +180 -0
  2. isa_common-0.1.0/README.md +148 -0
  3. isa_common-0.1.0/isa_common/__init__.py +183 -0
  4. isa_common-0.1.0/isa_common/base_client.py +145 -0
  5. isa_common-0.1.0/isa_common/duckdb_client.py +399 -0
  6. isa_common-0.1.0/isa_common/events/__init__.py +61 -0
  7. isa_common-0.1.0/isa_common/events/base_event_models.py +107 -0
  8. isa_common-0.1.0/isa_common/events/base_event_publisher.py +199 -0
  9. isa_common-0.1.0/isa_common/events/base_event_subscriber.py +500 -0
  10. isa_common-0.1.0/isa_common/events/billing_event_publisher.py +451 -0
  11. isa_common-0.1.0/isa_common/events/billing_events.py +286 -0
  12. isa_common-0.1.0/isa_common/minio_client.py +223 -0
  13. isa_common-0.1.0/isa_common/mqtt_client.py +220 -0
  14. isa_common-0.1.0/isa_common/nats_client.py +260 -0
  15. isa_common-0.1.0/isa_common/proto/__init__.py +22 -0
  16. isa_common-0.1.0/isa_common/proto/common_pb2.py +63 -0
  17. isa_common-0.1.0/isa_common/proto/common_pb2_grpc.py +24 -0
  18. isa_common-0.1.0/isa_common/proto/duckdb_service_pb2.py +208 -0
  19. isa_common-0.1.0/isa_common/proto/duckdb_service_pb2_grpc.py +1310 -0
  20. isa_common-0.1.0/isa_common/proto/loki_service_pb2.py +178 -0
  21. isa_common-0.1.0/isa_common/proto/loki_service_pb2_grpc.py +930 -0
  22. isa_common-0.1.0/isa_common/proto/minio_service_pb2.py +162 -0
  23. isa_common-0.1.0/isa_common/proto/minio_service_pb2_grpc.py +959 -0
  24. isa_common-0.1.0/isa_common/proto/mqtt_service_pb2.py +174 -0
  25. isa_common-0.1.0/isa_common/proto/mqtt_service_pb2_grpc.py +1137 -0
  26. isa_common-0.1.0/isa_common/proto/nats_service_pb2.py +220 -0
  27. isa_common-0.1.0/isa_common/proto/nats_service_pb2_grpc.py +1436 -0
  28. isa_common-0.1.0/isa_common/proto/redis_service_pb2.py +308 -0
  29. isa_common-0.1.0/isa_common/proto/redis_service_pb2_grpc.py +2725 -0
  30. isa_common-0.1.0/isa_common/proto/supabase_service_pb2.py +96 -0
  31. isa_common-0.1.0/isa_common/proto/supabase_service_pb2_grpc.py +671 -0
  32. isa_common-0.1.0/isa_common/supabase_client.py +428 -0
  33. isa_common-0.1.0/isa_common.egg-info/PKG-INFO +180 -0
  34. isa_common-0.1.0/isa_common.egg-info/SOURCES.txt +38 -0
  35. isa_common-0.1.0/isa_common.egg-info/dependency_links.txt +1 -0
  36. isa_common-0.1.0/isa_common.egg-info/requires.txt +11 -0
  37. isa_common-0.1.0/isa_common.egg-info/top_level.txt +1 -0
  38. isa_common-0.1.0/pyproject.toml +51 -0
  39. isa_common-0.1.0/setup.cfg +4 -0
  40. isa_common-0.1.0/setup.py +51 -0
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: isa-common
3
+ Version: 0.1.0
4
+ Summary: Shared Python infrastructure library for isA platform
5
+ Home-page: https://github.com/isa-platform/isA_Cloud
6
+ Author: isA Platform
7
+ Author-email: isA Platform <dev@isa-platform.com>
8
+ Project-URL: Homepage, https://github.com/isa-platform/isA_Cloud
9
+ Project-URL: Repository, https://github.com/isa-platform/isA_Cloud
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Requires-Python: >=3.8
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: grpcio>=1.50.0
20
+ Requires-Dist: grpcio-tools>=1.50.0
21
+ Requires-Dist: pydantic>=2.0.0
22
+ Requires-Dist: tenacity>=8.0.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
25
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
26
+ Requires-Dist: black>=23.0.0; extra == "dev"
27
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
28
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
29
+ Dynamic: author
30
+ Dynamic: home-page
31
+ Dynamic: requires-python
32
+
33
+ # isA Common
34
+
35
+ Shared Python infrastructure library for the isA platform.
36
+
37
+ ## Overview
38
+
39
+ This package provides common infrastructure components used across all isA Python services:
40
+ - **gRPC Clients**: NATS, MinIO, MQTT, DuckDB, Supabase, etc.
41
+ - **Event Framework**: Generic event-driven architecture base classes
42
+ - **Proto Files**: Generated gRPC proto files
43
+
44
+ ## Installation
45
+
46
+ ### Development Install (Recommended)
47
+
48
+ ```bash
49
+ pip install -e /Users/xenodennis/Documents/Fun/isA_Cloud/isA_common
50
+ ```
51
+
52
+ ### From Source
53
+
54
+ ```bash
55
+ cd /Users/xenodennis/Documents/Fun/isA_Cloud/isA_common
56
+ pip install .
57
+ ```
58
+
59
+ ## Usage
60
+
61
+ ### gRPC Clients
62
+
63
+ ```python
64
+ from grpc_clients import NATSClient, MinIOClient, get_client
65
+
66
+ # Option 1: Direct instantiation
67
+ nats = NATSClient(host='localhost', port=50056)
68
+
69
+ # Option 2: Factory pattern
70
+ nats = get_client('nats', user_id='my_service')
71
+ ```
72
+
73
+ ### Event Framework
74
+
75
+ ```python
76
+ from grpc_clients.events import BaseEvent, BaseEventPublisher
77
+
78
+ # Define your event
79
+ class MyEvent(BaseEvent):
80
+ event_type: str = "my.event"
81
+ data: str
82
+
83
+ # Define your publisher
84
+ class MyEventPublisher(BaseEventPublisher):
85
+ def service_name(self) -> str:
86
+ return "my_service"
87
+
88
+ async def publish_my_event(self, data: str) -> bool:
89
+ event = MyEvent(event_type="my.event", data=data)
90
+ return await self.publish_event(event, subject="my.event")
91
+ ```
92
+
93
+ ## Projects Using isA_common
94
+
95
+ - **isA_user** - Microservices platform (17 services)
96
+ - **isA_Model** - AI model inference service
97
+ - **isA_Agent** - AI agent service
98
+ - **isA_MCP** - Model Control Protocol service
99
+
100
+ ## Components
101
+
102
+ ### gRPC Clients (`grpc_clients/`)
103
+
104
+ - `NATSClient` - NATS message bus client (port 50056)
105
+ - `MinIOClient` - Object storage client (port 50051)
106
+ - `MQTTClient` - MQTT broker client (port 50053)
107
+ - `DuckDBClient` - Analytics database client (port 50052)
108
+ - `SupabaseClient` - Supabase client (port 50057)
109
+
110
+ ### Event Framework (`grpc_clients/events/`)
111
+
112
+ - `BaseEvent` - Base event model with metadata
113
+ - `EventMetadata` - Standard event metadata
114
+ - `BaseEventPublisher` - Base publisher class (abstract)
115
+ - `BaseEventSubscriber` - Base subscriber class with idempotency
116
+ - `EventHandler` - Event handler interface (abstract)
117
+ - `IdempotencyChecker` - Duplicate prevention
118
+ - `RetryPolicy` - Retry configuration
119
+
120
+ ## Development
121
+
122
+ ### Generating Proto Files
123
+
124
+ Proto files are generated from the parent `isA_Cloud` project:
125
+
126
+ ```bash
127
+ cd /Users/xenodennis/Documents/Fun/isA_Cloud
128
+ ./scripts/generate-grpc.sh
129
+ ```
130
+
131
+ This script generates:
132
+ - Go proto files → `api/proto/*.pb.go`
133
+ - Python proto files → `isA_common/grpc_clients/proto/*.py`
134
+
135
+ ### Running Tests
136
+
137
+ ```bash
138
+ cd /Users/xenodennis/Documents/Fun/isA_Cloud/isA_common
139
+ pytest tests/
140
+ ```
141
+
142
+ ## Package Structure
143
+
144
+ ```
145
+ isA_Cloud/
146
+ ├── isA_common/ # Python package
147
+ │ ├── grpc_clients/
148
+ │ │ ├── __init__.py
149
+ │ │ ├── base_client.py
150
+ │ │ ├── nats_client.py
151
+ │ │ ├── minio_client.py
152
+ │ │ ├── mqtt_client.py
153
+ │ │ ├── duckdb_client.py
154
+ │ │ ├── supabase_client.py
155
+ │ │ ├── events/
156
+ │ │ │ ├── __init__.py
157
+ │ │ │ ├── base_event_models.py
158
+ │ │ │ ├── base_event_publisher.py
159
+ │ │ │ └── base_event_subscriber.py
160
+ │ │ └── proto/
161
+ │ │ ├── __init__.py
162
+ │ │ ├── common_pb2.py
163
+ │ │ ├── nats_service_pb2.py
164
+ │ │ └── ...
165
+ │ ├── setup.py
166
+ │ ├── pyproject.toml
167
+ │ ├── README.md
168
+ │ └── requirements.txt
169
+ ├── scripts/
170
+ │ └── generate-grpc.sh # Generates to isA_common/
171
+ └── api/proto/ # Proto definitions
172
+ ```
173
+
174
+ ## Version
175
+
176
+ Current version: **0.1.0**
177
+
178
+ ## License
179
+
180
+ Proprietary - isA Platform
@@ -0,0 +1,148 @@
1
+ # isA Common
2
+
3
+ Shared Python infrastructure library for the isA platform.
4
+
5
+ ## Overview
6
+
7
+ This package provides common infrastructure components used across all isA Python services:
8
+ - **gRPC Clients**: NATS, MinIO, MQTT, DuckDB, Supabase, etc.
9
+ - **Event Framework**: Generic event-driven architecture base classes
10
+ - **Proto Files**: Generated gRPC proto files
11
+
12
+ ## Installation
13
+
14
+ ### Development Install (Recommended)
15
+
16
+ ```bash
17
+ pip install -e /Users/xenodennis/Documents/Fun/isA_Cloud/isA_common
18
+ ```
19
+
20
+ ### From Source
21
+
22
+ ```bash
23
+ cd /Users/xenodennis/Documents/Fun/isA_Cloud/isA_common
24
+ pip install .
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### gRPC Clients
30
+
31
+ ```python
32
+ from grpc_clients import NATSClient, MinIOClient, get_client
33
+
34
+ # Option 1: Direct instantiation
35
+ nats = NATSClient(host='localhost', port=50056)
36
+
37
+ # Option 2: Factory pattern
38
+ nats = get_client('nats', user_id='my_service')
39
+ ```
40
+
41
+ ### Event Framework
42
+
43
+ ```python
44
+ from grpc_clients.events import BaseEvent, BaseEventPublisher
45
+
46
+ # Define your event
47
+ class MyEvent(BaseEvent):
48
+ event_type: str = "my.event"
49
+ data: str
50
+
51
+ # Define your publisher
52
+ class MyEventPublisher(BaseEventPublisher):
53
+ def service_name(self) -> str:
54
+ return "my_service"
55
+
56
+ async def publish_my_event(self, data: str) -> bool:
57
+ event = MyEvent(event_type="my.event", data=data)
58
+ return await self.publish_event(event, subject="my.event")
59
+ ```
60
+
61
+ ## Projects Using isA_common
62
+
63
+ - **isA_user** - Microservices platform (17 services)
64
+ - **isA_Model** - AI model inference service
65
+ - **isA_Agent** - AI agent service
66
+ - **isA_MCP** - Model Control Protocol service
67
+
68
+ ## Components
69
+
70
+ ### gRPC Clients (`grpc_clients/`)
71
+
72
+ - `NATSClient` - NATS message bus client (port 50056)
73
+ - `MinIOClient` - Object storage client (port 50051)
74
+ - `MQTTClient` - MQTT broker client (port 50053)
75
+ - `DuckDBClient` - Analytics database client (port 50052)
76
+ - `SupabaseClient` - Supabase client (port 50057)
77
+
78
+ ### Event Framework (`grpc_clients/events/`)
79
+
80
+ - `BaseEvent` - Base event model with metadata
81
+ - `EventMetadata` - Standard event metadata
82
+ - `BaseEventPublisher` - Base publisher class (abstract)
83
+ - `BaseEventSubscriber` - Base subscriber class with idempotency
84
+ - `EventHandler` - Event handler interface (abstract)
85
+ - `IdempotencyChecker` - Duplicate prevention
86
+ - `RetryPolicy` - Retry configuration
87
+
88
+ ## Development
89
+
90
+ ### Generating Proto Files
91
+
92
+ Proto files are generated from the parent `isA_Cloud` project:
93
+
94
+ ```bash
95
+ cd /Users/xenodennis/Documents/Fun/isA_Cloud
96
+ ./scripts/generate-grpc.sh
97
+ ```
98
+
99
+ This script generates:
100
+ - Go proto files → `api/proto/*.pb.go`
101
+ - Python proto files → `isA_common/grpc_clients/proto/*.py`
102
+
103
+ ### Running Tests
104
+
105
+ ```bash
106
+ cd /Users/xenodennis/Documents/Fun/isA_Cloud/isA_common
107
+ pytest tests/
108
+ ```
109
+
110
+ ## Package Structure
111
+
112
+ ```
113
+ isA_Cloud/
114
+ ├── isA_common/ # Python package
115
+ │ ├── grpc_clients/
116
+ │ │ ├── __init__.py
117
+ │ │ ├── base_client.py
118
+ │ │ ├── nats_client.py
119
+ │ │ ├── minio_client.py
120
+ │ │ ├── mqtt_client.py
121
+ │ │ ├── duckdb_client.py
122
+ │ │ ├── supabase_client.py
123
+ │ │ ├── events/
124
+ │ │ │ ├── __init__.py
125
+ │ │ │ ├── base_event_models.py
126
+ │ │ │ ├── base_event_publisher.py
127
+ │ │ │ └── base_event_subscriber.py
128
+ │ │ └── proto/
129
+ │ │ ├── __init__.py
130
+ │ │ ├── common_pb2.py
131
+ │ │ ├── nats_service_pb2.py
132
+ │ │ └── ...
133
+ │ ├── setup.py
134
+ │ ├── pyproject.toml
135
+ │ ├── README.md
136
+ │ └── requirements.txt
137
+ ├── scripts/
138
+ │ └── generate-grpc.sh # Generates to isA_common/
139
+ └── api/proto/ # Proto definitions
140
+ ```
141
+
142
+ ## Version
143
+
144
+ Current version: **0.1.0**
145
+
146
+ ## License
147
+
148
+ Proprietary - isA Platform
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ gRPC Clients Package
4
+ 统一的 gRPC 客户端接口
5
+
6
+ 使用示例:
7
+ from core.clients import get_client, SupabaseClient, MinIOClient
8
+
9
+ # 方式 1: 使用工厂函数
10
+ supabase = get_client('supabase', user_id='user_123')
11
+
12
+ # 方式 2: 直接实例化
13
+ minio = MinIOClient(host='localhost', port=50051, user_id='user_123')
14
+
15
+ # 方式 3: 使用 with 语句自动管理连接
16
+ with SupabaseClient() as client:
17
+ client.query('users', select='*')
18
+ """
19
+
20
+ from typing import Optional, Dict
21
+ from .base_client import BaseGRPCClient
22
+ from .supabase_client import SupabaseClient
23
+ from .minio_client import MinIOClient
24
+ from .duckdb_client import DuckDBClient
25
+ from .mqtt_client import MQTTClient
26
+ from .nats_client import NATSClient
27
+
28
+ # Import events module (but don't unpack, keep as submodule)
29
+ from . import events
30
+
31
+ # 导出所有客户端
32
+ __all__ = [
33
+ 'BaseGRPCClient',
34
+ 'SupabaseClient',
35
+ 'MinIOClient',
36
+ 'DuckDBClient',
37
+ 'MQTTClient',
38
+ 'NATSClient',
39
+ 'get_client',
40
+ 'ClientFactory',
41
+ 'events', # Export events submodule
42
+ ]
43
+
44
+ # 默认配置
45
+ DEFAULT_PORTS: Dict[str, int] = {
46
+ 'minio': 50051,
47
+ 'duckdb': 50052,
48
+ 'mqtt': 50053,
49
+ 'loki': 50054,
50
+ 'redis': 50055,
51
+ 'nats': 50056,
52
+ 'supabase': 50057,
53
+ }
54
+
55
+ DEFAULT_HOST = 'localhost'
56
+
57
+
58
+ class ClientFactory:
59
+ """gRPC 客户端工厂"""
60
+
61
+ # 客户端映射
62
+ _clients = {
63
+ 'supabase': SupabaseClient,
64
+ 'minio': MinIOClient,
65
+ 'duckdb': DuckDBClient,
66
+ 'mqtt': MQTTClient,
67
+ 'nats': NATSClient,
68
+ # TODO: 添加其他客户端
69
+ # 'redis': RedisClient,
70
+ # 'loki': LokiClient,
71
+ }
72
+
73
+ @classmethod
74
+ def create(cls, service_name: str, host: Optional[str] = None,
75
+ port: Optional[int] = None, user_id: Optional[str] = None) -> BaseGRPCClient:
76
+ """
77
+ 创建 gRPC 客户端
78
+
79
+ Args:
80
+ service_name: 服务名称 (supabase, minio, duckdb, etc.)
81
+ host: 服务地址 (默认: localhost)
82
+ port: 服务端口 (默认: 根据服务自动选择)
83
+ user_id: 用户 ID
84
+
85
+ Returns:
86
+ 客户端实例
87
+
88
+ Raises:
89
+ ValueError: 如果服务名称不支持
90
+
91
+ 示例:
92
+ client = ClientFactory.create('supabase', user_id='user_123')
93
+ client = ClientFactory.create('minio', host='192.168.1.100', port=50051)
94
+ """
95
+ service_name = service_name.lower()
96
+
97
+ if service_name not in cls._clients:
98
+ available = ', '.join(cls._clients.keys())
99
+ raise ValueError(f"不支持的服务: {service_name}. 可用服务: {available}")
100
+
101
+ # 使用默认值
102
+ if host is None:
103
+ host = DEFAULT_HOST
104
+ if port is None:
105
+ port = DEFAULT_PORTS.get(service_name, 50051)
106
+
107
+ client_class = cls._clients[service_name]
108
+ return client_class(host=host, port=port, user_id=user_id)
109
+
110
+ @classmethod
111
+ def register_client(cls, service_name: str, client_class):
112
+ """
113
+ 注册新的客户端类
114
+
115
+ Args:
116
+ service_name: 服务名称
117
+ client_class: 客户端类 (必须继承 BaseGRPCClient)
118
+ """
119
+ if not issubclass(client_class, BaseGRPCClient):
120
+ raise TypeError(f"{client_class} 必须继承 BaseGRPCClient")
121
+
122
+ cls._clients[service_name.lower()] = client_class
123
+ print(f"✅ 注册客户端: {service_name} -> {client_class.__name__}")
124
+
125
+ @classmethod
126
+ def list_services(cls) -> list:
127
+ """列出所有可用的服务"""
128
+ return list(cls._clients.keys())
129
+
130
+
131
+ # 便捷函数
132
+ def get_client(service_name: str, host: Optional[str] = None,
133
+ port: Optional[int] = None, user_id: Optional[str] = None) -> BaseGRPCClient:
134
+ """
135
+ 获取 gRPC 客户端 (便捷函数)
136
+
137
+ Args:
138
+ service_name: 服务名称
139
+ host: 服务地址
140
+ port: 服务端口
141
+ user_id: 用户 ID
142
+
143
+ Returns:
144
+ 客户端实例
145
+
146
+ 示例:
147
+ from core.clients import get_client
148
+
149
+ supabase = get_client('supabase', user_id='user_123')
150
+ minio = get_client('minio', host='192.168.1.100')
151
+ """
152
+ return ClientFactory.create(service_name, host, port, user_id)
153
+
154
+
155
+ # 显示所有可用服务
156
+ def show_available_services():
157
+ """显示所有可用的 gRPC 服务"""
158
+ print("📦 可用的 gRPC 服务:")
159
+ print()
160
+ for service in sorted(ClientFactory.list_services()):
161
+ port = DEFAULT_PORTS.get(service, 'N/A')
162
+ client_class = ClientFactory._clients[service]
163
+ print(f" • {service:12} (端口: {port}) -> {client_class.__name__}")
164
+ print()
165
+
166
+
167
+ if __name__ == '__main__':
168
+ # 显示可用服务
169
+ show_available_services()
170
+
171
+ # 测试客户端创建
172
+ print("测试客户端创建:")
173
+ print("-" * 60)
174
+
175
+ # 使用工厂创建客户端
176
+ with get_client('supabase', user_id='test_user') as supabase:
177
+ supabase.health_check()
178
+
179
+ print()
180
+
181
+ with get_client('minio', user_id='test_user') as minio:
182
+ minio.health_check()
183
+
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Base gRPC Client
4
+ 所有 gRPC 客户端的基类,提供统一的连接管理和错误处理
5
+ """
6
+
7
+ import grpc
8
+ import logging
9
+ import threading
10
+ from typing import Optional
11
+ from abc import ABC, abstractmethod
12
+ from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
13
+
14
+ # Configure logging
15
+ logging.basicConfig(
16
+ level=logging.INFO,
17
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
18
+ )
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class BaseGRPCClient(ABC):
23
+ """gRPC 客户端基类"""
24
+
25
+ def __init__(self, host: str = 'localhost', port: int = 50051, user_id: Optional[str] = None,
26
+ lazy_connect: bool = True, enable_compression: bool = True, enable_retry: bool = True):
27
+ """
28
+ 初始化 gRPC 客户端
29
+
30
+ Args:
31
+ host: 服务地址
32
+ port: 服务端口
33
+ user_id: 用户 ID (用于多租户隔离)
34
+ lazy_connect: 是否延迟连接 (默认: True, 更快的启动速度)
35
+ enable_compression: 是否启用 gRPC 压缩 (默认: True)
36
+ enable_retry: 是否启用重试逻辑 (默认: True)
37
+ """
38
+ self.host = host
39
+ self.port = port
40
+ self.user_id = user_id or 'default_user'
41
+ self.address = f'{host}:{port}'
42
+ self.enable_compression = enable_compression
43
+ self.enable_retry = enable_retry
44
+
45
+ # Lazy initialization
46
+ self.channel = None
47
+ self.stub = None
48
+ self._connect_lock = threading.Lock()
49
+ self._connected = False
50
+
51
+ # Connect immediately if not lazy
52
+ if not lazy_connect:
53
+ self._ensure_connected()
54
+
55
+ def _ensure_connected(self):
56
+ """确保已连接(线程安全的延迟连接)"""
57
+ if self._connected and self.channel is not None:
58
+ return
59
+
60
+ with self._connect_lock:
61
+ # Double-check after acquiring lock
62
+ if self._connected and self.channel is not None:
63
+ return
64
+
65
+ logger.info(f"[{self.service_name()}] Connecting to {self.address}...")
66
+
67
+ # Build channel options
68
+ options = [
69
+ ('grpc.max_receive_message_length', 100 * 1024 * 1024), # 100MB
70
+ ('grpc.max_send_message_length', 100 * 1024 * 1024), # 100MB
71
+ ('grpc.keepalive_time_ms', 10000),
72
+ ('grpc.keepalive_timeout_ms', 5000),
73
+ ]
74
+
75
+ # Add compression if enabled
76
+ if self.enable_compression:
77
+ options.append(('grpc.default_compression_algorithm', grpc.Compression.Gzip))
78
+ options.append(('grpc.default_compression_level', grpc.Compression.Gzip))
79
+
80
+ # Create channel
81
+ self.channel = grpc.insecure_channel(self.address, options=options)
82
+
83
+ # Create stub
84
+ self.stub = self._create_stub()
85
+
86
+ # Mark as connected
87
+ self._connected = True
88
+ logger.info(f"[{self.service_name()}] Connected successfully to {self.address}")
89
+
90
+ @abstractmethod
91
+ def _create_stub(self):
92
+ """子类实现:创建特定服务的 stub"""
93
+ pass
94
+
95
+ @abstractmethod
96
+ def service_name(self) -> str:
97
+ """子类实现:返回服务名称"""
98
+ pass
99
+
100
+ def _call_with_retry(self, func, *args, **kwargs):
101
+ """带重试的 RPC 调用"""
102
+ if not self.enable_retry:
103
+ return func(*args, **kwargs)
104
+
105
+ @retry(
106
+ stop=stop_after_attempt(3),
107
+ wait=wait_exponential(multiplier=1, min=1, max=10),
108
+ retry=retry_if_exception_type(grpc.RpcError),
109
+ reraise=True
110
+ )
111
+ def _retry_wrapper():
112
+ self._ensure_connected()
113
+ return func(*args, **kwargs)
114
+
115
+ try:
116
+ return _retry_wrapper()
117
+ except grpc.RpcError as e:
118
+ logger.error(f"[{self.service_name()}] RPC failed after retries: {e.code()} - {e.details()}")
119
+ raise
120
+
121
+ def handle_error(self, e: Exception, operation: str = "操作"):
122
+ """统一错误处理"""
123
+ logger.error(f"[{self.service_name()}] {operation} 失败:")
124
+ if isinstance(e, grpc.RpcError):
125
+ logger.error(f" 错误代码: {e.code()}")
126
+ logger.error(f" 错误详情: {e.details()}")
127
+ else:
128
+ logger.error(f" 错误: {e}")
129
+ return None
130
+
131
+ def close(self):
132
+ """关闭连接"""
133
+ if self.channel is not None:
134
+ self.channel.close()
135
+ self._connected = False
136
+ logger.info(f"[{self.service_name()}] Connection closed")
137
+
138
+ def __enter__(self):
139
+ """支持 with 语句"""
140
+ return self
141
+
142
+ def __exit__(self, exc_type, exc_val, exc_tb):
143
+ """退出时自动关闭连接"""
144
+ self.close()
145
+