Skip to content

๐ŸงŠ ์šฐ์„ ์ˆœ์œ„ ํ๋กœ ์š”์ฒญ ์ œ์–ดํ•˜๊ธฐ

kimminsu edited this page Dec 4, 2024 · 3 revisions

์šฐ์„ ์ˆœ์œ„ ํ๋กœ ์š”์ฒญ ์ œ์–ดํ•˜๊ธฐ

๋ถ„์•ผ ์ž‘์„ฑ์ž ์ž‘์„ฑ์ผ
BE ๊น€๋ฏผ์ˆ˜ 24๋…„ 12์›” 03์ผ

๊ฐœ์š”

ํ˜„์žฌ ๊ฐ€๊ฒฉ ์ƒ์Šน, ํ•˜๋ฝ๋ฅ  ์ˆœ์œ„์— ๋Œ€ํ•œ API๋ฅผ ์š”์ฒญํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์—ˆ๋‹ค. ํ•ด๋‹น ๊ธฐ๋Šฅ์—์„œ๋Š” ๋‹ค์Œ์˜ ๊ณผ์ •์œผ๋กœ ์ฃผ์‹ API์— ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค.

  1. ์žฅ์ด ์—ด๋ฆฌ๋Š” ์‹œ๊ฐ„์— 1๋ถ„ ๊ฐ„๊ฒฉ์œผ๋กœ ์ƒ์Šน, ํ•˜๋ฝ๋ฅ  ์ˆœ์œ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๋Š”๋‹ค.
  2. ์ด 30๊ฐœ์˜ ์ข…๋ชฉ ์ค‘ ์ƒ์œ„ 20๊ฐœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ
  3. ์ƒ์œ„ 20๊ฐœ์˜ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญ(1์ดˆ์— 10๊ฐœ ์š”์ฒญ)
  4. ์ „๋‹ฌ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ ํ›„ ์ €์žฅ

์ด๋•Œ ์ฃผ์‹ API๋Š” ๊ณ„์ขŒ 1๊ฐœ ๋‹น 1์ดˆ์— 20๊ฐœ์˜ ์š”์ฒญ์ด ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ ์ ˆํ•œ ์กฐ์ ˆ์ด ํ•„์š”ํ–ˆ๋‹ค. ๊ทธ๋กœ์ธํ•ด 20๊ฐœ์˜ ์ข…๋ชฉ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ๋ฒˆ์— ๋ฐ›์ง€ ์•Š๊ณ  1์ดˆ์— 10๊ฐœ์”ฉ ๋‚˜๋ˆ„์–ด์„œ ์ „๋‹ฌํ•˜์˜€๊ณ  ์‹คํŒจํ–ˆ์„ ๋•Œ 2์ดˆ ๋Œ€๊ธฐ ํ›„ ์ตœ๋Œ€ 5๋ฒˆ ์ง„ํ–‰ํ•˜๋„๋ก ํ–ˆ๋‹ค.

async getDecreaseRankStocks(count = 5) {
    try {
      if (count === 0) return;
      const result = await this.getFluctuationRankApiStocks(false);
      const liveResult = await this.getFluctuationRankApiLive(result);
      await this.datasource.transaction(async (manager) => {
        await this.saveFluctuationRankStocks(result, manager);
        await this.saveLiveData(liveResult, manager);
        this.logger.info('decrease rank stocks updated');
      });
    } catch (error) {
      this.logger.warn(error);
      await new Promise((resolve) =>
        setTimeout(() => resolve(this.getDecreaseRankStocks(--count)), 2000),
      );
    }
  }

์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์–ด๋Š ์ •๋„ ์š”์ฒญ ์ œ์–ด๊ฐ€ ๋  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ ์˜ˆ์ƒ์™ธ๋กœ ์ƒ๊ฐ๋ณด๋‹ค ์š”์ฒญ ์ œ์–ด๊ฐ€ ์ž˜ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์•˜๋‹ค.

๋”ฐ๋ผ์„œ ํ•˜๋ฃจ๋™์•ˆ ์–ผ๋งˆ๋‚˜ ์š”์ฒญ์ด ์‹คํŒจ๋ฅผ ํ™•์ธํ–ˆ๋‹ค.

๋†€๋ž๊ฒŒ๋„ 1๋งŒ ๊ฐœ ์ด์ƒ์˜ ์š”์ฒญ์ด ์‹คํŒจ๊ฐ€ ๋˜์—ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ œํ•œ๋œ ์š”์ฒญ์„ ์ž˜ ์ œ์–ดํ•˜์ง€ ๋ชปํ•œ๋‹ค๋ฉด ๋งŽ์€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค. ์—๋Ÿฌ๊ฐ€ ๊ณ„์† ๋ฐœ์ƒํ•˜์—ฌ ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋Š” ๊ธฐํšŒ๊ฐ€ ๋ชจ๋‘ ๋‚ ์•„๊ฐ€ ๊ฒฐ๊ตญ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์ง€ ๋ชปํ•˜๋Š” ๋ฌธ์ œ๋‚˜ ๋ถˆํ•„์š”ํ•œ ์š”์ฒญ์ด ๋Š˜์–ด๋‚˜๋Š” ๋ฌธ์ œ ๊ทธ๋ฆฌ๊ณ  ํ•œ๋ฒˆ ์กฐ์ ˆ์„ ์‹คํŒจํ•˜๋ฉด ๋งŽ์€ ์š”์ฒญ์ด ์—ฐ์‡„์ ์œผ๋กœ ์‹คํŒจํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€๋Šฅ์„ฑ ๋“ฑ ๋‹ค์–‘ํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋ฅผ ์ž˜ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด์•ผ๋œ๋‹ค.

์›์ธ

์š”์ฒญ ์ˆ˜ ์ œ์–ด๋ฅผ ์‹คํŒจํ•œ ์›์ธ์€ ํ˜„์žฌ ์š”์ฒญ์„ ์ œ์–ดํ•˜๊ธฐ ์œ„ํ•œ ๋กœ์ง์ด ์‹ค์ œ๋กœ๋Š” ์ œ์–ด๊ฐ€ ์ž˜ ์•ˆ๋˜๊ธฐ ๋•Œ๋ฌธ์ด์—ˆ๋‹ค. ๊ธฐ์กด์— ์ง„ํ–‰ํ•œ ์š”์ฒญ ์ œ์–ด ๋กœ์ง์€ ํฌ๊ฒŒ 2๊ฐ€์ง€์˜€๋‹ค. ์šฐ์„  ์ฒซ ๋ฒˆ์งธ๋Š” ๋กœ์ง์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

ํ•ด๋‹น ๋กœ์ง์€ ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ์ผ์ • ์‹œ๊ฐ„์ด ๋„๋‹ฌํ•˜๋ฉด api ์š”์ฒญ์„ ์ง„ํ–‰ํ•˜๋Š” ๋กœ์ง์œผ๋กœ ๋งค์šฐ ๊ฐ„๋‹จํ•˜๋‹ค. ๋”ฐ๋ผ์„œ 1๋ถ„ ์ฃผ๊ธฐ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐ์ดํ„ฐ(๋ณ€๋™๋ฅ  ์ˆœ์œ„, ๊ทธ์™€ ์—ฐ๊ด€๋œ ๋ผ์ด๋ธŒ ๋ฐ์ดํ„ฐ ๋“ฑ)์— ์“ฐ์ด๊ฒŒ ๋œ๋‹ค. ํŠน์ • ์‹œ๊ฐ„์— ๋„๋‹ฌํ•˜์—ฌ ์‹œ์ž‘ํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ์ดˆ ๋‹น 20๊ฐœ์˜ ์š”์ฒญ์„ ์ง„ํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค.

ํ•˜์ง€๋งŒ ์œ„ ๋ฐฉ์‹์˜ ๋ฌธ์ œ์ ์œผ๋กœ๋Š” ๊ฐ ๋กœ์ง๋งˆ๋‹ค ๋™๊ธฐํ™”๊ฐ€ ์ œ๋Œ€๋กœ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด์„œ 1๋ถ„ ์ฃผ๊ธฐ๋กœ ๋ณ€๋™๋ฅ  ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋กœ์ง์„ ์ง„ํ–‰ํ–ˆ์„ ๋•Œ ๋™์‹œ์— ์กฐํšŒ์ˆ˜ ์ˆœ ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธ๋ฅผ ํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด, ๊ฐ ๋กœ์ง์—์„œ ์š”์ฒญ์ด ์–ด๋Š ์ •๋„ ์ผ์–ด๋‚˜๋Š”์ง€ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฐ๊ตญ ์กฐ์ ˆ์„ ์‹คํŒจํ•˜์—ฌ ์—๋Ÿฌ๋ฅผ ๋ฐ›๊ฒŒ ๋œ๋‹ค.

๋‘ ๋ฒˆ์งธ ๋กœ์ง์€ ์ฐจํŠธ ๋ฐ์ดํ„ฐ์™€ ๊ฐ™์ด ํ•œ๋ฒˆ์— ๋งŽ์€ ์š”์ฒญ์„ ์ œ์–ดํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ ๋กœ์ง์ด๋‹ค. ํ•ด๋‹น ๋ฐฉ์‹์€ ํ˜„์žฌ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ณ„์ขŒ๋ฅผ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•ด์„œ ๋งŽ์€ ์š”์ฒญ์„ ์ง„ํ–‰ํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ณ„์ขŒ์˜ ์ˆ˜๋งŒํผ Task chunk๋ฅผ ๋‚˜๋ˆ„์–ด์„œ setTimeout()์„ ํ†ตํ•ด ์ผ์ • ์‹œ๊ฐ„์ด ์ง€๋‚œ ํ›„ ์š”์ฒญ์„ ์ง„ํ–‰ํ•˜๋„๋ก ํ–ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ด๊ฒƒ๋„ ์š”์ฒญ์„ ์ œ์–ดํ•˜๊ธฐ ํž˜๋“ค์—ˆ๋‹ค. ํ•ด๋‹น ๋กœ์ง์˜ ๋ฌธ์ œ๋Š” ์ฒซ๋ฒˆ์งธ ๋กœ์ง๊ณผ ๊ฐ™์ด ๋‹ค๋ฅธ ์š”์ฒญ๊ณผ ๊ฒน์น˜๊ฒŒ ๋  ๋•Œ ์š”์ฒญ์ด ์ดˆ๊ณผ๋  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๊ณ , setTimeout์˜ ์˜ค์ฐจ๊ฐ€ ๋งŽ์ด ์Œ“์—ฌ ์˜ˆ์ธกํ•˜๊ธฐ ์–ด๋ ค์šด ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๋‹ค. ๋” ํฐ ๋ฌธ์ œ๋กœ ๋„ˆ๋ฌด ๋งŽ์€ setTimout์„ ์Œ“์•„๋†“์•˜๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ฒ„์˜ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ„ฐ์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€๋‹ค. ๊ฒฐ๊ตญ ์ œํ•œ๋œ ์š”์ฒญ ์ˆ˜๋ฅผ ์ž˜ ์ œ์–ดํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ƒˆ๋กœ์šด ๋กœ์ง์„ ๋งŒ๋“ค ์ˆ˜ ๋ฐ–์— ์—†์—ˆ๋‹ค.

์šฐ์„ ์ˆœ์œ„ ํ๋ฅผ ์‚ฌ์šฉํ•˜์ž

ํ•ด๋‹น ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ด๋ฅผ ์ž˜ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์ค‘๊ฐ„ ์ง€์ ์ด ํ•„์š”ํ–ˆ๊ณ , ์šฐ์„ ์ˆœ์œ„ ํ๋ฅผ ์ค‘๊ฐ„ ์ง€์ ์œผ๋กœ ๋‘์–ด ์š”์ฒญ ์ˆ˜๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ๋‹ค. ๊ฐœ์„ ๋œ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ์‚ฐ์ž - ํ - ์†Œ๋น„์ž ํ˜•ํƒœ์˜ ๊ตฌ์กฐ๋ฅผ ๊ฐ–๋„๋ก ํ–ˆ๋‹ค.

graph LR
    P1(์ฐจํŠธ ๋ฐ์ดํ„ฐ ์Šค์ผ€์ค„๋Ÿฌ) --> PriorityQueue
    P2(์ฃผ๊ฐ€ ์ƒ์„ธ ์ •๋ณด ์Šค์ผ€์ค„๋Ÿฌ) --> PriorityQueue
    P3(์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์š”์ฒญ) --> PriorityQueue
    P4(์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์š”์ฒญ) --> PriorityQueue
    PriorityQueue --> C1(๊ณ„์ขŒ1)
    PriorityQueue --> C2(๊ณ„์ขŒ2)
    PriorityQueue --> C3(๊ณ„์ขŒ3)
    PriorityQueue --> C4(๊ณ„์ขŒ4)

Loading

์™œ ์šฐ์„ ์ˆœ์œ„ ํ์ธ๊ฐ€?

ํ ๋Œ€์‹  ์šฐ์„ ์ˆœ์œ„ ํ๋ฅผ ์„ ํƒํ•œ ์ด์œ ๋Š” ์ตœ๋Œ€ํ•œ ์‹ค์‹œ๊ฐ„ ์„ฑ์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค. ๊ฐ ์š”์ฒญ์€ ์‚ฌ์šฉ์ž์— ์ž…์žฅ์—์„œ ๋ฐ˜๋“œ์‹œ ์‹ค์‹œ๊ฐ„ ์„ฑ์„ ์œ ์ง€ํ•ด์•ผํ•˜๋Š” ์š”์ฒญ๊ณผ ๋œ ์œ ์ง€ํ•ด์•ผํ•˜๋Š” ์š”์ฒญ์ด ์žˆ๋‹ค.

