برنامه نویسی

gRPC در سمت مشتری

اکثر اجزای ارتباطی بین سیستمی که از REST استفاده می کنند، محموله خود را در JSON سریال می کنند. در حال حاضر، JSON فاقد استاندارد اعتبارسنجی طرحواره پرکاربرد است: طرحواره JSON گسترده نیست. اعتبار سنجی طرحواره استاندارد اجازه می دهد تا اعتبارسنجی را به یک کتابخانه شخص ثالث واگذار کرده و با آن انجام شود. بدون یک، باید به اعتبار سنجی دستی در کد بازگردیم. بدتر از آن، باید کد اعتبار سنجی را با طرحواره هماهنگ نگه داریم.

XML دارای اعتبار سنجی طرحواره خارج از جعبه است: یک سند XML می تواند دستور زبانی را که باید با آن مطابقت داشته باشد، اعلام کند. SOAP که مبتنی بر XML است، از آن نیز سود می برد.

سایر جایگزین های سریال سازی یک گزینه اعتبار سنجی طرحواره دارند: به عنوان مثال، Avro، Kryo و بافرهای پروتکل. به اندازه کافی جالب توجه است که gRPC از Protobuf برای ارائه RPC در اجزای توزیع شده استفاده می کند:

gRPC یک فریم ورک متن باز مدرن با کارایی بالا Remote Procedure Call (RPC) است که می تواند در هر محیطی اجرا شود. این می تواند خدمات را در داخل و بین مراکز داده با پشتیبانی قابل اتصال برای تعادل بار، ردیابی، بررسی سلامت و احراز هویت به طور موثر متصل کند. همچنین در آخرین مایل محاسبات توزیع‌شده برای اتصال دستگاه‌ها، برنامه‌های کاربردی تلفن همراه و مرورگرها به خدمات پشتیبان قابل استفاده است.

— چرا gRPC؟

علاوه بر این، پروتکل یک است دودویی مکانیسم سریال سازی، صرفه جویی در پهنای باند زیادی. بنابراین، gRPC یک گزینه عالی برای ارتباطات بین سیستمی است. اما اگر همه اجزای شما با gRPC صحبت می کنند، مشتریان ساده چگونه می توانند آنها را فراخوانی کنند؟ در این پست، ما یک سرویس gRPC می سازیم و نحوه فراخوانی آن را از curl نشان می دهیم.

یک سرویس ساده gRPC

مستندات gRPC جامع است، بنابراین در اینجا خلاصه ای وجود دارد:

  • gRPC یک چارچوب فراخوانی از راه دور است
  • در طیف گسترده ای از زبان ها کار می کند
  • به بافرهای پروتکل متکی است:

    بافرهای پروتکل مکانیزمی غیر زبانی، پلتفرم خنثی و قابل توسعه Google برای سریال سازی داده های ساختاریافته هستند – XML ​​را در نظر بگیرید، اما کوچکتر، سریعتر و ساده تر. شما تعریف می‌کنید که می‌خواهید داده‌هایتان چگونه ساختاری داشته باشند، سپس می‌توانید از کد منبع تولید شده ویژه برای نوشتن و خواندن داده‌های ساخت‌یافته‌تان از و از انواع جریان‌های داده و با استفاده از زبان‌های مختلف استفاده کنید.

    – بافرهای پروتکل

  • این بخشی از نمونه کارها CNCF است و در حال حاضر در مرحله جوجه کشی است

بیایید سرویس gRPC خود را راه اندازی کنیم. ما از Java، Kotlin، Spring Boot و یک پروژه اختصاصی gRPC Spring Boot استفاده خواهیم کرد. ساختار پروژه دو پروژه دارد، یکی برای مدل و دیگری برای کد. بیایید با پروژه مدل شروع کنیم.

من چیز پیچیده ای نمی خواستم. استفاده مجدد از یک مثال ساده کافی است: درخواست یک رشته می فرستد و پاسخ آن را با پیشوند اضافه می کند Hello. ما این مدل را در یک فایل طرحواره Protobuf اختصاصی طراحی می کنیم:

