gRPC กับ REST: เริ่มต้นใช้งานโปรโตคอล API ที่ดีที่สุด

เผยแพร่แล้ว: 2022-07-22

ในแนวเทคโนโลยีในปัจจุบัน โครงการส่วนใหญ่ต้องการการใช้ API APIs เชื่อมโยงการสื่อสารระหว่างบริการต่างๆ ที่อาจเป็นตัวแทนของระบบเดียวที่ซับซ้อน แต่อาจอยู่ในเครื่องที่แยกจากกัน หรือใช้เครือข่ายหรือภาษาที่เข้ากันไม่ได้หลายเครื่อง

เทคโนโลยีมาตรฐานจำนวนมากตอบสนองความต้องการด้านการสื่อสารระหว่างบริการของระบบแบบกระจาย เช่น REST, SOAP, GraphQL หรือ gRPC แม้ว่า REST จะเป็นวิธีที่ได้รับความนิยม แต่ gRPC ก็เป็นคู่แข่งที่คู่ควร โดยนำเสนอประสิทธิภาพสูง สัญญาที่พิมพ์ได้ และเครื่องมือที่ยอดเยี่ยม

ภาพรวมส่วนที่เหลือ

การถ่ายโอนสถานะตัวแทน (REST) ​​เป็นวิธีการดึงหรือจัดการข้อมูลของบริการ โดยทั่วไป REST API ถูกสร้างขึ้นบนโปรโตคอล HTTP โดยใช้ URI เพื่อเลือกทรัพยากรและกริยา HTTP (เช่น GET, PUT, POST) เพื่อเลือกการดำเนินการที่ต้องการ เนื้อหาคำขอและการตอบสนองมีข้อมูลเฉพาะสำหรับการดำเนินการ ในขณะที่ส่วนหัวให้ข้อมูลเมตา มาดูตัวอย่างง่ายๆ ของการดึงข้อมูลผลิตภัณฑ์ผ่าน REST API

ที่นี่ เราขอทรัพยากรผลิตภัณฑ์ที่มีรหัส 11 และสั่งให้ API ตอบสนองในรูปแบบ 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) เป็นโปรโตคอลการสื่อสารข้ามแพลตฟอร์มแบบโอเพนซอร์สที่อิงตามสัญญา ซึ่งช่วยลดความยุ่งยากและจัดการการสื่อสารระหว่างบริการโดยเปิดเผยชุดของฟังก์ชันต่อไคลเอ็นต์ภายนอก

gRPC สร้างขึ้นบน HTTP/2 โดยใช้ประโยชน์จากคุณสมบัติต่างๆ เช่น การสตรีมแบบสองทิศทางและ Transport Layer Security (TLS) ในตัว gRPC ช่วยให้สามารถสื่อสารได้อย่างมีประสิทธิภาพมากขึ้นผ่านเพย์โหลดไบนารีแบบซีเรียลไลซ์ โดยค่าเริ่มต้นจะใช้บัฟเฟอร์โปรโตคอลเป็นกลไกสำหรับการจัดลำดับข้อมูลที่มีโครงสร้าง ซึ่งคล้ายกับการใช้ JSON ของ REST

อย่างไรก็ตาม บัฟเฟอร์โปรโตคอลต่างจาก JSON มากกว่ารูปแบบซีเรียลไลซ์ ประกอบด้วยส่วนสำคัญอื่นๆ อีกสามส่วน:

  • ภาษาคำจำกัดความของสัญญาที่พบในไฟล์ . .proto (เราจะทำตาม proto3 ซึ่งเป็นข้อกำหนดภาษาบัฟเฟอร์โปรโตคอลล่าสุด)
  • รหัสฟังก์ชั่นการเข้าถึงที่สร้างขึ้น
  • ไลบรารีรันไทม์เฉพาะภาษา

ฟังก์ชันระยะไกลที่มีอยู่ในบริการ (กำหนดไว้ในไฟล์ .proto ) จะแสดงรายการภายในโหนดบริการในไฟล์บัฟเฟอร์ของโปรโตคอล ในฐานะนักพัฒนา เราจะต้องกำหนดฟังก์ชันเหล่านี้และพารามิเตอร์โดยใช้ระบบ Rich Type ของบัฟเฟอร์โปรโตคอล ระบบนี้สนับสนุนประเภทตัวเลขและวันที่ รายการ พจนานุกรม และค่า nullables ต่างๆ เพื่อกำหนดข้อความอินพุตและเอาต์พุตของเรา

ข้อกำหนดบริการเหล่านี้ต้องพร้อมใช้งานสำหรับทั้งเซิร์ฟเวอร์และไคลเอ็นต์ ขออภัย ไม่มีกลไกเริ่มต้นในการแบ่งปันคำจำกัดความเหล่านี้ นอกเหนือจากการให้การเข้าถึงโดยตรงไปยังไฟล์ . .proto

