1. مقدمه
در این کد لبه، شما از gRPC-Go برای ایجاد یک کلاینت و سرور استفاده خواهید کرد که پایه و اساس برنامه نقشه برداری مسیر نوشته شده در Go را تشکیل می دهد.
در پایان آموزش، یک کلاینت خواهید داشت که با استفاده از gRPC به یک سرور راه دور متصل می شود تا نام یا آدرس پستی آنچه در مختصات خاصی روی نقشه قرار دارد را دریافت کند. یک برنامه کاربردی کاملاً پیشرفته ممکن است از این طراحی سرویس گیرنده-سرور برای برشمردن یا خلاصه کردن نقاط مورد علاقه در طول مسیر استفاده کند.
این سرویس در یک فایل Protocol Buffers تعریف شده است که از آن برای تولید کد boilerplate برای کلاینت و سرور استفاده می شود تا بتوانند با یکدیگر ارتباط برقرار کنند و در زمان و تلاش شما در اجرای آن عملکرد صرفه جویی شود.
این کد تولید شده نه تنها از پیچیدگی های ارتباط بین سرور و کلاینت، بلکه سریال سازی و سریال سازی داده ها نیز مراقبت می کند.
چیزی که یاد خواهید گرفت
- نحوه استفاده از بافرهای پروتکل برای تعریف API سرویس.
- نحوه ساخت یک کلاینت و سرور مبتنی بر gRPC از تعریف بافرهای پروتکل با استفاده از تولید کد خودکار.
- درک ارتباط مشتری-سرور با gRPC.
هدف این کد لبه توسعه دهندگان Go است که تازه به gRPC می پردازند یا به دنبال تازه سازی gRPC هستند یا هر فرد دیگری که علاقه مند به ساختن سیستم های توزیع شده است. هیچ تجربه قبلی gRPC مورد نیاز نیست.
2. قبل از شروع
پیش نیازها
مطمئن شوید که موارد زیر را نصب کرده اید:
- Go toolchain نسخه 1.24.5 یا بالاتر. برای دستورالعملهای نصب، به Go's Getting Start مراجعه کنید.
- کامپایلر بافر پروتکل،
protoc
، نسخه 3.27.1 یا بالاتر. برای دستورالعملهای نصب، به راهنمای نصب کامپایلر مراجعه کنید. - افزونه های کامپایلر بافر پروتکل برای Go و gRPC. برای نصب این افزونه ها، دستورات زیر را اجرا کنید:
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
متغیر PATH
خود را به روز کنید تا کامپایلر بافر پروتکل بتواند افزونه ها را پیدا کند:
export PATH="$PATH:$(go env GOPATH)/bin"
کد را دریافت کنید
برای اینکه مجبور نباشید به طور کامل از ابتدا شروع کنید، این کد لبه داربستی از کد منبع برنامه را برای شما فراهم می کند تا آن را تکمیل کنید. مراحل زیر به شما نشان می دهد که چگونه برنامه را تکمیل کنید، از جمله استفاده از افزونه های کامپایلر بافر پروتکل برای تولید کد gRPC دیگ بخار.
این کد منبع را به عنوان یک بایگانی .ZIP از GitHub دانلود کنید و محتوای آن را باز کنید.
متناوبا، اگر می خواهید از تایپ کردن در یک پیاده سازی صرفنظر کنید، کد منبع تکمیل شده در GitHub در دسترس است.
3. سرویس را تعریف کنید
اولین قدم شما این است که سرویس gRPC برنامه، روش RPC آن، و انواع پیام درخواست و پاسخ آن را با استفاده از Protocol Buffers تعریف کنید. خدمات شما ارائه خواهد کرد:
- یک روش RPC به نام
GetFeature
که سرور پیاده سازی می کند و کلاینت فراخوانی می کند. - انواع پیام
Point
وFeature
که ساختارهای داده ای هستند که هنگام استفاده از روشGetFeature
بین مشتری و سرور رد و بدل می شوند. مشتری مختصات نقشه را به عنوان یکPoint
در درخواستGetFeature
خود به سرور ارائه می دهد و سرور با یکFeature
مربوطه پاسخ می دهد که هر چیزی را که در آن مختصات قرار دارد را توصیف می کند.
این روش RPC و انواع پیام های آن در فایل routeguide/route_guide.proto
کد منبع ارائه شده تعریف می شوند.
بافرهای پروتکل معمولاً به عنوان پروتوباف شناخته می شوند. برای اطلاعات بیشتر در مورد اصطلاحات gRPC، به مفاهیم اصلی، معماری و چرخه حیات gRPC مراجعه کنید.
انواع پیام
در فایل routeguide/route_guide.proto
کد منبع، ابتدا نوع پیام Point
را تعریف کنید. یک Point
نشان دهنده یک جفت مختصات طول و عرض جغرافیایی بر روی نقشه است. برای این کد، از اعداد صحیح برای مختصات استفاده کنید:
message Point {
int32 latitude = 1;
int32 longitude = 2;
}
اعداد 1
و 2
شماره های شناسه منحصر به فرد برای هر یک از فیلدهای ساختار message
هستند.
سپس نوع پیام Feature
را تعریف کنید. یک Feature
از یک فیلد string
برای نام یا آدرس پستی چیزی در مکانی مشخص شده توسط یک Point
استفاده می کند:
message Feature {
// The name or address of the feature.
string name = 1;
// The point where the feature is located.
Point location = 2;
}
روش سرویس دهی
فایل route_guide.proto
دارای یک ساختار service
به نام RouteGuide
است که یک یا چند روش ارائه شده توسط سرویس برنامه را تعریف می کند.
روش rpc
GetFeature
در تعریف RouteGuide
اضافه کنید. همانطور که قبلاً توضیح داده شد، این روش نام یا آدرس یک مکان را از مجموعه مختصات معینی جستجو می کند، بنابراین از GetFeature
بخواهید یک Feature
برای یک Point
داده شده برگرداند:
service RouteGuide {
// Definition of the service goes here
// Obtains the feature at a given position.
rpc GetFeature(Point) returns (Feature) {}
}
این یک روش RPC یکنواخت است: یک RPC ساده که در آن کلاینت درخواستی را به سرور ارسال می کند و منتظر می ماند تا یک پاسخ بازگردد، درست مانند یک فراخوانی تابع محلی.
4. کد کلاینت و سرور را تولید کنید
سپس، کد gRPC را برای مشتری و سرور از فایل .proto
با استفاده از کامپایلر بافر پروتکل تولید کنید. در فهرست routeguide
، اجرا کنید:
protoc --go_out=. --go_opt=paths=source_relative \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \ route_guide.proto
این دستور فایل های زیر را تولید می کند:
-
route_guide.pb.go
، که شامل توابعی برای ایجاد انواع پیام های برنامه و دسترسی به داده های آن است. -
route_guide_grpc.pb.go
، که شامل توابعی است که کلاینت برای فراخوانی روش gRPC راه دور سرویس استفاده می کند، و توابعی که توسط سرور برای ارائه آن سرویس راه دور استفاده می شود.
در مرحله بعد، ما متد GetFeature
را در سمت سرور پیاده سازی می کنیم، به طوری که وقتی مشتری درخواستی را ارسال می کند، سرور بتواند با یک پاسخ پاسخ دهد.
5. سرویس را پیاده سازی کنید
تابع GetFeature
در سمت سرور جایی است که کار اصلی انجام می شود: این یک پیام Point
از مشتری می گیرد و در یک پیام Feature
اطلاعات مکان مربوطه را از لیست مکان های شناخته شده برمی گرداند. در اینجا اجرای تابع در server/server.go
آمده است:
func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) {
for _, feature := range s.savedFeatures {
if proto.Equal(feature.Location, point) {
return feature, nil
}
}
// No feature was found, return an unnamed feature
return &pb.Feature{Location: point}, nil
}
هنگامی که این روش به دنبال درخواستی از یک کلاینت راه دور فراخوانی می شود، تابع یک شی Context
که فراخوانی RPC را توصیف می کند، و یک شی بافر پروتکل Point
از آن درخواست مشتری ارسال می شود. تابع یک شی بافر پروتکل Feature
را برای مکان جستجو شده و در صورت لزوم یک error
برمی گرداند.
در این روش، یک شی Feature
را با اطلاعات مناسب برای Point
داده شده پر کنید، و سپس آن را همراه با یک خطای nil
return
تا به gRPC بگویید که کار با RPC را تمام کردهاید و اینکه شی Feature
میتواند به مشتری بازگردانده شود.
متد GetFeature
نیاز دارد که یک شی routeGuideServer
ایجاد و ثبت شود تا درخواستهای مشتریان برای جستجوی مکان به آن تابع هدایت شوند. این در main()
انجام می شود:
func main() {
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
var opts []grpc.ServerOption
grpcServer := grpc.NewServer(opts...)
s := &routeGuideServer{}
s.loadFeatures()
pb.RegisterRouteGuideServer(grpcServer, s)
grpcServer.Serve(lis)
}
در اینجا آنچه در main()
اتفاق می افتد، گام به گام است:
- پورت TCP را برای گوش دادن به درخواست های کلاینت راه دور با استفاده از
lis, err := net.Listen(...)
مشخص کنید. به طور پیش فرض، برنامه از پورت TCP50051
همانطور که توسطport
متغیر مشخص شده یا با عبور دادن سوئیچ--port
در خط فرمان هنگام اجرای سرور استفاده می کند. اگر پورت TCP باز نشود، برنامه با یک خطای مرگبار به پایان می رسد. - یک نمونه از سرور gRPC با استفاده از
grpc.NewServer(...)
ایجاد کنید و نام این نمونه راgrpcServer
بگذارید. - یک اشاره گر به
routeGuideServer
، ساختاری که سرویس API برنامه را نشان می دهد، ایجاد کنید و اشاره گر راs.
- از
s.loadFeatures()
برای پر کردن آرایهs.savedFeatures
با مکان هایی استفاده کنید که می توان از طریقGetFeature
جستجو کرد. - سرویس API را با سرور gRPC ثبت کنید تا تماسهای RPC به
GetFeature
به تابع مناسب هدایت شوند. -
Serve()
روی سرور با جزئیات پورت ما فراخوانی کنید تا منتظر بلاک کردن درخواست های مشتری باشد. این تا زمانی ادامه می یابد که فرآیند کشته شود یاStop()
فراخوانی شود.
تابع loadFeatures()
نگاشت مختصات به مکان خود را از server/testdata.go
دریافت می کند.
6. مشتری ایجاد کنید
اکنون client/client.go
را ویرایش کنید، جایی که کد مشتری را پیاده سازی خواهید کرد.
برای فراخوانی متدهای سرویس راه دور، ابتدا باید یک کانال gRPC برای ارتباط با سرور ایجاد کنیم. ما این را با ارسال رشته URI هدف سرور (که در این مورد فقط آدرس و شماره پورت است) به grpc.NewClient()
در تابع main()
کلاینت به صورت زیر ایجاد می کنیم:
conn, err := grpc.NewClient("dns:///"+*serverAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("fail to dial: %v", err)
}
defer conn.Close()
آدرس سرور که توسط متغیر serverAddr
تعریف میشود، بهطور پیشفرض localhost:50051
است و هنگام اجرای کلاینت میتواند توسط سوئیچ --addr
در خط فرمان لغو شود.
اگر سرویس گیرنده نیاز به اتصال به سرویسی داشته باشد که به اعتبارنامههای احراز هویت مانند اعتبارنامههای TLS یا JWT نیاز دارد، مشتری میتواند یک شی DialOptions
را به عنوان پارامتر به grpc.NewClient
ارسال کند که حاوی اعتبارنامههای مورد نیاز است. سرویس RouteGuide
به هیچ اعتباری نیاز ندارد.
هنگامی که کانال gRPC راهاندازی شد، برای انجام RPCها از طریق فراخوانی تابع Go به یک کلاینت نیاز داریم. ما آن خرد را با استفاده از روش NewRouteGuideClient
که توسط فایل route_guide_grpc.pb.go
تولید شده از فایل .proto
برنامه ارائه شده است، دریافت می کنیم.
import (pb "github.com/grpc-ecosystem/codelabs/getting_started_unary/routeguide")
client := pb.NewRouteGuideClient(conn)
روش های خدمات تماس
در gRPC-Go، RPC ها در حالت مسدود/همگام عمل می کنند، به این معنی که تماس RPC منتظر پاسخ سرور می ماند و یا یک پاسخ یا یک خطا را برمی گرداند.
RPC ساده
فراخوانی RPC GetFeature
ساده تقریباً به اندازه فراخوانی یک متد محلی است، در این مورد client.GetFeature
:
point := &pb.Point{Latitude: 409146138, Longitude: -746188906}
log.Printf("Getting feature for point (%d, %d)", point.Latitude, point.Longitude)
// Call GetFeature method on the client.
feature, err := client.GetFeature(context.TODO(), point)
if err != nil {
log.Fatalf("client.GetFeature failed: %v", err)
}
کلاینت متد موجود در خرد ایجاد شده قبلی را فراخوانی می کند. برای پارامترهای متد، مشتری یک شی بافر پروتکل درخواست Point
ایجاد و پر می کند. شما همچنین یک شی context.Context
را ارسال می کنید که به ما امکان می دهد در صورت لزوم رفتار RPC خود را تغییر دهیم، مانند تعیین محدودیت زمانی برای تماس یا لغو یک RPC در پرواز. اگر تماس خطایی را برنگرداند، مشتری میتواند اطلاعات پاسخ را از سرور از اولین مقدار بازگشتی بخواند:
log.Println(feature)
در کل تابع main()
کلاینت باید به شکل زیر باشد:
func main() {
flag.Parse()
// Set up a connection to the gRPC server.
conn, err := grpc.NewClient("dns:///"+*serverAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("fail to dial: %v", err)
}
defer conn.Close()
// Create a new RouteGuide stub.
client := pb.NewRouteGuideClient(conn)
point := &pb.Point{Latitude: 409146138, Longitude: -746188906}
log.Printf("Getting feature for point (%d, %d)", point.Latitude, point.Longitude)
// Call GetFeature method on the client.
feature, err := client.GetFeature(context.TODO(), point)
if err != nil {
log.Fatalf("client.GetFeature failed: %v", err)
}
log.Println(feature)
}
7. آن را امتحان کنید
با اجرای دستورات زیر در دایرکتوری کاری برنامه، تأیید کنید که سرور و کلاینت به درستی با یکدیگر کار می کنند:
- سرور را در یک ترمینال اجرا کنید:
cd server go run .
- کلاینت را از ترمینال دیگری اجرا کنید:
cd client go run .
خروجی را به صورت زیر مشاهده خواهید کرد که برای وضوح از مهرهای زمانی حذف شده است:
Getting feature for point (409146138, -746188906)
name:"Berkshire Valley Management Area Trail, Jefferson, NJ, USA" location:<latitude:409146138 longitude:-746188906 >
Getting feature for point (0, 0)
location:<>
8. بعدی چه خواهد شد
- نحوه عملکرد gRPC را در مقدمه مفاهیم gRPC و Core بیاموزید
- از طریق آموزش اصول کار کنید
- مرجع API را کاوش کنید