์˜ˆ๋ฅผ๋“ค์–ด์„œ ๋ณ€๋™๋ฅ ์ด๋‚˜ ์กฐํšŒ์ˆ˜ ์ˆœ์˜ ๋ฐ์ดํ„ฐ ์š”์ฒญ์˜ ๊ฒฝ์šฐ ์—…๋ฐ์ดํŠธ๊ฐ€ ๊ธด ์ฃผ๊ธฐ๋กœ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์‹œ๊ฐ„ ์„ฑ์˜ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋‚ฎ๋‹ค. ํ•˜์ง€๋งŒ, ํ˜„์žฌ ์‹œ์ ์˜ ์ฃผ๊ฐ€ ์ •๋ณด์˜ ๊ฒฝ์šฐ ํ•ด๋‹น ์‹œ์ ์˜ ๊ฐ€๊ฒฉ์€ ์‹ค์‹œ๊ฐ„ ์„ฑ์˜ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’๋‹ค. ๋”ฐ๋ผ์„œ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์€ ์š”์ฒญ์„ ๋จผ์ € ์ง„ํ–‰ํ•˜๊ณ , ๋‚ฎ์€ ์š”์ฒญ์„ ๋‚˜์ค‘์— ์ง„ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์šฐ์„ ์ˆœ์œ„ ํ๋ฅผ ๋„์ž…ํ–ˆ๋‹ค.

์ƒ์‚ฐ์ž

์šฐ์„  ์ƒ์‚ฐ์ž๋Š” ์ฃผ์‹ API ๋ฐ์ดํ„ฐ ์š”์ฒญ์„ ์ง„ํ–‰ํ•˜๋Š” ๊ฐ์ฒด๋กœ ์š”์ฒญ Task๋ฅผ ํ์— ์‚ฝ์ž…ํ•˜๋„๋ก ํ•œ๋‹ค.

  • ์š”์ฒญ url - ์ฃผ๊ฐ€ API์— ์š”์ฒญํ•  url
  • ์ฟผ๋ฆฌ - ์š”์ฒญ์— ํ•„์š”ํ•œ ์ฟผ๋ฆฌ
  • TR_ID - ๊ฑฐ๋ž˜ ๋ฒˆํ˜ธ
  • callback - ์š”์ฒญ์ด ์™„๋ฃŒ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜

์œ„์— ๋‚˜์™€์žˆ๋Š” ํ•ญ๋ชฉ์„ ํ†ตํ•ด ํ์˜ ๋…ธ๋“œ ํƒ€์ž…์„ ์ •ํ•˜์˜€๋‹ค.

interface Json {
  output: Record<string, string> | Record<string, string>[];
  output1: Record<string, string>[];
  output2: Record<string, string>[];
}

interface OpenapiQueueNodeValue {
  url: string;
  query: object;
  trId: TR_ID;
  callback: <T extends Json>(value: T) => Promise<void>;
}

์ƒ์‚ฐ์ž๋Š” ์š”์ฒญํ•˜๊ณ ์ž ํ•˜๋Š” API์— ๋งž๋Š” ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ํ์— ์‚ฝ์ž…ํ•˜๊ฒŒ ๋œ๋‹ค.

this.openApiQueue.enqueue({
  url: this.url,
  query,
  trId: TR_IDS.ITEM_CHART_PRICE,
  callback: this.getLiveDataSaveCallback(stock.id!, period),
});

์ƒ์‚ฐ์ž์˜ ์š”์ฒญ์„ ์ €์žฅํ•˜๋Š” ์ž๋ฃŒ๊ตฌ์กฐ์ด๋‹ค. ์ด๋•Œ ์š”์ฒญ์ด ์‹คํŒจํ•˜๊ฑฐ๋‚˜ ์šฐ์„  ์ˆœ์œ„๊ฐ€ ๋†’์€ ์ž‘์—…์„ ๋จผ์ € ๋น ์ ธ๋‚˜๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค. ์ด์ „์— ๋งŒ๋“  ์ œ๋„ˆ๋ฆญ ํƒ€์ž…์˜ ์šฐ์„ ์ˆœ์œ„ ํ๋ฅผ ํ†ตํ•ด์„œ Api์š”์ฒญ์— ๋Œ€ํ•œ ๋กœ์ง์ด ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ „์šฉ ํ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