ไฟล์ .proto ตัวอย่างนี้กำหนดฟังก์ชันเพื่อส่งคืนรายการผลิตภัณฑ์ โดยระบุ ID:

 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; }
Snippet 1: ProductCatalog คำจำกัดความของบริการ

การพิมพ์ที่เข้มงวดและการจัดลำดับฟิลด์ของ proto3 ทำให้การดีซีเรียลไลเซชันข้อความมีการเก็บภาษีน้อยกว่าการแยกวิเคราะห์ JSON

การเปรียบเทียบ REST กับ gRPC

สรุป จุดที่สำคัญที่สุดเมื่อเปรียบเทียบ REST กับ gRPC คือ:

พักผ่อน gRPC
ข้ามแพลตฟอร์ม ใช่ ใช่
รูปแบบข้อความ กำหนดเองแต่โดยทั่วไป JSON หรือ XML บัฟเฟอร์โปรโตคอล
ขนาดเพย์โหลดข้อความ กลาง/ใหญ่ เล็ก
ความซับซ้อนในการประมวลผล สูงกว่า (การแยกวิเคราะห์ข้อความ) ต่ำกว่า (โครงสร้างไบนารีที่กำหนดไว้อย่างดี)
รองรับเบราว์เซอร์ ใช่ (ดั้งเดิม) ใช่ (ผ่าน gRPC-เว็บ)

ในกรณีที่คาดว่าจะมีสัญญาที่เข้มงวดน้อยกว่าและต้องเพิ่มน้ำหนักบรรทุกบ่อยครั้ง 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" ในฟิลด์ Location และพิมพ์ "Inventory" ในฟิลด์ชื่อโซลูชัน . เราไม่เลือก "วางโซลูชันและโครงการในไดเรกทอรีเดียวกัน"

ทีนี้มาเลือกกัน NET 6.0 (การสนับสนุนระยะยาว) ตัวเลือกสำหรับเฟรมเวิร์กของเรา:

กล่องโต้ตอบข้อมูลเพิ่มเติมภายใน Visual Studio 2022 ในหน้าจอนี้ เราเลือก ".NET 6.0 (การสนับสนุนระยะยาว)" จากเมนูดร็อปดาวน์ของกรอบงาน เราปล่อยให้ "เปิดใช้งาน 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" } }); } } }
Snippet 2: ProductCatalogService

บริการส่งคืนผลิตภัณฑ์แบบฮาร์ดโค้ดแล้ว เพื่อให้การบริการทำงานได้ เราต้องเปลี่ยนการลงทะเบียนบริการใน Program.cs เพื่ออ้างอิงชื่อบริการใหม่เท่านั้น ในกรณีของเรา เราจะเปลี่ยนชื่อ app.MapGrpcService<GreeterService>(); ไปที่ app.MapGrpcService<ProductCatalogService>(); เพื่อให้ API ใหม่ของเราทำงานได้

คำเตือนที่เป็นธรรม: ไม่ใช่การทดสอบโปรโตคอลมาตรฐานของคุณ

แม้ว่าเราอาจถูกล่อลวงให้ลองใช้งาน แต่เราไม่สามารถทดสอบบริการ gRPC ของเราผ่านเบราว์เซอร์ที่มุ่งไปที่ปลายทางได้ หากเราพยายามทำเช่นนี้ เราจะได้รับข้อความแสดงข้อผิดพลาดที่ระบุว่าการสื่อสารกับปลายทาง gRPC ต้องทำผ่านไคลเอ็นต์ gRPC

การสร้างลูกค้า

ในการทดสอบบริการของเรา ให้ใช้เทมเพลต Console App พื้นฐานของ 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();
Snippet 3: New Program.cs

เตรียมเปิดตัว

ในการทดสอบโค้ดของเรา ใน VS เราจะคลิกขวาที่โซลูชันและเลือก Set Startup Projects ในกล่องโต้ตอบหน้าคุณสมบัติของโซลูชัน เราจะ:

  • เลือกปุ่มตัวเลือกข้าง Multiple startupโปรเจ็ กต์ และในเมนูดรอปดาวน์ Action ตั้งค่าทั้งสองโปรเจ็กต์ ( InventoryAPI และ InventoryApp ) เป็น Start
  • คลิก ตกลง

ตอนนี้ เราสามารถเริ่มวิธีแก้ปัญหาโดยคลิก เริ่ม ในแถบเครื่องมือ VS (หรือโดยการกดปุ่ม F5 ) หน้าต่างคอนโซลใหม่สองบานจะแสดงขึ้น: อันหนึ่งเพื่อบอกเราว่าบริการกำลังฟังอยู่ อีกบานหนึ่งเพื่อแสดงรายละเอียดของผลิตภัณฑ์ที่ดึงมา

