Triển khai microservices với gRPC và ASP.NET Core 3.1

Trong hướng dẫn này, bạn sẽ tìm hiểu gRPC là gì và cách bạn có thể sử dụng nó trong .NET Core 3.1 trở lên để xây dựng các dịch vụ hiệu suất cao trong  microservices. Trong suốt hướng dẫn, bạn sẽ xây dựng một dịch vụ và một ứng dụng client sẽ giới thiệu cho bạn chi tiết về cách sử dụng gRPC trong C#.

gRPC là gì?

gRPC là một framework để kết nối các dịch vụ và xây dựng hệ thống phân tán một cách hiệu quả. Ban đầu được thiết kế bởi Google, giờ đây nó là một dự án mã nguồn mở thúc đẩy mô hình Remote Procedure Call (RPC) để giao tiếp giữa các dịch vụ. Nó tập trung vào hiệu suất cao và sử dụng giao thức HTTP/2 để truyền tải các message nhị phân. Nó cũng dựa vào ngôn ngữ Protocal Buffer để định nghĩa các service contract. Protocal Buffer, còn được gọi là Protobuf, cho phép bạn định nghĩa interface được sử dụng trong dịch vụ để phục vụ giao tiếp độc lập với ngôn ngữ lập trình. Nhiều công cụ cho các ngôn ngữ lập trình phổ biến nhất có sẵn để dịch các interface Protobuf này thành mã.

gRPC framework cho phép các nhà phát triển tạo ra các dịch vụ có thể giao tiếp với nhau một cách hiệu quả và độc lập từ ngôn ngữ lập trình ưa thích của họ. Khi bạn định nghĩa service contract với Protobuf, service contract này có thể được sử dụng bởi từng dịch vụ để tự động tạo mã thiết lập cơ sở hạ tầng truyền thông. Khía cạnh này đơn giản hóa việc tạo ra sự tương tác của các dịch vụ và cùng với hiệu suất cao, làm cho gRPC trở thành framework lý tưởng để xây dựng các dịch vụ trong microservices.

Ngoài ra, gRPC đi kèm với bốn loại RPC khác nhau:

  • Unary RPC: đây là dạng RPC đơn giản nhất. Trong trường hợp này, client gửi một yêu cầu đến server và nhận được phản hồi.
  • Server streaming RPC: trong trường hợp này, client gửi một yêu cầu đến server và nhận được một chuỗi phản hồi.
  • Client streaming RPC: đây là trường hợp client gửi một chuỗi yêu cầu và nhận được một phản hồi duy nhất từ ​​server.
  • Bidirectional streaming RPC: trong trường hợp này, client và server trao đổi thông điệp theo cả hai hướng.

Sơ đồ sau đây tóm tắt kiến ​​trúc tổng thể của một hệ thống dựa trên gRPC cơ bản:

Sơ đồ kiến ​​trúc tổng thể của một hệ thống dựa trên gRPC

HTTP Web API và gRPC

Vì gRPC rất hiệu quả và tiện dụng, tại sao nó không thay thế công nghệ HTTP Web API nổi tiếng?

Có một số yếu tố cần tính đến. Web API, đặc biệt nếu được lấy cảm hứng từ kiến ​​trúc REST, dựa trên một mô hình lập trình khác. gRPC framework đề xuất mô hình RPC, đây là một mô hình trong đó client gọi một thủ tục từ xa sẽ được thực thi trên server. Web API tiêu chuẩn dựa trên cách tiếp cận hướng đến tài nguyên, đây là một mô hình trong đó client yêu cầu tài nguyên và áp dụng các hoạt động CRUD đơn giản trên các tài nguyên này.

Ngoài ra, Web API dựa trên giao thức HTTP/1.1, trong khi gRPC sử dụng HTTP/2. Ngoài ra, dữ liệu được trao đổi bởi Web API thường ở dạng văn bản, con người có thể đọc được (thường là JSON), trong khi gRPC sử dụng định dạng nhị phân nhỏ gọn. Mô hình tương tác trong Web API là client-server, trong khi gRPC hỗ trợ nhiều mô hình: từ client-server đến streaming hai chiều. Cuối cùng, Web API hỗ trợ trình duyệt, trong khi gRPC thì không.

Tóm lại, Web API và gRPC sử dụng các mô hình khác nhau để giao tiếp và có các trường hợp sử dụng khác nhau. Vì vậy, cả hai công nghệ này sẽ tiếp tục cùng tồn tại trong những năm tới.

gRPC và .NET Core

Trước khi phát hành .NET Core 3.0, bạn có thể sử dụng gRPC trong các ứng dụng C# của mình bằng cách sử dụng thư viện Grpc.Core. Về cơ bản, nó là một trình bao bọc C# xung quanh thư viện gRPC Core được viết bằng C. Nó không hoạt động tốt với Kestrel và các thư viện mã được quản lý khác như HttpClient. Bạn cũng không có bất kỳ project template nào giúp bạn thiết lập nhanh ứng dụng của mình với hỗ trợ gRPC.

Bắt đầu từ .NET Core 3.0, bạn có thể tận dụng thư viện grpc-dotnet, hoàn toàn được viết bằng mã được quản lý và được tích hợp tốt với nền tảng .NET. Nó cũng cung cấp cho bạn một project template cụ thể giúp bạn thiết lập ứng dụng của mình rất nhanh chóng, như bạn sẽ thấy trong hướng dẫn này.

Trong tương lai, bạn sẽ học cách sử dụng gRPC trong .NET Core 3.1 bằng cách xây dựng một dịch vụ đơn giản và một client giao tiếp thông qua mô hình Unary RPC.

Điều kiện tiên quyết

Trước khi bắt đầu, hãy đảm bảo bạn đã cài đặt .NET Core 3.1 SDK bằng cách nhập lệnh sau vào cửa sổ dòng lệnh:

dotnet --version

Kết quả là bạn sẽ nhận được giá trị 3.1.100 hoặc cao hơn. Nếu không, bạn nên tải xuống .NET Core 3.1 SDK và cài đặt nó trên máy của mình.

Lưu ý: Nếu bạn có phiên bản mới nhất của Visual Studio, bạn đã gói .NET Core 3.1 SDK.

Tạo dịch vụ gRPC

Ứng dụng bạn sẽ xây dựng trong hướng dẫn này là một dịch vụ đánh giá liệu khách hàng có được quyền nhận tín dụng cho một số tiền nhất định hay không. Sau đó, bạn cũng sẽ xây dựng một ứng dụng client yêu cầu dịch vụ này.

Bạn mở Visual Studio lên, tạo project mới bằng template gRPC như hình sau:

Tạo dịch vụ gRPC

Bạn đặt tên project là CreditRatingService.

Định nghĩa service contract

Bước đầu tiên trong quá trình tạo dịch vụ của bạn là định nghĩa service contract - là interface mà dịch vụ của bạn hiển thị để chấp nhận các yêu cầu đánh giá của client. Như đã nói trước đây, trong gRPC framework, interface này được định nghĩa thông qua Protobuf. Đặc biệt, định nghĩa này được lưu trữ trong file có định dạng .proto.

Vì vậy, hãy di chuyển vào thư mục CreditRatingService/Protos và xóa file greet.proto. Sau đó, thêm một file mới có tên credit-rating-service.proto trong cùng một thư mục và đưa nội dung sau vào đó:

// Protos/credit-rating-service.proto

syntax = "proto3";

option csharp_namespace = "CreditRatingService";

package CreditRating;

service CreditRatingCheck {
  rpc CheckCreditRequest (CreditRequest) returns (CreditReply);
}

message CreditRequest {
  string customerId = 1;
  int32 credit = 2;
}

message CreditReply {
  bool isAccepted = 1;
}

Hai hàng đầu tiên của file .proto khai báo phiên bản cú pháp Protobuf bạn muốn sử dụng và namespace C# nơi interface sẽ được triển khai. Sau đó, bạn định nghĩa package CreditRating. Chỉ thị package giúp ngăn chặn xung đột tên giữa các kiểu message.

Các mục tiếp theo trong file .proto định nghĩa interface dịch vụ RPC và hai kiểu message.

Interface dịch vụ RPC được đặt tên CreditRatingCheck và chứa một mục được đánh dấu bằng từ khóa rpc. Hãy coi nó như một lớp với các phương thức của nó. Vì vậy, nếu dịch vụ của bạn có nhiều chức năng, bạn sẽ có nhiều định nghĩa rpc. Mỗi định nghĩa rpc bao gồm tên, danh sách các kiểu đầu vào và kiểu đầu ra. Trong ví dụ trên, bạn định nghĩa một rpc tên là CheckCreditRequest với đầu vào là message CreditRequest và trả về kiểu CreditReply. Cả hai kiểu message này đều được định nghĩa bên dưới định nghĩa dịch vụ.

Các khai báo message định nghĩa cấu trúc của một thông diệp bao gồm một danh sách các trường với kiểu dữ liệu tương ứng. Bạn có thể sử dụng kiểu vô hướng , kiểu liệt kê hoặc các kiểu thông báo khác làm kiểu của mỗi trường. Ngoài ra, bạn có thể thấy rằng mỗi trường có một số duy nhất được chỉ định. Những con số này được sử dụng để xác định thứ tự các trường của bạn trong thông điệp sau khi chúng được chuyển đổi sang định dạng nhị phân. Bạn không nên thay đổi những con số này khi ứng dụng của bạn đang hoạt động. Trong ví dụ trên, bạn đã định nghĩa một message CreditRequest bao gồm mã định danh khách hàng và tín dụng được yêu cầu và message CreditReply chỉ chứa một giá trị boolean cho biết liệu tín dụng đã được chấp nhận hay chưa.

Sau khi service contract của bạn được định nghĩa, bạn cần làm cho ứng dụng của mình biết về file .proto này . Vì vậy, hãy cập nhật file CreditRatingService.csproj bằng cách thay thế nội dung của nó bằng nội dung sau:

<!-- CreditRatingService.csproj -->
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <Protobuf Include="Protos\credit-rating-service.proto" GrpcServices="Server" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Grpc.AspNetCore" Version="2.23.2" />
  </ItemGroup>
</Project>

Ở đây, phần có liên quan là sự hiện diện của phần tử Protobuf tham chiếu đến file .proto bạn vừa tạo và gán giá trị Server cho thuộc tính GrpcServices. Thông tin này cho phép hệ thống tạo ra mã C# cần thiết để hỗ trợ cơ sở hạ tầng cơ bản cho giao tiếp gRPC. Đặc biệt, nó sẽ tạo ra mã cần thiết cho phía server.

Triển khai dịch vụ

Sau khi định nghĩa service contract, hãy triển khai dịch vụ của bạn mà không cần lo lắng về lớp giao tiếp. Bạn chỉ cần di chuyển vào thư mục Services, đổi tên file GreeterService.cs thành CreditRatingService.cs và thay thế nội dung của nó bằng mã này:

// Services/CreditRatingService.cs

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Grpc.Core;
using Microsoft.Extensions.Logging;

namespace CreditRatingService
{
    public class CreditRatingCheckService: CreditRatingCheck.CreditRatingCheckBase
    {
        private readonly ILogger<CreditRatingCheckService> _logger;
        private static readonly Dictionary<string, Int32> customerTrustedCredit = new Dictionary<string, Int32>() 
        {
            {"id0201", 10000},
            {"id0417", 5000},
            {"id0306", 15000}
        };
        
        public CreditRatingCheckService(ILogger<CreditRatingCheckService> logger)
        {
            _logger = logger;
        }

        public override Task<CreditReply> CheckCreditRequest(CreditRequest request, ServerCallContext context)
        {
            return Task.FromResult(new CreditReply
            {
                IsAccepted = IsEligibleForCredit(request.CustomerId, request.Credit)
            });
        }

        private bool IsEligibleForCredit(string customerId, Int32 credit) {
            bool isEligible = false;

            if (customerTrustedCredit.TryGetValue(customerId, out Int32 maxCredit))
            {
                isEligible = credit <= maxCredit;
            }

            return isEligible;
        }
    }
}

Ở đây bạn đang triển khai lớp CreditRatingCheckService bằng cách kế thừa từ lớp CreditRatingCheck.CreditRatingCheckBase. Lớp cơ sở này được Visual Studio tạo tự động từ file .proto. Trong trường hợp này, nó được xây dựng từ định nghĩa dịch vụ CreditRatingCheck và nó đảm bảo rằng tất cả cơ sở hạ tầng truyền thông gRPC đều có sẵn cho việc triển khai của bạn.

Như bạn có thể thấy, phương thức CheckCreditRequest() không có gì khác hơn là việc triển khai định nghĩa rpc trong file .proto. Nó sử dụng CreditRequest và  CreditReply dựa trên các định nghĩa message trong file .proto. Việc đánh giá yêu cầu tín dụng thực tế được thực hiện trong phương thức IsEligibleForCredit(). Tất nhiên, nó mô phỏng việc đánh giá bằng cách đơn giản so sánh tín dụng được yêu cầu với tín dụng tối đa được phép cho khách hàng đó trong dictionary customerTrustedCredit.

