API 다중 호출 시 속도 관련 문의 드립니다.

안녕하세요!

주식 관련 프로그램을 만들고 있는데 속도 이슈 를 해결 하지 못하고 있어서 문의 드립니다.

현재 제가 만든 프로그램 에서 문제는 다음과 같습니다.

  1. 사용자가 조회를 하면 관심목록에 등록 해논 종목 들을 기준으로 1년치(250 row) 데이터를
    1번 api 사용하여 그리드에 표현을 해주고 있습니다. (속도 전혀 문제 없음)

  2. 1번 을 수행 하면서 가져온 250 row 갯수 만큼 특정 기준 값 을 가져오는 2번 api 를 호출 합니다.
    (row당 0.5초 걸림)
    1종목 마다 250 row * 0.5 초 를 순차적으로 수행 하면서 조회를 하고 있어서
    사용자는 1종목당 30초 정도 대기해야 하는 상황이 발생 합니다.
    종목이 많으면 많을 수록 시간은 더 지연 됩니다…

  3. 2번 처럼 개발한 이유는. 1번 api 를 가져오면서 1번 api 에 있는 데이터를
    2번 api 호출 할 때 파라미터 로 넘겨야 하기 때문 입니다.

  4. 2번 api 호출시 발생 하는 0.5초 는 Request 시 Response 까지 떨어 지는데 걸리는 시간 입니다.

이러한 이유로 인해서 사용자가 조회 시 종목이 많으면 많을 수록 속도가 느려지는

현상이 있는데 이것을 해결 할 수 있는 방법이 있을까요??

감사합니다.

1 Like

안녕하세요 @코스피200
혹시 1번에서 가져온 row만큼 병렬로 request 요청하면 문제가 될까요?

1 Like
  • 증권사에 따라 다르겠지만 2번에 대한 여러 종목을 동시에 요청하는 명령이 있을 것입니다.
  • 이와는 별개로 해당 조회가 캐싱이 가능하다면 서버 또는 클라이언트에서 캐싱을 하는게 좋겠습니다.
    ※ 서버에서 미리 조회한 후 값을 제공하는 것은 법적인 문제가 있을 수도 있습니다. 이는 살펴보셔야 합니다.
2 Likes

제 경험에 의하면 이럴 경우에는
한번에 여러개의 값을 가져올 수 있는 API를 새로 만들었었습니다.
API를 만들 수 없는 상황이라면 어쩔 수 없이 병렬로 처리하셔야 할 것 같네요
다만, 병렬로 처리하면 그쪽 서버엔 부하가 있지 않을까요?

2 Likes

안녕하세요
병렬 처리라 함은
스레드 사용 해서 멀티로 처리 하라는 이야기 이신가요??

2번 api 호출 할 때 무조건 1번 api 에 있는 데이터가 필요 합니다.
이런 상황에서 병렬 처리는 어떤 식으로 구현 해야 하나요?

2 Likes

제가 아직 병렬 처리 개념은 잘 이해 못하고 있습니다만
병렬 처리가 스레드 사용해서
프로세스 를 동시다발적으로 수행 하는 것을 이야기 하시는 건가요?

2 Likes

아래 링크에 병렬처리에 대한것과 쓰레드 처리에 대한것이 나오는데 한번 읽어보시고 적용하시면 좋을듯합니다.
저같은 경우는 엑셀데이타 DB입력작업이 몇만건씩 있는데 Task로 작업을 나누어서 처리했습니다.
괜찮았었습니다.

https://forum.dotnetdev.kr/t/c-parallel-programming/1975/3

3 Likes

불러올 데이터의 순서가 중요하지 않은 상황이라면
각각의 API의 응답을 기다린 후 다음 API를 호출하는 것이 아니라
동시에 모든 API를 호출하는 것을 말씀드린겁니다!

예를들어 3개의 데이터가 있다고 가정했을 때
순차적으로 [1번(0.5초대기) → 2번(0.5초대기) → 3번(0.5초대기)] 총 소요시간 소요시간 1.5초가 아니라
한번에 1,2,3번 API를 호출해서 총 소요시간이 0.5초가 될 수 있도록 말씀 드린겁니다!

다만, 이론적인 이야기고 항상 병렬프로그램이 속도를 이론처럼 빠르게 할 수 있다고는 장담할 수 없습니다.
그리고 병렬프로그래밍이 무엇인지 잘 모르고 작업하시면 올바로 프로그래밍 하시기 매우 힘드실 것입니다.

우선은 테스트로 API의 호출을 Task로 묶어서 병렬적으로 처리해보시는 것을 추천드립니다!

2 Likes

Task Que = Task.Factory.StartNew(() =>
{
1번api.Call();

    foreach(row in 1번api_data.row)
    {
             2번api.Call(1번api_변수1, 1번api_변수2, 1번api_변수3);
    }

});

현재 대략 간략하게 짜면 이런 코드 입니다.

여기서 병렬 처리라 함은

