Создание сервиса

Последнее обновление: 22.11.2022

Создадим свой сервис grpc. Для этого создадим либо в Visual Studio новый проект по шаблону ASP.NET Core Empty, либо с помощью .NET CLI новый проект по шаблону web:

Создание проекта сервиса grpc с нуля на C# и .NET

Допустим, проект будет называться GrpcServiceApp.

После создания проекта для работы с gRPC добавим в проект Nuget-пакет Grpc.AspNetCore.

Установка пакета Grpc.AspNetCore в проект на C# и .NET

Для файлов Protobuf создадим в проекте новую папку Protos. Далее в эту папку добавим новый файл, который назовем metanit.proto. Если мы работаем в Visual Studio, то мы можем воспользоваться встроенным шаблоном Protocol Buffer File:

Добавление файла protobuf для создания grpc сервиса в проект на C# и .NET

В ином случае можно просто добавить текстовый файл и переименовать его.

В добавленном файле metanit.proto определим следующий код:

syntax = "proto3";

option csharp_namespace = "GrpcServiceApp";

package metanit;

message Request{
	string word = 1;
}

message Response{
	string word = 1;
	string translation = 2;
}

service Translator {
  // определение метода Translate, 
  // который получает сообщение Request
  // и отправляет сообщение Response
  rpc Translate (Request) returns (Response);
}

Здесь определено сообщение Request, которое имеет одно поле - word. Предположим, что клиент будет посылать через это поле слово на перевод.

Также определено сообщение Response, которое представляет ответ сервера клиенту. Оно имеет два поля. Через поле word посылаем запрошенное слово, а через поле translation - его перевод.

Также определен сервис Translator, который имеет один метод Translate. Этот метод принимает сообщение Request и отправляет в ответ сообщение Response.

Далее отредактируем csproj-файл проекта. После добавления Nuget-пакета он выглядит следующим образом:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Grpc.AspNetCore" Version="2.57.0" />
  </ItemGroup>

</Project>

Добавим в него ссылку на файл metanit.proto, изменив следующим образом:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
	
	<ItemGroup>
		<Protobuf Include="Protos\metanit.proto" GrpcServices="Server" />
	</ItemGroup>
	
  <ItemGroup>
    <PackageReference Include="Grpc.AspNetCore" Version="2.57.0" />
  </ItemGroup>
</Project>

Сохраним файл. После этого на основании кода из файла metanit.proto должны автоматически сгенерироваться все необходимые классы C#. Они будут располагаться в проекте в папке obj\Debug\net8.0\Protos

Далее добавим в проект папку Services, а в нее новый класс, который назовем TranslatorService:

Добавление сервиса grpc в проект на C#

Определим в файле TranslatorService.cs следующий код:

using Grpc.Core;

namespace GrpcServiceApp.Services
{
    public class TranslatorService : Translator.TranslatorBase
    {
        Dictionary<string, string> words = new () { {"red", "красный"}, {"green", "зеленый" }, {"blue", "синий" } };
        public override Task<Response> Translate(Request request, ServerCallContext context)
        {
            // получаем отправленное слово
            var word = request.Word;
            Console.WriteLine($"Запрошено слово: {word}");
            // ищем в словаре и получаем его в переменную translation
            if(!words.TryGetValue(word, out var translation))
            {
                // если слово не найдено
                translation = "не найдено";
            }
            // отправляем ответ
            return Task.FromResult(new Response
            {
                Word = word,
                Translation = translation
            });
        }
    }
}

Функционал пакета Grpc.Tools по определению файла metanit.proto автоматически генерирует статический класс, который по умолчанию называется по имени сервиса из файла proto (то есть в данном случае Translator). И этот класс содержит собственно абстрактный класс сервиса C#, который называется по шаблону [имя_сервиса]Base (в данном случае TranslatorBase). То есть чтобы определить в коде C# свою реализацию сервиса, нам надо унаследовать класс от Translator.TranslatorBase.

И здесь класс TranslatorService как раз представляет конкретную реализацию сервиса Translator из файла proto и наследуется от автогенерируемого абстрактного класса Translator.TranslatorBase.

В классе TranslatorService в качестве условной базы данных определяем словарь words, в котором ключи - английские слова, а значения - их русскоязычный перевод.

Для обмена сообщениями с клиентом переопределяем метод Translate, который в качестве первого параметра принимает сообщение от клиента в виде объекта Request. Это объект автосгенерированного класса Request в соответствии с файлом metanit.proto.

Второй параметр - объект ServerCallContext передает контекст вызова сервиса. В данном случае он нам пока не нужен.

В самом методе Translate получаем отправленное клиентом слово:

var word = request.Word;

Пытаемся найти для него в словаре перевод, а в случае остутствия перевода устанавливаем заглушку. И в конце возвращаем объект автосгенерированного класса Response - сообщение, которое получит клиент:

