본문 바로가기
C#

[C#] gRPC 와 LiteDB 사용

by Jcoder 2020. 12. 15.

gRPC 참고 링크

   - What is gRPC? – gRPC

 

What is gRPC?

A high-performance, open source universal RPC framework

grpc.io

   - gRPC 서비스와 HTTP API 비교 | Microsoft Docs

 

gRPC 서비스와 HTTP API 비교

gRPC와 HTTP API를 비교한 방법과 권장 시나리오를 알아봅니다.

docs.microsoft.com

LiteDB 참고 링크

   - LiteDB :: A .NET embedded NoSQL database

 

LiteDB :: A .NET embedded NoSQL database

LiteDB : LiteDB - A .NET NoSQL Document Store database in a single data file.

www.litedb.org

readME.md

* 개발 환경

1. .NET 5

2. gRPC Service

Server proto Client proto
Server proto

 

- 서버 

솔루션 탐색기

using System;

using System.Collections.Concurrent;

using System.Collections.Generic;

using System.Linq;

using System.Threading;

using System.Threading.Tasks;

 

using Grpc.Core;

 

using LiteDB;

 

using Microsoft.Extensions.Logging;

 

namespace GrpcServiceTest

{

   public class LogingService : LogPT.LogPTBase

   {

      private readonly ILogger<LogingService_logger

      private ConcurrentQueue<LogMessageRequestlogsQueue// 로그 쌓는 큐

      private Thread InsertThread// insert 하는 쓰레드

      private LiteDatabase db// LiteDB

      private LiteCollection<LogMessageRequestLiteCollection// LogMessageRequest 형식의 컬렉션



      public LogingService(ILogger<LogingService> logger)

      {

         _logger = logger;

 

         logsQueue = new ConcurrentQueue<LogMessageRequest>(); // ConcurrentQueue 생성

 

         db = new LiteDatabase("Filename=.\\TestDB.db;connection=shared"); // connection=shared 가 빠지면 DB를 독점으로 사용해 다른 프로세스에서 접근 불가.

         LiteCollection = (LiteCollection<LogMessageRequest>)db.GetCollection<LogMessageRequest>(); // LogMessageRequest 형식의 컬렉션

 

         InsertThread = new Thread(new ThreadStart(InsertDB)); // 쓰레드 생성

         InsertThread.Start(); // 쓰레드 시작

      }

 

      public override async Task<LogMessageReply> SendLog(IAsyncStreamReader<LogMessageRequest> requestStream, ServerCallContext context// 스트림 형식 request

      {

         try

         {

            await foreach (var message in requestStream.ReadAllAsync()) // C# 8.0 이후 가능

            {

               logsQueue.Enqueue(message); // Queue에 Enqueue

               _logger.LogInformation($"Enqueue Success {message.ProgramName}, {message.DateTime}, {message.Severitylevel}, {message.Host}, {message.LogMessage}");

            }

         }

         catch (Exception e)

         {

            _logger.LogError(e.ToString());

            return await Task.FromResult(new LogMessageReply

            {

               ResultMessage = false // 실패시 false 리턴

            });

         }

         

         return await Task.FromResult(new LogMessageReply

         {

            ResultMessage = true // 성공시 true 리턴

         });

      }

 

      private void InsertDB()

      {

         LogMessageRequest logMessageRequest = new LogMessageRequest(); // 

         while (true)

         {

            if (logsQueue.Count > 0// queue에 있을 때만 Dequeue

            {

               if (logsQueue.TryDequeue(out logMessageRequest)) // ConcurrentQueue는 TryDequeue  사용

               {

                  LiteCollection.Insert(logMessageRequest); // db insert

                  _logger.LogInformation($"Insert Success {logMessageRequest.ProgramName}, {logMessageRequest.DateTime}, {logMessageRequest.Severitylevel}, {logMessageRequest.Host}, {logMessageRequest.LogMessage}");

 

                  var RequestList = LiteCollection.FindAll().ToList(); // db Select

 

                  foreach (var user in RequestList)

                  {

                     _logger.LogInformation($"select {user.ProgramName}, {user.DateTime}, {user.Severitylevel}, {user.Host}, {user.LogMessage}");

                  }

               }

               else

               {

                  _logger.LogWarning($"Insert Fail {logMessageRequest.ProgramName}, {logMessageRequest.DateTime}, {logMessageRequest.Severitylevel}, {logMessageRequest.Host}, {logMessageRequest.LogMessage}");

               }

            }            

         }

      }

   }

}

 

- 클라이언트

솔루션 탐색기

using System;

using System.Threading.Tasks;

 

using Grpc.Net.Client;

 

namespace gRPC_ClientTest

{

   class Program

   {

      static async Task Main(string[] args)

      {

         // The port number(5001) must match the port of the gRPC server.

         using var channel = GrpcChannel.ForAddress("https://localhost:5001");

         var client = new LogPT.LogPTClient(channel);

 

         using var call = client.SendLog();

         await call.RequestStream.WriteAsync(new LogMessageRequest { ProgramName = "Client", DateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm.ss.zzz"), Severitylevel = "info", Host = "127.0.0.1", LogMessage = "test" });

         await call.RequestStream.CompleteAsync(); // write 후 다 전송 했다고 서버에 complete 보냄.

 

         var response = await call;

 

         Console.WriteLine("LogPT: " + response.ResultMessage.ToString());

         Console.ReadKey();

      }

   }

}

 

실행 결과

 

와이어샤크 결과
http2, TLS 1.2 확인

ConcurrentQueue 참고 MSDN : ConcurrentQueue<T> 클래스 (System.Collections.Concurrent) | Microsoft Docs

 

ConcurrentQueue 클래스 (System.Collections.Concurrent)

스레드로부터 안전한 FIFO(선입선출) 방식의 컬렉션을 나타냅니다.Represents a thread-safe first in-first out (FIFO) collection.

docs.microsoft.com

스레드로부터 안전한 FIFO(선입선출) 방식의 컬렉션을 나타냅니다.