gRPC 服务
使用 RPC、消息和 Protocol Buffer 模式定义 gRPC 服务
gRPC 服务是 API 的核心构建块。每个服务定义了客户端可以调用的远程过程调用(RPC)集合,以及用于请求和响应的消息类型。
服务定义
在 .proto 文件中定义 gRPC 服务:
user_service.proto
syntax = "proto3";package userservice.v1;import "google/protobuf/empty.proto";import "google/protobuf/timestamp.proto";import "google/protobuf/field_mask.proto";// User management serviceservice UserService {// Create a new user accountrpc CreateUser(CreateUserRequest) returns (User) {option deprecated = false;}// Get user by IDrpc GetUser(GetUserRequest) returns (User);// Update user informationrpc UpdateUser(UpdateUserRequest) returns (User);// Delete a user accountrpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty);// List users with paginationrpc ListUsers(ListUsersRequest) returns (ListUsersResponse);// Search users by various criteriarpc SearchUsers(SearchUsersRequest) returns (SearchUsersResponse);}// User message definitionmessage User {string id = 1;string email = 2;string name = 3;int32 age = 4;UserStatus status = 5;google.protobuf.Timestamp created_at = 6;google.protobuf.Timestamp updated_at = 7;repeated string roles = 8;UserPreferences preferences = 9;}// User status enumerationenum UserStatus {USER_STATUS_UNSPECIFIED = 0;USER_STATUS_ACTIVE = 1;USER_STATUS_INACTIVE = 2;USER_STATUS_SUSPENDED = 3;USER_STATUS_DELETED = 4;}// Nested message for user preferencesmessage UserPreferences {bool email_notifications = 1;string timezone = 2;string language = 3;ThemeMode theme = 4;}enum ThemeMode {THEME_MODE_UNSPECIFIED = 0;THEME_MODE_LIGHT = 1;THEME_MODE_DARK = 2;THEME_MODE_AUTO = 3;}
请求和响应消息
定义清晰的请求和响应消息类型:
user_messages.proto
// Create user requestmessage CreateUserRequest {string email = 1 [(validate.rules).string.email = true];string name = 2 [(validate.rules).string.min_len = 1];int32 age = 3 [(validate.rules).int32.gte = 0];UserPreferences preferences = 4;}// Get user requestmessage GetUserRequest {string id = 1 [(validate.rules).string.uuid = true];}// Update user requestmessage UpdateUserRequest {string id = 1 [(validate.rules).string.uuid = true];User user = 2;google.protobuf.FieldMask update_mask = 3;}// Delete user requestmessage DeleteUserRequest {string id = 1 [(validate.rules).string.uuid = true];}// List users request with paginationmessage ListUsersRequest {int32 page_size = 1 [(validate.rules).int32 = {gte: 1, lte: 100}];string page_token = 2;string filter = 3;string order_by = 4;}// List users responsemessage ListUsersResponse {repeated User users = 1;string next_page_token = 2;int32 total_count = 3;}// Search users requestmessage SearchUsersRequest {string query = 1 [(validate.rules).string.min_len = 1];repeated UserStatus status_filter = 2;repeated string role_filter = 3;int32 page_size = 4 [(validate.rules).int32 = {gte: 1, lte: 100}];string page_token = 5;}// Search users responsemessage SearchUsersResponse {repeated User users = 1;string next_page_token = 2;int32 total_count = 3;SearchMetadata metadata = 4;}message SearchMetadata {int32 search_time_ms = 1;repeated string suggested_corrections = 2;}
服务实现
使用您首选的语言实现服务:
user_service.py
import grpcfrom grpc import ServicerContextimport user_service_pb2import user_service_pb2_grpcfrom google.protobuf import empty_pb2from typing import Iteratorclass UserServiceServicer(user_service_pb2_grpc.UserServiceServicer):def __init__(self, user_repository):self.user_repository = user_repositorydef CreateUser(self,request: user_service_pb2.CreateUserRequest,context: ServicerContext) -> user_service_pb2.User:"""Create a new user account."""try:# Validate requestif not request.email or not request.name:context.set_code(grpc.StatusCode.INVALID_ARGUMENT)context.set_details('Email and name are required')return user_service_pb2.User()# Check if user already existsif self.user_repository.get_by_email(request.email):context.set_code(grpc.StatusCode.ALREADY_EXISTS)context.set_details(f'User with email {request.email} already exists')return user_service_pb2.User()# Create useruser = self.user_repository.create_user(email=request.email,name=request.name,age=request.age,preferences=request.preferences)return userexcept Exception as e:context.set_code(grpc.StatusCode.INTERNAL)context.set_details(f'Failed to create user: {str(e)}')return user_service_pb2.User()def GetUser(self,request: user_service_pb2.GetUserRequest,context: ServicerContext) -> user_service_pb2.User:"""Get user by ID."""try:user = self.user_repository.get_by_id(request.id)if not user:context.set_code(grpc.StatusCode.NOT_FOUND)context.set_details(f'User with ID {request.id} not found')return user_service_pb2.User()return userexcept Exception as e:context.set_code(grpc.StatusCode.INTERNAL)context.set_details(f'Failed to get user: {str(e)}')return user_service_pb2.User()def UpdateUser(self,request: user_service_pb2.UpdateUserRequest,context: ServicerContext) -> user_service_pb2.User:"""Update user information."""try:# Check if user existsexisting_user = self.user_repository.get_by_id(request.id)if not existing_user:context.set_code(grpc.StatusCode.NOT_FOUND)context.set_details(f'User with ID {request.id} not found')return user_service_pb2.User()# Apply field mask for partial updatesupdated_user = self.user_repository.update_user(user_id=request.id,updates=request.user,field_mask=request.update_mask)return updated_userexcept Exception as e:context.set_code(grpc.StatusCode.INTERNAL)context.set_details(f'Failed to update user: {str(e)}')return user_service_pb2.User()def DeleteUser(self,request: user_service_pb2.DeleteUserRequest,context: ServicerContext) -> empty_pb2.Empty:"""Delete a user account."""try:# Check if user existsuser = self.user_repository.get_by_id(request.id)if not user:context.set_code(grpc.StatusCode.NOT_FOUND)context.set_details(f'User with ID {request.id} not found')return empty_pb2.Empty()# Soft delete userself.user_repository.delete_user(request.id)return empty_pb2.Empty()except Exception as e:context.set_code(grpc.StatusCode.INTERNAL)context.set_details(f'Failed to delete user: {str(e)}')return empty_pb2.Empty()def ListUsers(self,request: user_service_pb2.ListUsersRequest,context: ServicerContext) -> user_service_pb2.ListUsersResponse:"""List users with pagination."""try:# Apply paginationpage_size = min(request.page_size or 20, 100)users, next_page_token, total_count = self.user_repository.list_users(page_size=page_size,page_token=request.page_token,filter_expr=request.filter,order_by=request.order_by)return user_service_pb2.ListUsersResponse(users=users,next_page_token=next_page_token,total_count=total_count)except Exception as e:context.set_code(grpc.StatusCode.INTERNAL)context.set_details(f'Failed to list users: {str(e)}')return user_service_pb2.ListUsersResponse()def SearchUsers(self,request: user_service_pb2.SearchUsersRequest,context: ServicerContext) -> user_service_pb2.SearchUsersResponse:"""Search users by various criteria."""try:start_time = time.time()users, next_page_token, total_count = self.user_repository.search_users(query=request.query,status_filter=request.status_filter,role_filter=request.role_filter,page_size=request.page_size or 20,page_token=request.page_token)search_time_ms = int((time.time() - start_time) * 1000)metadata = user_service_pb2.SearchMetadata(search_time_ms=search_time_ms,suggested_corrections=[] # Add spell check suggestions if needed)return user_service_pb2.SearchUsersResponse(users=users,next_page_token=next_page_token,total_count=total_count,metadata=metadata)except Exception as e:context.set_code(grpc.StatusCode.INTERNAL)context.set_details(f'Failed to search users: {str(e)}')return user_service_pb2.SearchUsersResponse()
Protocol Buffer 最佳实践
字段编号
- 对于频繁使用的字段使用字段编号 1-15(编码更高效)
- 为已删除的字段保留字段编号以维持兼容性
- 永远不要重复使用字段编号
message User {// 频繁使用的字段 (1-15)string id = 1;string email = 2;string name = 3;// 不太频繁使用的字段UserPreferences preferences = 16;repeated string tags = 17;// 保留字段reserved 4, 5, 6;reserved "old_field_name", "deprecated_field";}
命名规范
- 字段名使用
snake_case - 消息和服务名使用
PascalCase - 枚举值使用
UPPER_SNAKE_CASE
service UserManagementService { // PascalCaserpc GetUser(GetUserRequest) returns (User);}message User { // PascalCasestring first_name = 1; // snake_caseUserStatus status = 2;}enum UserStatus {USER_STATUS_UNSPECIFIED = 0; // UPPER_SNAKE_CASEUSER_STATUS_ACTIVE = 1;}
版本管理
- 在包名中包含版本
- 对于破坏性更改使用语义版本控制
syntax = "proto3";package userservice.v1; // 包名中的版本option go_package = "example.com/userservice/v1";
多个服务
将相关功能组织到单独的服务中:
services.proto
// 用户管理service UserService {rpc CreateUser(CreateUserRequest) returns (User);rpc GetUser(GetUserRequest) returns (User);}// 身份验证service AuthService {rpc Login(LoginRequest) returns (LoginResponse);rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse);}// 通知服务service NotificationService {rpc SendNotification(SendNotificationRequest) returns (SendNotificationResponse);rpc GetNotificationPreferences(GetNotificationPreferencesRequest) returns (NotificationPreferences);}
gRPC 服务为构建分布式系统提供了强类型、高性能的基础,在客户端和服务器之间建立了清晰的合约。
自定义选项
Fern 提供自定义 Protocol Buffer 选项来增强您的 API 参考文档和生成的代码。
API 导航名称
使用 fern.summary 选项为端点设置明确的显示名称,比默认的 RPC 方法名称更用户友好。
services.proto
// Import Fern custom optionsimport "fern/options.proto";service CommentsService {// Description of the endpointrpc CreateComment(CreateCommentRequest) returns (CreateCommentResponse) {option (google.api.http) = {post: "/comments/v1/comments"response_body: "comment"body: "*"};// Display name shown in navigationoption (fern.summary) = "Create your comment";}}