class OpenapiQueue {
  private queue: PriorityQueue<OpenapiQueueNodeValue> = new PriorityQueue();

  enqueue(value: OpenapiQueueNodeValue, priority?: number) {
    if (!priority) priority = 2;
    this.queue.enqueue(value, priority);
  }

  dequeue(): OpenapiQueueNodeValue | undefined {
    return this.queue.dequeue();
  }

  isEmpty(): boolean {
    return this.queue.isEmpty();
  }
}

์†Œ๋น„์ž

์šฐ์„ ์ˆœ์œ„ ํ๋กœ๋ถ€ํ„ฐ ์š”์ฒญ์„ ๊ฐ€์ ธ์™€ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณณ์œผ๋กœ ์ฃผ์‹ API๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ ํ›„ callback ํ•จ์ˆ˜์— ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•œ๋‹ค. ์ด๋•Œ ์ฃผ๊ธฐ์ ์œผ๋กœ ํ๊ฐ€ ๋น„์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋ฉด์„œ ์ง„ํ–‰ํ•˜๊ณ , ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋” ๋†’์€ ์šฐ์„ ์ˆœ์œ„๋กœ ์žฌ์š”์ฒญ์„ ํ•˜๋„๋กํ–ˆ๋‹ค.

class OpenapiConsumer {
  private readonly REQUEST_COUNT_PER_SECOND = 20;
  private isProcessing: boolean = false;
  private currentTokenIndex = 0;

  constructor(
    private readonly queue: OpenapiQueue,
    private readonly openapiTokenApi: OpenapiTokenApi,
    @Inject('winston') private readonly logger: Logger,
  ) {
    this.start();
  }

  async start() {
    setInterval(() => this.consume(), 1000);
  }

  async consume() {
    if (this.isProcessing) {
      return;
    }

    while (!this.queue.isEmpty()) {
      this.isProcessing = true;
      await this.processQueueRequest();
      await new Promise((resolve) => setTimeout(resolve, 1000));
    }
    this.isProcessing = false;
  }

  private async processQueueRequest() {
    const tokenCount = (await this.openapiTokenApi.configs()).length;
    for (let i = 0; i < tokenCount; i++) {
      await this.processIndividualTokenRequest(this.currentTokenIndex);
      if (!this.isProcessing) {
        return;
      }
      this.currentTokenIndex = (this.currentTokenIndex + 1) % tokenCount;
    }
  }

  private async processIndividualTokenRequest(index: number) {
    for (let i = 0; i < this.REQUEST_COUNT_PER_SECOND; i++) {
      const node = this.queue.dequeue();
      if (!node) {
        return;
      }
      this.processRequest(node, index);
    }
  }

  private async processRequest(node: OpenapiQueueNodeValue, index: number) {
    try {
      const data = await getOpenApi(
        node.url,
        (await this.openapiTokenApi.configs())[index],
        node.query,
        node.trId,
      );
      await node.callback(data);
    } catch (error) {
      this.logger.warn(error);
      this.queue.enqueue(node, 1);
    }
  }
}

ํ•ด๋‹น ๊ตฌ์กฐ๋ฅผ ์ ์šฉํ•œ ํ›„ ์ด์ „์— ๋น„ํ•ด์„œ ์š”์ฒญ ์ œ์–ด ์‹คํŒจ์— ๋Œ€ํ•œ ์—๋Ÿฌ๊ฐ€ ํฌ๊ฒŒ ์ค„์–ด๋“  ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๐Ÿœ ํŒ€ ๊ฐœ๋ฏธ

๐Ÿ›๏ธ ํŒ€ ๋ฌธํ™”

๊ฐœ๋ฐœ ์œ„ํ‚ค

FE

BE

Infra

๐Ÿ—ฃ๏ธ ๋ฐœํ‘œ

๐Ÿ“š ํšŒ์˜๋ก

๐Ÿ”ด ์ธํ„ฐ๋ฏธ์…˜
๐ŸŸ  1์ฃผ์ฐจ
๐ŸŸก 2์ฃผ์ฐจ
๐ŸŸข 3์ฃผ์ฐจ
๐Ÿ”ต 4์ฃผ์ฐจ
๐ŸŸฃ 5์ฃผ์ฐจ
๐ŸŸค 6์ฃผ์ฐจ

๐Ÿ’ญ ํšŒ๊ณ 

๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ ๋ฉ˜ํ† ๋ง

Clone this wiki locally