프로젝트가 Window Background Service Worker에 구축 하다보니,
Service Worker 중심 기록이지만 일반 서비스라는 점만 빼면 똑같아보인다.

죄다 Greeter만 가지고 해서 실제 프로젝트 데이터로 기록.

 

순서

1. ".proto" 파일 만들기

2. gRPC Package 및 종속성 연결 (**)

3. ".proto"에 정의된 서비스 활용

4. Service Worker의 Program.cs에서 GrpcClient 정의


 

1. ".proto"파일 만들기

 

syntax = "proto3";

// well known types
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";

option csharp_namespace = "Framework.Grpc.Common";

package Framework.Grpc.Common;

service Command {
    rpc ManualMove(ManualMoveRequest) returns (CommonResponse) {}
    rpc GetVehicleStatus(VehicleStatusRequest) returns (VehicleStatus) {}
}

message ManualMoveRequest {
  string VehicleId = 1;
  string DestNode = 2;
}

message CommonResponse {
  bool Result = 1;
}

실제 프로젝트에서 통신하는 프로토 파일의 일부이다.

위 proto는 Server, Client에서 모두 똑같아야한다.
.proto에서 정의된 namespace도 동일해야한다.

그렇지 않으면 호출시 서로 정의된 함수가 없다고 Exception 발생.

위 코드에 대해서 하나씩 파악해보자. 원리는... 검색해서 공부하는걸로 하고

 

1.1. namespace, package

Framework.Grpc라는 Project의 Common 폴더안에 있는 파일이고,
다른 Project에서 저 위치를 참조할 것이다.


1.2 service Command

proto문서에서 다른 코드에서 사용할때 Class 문법처럼 사용된다.
실제로 다른 클래스에서 참조하여 사용 할 때 아래처럼 정의해서 사용한다.

private Command.Client protoClient { get; set; }

1.3 rpc ManualMove (ManualMoveRequest) returns (CommonResponse) { }

ManualMove라는 이름의 함수.
파라미터는 아래 message로 정의된 ManualMoveRequest.
서버로부터 돌아오는 리턴값은 message로 정의된 CommonResponse.

따라서 서버와 클라이언트는 항상 동일한 proto파일을 가지고 활용한다.

message뒤에 =1, =2로 되어있는건 인덱스 매기는 것으로 파라미터 자체에 영향을 주진 않는다.

 


2. gRPC Package 및 종속성 연결 (**)

 

2.1 Package

Nuget
1. "Grpc.Tools" (제일 중요 .cs로 변환하는 패키지임.)
2. "Google.Protobuf"
3. "Grpc.Core"
4. 필요하다면 Grpc.Client나 Grpc.ClientFactory 등등... 사용할때.

proto파일이 있다고 해당 프로젝트에 종속성을 넣어야 하는건 아니다.
해당 proto 실제로 사용하기위해 정의하는 client Project에 환경을 추가하고, Import해야한다.

 

결과적으로 .proto 빌드시 컴파일러가 내부적으로 .proto를 .cs로 변환된 코드를 가지고 있기 때문에
사용할 위치에 종속성을 추가해야한다.
빌드하고 함수 정의 트래킹 해보면 이상한 파일로 가는 것을 볼 수 있다.

2.2 Project Binding

(사용할) (Client 역할을 할) Project 세팅 ** 중요함.

프로젝트를 더블클릭 혹은 메모장으로 열면 지정된 Property Group과 Item Group을 확인할 수 있다.
아래 문구를 중간 아무데나 Tag depth만 맞게 추가하자.

이거 몇번 잊어버렸다가 패키지는 죄다있는데 바인딩은 안되고 스트레스받았다...

Include경로는 내가 정의한 .proto 파일의 경로이다.

<ItemGroup>
  <Protobuf Include="..\FrameWork.Grpc\Common\command.proto" GrpcServices="Client" />
</ItemGroup>

해당 grpc를 사용할 클래스 파일에 using 쓰는건 기본이니 생략.

 


3.".proto"에 정의된 서비스 활용

예시 하나만 보면 감이 올 것이다.

namespace myClient
{
    public class CommandController
    {
        private Command.Client client { get; set; }
        public void CommandController(Command.Client clientSingletonInjected)
        {
            client = clientSingletonInjected;
        }
        
        public async bool RequestManualMove(string vehicleId, string destNode)
        {
            CommonResponse result = await client.ManualMoveAsync(
                new ManualMoveRequest() {
                    VehicleId = vehicleId,
                    DestNode = destNode
                });
            return result.Result;
        }
    }
}

빌드 후 .proto가 .cs로 내부 변환이되다보니

각 함수에 대해 Async가 생겨있고 CommonResponseManualMoveRequest 데이터 구조도
바로 직접 참조가 가능해진다.

물론...

client.myFunction을 사용할때는 이미 서버와 연결이 되어있어야한다.

 


4. Service Worker의 Program.cs에서 GrpcClient 연결

 

services.AddHostedService<Worker>(); // 원래 있음.


// 또 다른 클래스에서 CommandClient를 사용하기위해 Singleton 사용.
services.AddScoped<CommandClient>();

// AddGrpcClient를 통해 Client를 서비스에 등록해주며
// 동시에 Singleton 주입 가능해진다.
services.AddGrpcClient<Tester.TesterClient>(options =>
{
    options.Address = new Uri("https://localhost:50151");
});

+ Recent posts