syntax = "proto3";                                        //1

package ch.frankel.blog.grpc.model;                       //2

option java_multiple_files = true;                        //3
option java_package = "ch.frankel.blog.grpc.model";       //3
option java_outer_classname = "HelloProtos";              //3

service HelloService {                                    //4
    rpc SayHello (HelloRequest) returns (HelloResponse) {
    }
}

message HelloRequest {                                    //5
    string name = 1;                                      //6
}

message HelloResponse {                                   //7
    string message = 1;                                   //6
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. نسخه تعریف پروتوباف
  2. بسته
  3. پیکربندی خاص جاوا
  4. تعریف خدمات
  5. درخواست تعریف
  6. تعریف میدان. ابتدا نوع، سپس نام، و در نهایت، ترتیب می آید
  7. تعریف پاسخ

ما از Maven برای تولید کد دیگ بخار جاوا استفاده خواهیم کرد:

<project>
  <dependencies>
    <dependency>
      <groupId>io.grpc</groupId>                         <!--1-->
      <artifactId>grpc-stub</artifactId>
      <version>${grpc.version}</version>
    </dependency>
    <dependency>
      <groupId>io.grpc</groupId>                         <!--1-->
      <artifactId>grpc-protobuf</artifactId>
      <version>${grpc.version}</version>
    </dependency>
    <dependency>
      <groupId>jakarta.annotation</groupId>              <!--1-->
      <artifactId>jakarta.annotation-api</artifactId>
      <version>1.3.5</version>
      <optional>true</optional>
    </dependency>
  </dependencies>
  <build>
    <extensions>
      <extension>
        <groupId>kr.motd.maven</groupId>                 <!--2-->
        <artifactId>os-maven-plugin</artifactId>
        <version>1.7.1</version>
      </extension>
    </extensions>
    <plugins>
      <plugin>
        <groupId>org.xolstice.maven.plugins</groupId>    <!--3-->
        <artifactId>protobuf-maven-plugin</artifactId>
        <version>${protobuf-plugin.version}</version>
        <configuration>
          <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
          <pluginId>grpc-java</pluginId>
          <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <goal>compile-custom</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. وابستگی های زمان کامپایل
  2. اطلاعات مربوط به سیستم عامل را بخرید. در افزونه بعدی استفاده شده است
  3. کد جاوا را از proto فایل

پس از کامپایل، ساختار باید چیزی شبیه به زیر باشد:

ساختار پروژه مدل پروتو

ما می توانیم کلاس ها را در یک JAR بسته بندی کنیم و از آن در یک پروژه برنامه وب استفاده کنیم. دومی در Kotlin است، اما فقط به این دلیل که زبان JVM مورد علاقه من است.

برای ادغام نقاط انتهایی gRPC با Spring Boot فقط به یک وابستگی خاص به شروع بوت Spring نیاز داریم:

<dependency>
  <groupId>net.devh</groupId>
  <artifactId>grpc-server-spring-boot-starter</artifactId>
  <version>2.14.0.RELEASE</version>
</dependency>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نکته مهم اینجاست:

@GrpcService                                                        //1
class HelloService : HelloServiceImplBase() {                       //2
  override fun sayHello(
      request: HelloRequest,                                        //2
      observer: StreamObserver<HelloResponse>                       //3
  ) {
    with(observer) {
      val reply = HelloResponse.newBuilder()                        //2
                               .setMessage("Hello ${request.name}") //4
                               .build()
      onNext(reply)                                                 //5
      onCompleted()                                                 //5
    }
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. را grpc-server-spring-boot-starter حاشیه نویسی را تشخیص می دهد و جادوی آن را انجام می دهد
  2. کلاس های مرجع تولید شده در پروژه فوق
  3. امضای روش اجازه می دهد تا a StreamObserver پارامتر. کلاس می آید از grpc-stub.jar
  4. درخواست را دریافت کنید و پیشوند آن را برای ساخت پیام پاسخ قرار دهید
  5. رویدادها را پخش کنید

اکنون می توانیم برنامه وب را با آن شروع کنیم ./mvnw spring-boot:run.

تست سرویس gRPC

کل ایده پشت پست این است که دسترسی به سرویس gRPC با ابزارهای معمولی غیرممکن است. با این وجود، برای آزمایش، به یک ابزار اختصاصی نیاز داریم. grpcurl رو پیدا کردم بیایید آن را نصب کنیم و از آن برای فهرست کردن خدمات موجود استفاده کنیم:

grpcurl --plaintext localhost:9090 list   #1-2
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. تمام خدمات gRPC موجود را فهرست کنید بدون تأیید TLS
  2. برای جلوگیری از برخورد بین gRPC و کانال های دیگر، به عنوان مثال، REST، Spring Boot از پورت دیگری استفاده می کند
ch.frankel.blog.grpc.model.HelloService   #1
grpc.health.v1.Health                     #2
grpc.reflection.v1alpha.ServerReflection  #2
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. سرویس gRPC که ما تعریف کردیم
  2. دو سرویس اضافی ارائه شده توسط استارت سفارشی

همچنین می‌توانیم ساختار سرویس را بررسی کنیم:

grpcurl --plaintext localhost:9090 describe ch.frankel.blog.grpc.model.HelloService
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

service HelloService {
  rpc SayHello ( .ch.frankel.blog.grpc.model.HelloRequest ) returns ( .ch.frankel.blog.grpc.model.HelloResponse );
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در نهایت، می توانیم سرویس را با داده ها فراخوانی کنیم:

grpcurl --plaintext -d '{"name": "John"}' localhost:9090 ch.frankel.blog.grpc.model.HelloService/SayHello
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

{
  "message": "Hello John"
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

دسترسی به سرویس gRPC با ابزارهای معمولی

تصور کنید که ما یک برنامه معمولی سمت کلاینت جاوا اسکریپت داریم که نیاز به دسترسی به سرویس gRPC دارد. جایگزین ها چه خواهند بود؟

رویکرد کلی از طریق grpc-web:

اجرای جاوا اسکریپت gRPC برای مشتریان مرورگر. برای اطلاعات بیشتر، از جمله شروع سریع، به مستندات gRPC-web مراجعه کنید.

سرویس گیرندگان gRPC-web از طریق یک پروکسی خاص به خدمات gRPC متصل می شوند. به طور پیش فرض، gRPC-web از Envoy استفاده می کند.

در آینده، ما انتظار داریم که gRPC-web در چارچوب های وب خاص زبان برای زبان هایی مانند Python، Java و Node پشتیبانی شود. برای جزئیات، نقشه راه را ببینید.

— grpc-web

توضیحات یک محدودیت واحد را بیان می کند: این فقط برای جاوا اسکریپت (در حال حاضر) کار می کند. با این حال، یکی دیگر وجود دارد. خیلی سرزده است شما باید دریافت کنید proto فایل، کد boilerplate تولید کنید، و کد خود را به آن فراخوانی کنید. شما باید این کار را برای هر نوع مشتری انجام دهید. بدتر از آن، اگر فایل پروتو تغییر کند، باید کد کلاینت را در هر یک از آنها بازسازی کنید.

اگر از یک API Gateway استفاده می کنید، یک جایگزین وجود دارد. من نحوه انجام آن را با Apache APISIX توضیح خواهم داد، اما شاید سایر دروازه ها نیز بتوانند همین کار را انجام دهند. grpc-transcode پلاگینی است که به شما اجازه می دهد تا تماس های REST را به gRPC و دوباره برگردانید.

اولین قدم ثبت فایل پروتو در Apache APISIX است:

curl http://localhost:9180/apisix/admin/protos/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d "{ \"content\": \"$(sed 's/"/\\"/g' ../model/src/main/proto/model.proto)\" }"
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مرحله دوم ایجاد مسیر با افزونه بالا است:

curl http://localhost:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
  "uri": "/helloservice/sayhello",                           #1
  "plugins": {
    "grpc-transcode": {
      "proto_id": "1",                                       #2
      "service": "ch.frankel.blog.grpc.model.HelloService",  #3
      "method": "SayHello"                                   #4
    }
  },
  "upstream": {
    "scheme": "grpc",
    "nodes": {
      "server:9090": 1
    }
  }
}'
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. یک مسیر دانه ای تعریف کنید
  2. به فایل پروتو تعریف شده در دستور قبلی مراجعه کنید
  3. سرویس gRPC
  4. روش gRPC

در این مرحله، هر مشتری می تواند یک درخواست HTTP به نقطه پایانی تعریف شده ارسال کند. Apache APISIX تماس را به gRPC تبدیل می کند، آن را به سرویس تعریف شده ارسال می کند، پاسخ را دریافت می کند و دوباره آن را رمزگذاری می کند.

curl localhost:9080/helloservice/sayhello?name=John
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

{"message":"Hello John"}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در مقایسه با grpc-web، رویکرد API Gateway به اشتراک گذاری اجازه می دهد proto فایل با یک جزء: خود دروازه.

مزایای رمزگذاری

در این مرحله، می‌توانیم از قابلیت‌های API Gateway استفاده کنیم. تصور کنید که اگر نه، یک مقدار پیش فرض می خواهیم name گذشت، به عنوان مثال، World. توسعه دهندگان با خوشحالی آن را در کد تنظیم می کنند، اما هر تغییری در مقدار نیاز به ساخت و استقرار کامل دارد. اگر مقدار پیش‌فرض را در زنجیره پردازش مسیرهای Gateway قرار دهیم، می‌توانند تقریباً فوری باشند. بیایید مسیر خود را بر این اساس تغییر دهیم:

curl http://localhost:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
  "uri": "/helloservice/sayhello",
  "plugins": {
    "grpc-transcode": {
      ...
    },
    "serverless-pre-function": {                    #1
      "phase": "rewrite",                           #2
      "functions" : [
        "return function(conf, ctx)                 #3
          local core = require(\"apisix.core\")
          if not ngx.var.arg_name then
            local uri_args = core.request.get_uri_args(ctx)
            uri_args.name = \"World\"
            ngx.req.set_uri_args(uri_args)
          end
        end"
      ]
    }
  },
  "upstream": {
      ...
  }
}'
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. پلاگین همه منظوره عمومی زمانی که هیچکدام مناسب نیستند
  2. درخواست را دوباره بنویسید
  3. کد Magic Lua که این کار را انجام می دهد

اکنون می‌توانیم درخواست را با یک آرگومان خالی اجرا کنیم و نتیجه مورد انتظار را دریافت کنیم:

curl localhost:9080/helloservice/sayhello?name
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

{"message":"Hello World"}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه

در این پست، ما به طور مختصر gRPC و مزایای آن در ارتباطات بین سرویس را توضیح داده ایم. ما یک سرویس gRPC ساده را با استفاده از Spring Boot و توسعه دادیم grpc-server-spring-boot-starter. با این حال، هزینه ای دارد: مشتریان عادی نمی توانند به خدمات دسترسی داشته باشند. مجبور شدیم متوسل شویم grpcurl برای آزمایش آن همین امر در مورد مشتریان مبتنی بر جاوا اسکریپت – یا مرورگر نیز صدق می کند.

برای دور زدن این محدودیت، می‌توانیم از یک API Gateway استفاده کنیم. من نحوه پیکربندی Apache APISIX را با grpc-transcode افزونه برای رسیدن به نتیجه دلخواه.

کد منبع کامل این پست را می توانید در GitHub بیابید:

فراتر رفتن:

در ابتدا در A Java Geek در 16 مارس منتشر شدهفتم، 2023

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا