using System;
namespace test
{
/// <summary>
/// 독립 모듈에서 로그를 전달받을때 사용
/// </summary>
public class BaseLogReceiver
{
public virtual void OnError(Exception e, string message) { }
}
}
using Newtonsoft.Json;
using System;
namespace test
{
// http request base item
public class BaseItem
{
public virtual void Execute() { }
public override string ToString()
{
return $"[{GetType().Name}] {JsonConvert.SerializeObject(this)}";
}
}
public class Item<T> : BaseItem
where T : Item<T>
{
[Newtonsoft.Json.JsonIgnore]
public Action<T> WorkProcess { get; set; }
public override void Execute()
{
WorkProcess?.Invoke((T)this);
}
}
}
using Newtonsoft.Json;
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Threading;
using System.Threading.Tasks;
namespace test
{
/// <summary>
/// rest api 호출 용 http client
/// HttpClient: https://docs.microsoft.com/ko-kr/dotnet/api/system.net.http.httpclient?view=netcore-3.1
///
/// 발생 가능한 exception
/// - InvalidOperationException: requestUri가 절대 URI이거나 BaseAddress가 설정되어야 합니다.
/// - HttpRequestException: 네트워크 연결, DNS 오류, 서버 인증서 유효성 검사 또는 시간 초과와 같은 기본 문제로 인해 요청이 실패한 경우
/// - TaskCanceledException: 시간 제한으로 인해 요청이 실패했습니다.
/// - Exception: 기타
/// </summary>
public class HttpRestClient
{
public string BaseAddress { get; private set; }
private HttpClient _httpClient;
public static HttpRestClient CreateDefault(string baseAddress = null)
{
int maxConnectionsPerServer = 10;
int readWriteTimeoutSec = 10;
return Create(
new SocketsHttpHandler()
{
ConnectTimeout = TimeSpan.FromSeconds(5), // 연결 설정이 시간 초과될 때까지 대기하는 시간 간격 (default: 무한)
ResponseDrainTimeout = TimeSpan.FromSeconds(2), // 응답에서 데이터가 배출될 때까지 대기하는 시간 간격 (default: 2초)
MaxConnectionsPerServer = maxConnectionsPerServer, // 단일 서버에서 허용하는 최대 동시 TCP 연결 수 (default: 2147483647)
PooledConnectionLifetime = Timeout.InfiniteTimeSpan, // 풀에서 연결이 재사용 가능한 것으로 간주될 수 있는 기간 (default: 무한)
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2), // 연결이 풀에서 유휴 상태일 수 있는 기간 (default: 2분)
},
baseAddress,
readWriteTimeoutSec
);
}
public static HttpRestClient Create(HttpMessageHandler handler, string baseAddress, int readWriteTimeoutSec)
{
HttpRestClient http = new HttpRestClient
{
_httpClient = new HttpClient(handler)
{
Timeout = TimeSpan.FromSeconds(readWriteTimeoutSec), // read, write timeout (default: 100 초)
BaseAddress = (string.IsNullOrWhiteSpace(baseAddress)) ? null : new Uri(baseAddress)
}
};
// json 통신 사용을 위한 공통헤더 세팅
http._httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json)
);
http.BaseAddress = baseAddress;
return http;
}
// GET
public RspT Get<RspT>(string uri, out HttpStatusCode httpStatusCode)
{
return Send<RspT>(_httpClient.GetAsync, uri, out httpStatusCode);
}
// POST
public RspT Post<ReqT, RspT>(string uri, ReqT req, out HttpStatusCode httpStatusCode)
{
return Send<ReqT, RspT>(_httpClient.PostAsync, uri, req, out httpStatusCode);
}
// PUT
public RspT Put<ReqT, RspT>(string uri, ReqT req, out HttpStatusCode httpStatusCode)
{
return Send<ReqT, RspT>(_httpClient.PutAsync, uri, req, out httpStatusCode);
}
// PATCH
public RspT Patch<ReqT, RspT>(string uri, ReqT req, out HttpStatusCode httpStatusCode)
{
return Send<ReqT, RspT>(_httpClient.PatchAsync, uri, req, out httpStatusCode);
}
// DELETE
public RspT Delete<RspT>(string uri, out HttpStatusCode httpStatusCode)
{
return Send<RspT>(_httpClient.DeleteAsync, uri, out httpStatusCode);
}
// DELETE
public void Delete(string uri, out HttpStatusCode httpStatusCode)
{
_ = Send(_httpClient.DeleteAsync, uri, out httpStatusCode);
}
// send: json 변환
private RspT Send<RspT>(Func<string, Task<HttpResponseMessage>> func, string uri, out HttpStatusCode httpStatusCode)
{
return JsonConvert.DeserializeObject<RspT>(
Send(func, uri, out httpStatusCode)
);
}
// send: json 변환
private RspT Send<ReqT, RspT>(Func<string, HttpContent, Task<HttpResponseMessage>> func, string uri, ReqT req, out HttpStatusCode httpStatusCode)
{
string reqJsonString = JsonConvert.SerializeObject(req);
return JsonConvert.DeserializeObject<RspT>(
Send(func, uri, reqJsonString, out httpStatusCode)
);
}
// send: raw string
private string Send(Func<string, Task<HttpResponseMessage>> func, string uri, out HttpStatusCode httpStatusCode)
{
using HttpResponseMessage httpResponseMessage = func(uri).Result;
httpStatusCode = httpResponseMessage.StatusCode;
return httpResponseMessage.Content.ReadAsStringAsync().Result;
}
// send: raw string
private string Send(Func<string, HttpContent, Task<HttpResponseMessage>> func, string uri, string reqJsonString, out HttpStatusCode httpStatusCode)
{
using StringContent content = new StringContent(reqJsonString, System.Text.Encoding.UTF8, MediaTypeNames.Application.Json);
using HttpResponseMessage httpResponseMessage = func(uri, content).Result;
httpStatusCode = httpResponseMessage.StatusCode;
return httpResponseMessage.Content.ReadAsStringAsync().Result;
}
}
}
using System;
namespace test
{
public class HttpWorkThread : BaseChannelThread<BaseItem>
{
private readonly ObjectTypePool _pool;
private readonly BaseLogReceiver _log;
public HttpWorkThread(string threadName, ObjectTypePool pool, BaseLogReceiver logReceiver)
: base(threadName)
{
_pool = pool;
_log = logReceiver;
}
// thread 처리 로직
public override void Loop(BaseItem item)
{
try
{
item.Execute();
}
catch (Exception e)
{
_log?.OnError(e, $"{item}");
}
finally
{
_pool.Return(item);
}
}
}
}
namespace test
{
/// <summary>
/// http rest api client work manager
/// </summary>
public class HttpWorkManager
{
/// <summary>
/// 초기화 파라미터 클래스
/// </summary>
public class Param
{
public BaseLogReceiver LogReceiver { get; set; }
public int ThreadCount { get; set; }
}
// 스레드 관리자
private readonly ChannelThreadsManager<HttpWorkThread, BaseItem> _threads = new ChannelThreadsManager<HttpWorkThread, BaseItem>();
// Type 별 Pool 관리자
private readonly ObjectTypePool _pool = new ObjectTypePool();
// 초기화 파라미터
private Param _param;
// Init
public void Init(Param param)
{
_param = param;
for (int i = 0; i < _param.ThreadCount; i++)
{
_threads.AddThread(new HttpWorkThread($"http-{i}", _pool, _param.LogReceiver));
}
_threads.StartThread();
}
// Uninit
internal void Uninit()
{
_threads.StopAndWaitThread();
}
// pool 의 item 렌트
public T Rent<T>()
where T : BaseItem
{
return _pool.Rent<T>();
}
// pool 의 item 리턴
public void Return<T>(T item)
where T : BaseItem
{
_pool.Return(item);
}
// item 실행
public void Run<T>(T item)
where T : BaseItem
{
_threads.AddItem(item);
}
}
}
'C#' 카테고리의 다른 글
c# DebuggerDisplay, DebuggerTypeProxy attribute (0) | 2022.03.16 |
---|---|
c# method attribute 의 값을 해당 메소드에서 가져오기 (0) | 2022.03.15 |
c# channel (0) | 2022.03.15 |
object pool, object type pool (0) | 2022.03.11 |
GC TEST (0) | 2022.03.08 |
GC EventPipe 모니터링 (0) | 2022.03.08 |
C# .net core 빌드 및 powershell 전송 (0) | 2022.03.01 |
c# stack size 확인 (0) | 2022.02.24 |