c# HttpClient

C# 2022. 3. 15. 10:04

 

 

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