gRPC مقابل REST: الشروع في العمل مع أفضل بروتوكول API

نشرت: 2022-07-22

في المشهد التكنولوجي اليوم ، تتطلب معظم المشاريع استخدام واجهات برمجة التطبيقات. تعمل واجهات برمجة التطبيقات على ربط الاتصال بين الخدمات التي قد تمثل نظامًا واحدًا معقدًا ولكنها قد تكون موجودة أيضًا على أجهزة منفصلة أو تستخدم شبكات أو لغات متعددة وغير متوافقة.

تتناول العديد من التقنيات القياسية احتياجات الاتصال بين الخدمات للأنظمة الموزعة ، مثل REST أو SOAP أو GraphQL أو gRPC. في حين أن REST هو نهج مفضل ، فإن gRPC هي منافس جدير بتقديم أداء عالي وعقود مكتوبة وأدوات ممتازة.

نظرة عامة على REST

نقل الحالة التمثيلية (REST) ​​هو وسيلة لاسترداد بيانات الخدمة أو معالجتها. يتم إنشاء REST API بشكل عام على بروتوكول HTTP ، باستخدام URI لتحديد مورد وفعل HTTP (على سبيل المثال ، GET ، PUT ، POST) لتحديد العملية المطلوبة. تحتوي هيئات الطلب والاستجابة على بيانات خاصة بالعملية ، بينما توفر رؤوسها البيانات الوصفية. للتوضيح ، دعنا نلقي نظرة على مثال مبسط لاسترداد منتج عبر واجهة برمجة تطبيقات REST.

هنا ، نطلب مورد منتج برقم 11 ونوجه واجهة برمجة التطبيقات للرد بتنسيق JSON:

 GET /products/11 HTTP/1.1 Accept: application/json

بناءً على هذا الطلب ، قد يبدو ردنا (تم حذف العناوين غير ذات الصلة) كما يلي:

 HTTP/1.1 200 OK Content-Type: application/json { id: 11, name: "Purple Bowtie", sku: "purbow", price: { amount: 100, currencyCode: "USD" } }

على الرغم من أن JSON قد يكون قابلاً للقراءة من قبل الإنسان ، إلا أنه ليس مثاليًا عند استخدامه بين الخدمات. يمكن أن تؤدي الطبيعة المتكررة للإشارة إلى أسماء الخصائص - حتى عند ضغطها - إلى تضخم الرسائل. دعونا نلقي نظرة على بديل لمعالجة هذا القلق.

نظرة عامة على gRPC

gRPC Remote Procedure Call (gRPC) هو بروتوكول اتصال مفتوح المصدر وقائم على عقد وعبر الأنظمة الأساسية يعمل على تبسيط وإدارة الاتصال بين الخدمات من خلال عرض مجموعة من الوظائف للعملاء الخارجيين.

بنيت على رأس HTTP / 2 ، gRPC تعزز ميزات مثل التدفق ثنائي الاتجاه وأمن طبقة النقل (TLS) المدمج. يتيح gRPC اتصالاً أكثر كفاءة من خلال الحمولات الثنائية المتسلسلة. يستخدم مخازن البروتوكول بشكل افتراضي كآليته لتسلسل البيانات المنظمة ، على غرار استخدام REST لـ JSON.

على عكس JSON ، فإن المخازن المؤقتة للبروتوكول هي أكثر من تنسيق متسلسل. وهي تشمل ثلاثة أجزاء رئيسية أخرى:

  • لغة تعريف العقد موجودة في ملفات .proto (سنتبع proto3 ، أحدث مواصفات لغة المخزن المؤقت للبروتوكول.)
  • تم إنشاء رمز وظيفة الموصّل
  • مكتبات وقت تشغيل خاصة بلغة معينة

يتم سرد الوظائف البعيدة المتوفرة في الخدمة (المعرفة في ملف .proto ) داخل عقدة الخدمة في ملف المخزن المؤقت للبروتوكول. كمطورين ، نحصل على هذه الوظائف ومعلماتها باستخدام نظام النوع الغني للمخازن المؤقتة للبروتوكول. يدعم هذا النظام مختلف أنواع الأرقام والتاريخ والقوائم والقواميس والأرقام الفارغة لتحديد رسائل الإدخال والإخراج.

يجب أن تكون تعريفات الخدمة هذه متاحة لكل من الخادم والعميل. لسوء الحظ ، لا توجد آلية افتراضية لمشاركة هذه التعريفات بخلاف توفير الوصول المباشر إلى ملف .proto نفسه.

يعرّف ملف .proto هذا المثال دالة لإرجاع إدخال منتج ، بمعرّف:

 syntax = "proto3"; package product; service ProductCatalog { rpc GetProductDetails (ProductDetailsRequest) returns (ProductDetailsReply); } message ProductDetailsRequest { int32 id = 1; } message ProductDetailsReply { int32 id = 1; string name = 2; string sku = 3; Price price = 4; } message Price { float amount = 1; string currencyCode = 2; }
