gRpc서비스를 만드는 중임
서비스에 필요한 인스턴스에 초기 설정작업이 필요한데
실제 서비스 호출중에 처리하기엔 너무 무거우니까
최초 호출 때 생성하는 대신 미리 만든 인스턴스를 컨테이너에 할당하는 식으로 진행중
문제는 이렇게 되면 컨테이너가 런타임에 생성자를 호출하면서 이뤄지던 의존성 주입이 불가능.
아래 링크의 질문과 유사한 상황임. 댓글에 달려있는 답변들 보면서 해석해봤는데
아직 .net core에 대해서 제대로 모르기도 하고, 더 나은 방법이 있지 않을까 해서 글 올려봄
https://stackoverflow.com/questions/39005861/asp-net-core-initialize-singleton-after-configuring-di
[지금까지 생각해낸 해결 방법]
1. 별도의 WarmUp용 API에 MySingleton.WarmingUp() 를 할당. 호스팅 시작 직후 사용자에게 제공하기 전에 호출.
2. 의존성 주입 포기. 해당 인스턴스의 로거 등등은 별도로 내부에서 처리.
이거 땜에 MSDN 페이지 20개 넘게 헤맨듯..
[예시 코드 1] : Program.cs
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | using GrpcService1.Services; using GrpcService1.InnerRepos; using Grpc.AspNetCore.Server; using Grpc.Reflection; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddGrpc(); builder.Services.AddGrpcReflection(); builder.Logging.AddConsole(); #region <질문내용> // CASE 1. // 컨테이너가 런타임 도중에 싱글턴 인스턴스 생성. // 첫번째 요청이 일어날 때 생성됨. builder.Services.AddSingleton<MySingleton>(); // 2. 미리 생성한 인스턴스를 할당. // 이 경우 ILogger, IConfigure, IWebHostEnvironment등에 대한 // 의존성 주입을 런타임에 할 수 있는지? MySingleton mySingleton = new MySingleton(); builder.Services.AddSingleton(mySingleton); #endregion <질문내용> var app = builder.Build(); // Configure the HTTP request pipeline. app.MapGrpcReflectionService(); app.MapGrpcService<GreeterService>(); app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client."); app.Run(); |
[예시 코드 2] : MySingleton.cs
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | namespace GrpcService1.InnerRepos { public class MySingleton { private readonly ILogger<MySingleton> _logger; public int Id { get; set; } private Graph HEAVY_GRAPH = null; public MySingleton(ILoggerFactory loggerFactory) { Id = 1; _logger = loggerFactory.CreateLogger<MySingleton>(); } public int DoService(int requestParam1,int requestParam2) { _logger.LogDebug($"New ID : {Id++}"); return HEAVY_GRAPH.ServiceProcess(Id,requestParam1, requestParam2); } public void WarmingUp() { DB DBManager = new DB(); HEAVY_GRAPH = DBManager.LoadRawData().ConstructGraph(); // 1. DB에서 대량의 데이터를 읽어 들인 뒤, // 2. 20~30초 이상 걸리는 연산을 통해 내부 상태용 자료구조로 재구축. // 3. 이후 사용자 요청 수행 시 마다 내부 상태를 이용 및 갱신. } private class Graph { internal int ServiceProcess(int id, int requestParam1, int requestParam2) { int ServiceResult = 999; return ServiceResult; } } private class DB { public DB() { } internal RawData LoadRawData() { throw new NotImplementedException(); } internal class RawData { internal Graph ConstructGraph() { return new Graph(); } } } } } |
[예시 코드 3] : GreeterService.cs (gRpc 예제 템플릿)
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | using Grpc.Core; using GrpcService1; using GrpcService1.InnerRepos; namespace GrpcService1.Services { public class GreeterService : Greeter.GreeterBase { private readonly ILogger<GreeterService> _logger; private readonly MySingleton _singleton; public GreeterService(ILogger<GreeterService> logger, MySingleton mySingleton) { _logger = logger; _singleton = mySingleton; } public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context) { int result = _singleton.DoService(request.field1, request.field2); return Task.FromResult(new HelloReply { Message = $"Hello { request.Name }, Result is {result}." }); } } } |
injectable 서비스에 warmupAttribute 같은걸 만들어서 붙이고 프로그램 실행할때 해당 인스턴스 한번씩만 resolve 하면 될듯? - dc App
보통 provider라는 이름으로 초기화 로직을 빼긴하는데 닷넷이라 모르겠다 - dc App
일단은 warmingUp 로직 구동하는게 우선순위라 로거랑 환경변수들 메인 app에서 setter로 꽂아뒀는데 정석적인 구현은 자고나서 맑은 정신일때 해야할듯...