การแบ่งปันสัญญา gRPC

ตอนนี้ ลองใช้วิธีอื่นในการเชื่อมต่อไคลเอ็นต์ gRPC กับคำจำกัดความของบริการของเรา โซลูชันการแชร์สัญญาที่ลูกค้าเข้าถึงได้มากที่สุดคือการทำให้คำจำกัดความของเราพร้อมใช้งานผ่าน URL ตัวเลือกอื่นอาจเปราะบางมาก (ไฟล์ที่แชร์ผ่านพาธ) หรือต้องใช้ความพยายามมากขึ้น (สัญญาที่แชร์ผ่านแพ็กเกจดั้งเดิม) การแชร์ผ่าน URL (เช่น SOAP และ Swagger/OpenAPI) มีความยืดหยุ่นและต้องใช้โค้ดน้อยกว่า

ในการเริ่มต้น ให้ไฟล์ .proto พร้อมใช้งานเป็นเนื้อหาแบบคงที่ เราจะอัปเดตโค้ดของเราด้วยตนเองเนื่องจาก UI ในการดำเนินการบิลด์ถูกตั้งค่าเป็น "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>
Snippet 4: Code to Add to the InventoryAPI Service Project File

ต่อไป เราแทรกโค้ดใน Snippet 5 ที่ด้านบนของไฟล์ ProductCatalogService.cs เพื่อตั้งค่าปลายทางเพื่อส่งคืนไฟล์ .proto ของเรา:

 using System.Net.Mime; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.FileProviders;
Snippet 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();
Snippet 6: รหัสสำหรับสร้างไฟล์ .proto สามารถเข้าถึงได้ผ่าน API

เมื่อเพิ่ม 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 เราจะทำซ้ำขั้นตอนตามรายละเอียดในส่วน การสร้างโครงการ API แต่คราวนี้เราจะเลือกเทมเพลต แอปคอนโซล เราจะตั้งชื่อโครงการและโซลูชันของเรา InventoryAppConnected
  4. เมื่อสร้างแชสซีไคลเอ็นต์แล้ว เราจะเชื่อมต่อกับเซิร์ฟเวอร์ gRPC ของเรา ขยายโครงการใหม่ใน VS Solution Explorer
    1. คลิกขวาที่ Dependencies และในเมนูบริบท ให้เลือก Manage Connected Services
    2. บนแท็บ Connected Services ให้คลิก Add a service reference แล้วเลือก gRPC
    3. ในกล่องโต้ตอบเพิ่มการอ้างอิงบริการ ให้เลือกตัวเลือก URL และป้อนเวอร์ชัน http ของที่อยู่บริการ (อย่าลืมดึงหมายเลขพอร์ตที่สร้างแบบสุ่มจาก launchsettings.json )
    4. คลิก เสร็จสิ้น เพื่อเพิ่มการอ้างอิงบริการที่สามารถดูแลรักษาได้ง่าย

อย่าลังเลที่จะตรวจสอบงานของคุณกับโค้ดตัวอย่างสำหรับตัวอย่างนี้ เนื่องจากภายใต้ประทุน VS ได้สร้างไคลเอนต์เดียวกันกับที่เราใช้ในการทดสอบรอบแรกของเรา เราจึงสามารถใช้เนื้อหาของไฟล์ Program.cs ซ้ำจากคำต่อคำของบริการก่อนหน้าได้

เมื่อเราเปลี่ยนสัญญา เราจำเป็นต้องแก้ไขข้อกำหนด .proto ของลูกค้าเพื่อให้ตรงกับคำจำกัดความ .proto ที่อัปเดต ในการดำเนินการดังกล่าว เราจำเป็นต้องเข้าถึง Connected Services ของ VS และรีเฟรชรายการบริการที่เกี่ยวข้องเท่านั้น ตอนนี้ โปรเจ็กต์ gRPC ของเราเสร็จสมบูรณ์แล้ว และง่ายต่อการซิงค์บริการและไคลเอ็นต์ของเรา

ผู้สมัครโครงการต่อไปของคุณ: gRPC

การใช้งาน gRPC ของเราช่วยให้เห็นประโยชน์ของการใช้ gRPC ได้โดยตรง REST และ gRPC ต่างก็มีกรณีการใช้งานที่เหมาะสมที่สุด ขึ้นอยู่กับประเภทของสัญญา อย่างไรก็ตาม เมื่อทั้งสองตัวเลือกเหมาะสม เราขอแนะนำให้คุณลองใช้ gRPC ซึ่งจะทำให้คุณล้ำหน้าในอนาคตของ API