المقتطف 1: ProductCatalog خدمة كتالوج المنتج

تجعل الكتابة الصارمة والترتيب الميداني لـ proto3 عملية إلغاء تسلسل الرسالة أقل ضرائب من تحليل JSON.

مقارنة REST مقابل gRPC

للتلخيص ، أهم النقاط عند مقارنة REST مقابل gRPC هي:

راحة gRPC
عبر منصة نعم نعم
تنسيق الرسالة مخصص ولكن بشكل عام JSON أو XML مخازن البروتوكول
حجم حمولة الرسالة متوسط ​​/ كبير صغير
تعقيد المعالجة أعلى (تحليل النص) أقل (هيكل ثنائي واضح المعالم)
دعم المتصفح نعم (أصلي) نعم (عبر gRPC-Web)

حيث من المتوقع وجود عقود أقل صرامة وإضافات متكررة إلى الحمولة ، فإن JSON و REST مناسبان بشكل كبير. عندما تميل العقود إلى البقاء ثابتة وتكون السرعة ذات أهمية قصوى ، تفوز gRPC بشكل عام. في معظم المشاريع التي عملت عليها ، أثبت gRPC أنه أخف وزنًا وأكثر أداءً من REST.

تنفيذ خدمة gRPC

لنقم ببناء مشروع مبسط لاستكشاف مدى سهولة اعتماد gRPC.

إنشاء مشروع API

للبدء ، سننشئ مشروع .NET 6 في Visual Studio 2022 Community Edition (VS). سنقوم بتحديد قالب ASP.NET Core gRPC Service وتسمية المشروع (سنستخدم InventoryAPI ) والحل الأول ضمنه ( Inventory ) .

مربع حوار "تكوين مشروعك الجديد" داخل Visual Studio 2022. في هذه الشاشة ، كتبنا "InventoryAPI" في حقل اسم المشروع ، وحددنا "C: \ MyInventoryService" في حقل الموقع ، وكتبنا "Inventory" في حقل اسم الحل . تركنا "وضع الحل والمشروع في نفس الدليل" بدون تحديد.

الآن ، دعنا نختار ملف. خيار NET 6.0 (دعم طويل الأجل) لإطار عملنا:

مربع حوار معلومات إضافية داخل Visual Studio 2022. في هذه الشاشة اخترنا ".NET 6.0 (دعم طويل الأجل)" من قائمة Framework المنسدلة. تركنا "تمكين Docker" بدون تحديد.

تحديد خدمة منتجاتنا

الآن بعد أن أنشأنا المشروع ، تعرض VS خدمة تعريف نموذج أولي لـ gRPC تسمى Greeter . سنعيد استخدام ملفات Greeter الأساسية لتناسب احتياجاتنا.

  • لإنشاء العقد الخاص بنا ، سنقوم باستبدال محتويات greet.proto بـ Snippet 1 ، وإعادة تسمية الملف product.proto .
  • لإنشاء خدمتنا ، سنقوم باستبدال محتويات ملف GreeterService.cs بـ Snippet 2 ، وإعادة تسمية الملف ProductCatalogService.cs .
 using Grpc.Core; using Product; namespace InventoryAPI.Services { public class ProductCatalogService : ProductCatalog.ProductCatalogBase { public override Task<ProductDetailsReply> GetProductDetails( ProductDetailsRequest request, ServerCallContext context) { return Task.FromResult(new ProductDetailsReply { Id = request.Id, Name = "Purple Bowtie", Sku = "purbow", Price = new Price { Amount = 100, CurrencyCode = "USD" } }); } } }
المقتطف 2: ProductCatalogService

ترجع الخدمة الآن منتجًا مشفرًا. لإنجاح الخدمة ، نحتاج فقط إلى تغيير تسجيل الخدمة في Program.cs للإشارة إلى اسم الخدمة الجديد. في حالتنا ، سنعيد تسمية app.MapGrpcService<GreeterService>(); إلى app.MapGrpcService<ProductCatalogService>(); لجعل واجهة برمجة التطبيقات الجديدة الخاصة بنا قابلة للتشغيل.

تحذير عادل: ليس اختبار البروتوكول القياسي الخاص بك

في حين أننا قد نميل إلى تجربته ، لا يمكننا اختبار خدمة gRPC الخاصة بنا من خلال متصفح يستهدف نقطة النهاية الخاصة به. إذا حاولنا ذلك ، فسوف نتلقى رسالة خطأ تشير إلى أن الاتصال بنقاط نهاية gRPC يجب أن يتم من خلال عميل gRPC.