Sau khi triển khai dịch vụ, đã đến lúc tích hợp nó vào cơ sở hạ tầng gRPC. Mở file Startup.cs và thay thế nội dung của nó bằng nội dung sau:

// Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace CreditRatingService
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddGrpc();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGrpcService<CreditRatingCheckService>();

                endpoints.MapGet("/", async context =>
                {
                    await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client.");
                });
            });
        }
    }
}

Trong phương thức ConfigureServices() của lớp Startup thêm gRPC vào các dịch vụ ứng dụng. Trong phương thức Configure(), bạn sẽ thấy định nghĩa của các endpoint. Lưu ý cách phương thức MapGrpcService() ánh xạ việc triển khai lớp CreditRatingCheckService với cơ sở hạ tầng gRPC.

Bây giờ, hãy nhấn F5 để chạy dịch vụ của bạn.

Lưu ý: Nếu bạn gặp lỗi ngay sau khi chạy dịch vụ, đừng lo lắng. Nó sẽ được giải quyết trong giây lát.

Nếu tất cả diễn ra như mong đợi, dịch vụ gRPC đầu tiên của bạn đang chạy. Tất nhiên, để tương tác với dịch vụ này và kiểm tra xem nó có hoạt động như mong đợi hay không, bạn cần một ứng dụng gRPC client. Đây là những gì bạn sẽ xây dựng trong phần tiếp theo.

Khắc phục sự cố khi chạy dịch vụ

Khi khởi động dịch vụ gRPC, bạn có thể gặp lỗi như sau:

Microsoft.AspNetCore.Server.Kestrel[0]
      Unable to start Kestrel.
System.IO.IOException: Failed to bind to address https://localhost:5001.
 ---> System.AggregateException: One or more errors occurred. (HTTP/2 over TLS is not supported on macOS due to missing ALPN support.) (HTTP/2 over TLS is not supported on macOS due to missing ALPN support.)
 ---> System.NotSupportedException: HTTP/2 over TLS is not supported on macOS due to missing ALPN support.

Đây là một sự cố đã biết ảnh hưởng đến macOS và các phiên bản Windows cũ hơn. Điều này xảy ra vì Kestrel, máy chủ Web tích hợp được ASP.NET sử dụng, không hỗ trợ HTTP/2 với TLS trên các hệ điều hành này. TLS là một trong những ràng buộc nằm trong khuôn khổ gRPC. Để tuân theo ràng buộc này, mẫu dự án .NET Core gRPC sử dụng TLS theo mặc định. Vì vậy, một giải pháp khả thi cho vấn đề này trong các hệ điều hành đó là sử dụng HTTP/2 mà không có TLS.

Lưu ý: Bạn chỉ nên tắt hỗ trợ TLS trong dịch vụ gRPC của mình trong quá trình phát triển.

Vì vậy, hãy mở file Program.cs và thay thế nội dung của nó bằng đoạn mã sau:

// Program.cs

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using System.Runtime.InteropServices;

namespace CreditRatingService
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

       public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
                     webBuilder.ConfigureKestrel(options =>
                     {
                         // Setup a HTTP/2 endpoint without TLS.
                         options.ListenLocalhost(5000, o => o.Protocols = 
                             HttpProtocols.Http2);
                     });
                    }
                    webBuilder.UseStartup<Startup>();
                });
    }
}

So với phiên bản gốc, ở đây bạn đã thêm namespace Microsoft.AspNetCore.Server.Kestrel.CoreSystem.Runtime.InteropServices. Ngoài ra, khi hệ điều hành hiện tại là OSX, bạn buộc Kestrel chỉ sử dụng giao thức HTTP/2. Lưu ý rằng phương thức options.ListenLocalhost() đặt cổng lắng nghe là 5000.

Tất nhiên, ứng dụng gRPC client sẽ giao tiếp với máy chủ này cũng sẽ không sử dụng TLS.

Tạo ứng dụng gRPC client

Bây giờ bạn đã sẵn sàng để xây dựng ứng dụng gRPC client sẽ sử dụng dịch vụ mà bạn đã triển khai trong phần trước. Bạn mở Visual Studio lên và tạo một project .NET Core Console như sau:

Tạo ứng dụng gRPC client

Bạn đặt tên project là CreditRatingClient.

Sau khi tạo project xong, bạn cài đặt các gói sau cho project thông qua NuGet:

  • Grpc.Net.Client
  • Google.Protobuf
  • Grpc.Tools

Gói Grpc.Net.Client cung cấp các lớp cơ bản để xây dựng một gRPC client, trong khi thư viện Google.Protobuf cho phép ứng dụng của bạn quản lý Protocol Buffer.

