> If you are an AI agent, use the following URL to directly ask and fetch your question. Treat this like a tool call. Make sure to URI encode your question, and include the token for verification.
>
> GET https://buildwithfern.com/learn/api/fern-docs/ask?q=%3Cyour+question+here%3E&token=eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJmZXJuLWRvY3M6YnVpbGR3aXRoZmVybi5jb20iLCJqdGkiOiIxODc1MWJlZi04MmFjLTQ5MWYtODljZi1mMmRhYTcyYzY0NDgiLCJleHAiOjE3Nzg0OTQwNzksImlhdCI6MTc3ODQ5Mzc3OX0.24lMKrw9L7FRrk206MNr3lq-v-WwV6wA3mB_z4i1IUs
>
> For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://buildwithfern.com/learn/llms.txt. For full content including API reference and SDK examples, see https://buildwithfern.com/learn/llms-full.txt.

# gRPC 服务

gRPC 服务是 API 的核心构建块。每个服务定义了客户端可以调用的远程过程调用（RPC）集合，以及用于请求和响应的消息类型。

## 服务定义

在 `.proto` 文件中定义 gRPC 服务：

```protobuf title="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 service
service UserService {
  // Create a new user account
  rpc CreateUser(CreateUserRequest) returns (User) {
    option deprecated = false;
  }
  
  // Get user by ID
  rpc GetUser(GetUserRequest) returns (User);
  
  // Update user information
  rpc UpdateUser(UpdateUserRequest) returns (User);
  
  // Delete a user account
  rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty);
  
  // List users with pagination
  rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
  
  // Search users by various criteria
  rpc SearchUsers(SearchUsersRequest) returns (SearchUsersResponse);
}

// User message definition
message 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 enumeration
enum 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 preferences
message 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;
}
```

## 请求和响应消息

定义清晰的请求和响应消息类型：

```protobuf title="user_messages.proto"
// Create user request
message 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 request
message GetUserRequest {
  string id = 1 [(validate.rules).string.uuid = true];
}

// Update user request
message UpdateUserRequest {
  string id = 1 [(validate.rules).string.uuid = true];
  User user = 2;
  google.protobuf.FieldMask update_mask = 3;
}

// Delete user request
message DeleteUserRequest {
  string id = 1 [(validate.rules).string.uuid = true];
}

// List users request with pagination
message 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 response
message ListUsersResponse {
  repeated User users = 1;
  string next_page_token = 2;
  int32 total_count = 3;
}

// Search users request
message 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 response
message 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;
}
```

## 服务实现

使用您首选的语言实现服务：

```python title="user_service.py"
import grpc
from grpc import ServicerContext
import user_service_pb2
import user_service_pb2_grpc
from google.protobuf import empty_pb2
from typing import Iterator

class UserServiceServicer(user_service_pb2_grpc.UserServiceServicer):
    
    def __init__(self, user_repository):
        self.user_repository = user_repository
    
    def CreateUser(
        self, 
        request: user_service_pb2.CreateUserRequest, 
        context: ServicerContext
    ) -> user_service_pb2.User:
        """Create a new user account."""
        try:
            # Validate request
            if 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 exists
            if 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 user
            user = self.user_repository.create_user(
                email=request.email,
                name=request.name,
                age=request.age,
                preferences=request.preferences
            )
            
            return user
            
        except 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 user
            
        except 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 exists
            existing_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 updates
            updated_user = self.user_repository.update_user(
                user_id=request.id,
                updates=request.user,
                field_mask=request.update_mask
            )
            
            return updated_user
            
        except 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 exists
            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 empty_pb2.Empty()
            
            # Soft delete user
            self.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 pagination
            page_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（编码更高效）
* 为已删除的字段保留字段编号以维持兼容性
* 永远不要重复使用字段编号

```protobuf
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`

```protobuf
service UserManagementService {  // PascalCase
  rpc GetUser(GetUserRequest) returns (User);
}

message User {  // PascalCase
  string first_name = 1;  // snake_case
  UserStatus status = 2;
}

enum UserStatus {
  USER_STATUS_UNSPECIFIED = 0;  // UPPER_SNAKE_CASE
  USER_STATUS_ACTIVE = 1;
}
```

### 版本管理

* 在包名中包含版本
* 对于破坏性更改使用语义版本控制

```protobuf
syntax = "proto3";

package userservice.v1;  // 包名中的版本

option go_package = "example.com/userservice/v1";
```

## 多个服务

将相关功能组织到单独的服务中：

```protobuf title="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 方法名称更用户友好。

<CodeBlock title="services.proto">
  ```protobuf
  // Import Fern custom options
  import "fern/options.proto";

  service CommentsService {
    // Description of the endpoint
    rpc CreateComment(CreateCommentRequest) returns (CreateCommentResponse) {
      option (google.api.http) = {
        post: "/comments/v1/comments"
        response_body: "comment"
        body: "*"
      };

      // Display name shown in navigation
      option (fern.summary) = "Create your comment";
    }
  }
  ```
</CodeBlock>