{"componentChunkName":"component---src-templates-post-template-jsx","path":"/works/posts/2024-03-23--001","result":{"data":{"site":{"siteMetadata":{"title":"Blog by Eunyoung","subtitle":"작업 기록 블로그","copyright":"© All rights reserved.","author":{"name":"EunYoung","twitter":"#"},"disqusShortname":"","url":"https://ssongey.github.io"}},"markdownRemark":{"id":"28a5b8b1-0f8c-577f-9c7f-40b6a4db3edc","html":"<p>진행하고 있는 프로젝트에서 사용하고 있는 backoff 내용을 정리한다. <br/>\n사용중인 메시지 브로커: RabbitMQ</p>\n<h2>1. 용어 정리</h2>\n<h3>✔︎ Exchange</h3>\n<ul>\n<li>Producer로 부터 전달받은 메시지를 어떤 큐들에게 발송할지 결정하는 객체</li>\n<li>4가지의 타입이 있으며, 라우터 기능을 한다.</li>\n<li>\n<p>타입 4가지</p>\n<ul>\n<li>Direct: 라우팅 키가 정확히 일치하는 큐에 메시지 전송 (unicast)</li>\n<li>Topic: 라우팅 키 패턴이 일치하는 큐에 메시지 전송 (multicast)</li>\n<li>Headers: [key:value]로 이루어진 header값을 기존으로 일치하는 queue에 메시지 전송 (multicast)</li>\n<li>Fanout: 해당 exchange에 등록된 모든 큐에 메시지 전송 (broadcast)</li>\n</ul>\n</li>\n</ul>\n<h3>✔︎ Binding</h3>\n<ul>\n<li>Exchange와 큐를 연결하는 관계</li>\n<li>모든 메시지는 Exchange가 가장 먼저 수신하는데, Exchange 타입과 binding 규칙에 따라 적절한 큐로 전달된다.</li>\n</ul>\n<h3>✔︎ Dead Letter Queue(DLQ)</h3>\n<ul>\n<li>오류로 인해 처리할 수 없는 메시지를 임시로 저장하는 메시지 대기열</li>\n<li>소스 대기열에 처리되지 않은 메시지가 넘치지 않도록 잘못된 메시지 및 실패한 메시지의 임시 저장소 역할을 수행한다.</li>\n<li>DLQ에 TTL을 지정하여, 일정 시간이 지난 후 다시 작업 큐로 라우팅 되도록 한다.</li>\n</ul>\n<br/>\n<p>일반적인 메시지 큐에서 <strong>메시지가 정상적으로 소비되지 못하면</strong> 어떻게 될까?</p>\n<ul>\n<li>계속 큐에 들어가게 되어 무한 재시도 수행, 이는 시스템 부하의 원인이 됨</li>\n<li>무한 대기 상태가 되면 큐가 가득 차서 다른 메시지도 처리 불가능</li>\n</ul>\n<p>문제가 있는 메시지는 기록하고 분석할 필요가 있기 때문에 RabbitMQ, Kafka 같은 메시지 브로커는 이런 메시지를 별도의 “Dead Letter Queue(DLQ)“에 저장하도록 지원</p>\n<br/>\n<h2>2. 구조</h2>\n<p>담당중인 서비스의 기능 중, 파트너사에 요청한 작업 상태를 polling 방식으로 체크하여 작업 완료시 사용자에게 알림을 보내는 기능이 있다.<br/>\n해당 기능은 비동기로 수행되며, 이를 위해 RabbitMQ를 사용중이고, 수행 플로우는 아래와 같다.</p>\n<h3>플로우</h3>\n<ul>\n<li>파트너사로 작업을 요청 후, polling을 위해 작업 큐에 메시지를 넣는다.</li>\n<li>큐에 담긴 메시지를 consume 하여 해당 message 기반으로 파트너사 쪽으로 상태를 체크한다.</li>\n<li>만약 내부 서비스 오류 또는 통신 오류 등, 재시도가 필요한 오류가 발생하게 되면, retry 큐에 들어가게 된다.</li>\n<li>이때, backoff 정책에 의해, 재시도를 수행한 횟수에 따라 매번 다른 dead letter queue에 들어가게 된다.</li>\n<li>DLQ에 지정된 TTL이 만료되면 다시 작업 큐에 들어가게 된다.</li>\n</ul>\n<p>\n  <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/devHistoryBlog/static/011fd273a1f411959a6e2b8ca4861432/d6331/001.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n  \n  <span\n    class=\"gatsby-resp-image-wrapper\"\n    style=\"position: relative; display: block;  max-width: 702px; margin-left: auto; margin-right: auto;\"\n  >\n    <span\n      class=\"gatsby-resp-image-background-image\"\n      style=\"padding-bottom: 72.91666666666666%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB8ElEQVQ4y4WU6W7qQAyF8/7SvW9DxU8koGIR+yr2vWGPgLAG3HxWh4ZUai1ZmWTGxz7HnlgSsOFwKI1GQwaDgdRqNdnv988927al1WpJu92WyWQSDJP7/a7Px+Mh1na7ld1up8EcxJfLpXS7XZnP53I8HuVwOAjnNpuNOI4j6/Vav7muq369Xr8Bi8WiFAoFyefzCnC5XHRjOp1KpVLRikqlkjhbR/cwKu33+/okMUlfKlytVupk926eeJ4np9NJRqORAiIDyQw9ABaLhSbFXwDlF+PA7XZTNzqFteOMOYtZLMKOnc9naTab2iiowcQEAgR9WJ38c8ECrOBL0KGNdtFoVGKxmDbOGHSRoepPQrValYm/fnwxsMIUTXWGKhqibdDo7Hg89hszkPT7u7z9/yc7nwGVW8HqzNo9uLoJMFSh3ul01BkXY+xnMhlJptOSSqW0iT8A6W5wrphJaDEeAPJumgF4uVyWRDwuyWRSmVgGCIMigADgzCiUEZ9qaAQjwgihI/vcKPQl9qmhyUgQayrh+jG8s9lMQQhgH+dbvV7XquJ+daZhzy5zTxOJhJafy+U0O8JDiaoIIIn9YSs4kjAFBiS4VkDuMRrROYRFi/PXfAGazWYlEoloNUgQHuYfg/2X8VNg7nq93ss1C18Gnp9jaXb18QnVCgAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n    >\n      <img\n        class=\"gatsby-resp-image-image\"\n        style=\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\"\n        alt=\"001\"\n        title=\"\"\n        src=\"/devHistoryBlog/static/011fd273a1f411959a6e2b8ca4861432/d6331/001.png\"\n        srcset=\"/devHistoryBlog/static/011fd273a1f411959a6e2b8ca4861432/8ff5a/001.png 240w,\n/devHistoryBlog/static/011fd273a1f411959a6e2b8ca4861432/e85cb/001.png 480w,\n/devHistoryBlog/static/011fd273a1f411959a6e2b8ca4861432/d6331/001.png 702w\"\n        sizes=\"(max-width: 702px) 100vw, 702px\"\n      />\n    </span>\n  </span>\n  \n  </a>\n    </p>\n<br/>\n<h2>3. Backoff 구성</h2>\n<ul>\n<li>일반적으로 재처리를 위해 requeue를 하는 방법을 사용하는데, 처리 서버 부하로 인해 requeue 를 반복적으로 수행하면 이는 부하만 가중될 뿐이다. (최악은 DDos)</li>\n<li>일정시간 간격을 두고 재처리를 하는것도 동일하게 부하가 될 가능성이 크다고 한다.</li>\n<li>\n<p>그래서 도입된게 Exponential Backoff 전략이였나보다.</p>\n<ul>\n<li>찾아보니 이 역시도 한계가 있다고 하며, Jitter(지연변이)를 사용하는 곳도 있다고 한다.</li>\n<li>Jitter: 재시도 간격에 무작위성을 추가하여 요청하는 시간대를 분산시키는 개념</li>\n</ul>\n</li>\n</ul>\n<p>\n  <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/devHistoryBlog/static/79df5d4c1b00a317486cd9e16377376f/f09ab/002.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n  \n  <span\n    class=\"gatsby-resp-image-wrapper\"\n    style=\"position: relative; display: block;  max-width: 960px; margin-left: auto; margin-right: auto;\"\n  >\n    <span\n      class=\"gatsby-resp-image-background-image\"\n      style=\"padding-bottom: 25.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAABC0lEQVQY012Q2UrDUBCG87Y+gs8jeOOlgmDxxuWiFVEEtSWiJWljTJs055yszXY+T1rr9sPMwD/bP2PVVYUSgiLPAQ1dQw/Pn3F9c8783WUHrTW+55FnGb/R8zuzApVyaTuM/RDWkipZ0pUZQeRxenWIv5xt9jRNQ1XXnNyPccJ4O6iDuqkJVi7zxStpLrGGTsD+2S0Hdy9QKKQIjeIYmUaMngeIJKQoSrI0YSUVe8dDLt58pFwwcW1kIjZ1g+ER3mKKVZQlUSxQSfLnDJmseJiMTIy/udaoDMKIzLxH5IJAfRjxmrZtWVfr7cnG/zSYhO46RCawfZtH94mp2apyRWf43v6j5/TXjL7/E8IZe1uEbMhLAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n    >\n      <img\n        class=\"gatsby-resp-image-image\"\n        style=\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\"\n        alt=\"002\"\n        title=\"\"\n        src=\"/devHistoryBlog/static/79df5d4c1b00a317486cd9e16377376f/d9199/002.png\"\n        srcset=\"/devHistoryBlog/static/79df5d4c1b00a317486cd9e16377376f/8ff5a/002.png 240w,\n/devHistoryBlog/static/79df5d4c1b00a317486cd9e16377376f/e85cb/002.png 480w,\n/devHistoryBlog/static/79df5d4c1b00a317486cd9e16377376f/d9199/002.png 960w,\n/devHistoryBlog/static/79df5d4c1b00a317486cd9e16377376f/f09ab/002.png 1250w\"\n        sizes=\"(max-width: 960px) 100vw, 960px\"\n      />\n    </span>\n  </span>\n  \n  </a>\n    </p>\n<h3>플로우</h3>\n<ul>\n<li>작업에 실패하게 되면 retry.x Exchange에서 해당 작업의 재시도 횟수를 확인하여 그에 맞는 DLQ로 라우팅한다.</li>\n<li>DLQ에 들어온 메시지는 지정된 TTL 동안 머무른다.</li>\n<li>TTL이 만료되면 backoff.x Exchange로 이동하게 되고, header 값에 저장되어 있는 작업 큐 이름을 확인하여 해당 작업 큐로 requeue 된다.</li>\n<li>이렇게 실패 횟수에 따라 1초, 5초, 10초 … exponential 하게 재시도 시간을 증가시키며 특정 작업을 반복한다.</li>\n</ul>","fields":{"tagSlugs":["/tags/backoff/","/tags/rabbit-mq/"],"slug":"/works/posts/2024-03-23--001"},"frontmatter":{"title":"Backoff 정책 정리","tags":["backoff","RabbitMQ"],"date":"2024-03-23","description":""}}},"pageContext":{"slug":"/works/posts/2024-03-23--001"}},"staticQueryHashes":[]}