1. مقدمه
در این کد لبه، شما از gRPC-Java برای ایجاد یک سرویس گیرنده و سرور استفاده خواهید کرد که پایه و اساس برنامه نقشه برداری مسیر نوشته شده در جاوا را تشکیل می دهد.
در پایان آموزش، یک کلاینت خواهید داشت که با استفاده از gRPC به یک سرور راه دور متصل می شود تا نام یا آدرس پستی آنچه در مختصات خاصی روی نقشه قرار دارد را دریافت کند. یک برنامه کاربردی کاملاً پیشرفته ممکن است از این طراحی سرویس گیرنده-سرور برای برشمردن یا خلاصه کردن نقاط مورد علاقه در طول مسیر استفاده کند.
API سرور در یک فایل Protocol Buffers تعریف شده است که برای تولید کد boilerplate برای کلاینت و سرور استفاده می شود تا بتوانند با یکدیگر ارتباط برقرار کنند و در زمان و تلاش شما در اجرای آن عملکرد صرفه جویی می شود.
این کد تولید شده نه تنها از پیچیدگی های ارتباط بین سرور و کلاینت، بلکه سریال سازی و سریال سازی داده ها نیز مراقبت می کند.
چیزی که یاد خواهید گرفت
- نحوه استفاده از بافرهای پروتکل برای تعریف API سرویس.
- نحوه ساخت یک کلاینت و سرور مبتنی بر gRPC از تعریف بافرهای پروتکل با استفاده از تولید کد خودکار.
- درک ارتباط مشتری-سرور با gRPC.
این کد لبه برای توسعه دهندگان جاوا که تازه به gRPC می پردازند یا به دنبال تازه سازی gRPC هستند یا هرکس دیگری که علاقه مند به ساختن سیستم های توزیع شده است، طراحی شده است. هیچ تجربه قبلی gRPC مورد نیاز نیست.
2. قبل از شروع
پیش نیازها
- JDK نسخه 8 یا بالاتر
کد را دریافت کنید
برای اینکه مجبور نباشید به طور کامل از ابتدا شروع کنید، این کد لبه داربستی از کد منبع برنامه را برای شما فراهم می کند تا آن را تکمیل کنید. مراحل زیر به شما نشان می دهد که چگونه برنامه را تکمیل کنید، از جمله استفاده از افزونه های کامپایلر بافر پروتکل برای تولید کد gRPC دیگ بخار.
ابتدا پوشه کاری codelab و cd را در آن ایجاد کنید:
mkdir grpc-java-getting-started && cd grpc-java-getting-started
کد لبه را دانلود و استخراج کنید:
curl -sL https://github.com/grpc-ecosystem/grpc-codelabs/archive/refs/heads/v1.tar.gz \
| tar xvz --strip-components=4 \
grpc-codelabs-1/codelabs/grpc-java-getting-started/start_here
یا می توانید فایل .zip را که فقط شامل دایرکتوری Codelab است دانلود کرده و به صورت دستی آن را از حالت فشرده خارج کنید.
اگر می خواهید از تایپ کردن در یک پیاده سازی صرفنظر کنید، کد منبع تکمیل شده در GitHub موجود است.
3. سرویس را تعریف کنید
اولین قدم شما این است که سرویس gRPC برنامه، روش RPC آن، و انواع پیام درخواست و پاسخ آن را با استفاده از Protocol Buffers تعریف کنید. خدمات شما ارائه خواهد کرد:
- یک روش RPC به نام
GetFeature
که سرور پیاده سازی می کند و کلاینت فراخوانی می کند. - انواع پیام
Point
وFeature
که ساختارهای داده ای هستند که هنگام استفاده از روشGetFeature
بین مشتری و سرور رد و بدل می شوند. مشتری مختصات نقشه را به عنوان یکPoint
در درخواستGetFeature
خود به سرور ارائه می دهد و سرور با یکFeature
مربوطه پاسخ می دهد که هر چیزی را که در آن مختصات قرار دارد را توصیف می کند.
این روش RPC و انواع پیام آن همه در فایل src/main/proto/routeguide/route_guide.proto
کد منبع ارائه شده تعریف خواهند شد.
بافرهای پروتکل معمولاً به عنوان پروتوباف شناخته می شوند. برای اطلاعات بیشتر در مورد اصطلاحات gRPC، به مفاهیم اصلی، معماری و چرخه حیات gRPC مراجعه کنید.
از آنجایی که در این مثال در حال تولید کد جاوا هستیم، یک گزینه فایل java_package
و یک نام برای کلاس جاوا در .proto
خود مشخص کرده ایم:
option java_package = "io.grpc.examples.routeguide";
option java_outer_classname = "RouteGuideProto";
انواع پیام
در فایل 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
خود تولید کنیم. ما این کار را با استفاده از پروتکل کامپایلر بافر protoc
با یک پلاگین مخصوص gRPC جاوا انجام می دهیم. برای تولید سرویس های gRPC باید از کامپایلر proto3 (که از دستورات proto2 و proto3 پشتیبانی می کند) استفاده کنید.
هنگام استفاده از Gradle یا Maven، افزونه protoc
بیلد می تواند کدهای لازم را به عنوان بخشی از بیلد ایجاد کند. برای نحوه تولید کد از فایل های .proto
خود می توانید به grpc-java README مراجعه کنید.
برای ساخت این پروژه، محیط و پیکربندی Gradle را در سورس کد لبه کد ارائه کرده ایم.
در دایرکتوری grpc-java-getting-started
دستور زیر را اجرا کنید:
$ chmod +x gradlew $ ./gradlew generateProto
کلاس های زیر از تعریف سرویس ما تولید می شوند:
-
Feature.java
،Point.java
، و موارد دیگر که حاوی تمام کد بافر پروتکل برای پر کردن، سریال سازی و بازیابی انواع پیام درخواست و پاسخ ما هستند. -
RouteGuideGrpc.java
که شامل (همراه با برخی کدهای مفید دیگر) یک کلاس پایه برای سرورهایRouteGuide
برای پیاده سازی است،RouteGuideGrpc.RouteGuideImplBase
، با تمام روش های تعریف شده در سرویسRouteGuide
و کلاس های خرد برای استفاده از کلاینت ها.
5. سرور را پیاده سازی کنید
ابتدا بیایید نحوه ایجاد سرور RouteGuide
را بررسی کنیم. دو بخش برای اینکه سرویس RouteGuide
ما کار خود را انجام دهد وجود دارد:
- پیاده سازی رابط سرویس ایجاد شده از تعریف سرویس ما، که "کار" واقعی سرویس ما را انجام می دهد.
- اجرای یک سرور gRPC برای گوش دادن به درخواست های مشتریان و ارسال آنها به اجرای سرویس مناسب.
RouteGuide را پیاده سازی کنید
همانطور که می بینید، سرور ما یک کلاس RouteGuideService
دارد که کلاس انتزاعی RouteGuideGrpc.RouteGuideImplBase
ایجاد شده را گسترش می دهد:
private static class RouteGuideService extends RouteGuideGrpc.RouteGuideImplBase {
...
}
ما 2 فایل زیر را برای مقداردهی اولیه سرور شما با ویژگی ها ارائه کرده ایم:
./src/main/java/io/grpc/examples/routeguide/RouteGuideUtil.java
./src/main/resources/io/grpc/examples/routeguide/route_guide_db.json
بیایید پیاده سازی ساده RPC را با جزئیات بررسی کنیم.
Unary RPC
RouteGuideService
تمام روش های خدمات ما را پیاده سازی می کند. در این مورد فقط GetFeature()
است، یک پیام Point
از مشتری می گیرد و در یک پیام Feature
اطلاعات مکان مربوطه را از لیست مکان های شناخته شده برمی گرداند.
@Override
public void getFeature(Point request, StreamObserver<Feature> responseObserver) {
responseObserver.onNext(checkFeature(request));
responseObserver.onCompleted();
}
متد getFeature()
دو پارامتر دارد:
-
Point
: درخواست -
StreamObserver<Feature>
: یک مشاهده گر پاسخ که یک رابط ویژه برای سرور است تا با پاسخ خود تماس بگیرد.
برای بازگرداندن پاسخ ما به مشتری و تکمیل تماس:
- همانطور که در تعریف سرویس ما مشخص شده است، یک شی پاسخ
Feature
برای بازگشت به مشتری ایجاد و پر می کنیم. در این مثال، ما این کار را در یک متد خصوصیcheckFeature()
انجام می دهیم. - ما از متد
onNext()
ناظر پاسخ برای برگرداندنFeature
استفاده می کنیم. - ما از متد
onCompleted()
ناظر پاسخ استفاده می کنیم تا مشخص کنیم که کار با RPC به پایان رسیده است.
سرور را راه اندازی کنید
هنگامی که همه روشهای خدمات خود را پیادهسازی کردیم، باید یک سرور gRPC راهاندازی کنیم تا مشتریان بتوانند واقعاً از خدمات ما استفاده کنند. ما ایجاد شی ServerBuilder را در boilerplate خود قرار می دهیم:
ServerBuilder.forPort(port), port, RouteGuideUtil.parseFeatures(featureFile)
ما سرویس را در سازنده می سازیم:
- پورتی را که میخواهیم برای گوش دادن به درخواستهای مشتری با استفاده از متد
forPort()
سازنده استفاده کنیم را مشخص کنید (از آدرس wildcard استفاده میکند). - نمونه ای از کلاس پیاده سازی سرویس ما
RouteGuideService
ایجاد کنید و آن را به متدaddService()
سازنده ارسال کنید. - برای ایجاد یک سرور RPC برای سرویس ما،
build()
در سازنده فراخوانی کنید.
قطعه زیر نحوه ایجاد یک شی ServerBuilder
را نشان می دهد.
/** Create a RouteGuide server listening on {@code port} using {@code featureFile} database. */
public RouteGuideServer(int port, URL featureFile) throws IOException {
this(Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create()),
port, RouteGuideUtil.parseFeatures(featureFile));
}
قطعه زیر نشان می دهد که چگونه یک شی سرور برای سرویس RouteGuide
خود ایجاد می کنیم.
/** Create a RouteGuide server using serverBuilder as a base and features as data. */
public RouteGuideServer(ServerBuilder<?> serverBuilder, int port, Collection<Feature> features) {
this.port = port;
server = serverBuilder.addService(new RouteGuideService(features))
.build();
}
یک متد شروع را پیاده سازی کنید که فراخوانی ها در سروری که در بالا ایجاد کردیم start
.
public void start() throws IOException {
server.start();
logger.info("Server started, listening on " + port);
}
روشی را اجرا کنید تا منتظر بمانید تا سرور تکمیل شود تا فوراً از آن خارج نشود.
/** Await termination on the main thread since the grpc library uses daemon threads. */
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
همانطور که می بینید، ما سرور خود را با استفاده از یک ServerBuilder
می سازیم و راه اندازی می کنیم.
در روش اصلی ما:
- یک نمونه
RouteGuideServer
ایجاد کنید. - برای فعال کردن سرور RPC برای سرویس ما،
start()
را فراخوانی کنید. - با فراخوانی
blockUntilShutdown()
منتظر بمانید تا سرویس متوقف شود.
public static void main(String[] args) throws Exception {
RouteGuideServer server = new RouteGuideServer(8980);
server.start();
server.blockUntilShutdown();
}
6. مشتری ایجاد کنید
در این بخش، ایجاد یک کلاینت برای سرویس RouteGuide
خود را بررسی خواهیم کرد.
نمونه خرد
برای فراخوانی روش های سرویس، ابتدا باید یک خرد ایجاد کنیم. دو نوع خرد وجود دارد، اما ما فقط باید از یک مسدود کننده برای این کد لبه استفاده کنیم. این دو نوع عبارتند از:
- یک خرد مسدود کننده/همگام که یک تماس RPC برقرار می کند و منتظر پاسخگویی سرور می ماند و یا پاسخی را برمی گرداند یا استثنایی ایجاد می کند.
- یک خرد غیر مسدود/ناهمزمان که تماسهای غیرانسدادی را با سرور برقرار میکند، جایی که پاسخ به صورت ناهمزمان برگردانده میشود. میتوانید انواع خاصی از تماسهای جریانی را فقط با استفاده از خرد ناهمزمان برقرار کنید.
ابتدا باید یک کانال gRPC ایجاد کنیم و سپس از کانال برای ایجاد خرد خود استفاده کنیم.
میتوانستیم مستقیماً از ManagedChannelBuilder
برای ایجاد کانال استفاده کنیم.
ManagedChannelBuilder.forAddress(host, port).usePlaintext().build
اما بیایید از یک روش کاربردی استفاده کنیم که یک رشته با hostname:port
می گیرد.
Grpc.newChannelBuilder(target, InsecureChannelCredentials.create()).build();
اکنون میتوانیم از کانال برای ایجاد خرد مسدودکننده استفاده کنیم. برای این کد لبه، ما فقط RPC های مسدود کننده داریم، بنابراین از روش newBlockingStub
ارائه شده در کلاس RouteGuideGrpc
که از .proto
خود تولید کردیم استفاده می کنیم.
blockingStub = RouteGuideGrpc.newBlockingStub(channel);
روش های خدمات تماس
حال بیایید ببینیم که چگونه روشهای خدمات خود را فراخوانی میکنیم.
RPC ساده
فراخوانی ساده RPC GetFeature
تقریباً به آسانی فراخوانی یک متد محلی است.
ما یک شی بافر پروتکل درخواست را ایجاد و پر می کنیم (در مورد ما Point
)، آن را به متد getFeature()
در قسمت خرد مسدودکننده خود منتقل می کنیم و یک Feature
برمی گردانیم.
اگر خطایی رخ دهد، به عنوان یک Status
کدگذاری میشود که میتوانیم آن را از StatusRuntimeException
دریافت کنیم.
Point request = Point.newBuilder().setLatitude(lat).setLongitude(lon).build();
Feature feature;
try {
feature = blockingStub.getFeature(request);
} catch (StatusRuntimeException e) {
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
return;
}
دیگ بخار پیامی حاوی محتویات را بر اساس وجود یا نبودن ویژگی در نقطه مشخص شده ثبت می کند.
7. آن را امتحان کنید!
- در پوشه
start_here
دستور زیر را اجرا کنید:
$ ./gradlew installDist
این کد شما را کامپایل می کند، آن را در یک شیشه بسته بندی می کند و اسکریپت هایی را ایجاد می کند که نمونه را اجرا می کنند. آنها در دایرکتوری build/install/start_here/bin/
ایجاد خواهند شد. اسکریپت ها عبارتند از: route-guide-server
و route-guide-client
.
سرور باید قبل از راه اندازی کلاینت در حال اجرا باشد.
- سرور را اجرا کنید:
$ ./build/install/start_here/bin/route-guide-server
- کلاینت را اجرا کنید:
$ ./build/install/start_here/bin/route-guide-client
خروجی را به صورت زیر مشاهده خواهید کرد که برای وضوح از مهرهای زمانی حذف شده است:
INFO: *** GetFeature: lat=409,146,138 lon=-746,188,906 INFO: Found feature called "Berkshire Valley Management Area Trail, Jefferson, NJ, USA" at 40.915, -74.619 INFO: *** GetFeature: lat=0 lon=0 INFO: Found no feature at 0, 0
8. بعدی چه خواهد شد
- نحوه عملکرد gRPC را در مقدمه مفاهیم gRPC و Core بیاموزید
- از طریق آموزش اصول کار کنید
- مرجع API را کاوش کنید.