إنشاء العميل

لاختبار خدمتنا ، دعنا نستخدم نموذج تطبيق وحدة التحكم الأساسي الخاص بـ VS وإنشاء عميل gRPC لاستدعاء API. قمت بتسمية تطبيق InventoryApp بي.

من أجل النفعية ، دعنا نشير إلى مسار ملف نسبي سنشارك من خلاله عقدنا. سنضيف المرجع يدويًا إلى ملف .csproj . بعد ذلك ، سنقوم بتحديث المسار وتعيين وضع Client . ملاحظة: أوصيك بالتعرف على بنية المجلد المحلي لديك وثقة في هيكلها قبل استخدام المراجع النسبية.

فيما يلي مراجع .proto ، كما تظهر في ملفات مشروع العميل والخدمة:

ملف مشروع الخدمة
(رمز لنسخه إلى ملف مشروع العميل)
ملف مشروع العميل
(بعد اللصق والتحرير)
 <ItemGroup> <Content Update="Protos\product.proto" GrpcServices="Server" /> </ItemGroup>
 <ItemGroup> <Protobuf Include="..\InventoryAPI\Protos\product.proto" GrpcServices="Client" /> </ItemGroup>

الآن ، للاتصال بخدمتنا ، سنقوم باستبدال محتويات Program.cs . سيحقق الكود الخاص بنا عددًا من الأهداف:

  1. قم بإنشاء قناة تمثل موقع نقطة نهاية الخدمة (قد يختلف المنفذ ، لذا راجع ملف launchsettings.json لمعرفة القيمة الفعلية).
  2. قم بإنشاء كائن العميل.
  3. أنشئ طلبًا بسيطًا.
  4. أرسل الطلب.
 using System.Text.Json; using Grpc.Net.Client; using Product; var channel = GrpcChannel.ForAddress("https://localhost:7200"); var client = new ProductCatalog.ProductCatalogClient(channel); var request = new ProductDetailsRequest { Id = 1 }; var response = await client.GetProductDetailsAsync(request); Console.WriteLine(JsonSerializer.Serialize(response, new JsonSerializerOptions { WriteIndented = true })); Console.ReadKey();
المقتطف 3: Program.cs الجديد

التحضير للإطلاق

لاختبار الكود الخاص بنا ، في VS ، سننقر بزر الماوس الأيمن فوق الحل ونختار Set Startup Projects . في مربع حوار صفحات خصائص الحل ، سنقوم بما يلي:

  • حدد زر الاختيار بجوار مشاريع بدء التشغيل المتعددة ، وفي القائمة المنسدلة الإجراء ، اضبط كلا المشروعين ( InventoryAPI و InventoryApp ) على البدء .
  • انقر فوق "موافق" .

يمكننا الآن بدء الحل بالنقر فوق " ابدأ " في شريط أدوات VS (أو بالضغط على مفتاح F5 ). سيتم عرض نافذتين جديدتين لوحدة التحكم: واحدة تخبرنا أن الخدمة تستمع ، والأخرى لتظهر لنا تفاصيل المنتج المسترجع.

مشاركة عقد gRPC

لنستخدم الآن طريقة أخرى لتوصيل عميل gRPC بتعريف خدمتنا. إن أكثر حلول مشاركة العقود التي يمكن الوصول إليها من قبل العميل هو إتاحة تعريفاتنا من خلال عنوان URL. الخيارات الأخرى إما هشة للغاية (ملف مشترك من خلال مسار) أو تتطلب المزيد من الجهد (عقد مشترك من خلال حزمة أصلية). المشاركة من خلال عنوان URL (كما يفعل SOAP و Swagger / OpenAPI) تتسم بالمرونة وتتطلب رمزًا أقل.

للبدء ، اجعل ملف .proto متاحًا كمحتوى ثابت. سنقوم بتحديث الكود الخاص بنا يدويًا لأن واجهة المستخدم في إجراء الإنشاء مضبوطة على "Protobuf Compiler". يوجه هذا التغيير المترجم لنسخ ملف .proto بحيث يمكن تقديمه من عنوان ويب. إذا تم تغيير هذا الإعداد من خلال VS UI ، فسيتعطل الإصدار. خطوتنا الأولى إذن هي إضافة Snippet 4 إلى ملف InventoryAPI.csproj :

 <ItemGroup> <Content Update="Protos\product.proto"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> </ItemGroup> <ItemGroup> <Content Include="Protos\product.proto" CopyToPublishDirectory="PreserveNewest" /> </ItemGroup>
المقتطف 4: رمز للإضافة إلى ملف مشروع خدمة InventoryAPI

بعد ذلك ، نقوم بإدخال الكود في Snippet 5 أعلى ملف ProductCatalogService.cs لإعداد نقطة نهاية لإرجاع ملف .proto بنا:

 using System.Net.Mime; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.FileProviders;