Gói Grpc.Tools biên dịch file .proto thành mã C#. Nó chịu trách nhiệm tạo mã thiết lập cơ sở hạ tầng cơ bản cho giao tiếp gRPC. Gói này chỉ được yêu cầu tại thời điểm xây dựng và không nên được đưa vào đầu ra được tạo của quá trình xây dựng. Vì lý do này, quá trình cài đặt đánh dấu tham chiếu đến gói Grpc.Tools có thuộc tính PrivateAssets.

Sử dụng service contract

gRPC Client của bạn sẽ giao tiếp với dịch vụ gRPC mà bạn vừa xây dựng. Vì vậy, gRPC client của bạn cần biết chi tiết về cách gọi thủ tục từ xa được cung cấp bởi dịch vụ, kiểu dữ liệu nào cần chuyển và kiểu dữ liệu nào mong đợi là kết quả.

Bạn có thể thu thập tất cả thông tin này bằng cách sử dụng service contract mà bạn đã xác định trong file .proto ở phía server. Vì vậy, hãy tạo một thư mục Protos trong project CreditRatingClient. Sau đó, sao chép file credit-rating-service.proto từ thư mục Protos của dịch vụ gRPC vào thư mục Protos mới này của dự án gRPC client.

Sau thao tác này, hãy chỉnh sửa file CreditRatingClient.csproj trong dự án gRPC client và thêm ItemGroup có phần tử Protobuf trỏ đến file credit-rating-service.proto. Kết quả cuối cùng sẽ như sau:

<!-- CreditRatingClient.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Google.Protobuf" Version="3.10.1" />
    <PackageReference Include="Grpc.Net.Client" Version="2.25.0" />
    <PackageReference Include="Grpc.Tools" Version="2.25.0">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>
  <ItemGroup>
    <Protobuf Include="Protos\credit-rating-service.proto" GrpcServices="Client" />
  </ItemGroup>
</Project>

Lưu ý rằng, trong trường hợp này, phần tử Protobuf có thuộc tính GrpcServices với giá trị Client. Điều này làm cho hệ thống tạo ra mã C# cần thiết cho phía client của hệ thống gRPC của bạn. Mã phía client này thường được gọi là sơ khai.

Khi bạn lưu project, Visual Studio sẽ tự tạo các class cần thiết cho gRPC.

Để triển khai logic của ứng dụng gRPC client của bạn, hãy chỉnh sửa file Program.cs và thay thế nội dung của nó bằng mã sau:

// Program.cs

using System;
using System.Threading.Tasks;
using CreditRatingService;
using Grpc.Net.Client;

namespace GrpcGreeterClient
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // The port number(5001) must match the port of the gRPC server.
            var channel = GrpcChannel.ForAddress("https://localhost:5001");
            var client =  new CreditRatingCheck.CreditRatingCheckClient(channel);
            var creditRequest = new CreditRequest { CustomerId = "id0201", Credit = 7000};
            var reply = await client.CheckCreditRequestAsync(creditRequest);

            Console.WriteLine($"Credit for customer {creditRequest.CustomerId} {(reply.IsAccepted ? "approved" : "rejected")}!");
            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
        }
    }
}

Mã này tạo kênh gRPC trên cổng 5001 của máy hiện tại. Đây là cổng mặc định cũng được sử dụng ở phía máy chủ. Kênh này được chuyển đến phương thức khởi tạo của lớp CreditRatingCheckClient, là ứng dụng gRPC client thực tế. Sau đó, khởi tạo message kiểu CreditRequest cho yêu cầu được gửi đến dịch vụ. Cuối cùng, message này được gửi đến dịch vụ từ xa bằng cách gọi phương thức CheckCreditRequestAsync() của gRPC client.

Bạn nhận được phản hồi từ dịch vụ trong biến reply và sử dụng thuộc tính IsAccepted của nó để hiển thị thông báo thích hợp trên ứng dụng console.

Client và Server cùng hoạt động

Tại thời điểm này, bạn có cả dịch vụ gRPC và gRPC client đã sẵn sàng để giao tiếp. Vì vậy, hãy đảm bảo rằng dịch vụ gRPC của bạn đang chạy. Nhấn F5 để chạy ứng dụng gRPC client. Trong cửa sổ dòng lệnh, bạn sẽ thấy thông báo sau:

Credit for customer id0201 approved!
Press any key to exit...

