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 通讯机制,适合生产环境使用。 |