return Task.FromResult(new Response
{
  Word = word,
  Translation = translation
});

Поскольку метод возвращает не просто Response, а Task<Response>, то возвращаем объект через метод Task.FromResult()

Встраиваение сервиса в приложение

Но по умолчанию сервис TranslatorService никак не участвует в обработке запросов. Для этого изменим код в файле Program.cs следующим образом:

using GrpcServiceApp.Services; // пространство имен сервиса TranslatorService

var builder = WebApplication.CreateBuilder(args);
// добавляем сервисы для работы с gRPC
builder.Services.AddGrpc();

var app = builder.Build();

// встраиваем TranslatorService в обработку запроса
app.MapGrpcService<TranslatorService>();
app.MapGet("/", () =>"Hello World!");

app.Run();

Вначале добавляется поддержка сервисов gRPC:

builder.Services.AddGrpc();

Затем встраиваем сервис TranslatorService в систему маршрутизации для обработки запроса:

app.MapGrpcService<TranslatorService>();

И в конце запустим приложение:

Создание с нуля grpc-сервиса в C# и .NET

Создание тестового клиента

Для теста gRPC-сервиса создадим проект консольного приложения, которое пусть будет называться GprcClientApp. И после создания прежде всего добавим в него следующие Nuget-пакеты:

  • Grpc.Net.Client

  • Google.Protobuf

  • Grpc.Tools

Далее создадим в проекте консольного приложения новую папку Protos и в нее скопируем из проекта сервиса файл metanit.proto

Создание консольного клиента для gRPC-сервиса на языке C#
option csharp_namespace = "GrpcServiceApp";

на строку

option csharp_namespace = "GrpcClientApp";

Далее нам надо отредактировать файл проекта с расширением csproj, который называется по имени проекта. После добавления пакетов он выглядит примерно следующим образом:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Google.Protobuf" Version="3.25.1" />
    <PackageReference Include="Grpc.Net.Client" Version="2.59.0" />
    <PackageReference Include="Grpc.Tools" Version="2.59.0">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>

</Project>

В узел <Project> добавим следующий элемент:

<ItemGroup>
  <Protobuf Include="Protos\metanit.proto" GrpcServices="Client" />
</ItemGroup>

То есть после изменения файл проекта будет выглядеть следующим образом:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <Protobuf Include="Protos\metanit.proto" GrpcServices="Client" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Google.Protobuf" Version="3.25.1" />
    <PackageReference Include="Grpc.Net.Client" Version="2.59.0" />
    <PackageReference Include="Grpc.Tools" Version="2.59.0">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>

</Project>

Далее в проекте консольного клиента изменим код файла Program.cs следующим образом:

using Grpc.Net.Client;
using GrpcClientApp;

// список слов для получения перевода
var words = new List<string>() { "red", "yellow", "green" };

// создаем канал для обмена сообщениями с сервером
// параметр - адрес сервера gRPC
using var channel = GrpcChannel.ForAddress("https://localhost:7229");

// создаем клиент
var client = new Translator.TranslatorClient(channel);

// отправляем каждое слово сервису для получения перевода
foreach(var word in words)
{ 
    // формируем сообщение для отправки
    Request request = new Request { Word= word };
    // отправляем сообщение и получаем ответ
    Response response = await client.TranslateAsync(request);
    // выводим слово и его перевод
    Console.WriteLine($"{response.Word} : {response.Translation}");
}

Для взаимодействия с сервисом нужен соответствующий класс клиента. И в проекте клиента опять же с помощью пакета Grpc.Tools будут автоматически генерироваться нужные класса. В частности, будет генерироваться статический класс, который называется по имени сервиса из файла proto (в данном случае Translator). А в этом статическом классе будет создаваться класс клиента, который по умолчанию назвается [название_сервиса_proto]Client (в данном случае TranslatorClient). Соответственно для обращения к клиенту нам необходимо использовать класс Translator.TranslatorClient.

В конструктор этого класса передается канал для взаимодействия с сервисом

using var channel = GrpcChannel.ForAddress("https://localhost:7229");
var client = new Translator.TranslatorClient(channel);

Для отправки сервису сообщений у клиента определен метод Translate/TranslateAsync, который в качестве параметра принимает объект автосгенерированного класса Request, а возвращает - объект автогенерируемого класса Response. И в данном случае в цикле пробегаемся по списку слов, для каждого из них создаем сообщение Request и посылаем на сервер методом TranslateAsync. А в качестве результата получаем объект Response с переводом слова.

Request request = new Request { Word= word };
Response response = await client.TranslateAsync(request);

Запустим проект консольного клиента:

Создание консольного клиента для grpc-сервиса на примере приложения на C# и .NET
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850