283 lines
7.0 KiB
Markdown
283 lines
7.0 KiB
Markdown
|
|
# 前言
|
|||
|
|
|
|||
|
|
sanic 和 服务之间基于grpc 解绑,一个服务一个grpc
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
可以参考接口,grpc 最好留有健康校验
|
|||
|
|
|
|||
|
|
# grpc demo
|
|||
|
|
|
|||
|
|
为了增强 gRPC 通讯的可靠性,我们可以添加以下功能:
|
|||
|
|
|
|||
|
|
1. 服务器健康检查服务
|
|||
|
|
2. 客户端连接前检查服务器状态
|
|||
|
|
3. 通讯过程中的错误处理和重试机制
|
|||
|
|
|
|||
|
|
## 1. 修改 protobuf 定义 (task.proto)
|
|||
|
|
|
|||
|
|
首先添加健康检查服务定义:
|
|||
|
|
|
|||
|
|
```protobuf
|
|||
|
|
syntax = "proto3";
|
|||
|
|
|
|||
|
|
package task;
|
|||
|
|
|
|||
|
|
service TaskService {
|
|||
|
|
rpc ProcessTask (TaskRequest) returns (TaskResponse);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加健康检查服务
|
|||
|
|
service HealthCheck {
|
|||
|
|
rpc Check (HealthCheckRequest) returns (HealthCheckResponse);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
message HealthCheckRequest {
|
|||
|
|
string service = 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
message HealthCheckResponse {
|
|||
|
|
enum ServingStatus {
|
|||
|
|
UNKNOWN = 0;
|
|||
|
|
SERVING = 1;
|
|||
|
|
NOT_SERVING = 2;
|
|||
|
|
SERVICE_UNKNOWN = 3;
|
|||
|
|
}
|
|||
|
|
ServingStatus status = 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
message TaskRequest {
|
|||
|
|
string task_id = 1;
|
|||
|
|
string sn = 2;
|
|||
|
|
ContentBody content_body = 3;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
message ContentBody {
|
|||
|
|
string org_code = 1;
|
|||
|
|
repeated int32 func_id = 2;
|
|||
|
|
string source_url = 3;
|
|||
|
|
string push_url = 4;
|
|||
|
|
float confidence = 5;
|
|||
|
|
repeated ParaList para_list = 6;
|
|||
|
|
Invade invade = 7;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
message ParaList {
|
|||
|
|
int32 func_id = 1;
|
|||
|
|
bool para_invade_enable = 2;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
message Invade {
|
|||
|
|
string invade_file = 1;
|
|||
|
|
string camera_para_url = 2;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
message TaskResponse {
|
|||
|
|
string task_id = 1;
|
|||
|
|
bool success = 2;
|
|||
|
|
string message = 3;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 2. 重新生成 Python 代码
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. task.proto
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 3. 增强服务端实现 (server.py)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from concurrent import futures
|
|||
|
|
import grpc
|
|||
|
|
import time
|
|||
|
|
import task_pb2
|
|||
|
|
import task_pb2_grpc
|
|||
|
|
|
|||
|
|
class TaskServiceServicer(task_pb2_grpc.TaskServiceServicer):
|
|||
|
|
def ProcessTask(self, request, context):
|
|||
|
|
print(f"Received task_id: {request.task_id}")
|
|||
|
|
# ... 原有处理逻辑
|
|||
|
|
return task_pb2.TaskResponse(
|
|||
|
|
task_id=request.task_id,
|
|||
|
|
success=True,
|
|||
|
|
message="Task processed successfully"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
class HealthCheckServicer(task_pb2_grpc.HealthCheckServicer):
|
|||
|
|
def Check(self, request, context):
|
|||
|
|
# 简单实现:总是返回SERVING状态
|
|||
|
|
# 实际应用中可以根据服务状态返回不同值
|
|||
|
|
return task_pb2.HealthCheckResponse(
|
|||
|
|
status=task_pb2.HealthCheckResponse.ServingStatus.SERVING
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def serve():
|
|||
|
|
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
|
|||
|
|
|
|||
|
|
# 添加服务实现
|
|||
|
|
task_pb2_grpc.add_TaskServiceServicer_to_server(TaskServiceServicer(), server)
|
|||
|
|
task_pb2_grpc.add_HealthCheckServicer_to_server(HealthCheckServicer(), server)
|
|||
|
|
|
|||
|
|
server.add_insecure_port('[::]:50051')
|
|||
|
|
server.start()
|
|||
|
|
print("Server started, listening on port 50051...")
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
while True:
|
|||
|
|
time.sleep(86400) # 保持运行
|
|||
|
|
except KeyboardInterrupt:
|
|||
|
|
server.stop(0)
|
|||
|
|
|
|||
|
|
if __name__ == '__main__':
|
|||
|
|
serve()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 4. 增强客户端实现 (client.py)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import grpc
|
|||
|
|
import time
|
|||
|
|
import task_pb2
|
|||
|
|
import task_pb2_grpc
|
|||
|
|
|
|||
|
|
def check_server_status(channel):
|
|||
|
|
try:
|
|||
|
|
health_stub = task_pb2_grpc.HealthCheckStub(channel)
|
|||
|
|
response = health_stub.Check(task_pb2.HealthCheckRequest(service="TaskService"))
|
|||
|
|
return response.status == task_pb2.HealthCheckResponse.ServingStatus.SERVING
|
|||
|
|
except grpc.RpcError as e:
|
|||
|
|
print(f"Health check failed: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def run_with_retry(max_retries=3, delay=5):
|
|||
|
|
channel = None
|
|||
|
|
retries = 0
|
|||
|
|
|
|||
|
|
while retries < max_retries:
|
|||
|
|
try:
|
|||
|
|
# 创建通道
|
|||
|
|
channel = grpc.insecure_channel('localhost:50051')
|
|||
|
|
|
|||
|
|
# 检查服务器状态
|
|||
|
|
if not check_server_status(channel):
|
|||
|
|
raise Exception("Server is not healthy")
|
|||
|
|
|
|||
|
|
stub = task_pb2_grpc.TaskServiceStub(channel)
|
|||
|
|
|
|||
|
|
# 创建请求消息
|
|||
|
|
request = task_pb2.TaskRequest(
|
|||
|
|
task_id="d6118954-a170-4e1c-84bd-ddbd3114b354",
|
|||
|
|
sn="8UUXN6S00A0CK7",
|
|||
|
|
content_body=task_pb2.ContentBody(
|
|||
|
|
org_code="HMZHB",
|
|||
|
|
func_id=[101204],
|
|||
|
|
source_url="rtmp://222.212.85.86:1935/live/1581F8HGX253S00A05L8",
|
|||
|
|
push_url="",
|
|||
|
|
confidence=0.4,
|
|||
|
|
para_list=[
|
|||
|
|
task_pb2.ParaList(
|
|||
|
|
func_id=101204,
|
|||
|
|
para_invade_enable=True
|
|||
|
|
)
|
|||
|
|
],
|
|||
|
|
invade=task_pb2.Invade(
|
|||
|
|
invade_file="meta_data/高压线-0826.geojson",
|
|||
|
|
camera_para_url="meta_data/camera_para/hami_camera_para .txt"
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 调用远程方法
|
|||
|
|
response = stub.ProcessTask(request)
|
|||
|
|
print(f"Response: task_id={response.task_id}, success={response.success}, message={response.message}")
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except grpc.RpcError as e:
|
|||
|
|
retries += 1
|
|||
|
|
print(f"RPC error occurred (attempt {retries}/{max_retries}): {e}")
|
|||
|
|
if retries < max_retries:
|
|||
|
|
print(f"Retrying in {delay} seconds...")
|
|||
|
|
time.sleep(delay)
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"Error occurred: {e}")
|
|||
|
|
retries += 1
|
|||
|
|
if retries < max_retries:
|
|||
|
|
print(f"Retrying in {delay} seconds...")
|
|||
|
|
time.sleep(delay)
|
|||
|
|
finally:
|
|||
|
|
if channel:
|
|||
|
|
channel.close()
|
|||
|
|
|
|||
|
|
print("All retry attempts failed")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
if __name__ == '__main__':
|
|||
|
|
run_with_retry()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 5. 增强功能说明
|
|||
|
|
|
|||
|
|
1. 健康检查服务
|
|||
|
|
|
|||
|
|
- 添加了标准的 gRPC 健康检查服务
|
|||
|
|
- 客户端可以在执行主要操作前检查服务状态
|
|||
|
|
|
|||
|
|
2. 错误处理和重试机制
|
|||
|
|
|
|||
|
|
:
|
|||
|
|
|
|||
|
|
- 客户端现在会捕获 `grpc.RpcError` 和其他异常
|
|||
|
|
- 实现了最大重试次数和重试间隔
|
|||
|
|
- 每次重试前都会检查服务器状态
|
|||
|
|
|
|||
|
|
3. 资源管理
|
|||
|
|
|
|||
|
|
- 确保在所有情况下都正确关闭通道
|
|||
|
|
- 使用上下文管理器处理通道生命周期
|
|||
|
|
|
|||
|
|
4. 状态反馈
|
|||
|
|
|
|||
|
|
:
|
|||
|
|
|
|||
|
|
- 提供更详细的错误信息
|
|||
|
|
- 记录重试尝试
|
|||
|
|
|
|||
|
|
## 6. 运行说明
|
|||
|
|
|
|||
|
|
1. 安装依赖:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
pip install grpcio grpcio-tools
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. 生成 protobuf 代码:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. task.proto
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
3. 启动服务器:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python server.py
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
4. 运行客户端:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python client.py
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 7. 测试场景
|
|||
|
|
|
|||
|
|
1. 服务器未运行
|
|||
|
|
- 客户端会检测到连接失败并重试
|
|||
|
|
- 最终显示所有重试失败
|
|||
|
|
2. 服务器运行但健康检查失败
|
|||
|
|
- 可以修改 `HealthCheckServicer` 返回 `NOT_SERVING` 状态进行测试
|
|||
|
|
- 客户端会拒绝执行主要操作
|
|||
|
|
3. 网络中断
|
|||
|
|
- 客户端会捕获异常并尝试重试
|
|||
|
|
|
|||
|
|
这个增强版本提供了更健壮的 gRPC 通讯机制,适合生产环境使用。
|