Điều này có nghĩa là gRPC client của bạn đã gửi yêu cầu đến dịch vụ của bạn và nhận được phản hồi như mong đợi.

Bạn có thể thử thay đổi các thông số của yêu cầu để nhận được các phản hồi khác nhau.

Khắc phục sự cố khi bắt đầu ứng dụng khách

Nếu bạn cần tắt TLS trong dịch vụ gRPC của mình vì sự cố được đề cập ở trên, bạn sẽ gặp lỗi sau khi client cố gắng gọi dịch vụ của bạn:

Unhandled exception. Grpc.Core.RpcException: Status(StatusCode=Internal, Detail="Error starting gRPC call: Connection refused")
   at GrpcGreeterClient.Program.Main(String[] args) in /Users/andreachiarelli/Documents/Playground/grpc-dotnet/CreditRatingClient/Program.cs:line 17
   at GrpcGreeterClient.Program.<Main>(String[] args)

Điều này xảy ra vì, như đã nói trước đây, gRPC yêu cầu kết nối được mã hóa, nhưng dịch vụ của bạn không hỗ trợ TLS. Bạn cũng cần phải tắt TLS trên ứng dụng client của mình bằng cách áp dụng một chút thay đổi đối với mã của nó.

Mở file Program.cs và thay thế nội dung của nó bằng nội dung sau:

// Program.cs

using System;
using System.Threading.Tasks;
using CreditRatingService;
using Grpc.Net.Client;
using System.Runtime.InteropServices;

namespace CreditRatingClient
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var serverAddress = "https://localhost:5001";

            if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
                // The following statement allows you to call insecure services. To be used only in development environments.
                AppContext.SetSwitch(
                    "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
                serverAddress = "http://localhost:5000";
            }

            var channel = GrpcChannel.ForAddress(serverAddress);
            var client =  new CreditRatingCheck.CreditRatingCheckClient(channel);
            var creditRequest = new CreditRequest { CustomerId = "id0201", Credit = 7000};
            var reply = await client.CheckCreditRequestAsync(creditRequest);

            Console.WriteLine($"Credit for customer {creditRequest.CustomerId} {(reply.IsAccepted ? "approved" : "rejected")}!");
            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
        }
    }
}

Sự khác biệt đầu tiên là việc thêm namespace System.Runtime.InteropServices, như bạn đã làm ở phía server. Namespace này cho phép bạn kiểm tra xem hệ điều hành hiện tại có phải là OSX hay không. Trong trường hợp này, câu lệnh AppContext.SetSwitch() vô hiệu hóa hỗ trợ TLS và bạn chỉ định một giá trị khác cho biến serverAddress. Địa chỉ mới sử dụng giao thức HTTP trên cổng 5000, cổng mặc định cho các giao tiếp không an toàn trong Kestrel.

Lưu ý: Hãy nhớ rằng bạn chỉ nên tắt TLS trong quá trình phát triển.
ASP.NET Core Web APIMicroservicesASP.NET Core
Bài Viết Liên Quan:
Load test cho ứng dụng ASP.NET Core với Apache JMeter
Trung Nguyen 19/06/2021
Load test cho ứng dụng ASP.NET Core với Apache JMeter

Trong bài viết này, bạn sẽ tìm hiểu sâu về Apache JMeter để load test cho một ứng dụng REST API được viết bằng ASP.NET Core.

Distributed Tracing trong ASP.NET Core với Jaeger và Tye
Trung Nguyen 01/05/2021
Distributed Tracing trong ASP.NET Core với Jaeger và Tye

Trong bài viết này, chúng ta sẽ tìm hiểu về tiêu chuẩn kỹ thuật open tracing và sử dụng Jaeger để theo dõi phân tán cho các microservices viết bằng ASP.NET Core.

Cách xây dựng REST API sử dụng ASP.NET Core, Entity Framework Core và JWT
Trung Nguyen 08/04/2021
Cách xây dựng REST API sử dụng ASP.NET Core, Entity Framework Core và JWT

Trong hướng dẫn này, chúng ta sẽ học cách tạo CRUD REST API bằng ASP.NET Core, Entity Framework Core và bảo mật API bằng JWT.

Hướng dẫn tuyệt vời về cách xây dựng API RESTful với ASP.NET Core
Trung Nguyen 04/10/2020
Hướng dẫn tuyệt vời về cách xây dựng API RESTful với ASP.NET Core

Hướng dẫn từng bước về cách triển khai các API RESTful sạch, có thể bảo trì sử dụng ASP.NET Core.