المقتطف 5: عمليات استيراد Namespace الاسم

والآن ، نضيف Snippet 6 قبل app.Run() ، أيضًا في ملف ProductCatalogService.cs :

 var provider = new FileExtensionContentTypeProvider(); provider.Mappings.Clear(); provider.Mappings[".proto"] = MediaTypeNames.Text.Plain; app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider(Path.Combine(app.Environment.ContentRootPath, "Protos")), RequestPath = "/proto", ContentTypeProvider = provider }); app.UseRouting();
المقتطف 6: رمز لجعل الوصول إلى ملفات .proto من خلال واجهة برمجة التطبيقات

مع إضافة Snippets 4-6 ، يجب أن تكون محتويات ملف .proto مرئية في المتصفح.

عميل اختبار جديد

نريد الآن إنشاء عميل وحدة تحكم جديد سنقوم بتوصيله بخادمنا الحالي باستخدام معالج تبعية VS. المشكلة هي أن هذا المعالج لا يتحدث عن HTTP / 2. لذلك ، نحتاج إلى ضبط خادمنا للتحدث عبر HTTP / 1 وبدء الخادم. مع إتاحة خادمنا الآن لملف .proto ، يمكننا بناء عميل اختبار جديد يتم ربطه بخادمنا عبر معالج gRPC.

  1. لتغيير خادمنا للتحدث عبر HTTP / 1 ، سنقوم بتحرير ملف appsettings.json JSON الخاص بنا:
    1. اضبط حقل Protocol (الموجود في مسار Kestrel.EndpointDefaults.Protocols ) لقراءة Https .
    2. حفظ الملف.
  2. لكي يقرأ عميلنا الجديد هذه المعلومات proto ، يجب أن يكون الخادم قيد التشغيل. في الأصل ، بدأنا كلاً من العميل السابق وخادمنا من مربع حوار Set Startup Projects في VS. اضبط حل الخادم لبدء مشروع الخادم فقط ، ثم ابدأ الحل. (الآن بعد أن قمنا بتعديل إصدار HTTP ، لم يعد بإمكان عميلنا القديم الاتصال بالخادم.)
  3. بعد ذلك ، قم بإنشاء عميل الاختبار الجديد. قم بتشغيل مثيل آخر من VS. سنكرر الخطوات كما هو مفصل في قسم إنشاء مشروع واجهة برمجة التطبيقات ، ولكن هذه المرة ، سنختار نموذج تطبيق وحدة التحكم . سنقوم بتسمية مشروعنا والحل InventoryAppConnected .
  4. مع إنشاء هيكل العميل ، سنتصل بخادم gRPC الخاص بنا. قم بتوسيع المشروع الجديد في VS Solution Explorer.
    1. انقر بزر الماوس الأيمن فوق التبعيات ، وفي قائمة السياق ، حدد إدارة الخدمات المتصلة .
    2. في علامة التبويب "الخدمات المتصلة" ، انقر فوق إضافة مرجع خدمة واختر gRPC .
    3. في مربع الحوار Add Service Reference ، حدد خيار URL وأدخل إصدار http من عنوان الخدمة (تذكر الحصول على رقم المنفذ الذي تم إنشاؤه عشوائيًا من launchsettings.json ) .
    4. انقر فوق " إنهاء " لإضافة مرجع خدمة يمكن صيانته بسهولة.

لا تتردد في التحقق من عملك مقابل نموذج التعليمات البرمجية لهذا المثال. نظرًا لأن VS قد أنشأت ، تحت الغطاء ، نفس العميل الذي استخدمناه في الجولة الأولى من الاختبار ، يمكننا إعادة استخدام محتويات ملف Program.cs من الخدمة السابقة حرفياً.

عندما نغير عقدًا ، نحتاج إلى تعديل تعريف gRPC الخاص بالعميل لمطابقة تعريف .proto المحدث. للقيام بذلك ، نحتاج فقط إلى الوصول إلى الخدمات المتصلة الخاصة بـ VS وتحديث إدخال الخدمة ذات الصلة. الآن ، اكتمل مشروع gRPC الخاص بنا ، ومن السهل الحفاظ على تزامن خدمتنا وعميلنا.

المرشح التالي لمشروعك: gRPC

يوفر تطبيق gRPC الخاص بنا لمحة مباشرة عن فوائد استخدام gRPC. لكل من REST و gRPC حالات الاستخدام المثالية الخاصة بهم اعتمادًا على نوع العقد. ومع ذلك ، عندما يكون كلا الخيارين مناسبين ، فإنني أشجعك على تجربة gRPC - فهي ستضعك في مقدمة المنحنى في مستقبل واجهات برمجة التطبيقات.