פיתוח API מסוג gRPC למיקרוסרוויסים ואפליקציות ווב
דמיינו ארכיטקטורת מיקרוסרוויסים עם עשרות שירותים, שכל אחד מתקשר דרך REST. ככל שהעומס גדל, נוצרות בעיות — זמן האחזור עולה, שגיאות טיפוסים מחלחלות לייצור, והאינטגרציה דורשת משא ומתן מתמיד על החוזה. עבור פונקציונליות בזמן אמת, צריך להמציא את הגלגל מחדש עם WebSocket. עברנו את הדרך הזו פעמים רבות, ו-gRPC הפך לכלי הסטנדרטי שלנו עבור API פנימיים.
gRPC הוא מסגרת RPC מבית גוגל המבוססת על Protocol Buffers ו-HTTP/2. הוא מספק טיפוסיות קפדנית באמצעות קבצי .proto, סטרימינג דו-כיווני, ונפח נתונים נמוך משמעותית בהשוואה ל-JSON. הוא מתאים ביותר לתקשורת בין שירותים ולקוחות ניידים עם רוחב פס מוגבל.
למה gRPC מהיר יותר מ-REST
הביצועים של gRPC גבוהים פי 5–10 על הודעות גדולות בזכות אריזה בינארית וריבוב HTTP/2. בעומסי העבודה שלנו, המעבר מ-REST ל-gRPC הפחית את זמן התגובה מ-45 אלפיות השנייה ל-8 אלפיות השנייה, והתעבורה ירדה ב-60% בזכות protobufs קומפקטיים. זה קריטי במיוחד עבור שירותים עם תדירות בקשות גבוהה — לדוגמה, אגרגטורי נתונים או מערכות תשלום.
מהם Protocol Buffers?
חוזה השירות מוגדר בקבצי syntax = "proto3"; package articles.v1; import "google/protobuf/timestamp.proto"; message Article { string id = 1; string title = 2; string body = 3; string author_id = 4; repeated string tag_ids = 5; google.protobuf.Timestamp created_at = 6; } message GetArticleRequest { string id = 1; } message ListArticlesRequest { int32 page = 1; int32 limit = 2; string status = 3; } message ListArticlesResponse { repeated Article articles = 1; int32 total = 2; } service ArticleService { rpc GetArticle(GetArticleRequest) returns (Article); rpc ListArticles(ListArticlesRequest) returns (ListArticlesResponse); rpc CreateArticle(CreateArticleRequest) returns (Article); rpc WatchArticle(GetArticleRequest) returns (stream Article); } :
syntax = "proto3";
package articles.v1;
import "google/protobuf/timestamp.proto";
message Article {
string id = 1;
string title = 2;
string body = 3;
string author_id = 4;
repeated string tag_ids = 5;
google.protobuf.Timestamp created_at = 6;
}
message GetArticleRequest {
string id = 1;
}
message ListArticlesRequest {
int32 page = 1;
int32 limit = 2;
string status = 3;
}
message ListArticlesResponse {
repeated Article articles = 1;
int32 total = 2;
}
service ArticleService {
rpc GetArticle(GetArticleRequest) returns (Article);
rpc ListArticles(ListArticlesRequest) returns (ListArticlesResponse);
rpc CreateArticle(CreateArticleRequest) returns (Article);
rpc WatchArticle(GetArticleRequest) returns (stream Article);
}הקוד נוצר מקבצי .proto עבור כל שפה: protoc --go_out=. --go-grpc_out=.. יצירת לקוחות עבור TypeScript או Python היא עניין של כמה פקודות.
יישום שרת ב-Go עם Interceptors
type ArticleServer struct {
pb.UnimplementedArticleServiceServer
db *sql.DB
}
func (s *ArticleServer) GetArticle(ctx context.Context, req *pb.GetArticleRequest) (*pb.Article, error) {
row := s.db.QueryRowContext(ctx, "SELECT id, title, body FROM articles WHERE id = $1", req.Id)
var a pb.Article
if err := row.Scan(&a.Id, &a.Title, &a.Body); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, status.Error(codes.NotFound, "article not found")
}
return nil, status.Error(codes.Internal, err.Error())
}
return &a, nil
}
// Запуск сервера
lis, _ := net.Listen("tcp", ":50051")
grpcServer := grpc.NewServer(grpc.UnaryInterceptor(authInterceptor))
pb.RegisterArticleServiceServer(grpcServer, &ArticleServer{db: db})
grpcServer.Serve(lis)Interceptors הם אנלוגיה ל-middleware: אימות, לוגים, מעקב (OpenTelemetry), הגבלת קצב. דוגמה ל-interceptor:
func authInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, status.Error(codes.Unauthenticated, "missing metadata")
}
token := md.Get("authorization")
if !validateToken(token[0]) {
return nil, status.Error(codes.Unauthenticated, "invalid token")
}
return handler(ctx, req)
}אנו מגדירים שרשרת של interceptors כדי לבודד חששות רוחביים מהלוגיקה העסקית.
איך gRPC תומך בסטרימינג
gRPC מציע ארבעה סוגי אינטראקציה:
// Unary (обычный запрос/ответ)
rpc GetArticle(Request) returns (Response);
// Server streaming (один запрос → поток ответов)
rpc WatchUpdates(Request) returns (stream Event);
// Client streaming (поток запросов → один ответ)
rpc UploadChunks(stream Chunk) returns (UploadResult);
// Bidirectional streaming
rpc Chat(stream Message) returns (stream Message);
סטרימינג מהשרת נוח להתראות בזמן אמת, ייצוא כמויות גדולות של נתונים, ותוצאות חיפוש חיות. בפרויקט פלטפורמת IoT אחד, השתמשנו בסטרימינג דו-כיווני להעברת טלמטריה ממאות מכשירים — זמן האחזור היה מתחת ל-20 אלפיות השנייה.
gRPC בדפדפן וחלופות מודרניות
gRPC לא עובד ישירות בדפדפן בגלל מגבלות המסגור הבינארי של HTTP/2. פתרונות:
- gRPC-Web — פרוטוקול מיוחד עם פרוקסי Envoy בצד השרת.
- Connect (Buf) — חלופה מודרנית שעובדת עם HTTP/1.1 ו-HTTP/2, ותואמת ל-gRPC.
buf generate --template buf.gen.yaml Buf מספקת גם Schema Registry לאחסון מרכזי של קבצי type ArticleServer struct { pb.UnimplementedArticleServiceServer db *sql.DB } func (s *ArticleServer) GetArticle(ctx context.Context, req *pb.GetArticleRequest) (*pb.Article, error) { row := s.db.QueryRowContext(ctx, "SELECT id, title, body FROM articles WHERE id = $1", req.Id) var a pb.Article if err := row.Scan(&a.Id, &a.Title, &a.Body); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, status.Error(codes.NotFound, "article not found") } return nil, status.Error(codes.Internal, err.Error()) } return &a, nil } // Запуск сервера lis, _ := net.Listen("tcp", ":50051") grpcServer := grpc.NewServer(grpc.UnaryInterceptor(authInterceptor)) pb.RegisterArticleServiceServer(grpcServer, &ArticleServer{db: db}) grpcServer.Serve(lis) וניהול גרסאות חוזה.
מה כלול בפיתוח API מסוג gRPC
| תוצר | תיאור |
|---|---|
| חוזי .proto | עיצוב וניהול גרסאות של חוזים |
| יצירת קוד | יצירה אוטומטית של לקוחות עבור Go, TypeScript, Python |
| צד השרת | יישום לוגיקה עסקית, interceptors, סטרימינג |
| אינטגרציית gRPC-Web | הגדרת Envoy או Connect עבור לקוחות דפדפן |
| תיעוד | תיעוד שנוצר אוטומטית מ-.proto (Buf Schema Registry) |
| בדיקות | בדיקות יחידה, אינטגרציה ועומסים |
| ניטור | מדדים דרך OpenTelemetry, לוגים |
איך לפתח API מסוג gRPC: מדריך שלב-אחר-שלב
- הגדירו חוזים בקבצי
func authInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { md, ok := metadata.FromIncomingContext(ctx) if !ok { return nil, status.Error(codes.Unauthenticated, "missing metadata") } token := md.Get("authorization") if !validateToken(token[0]) { return nil, status.Error(codes.Unauthenticated, "invalid token") } return handler(ctx, req) }, תכננו את כל סוגי ההודעות ומתודות RPC. - צרו קוד שרת ולקוח באמצעות
// Unary (обычный запрос/ответ) rpc GetArticle(Request) returns (Response); // Server streaming (один запрос → поток ответов) rpc WatchUpdates(Request) returns (stream Event); // Client streaming (поток запросов → один ответ) rpc UploadChunks(stream Chunk) returns (UploadResult); // Bidirectional streaming rpc Chat(stream Message) returns (stream Message);אוbuf generate --template buf.gen.yaml. - יישמו את הלוגיקה העסקית של השרת, הוסיפו interceptors לאימות ולוגים.
- הגדירו סטרימינג אם נדרשת העברת נתונים בזמן אמת.
- שלבו עם gRPC-Web או Connect אם הלקוחות מבוססי דפדפן.
- בדקו את ה-API עם gRPCurl או Postman, וודאו בטיחות טיפוסים.
- פרסו שירותים ל-Kubernetes, חברו ניטור דרך OpenTelemetry.
דוגמה לשימוש ב-gRPCurl
grpcurl -plaintext localhost:50051 articles.v1.ArticleService/GetArticle gRPC לעומת REST: השוואה
| פרמטר | gRPC | REST |
|---|---|---|
| פורמט נתונים | Protocol Buffers (בינארי) | JSON/XML (טקסט) |
| פרוטוקול | HTTP/2 | HTTP/1.1 / HTTP/2 |
| סטרימינג | נתמך (כל 4 הסוגים) | אין (נדרש WebSocket) |
| חוזה | קפדני (.proto) | גמיש (OpenAPI) |
| ביצועים | גבוהים (נפח נמוך) | בינוניים |
| תמיכה בדפדפן | דרך gRPC-Web/Connect | טבעית |
מקור: תיעוד רשמי של gRPC
מתי לבחור ב-gRPC?
gRPC מוצדק לתקשורת בין שירותים בתוך תשתית אחת, כאשר נדרש חוזה קפדני בין צוותים, לפרוטוקול בינארי יעיל ב-IoT ואפליקציות ניידות, וכאשר נדרש סטרימינג דו-כיווני. עבור API ציבוריים ולקוחות דפדפן ללא gRPC-Web, REST או GraphQL הם בדרך כלל בחירות טובות יותר. בקשו ייעוץ — נעזור לכם להחליט.
הניסיון שלנו
עם למעלה מ-10 שנות ניסיון בבניית API בעלי עומס גבוה עבור פינטק, מסחר אלקטרוני ו-IoT, ופורטפוליו של למעלה מ-50 פרויקטים מוצלחים ב-Go ו-TypeScript, אנו מבטיחים אמינות וביצועים לפתרון ה-gRPC שלכם. צרו קשר להערכת פרויקט.