Task Que = Task.Factory.StartNew(() =>
{
1번api.Call();

             2번api.Call(1번api_변수1[0], 1번api_변수2, 1번api_변수3);
             2번api.Call(1번api_변수1[1], 1번api_변수2, 1번api_변수3);
             2번api.Call(1번api_변수1[2], 1번api_변수2, 1번api_변수3);
             2번api.Call(1번api_변수1[3], 1번api_변수2, 1번api_변수3);
             2번api.Call(1번api_변수1[4], 1번api_변수2, 1번api_변수3);

});

이런걸 이야기 하시는 건가요???

2 Likes

안녕하세요 @코스피200
닷넷 버전을 정확하게 몰라 보수적으로 .net framework 4.0부터 사용 가능한 병렬 반복문을 아래에 예시로 적었습니다
@코스피200 님의 위 예제로 보았을때는 Parallel.Foreach문이 좋을것으로 보입니다.

// Parallel.For문을 이용하여 for문을 병열로 처리하게 됩니다.
System.Threading.Tasks.Parallel.For(0, 250, (i) => {
    /// 들어갈 내용
});
var rows = 1번api.Call;
System.Threading.Tasks.Parallel.Foreach(rows, row => {
    2번api.Call(row[0], row[1], row[2]);
});

사실 이 외에도 다양하게 병렬처리할 수 있는 방법들이 많이 있습니다.
위 방법은 제가 생각했을때 현재 상황에서 적절한것으로 생각되어 적은거라 참고만 부탁드리겠습니다

Parallel과 관련된 내용은 링크 글을 참고해주시면 좋을거같습니다.

제가 작성한게 문제있거나 더 좋은 의견있으시면 적어주시면 좋을거같습니다 :grinning:
감사합니다!!

2 Likes

작성해 주신 내용은 비동기 코드로 병렬처리와는 다릅니다.

작성해 주신 Task.Factory.StartNew()는 비동기적으로 실행되지만 파라미터로 넘겨주신 함수들은 ThreadPool에서 할당 받은 Thread에서 동기적으로 차례대로 실행 될 것입니다.

@code-monkey 님이 말씀해 주신 대로 Tasks.Parallel.For를 사용하시는 방법도 있습니다.

저는 Task.WhenAll로 처리 하는 것을 예제로 드리니 도움이 되셨으면 좋겠습니다!

        private async void _this_Loaded(object sender, RoutedEventArgs e)
        {
            var mainTasks = new List<Task>();

            // 각각의 API호출을 대기하는 동작
            mainTasks.Add(Task.Run(() =>
            {
                for (int i = 0; i < 5; i++)
                {
                    Add();
                }

                Datas = new ObservableCollection<Data>(_lists);
            }));

            // API 호출을 대기하지 않고 병렬적으로 실행
            mainTasks.Add(Task.Run(async () =>
            {
                var tasks = new List<Task>();
                for (int i = 0; i < 45; i++) 
                {
                    tasks.Add(Task.Run(() => {
                        AddParallel();
                    }));
                }

                // 45개의 Task가 모두 끝나길 비동기적으로 대기
                await Task.WhenAll(tasks);

                ParallelDatas = new ObservableCollection<Data>(_bags);
            }));

            await Task.WhenAll(mainTasks);
        }

        private void Add()
        {   
            Thread.Sleep(1000);
            _lists.Add(new Data() { Name = "홍길동" });
        }

        private void AddParallel()
        {
            Thread.Sleep(1000);
            _bags.Add(new Data() { Name = "홍길동" });
        }

참고로 제 로컬에서는 5개를 동기적으로 처리하는 것과 45개를 병렬적으로 처리하는 것의 속도가 거의 비슷했습니다.

도움이 되었으면 좋겠습니다!

2 Likes

답변 주신 분들 모두 감사드립니다.
위에 두분이서 제안해 주신 부분으로 진행 해보았는데 cpu 사용율이 너무 올라가는
부분이 있고 cpu 가 과도하게 올라가서 데이터가 누락 되는 현상도 나오면서
프로그램이 강제로 셧다운 되는 상황이 오네요 ㅎㅎ
몬가 다른 방법이 있는지 계속 찾아 봐야 겠습니다.
친절하게 답변 주셔서 감사드립니다.

2 Likes

누락되는 현상은 아마도 증권사에서 제공하는 API의 시간 제한 & 동시 명령어 가능 유무 때문일 꺼에요.

어떤 증권사 모듈을 사용하시는지 알수는 없어서 정확히 답변을 드리기는 어렵지만, 분명히 다중 조회 하는 기능이 있을꺼에요.

그리고 데이터의 특성상 캐싱이 가능하다면 캐싱 전략도 좋습니다.

2 Likes

예상되는 결과이기는 합니다만… :sob:

@dimohy 님이 말씀하시는 것처럼 분명 다른 API가 있을 것 같아요…

그쪽 회사에 한번 문의해보시는 것도 좋으실 것 같습니다!

3 Likes