<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Maru's  Study Memo</title>
    <link>https://maru-itdeveloper.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 2 Aug 2026 22:09:01 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>maruIsCat</managingEditor>
    <item>
      <title>[Kafka] Replication</title>
      <link>https://maru-itdeveloper.tistory.com/82</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Replication of Partition:&lt;/b&gt;&lt;span&gt; 장애를 대비하기 위한 기술&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J5BWC/btryHGBnaE4/9EqT79gFu9x6pf5RicpKA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J5BWC/btryHGBnaE4/9EqT79gFu9x6pf5RicpKA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J5BWC/btryHGBnaE4/9EqT79gFu9x6pf5RicpKA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ5BWC%2FbtryHGBnaE4%2F9EqT79gFu9x6pf5RicpKA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;599&quot; height=&quot;327&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Producer/Consumer는 Leader와만 통신:&lt;/b&gt;&lt;span&gt;&lt;span&gt; follower는 복제만&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Producer는 Leader만 Write하고 Consumer는 Leader로부터만 Read함&lt;br /&gt;Follower는 Broker 장애시 안정성을 제공하기 위해서만 존재&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Follower는 Leader의 Commit Log에서 데이터를 가져오기 위해 요청(Fetch Request)으로 복제&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1075&quot; data-origin-height=&quot;591&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vS96C/btryJlQHdHA/SAsvzM1tqbK6bFdDFaYVp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vS96C/btryJlQHdHA/SAsvzM1tqbK6bFdDFaYVp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vS96C/btryJlQHdHA/SAsvzM1tqbK6bFdDFaYVp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvS96C%2FbtryJlQHdHA%2FSAsvzM1tqbK6bFdDFaYVp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;330&quot; data-origin-width=&quot;1075&quot; data-origin-height=&quot;591&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Leader 장애 -&amp;gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; 새로운 Leader를 선출&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka 클러스터는 Follower 중에서 새로운 Leader를 선출&lt;br /&gt;Client(Producer/Consumer)는 자동으로 새 Leader로 전환&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;834&quot; data-origin-height=&quot;455&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brk7oz/btryJY1UitB/knRCxgWTJl5Ry6YkMccqR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brk7oz/btryJY1UitB/knRCxgWTJl5Ry6YkMccqR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brk7oz/btryJY1UitB/knRCxgWTJl5Ry6YkMccqR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbrk7oz%2FbtryJY1UitB%2FknRCxgWTJl5Ry6YkMccqR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;620&quot; height=&quot;338&quot; data-origin-width=&quot;834&quot; data-origin-height=&quot;455&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Partition Leader에 대한 자동 분산:&lt;/b&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; Hot Spot 방지&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;auto.leader.rebalance.enable&lt;/span&gt;&lt;/b&gt;: 기본값 enable&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;leader.imbalance.check.interval.seconds&lt;/b&gt;&lt;/span&gt;: 기본값 300 sec&lt;br /&gt;. 300초마다 leader 분산에 불균형 여부 check&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;leader.imbalnce.per.broker.percentage&lt;/span&gt;&lt;/b&gt;: 기본값 10&lt;br /&gt;. 다른 브로커보다 10% 이상 더 많이 가져가면 불균형으로 판단&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1057&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDuOSA/btryFLQeZDe/9KfQPqKg9Sw9TnJg4UoIEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDuOSA/btryFLQeZDe/9KfQPqKg9Sw9TnJg4UoIEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDuOSA/btryFLQeZDe/9KfQPqKg9Sw9TnJg4UoIEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDuOSA%2FbtryFLQeZDe%2F9KfQPqKg9Sw9TnJg4UoIEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1057&quot; height=&quot;436&quot; data-origin-width=&quot;1057&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Rack Awareness:&lt;/b&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; Rack간 분산하여 Rack 장애를 대비&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 Rack 혹은 Available Zone상의 Broker들에 동일한 &quot;rack name&quot; 지정&lt;br /&gt;복제본(Replica-Leader/Follower)은 최대한 Rack 간에 균형을 유지하여 Rack 장애 대비&lt;br /&gt;Topic 생성시 또는 Auto Data Balancer/Self Balancing Cluster 동작 때만 실행&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;453&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w9qRh/btryFnP5Iv3/SJtGOK5GfIZKTKpLR34bJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w9qRh/btryFnP5Iv3/SJtGOK5GfIZKTKpLR34bJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w9qRh/btryFnP5Iv3/SJtGOK5GfIZKTKpLR34bJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw9qRh%2FbtryFnP5Iv3%2FSJtGOK5GfIZKTKpLR34bJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1046&quot; height=&quot;453&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;453&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>~2022/Apache Kafka</category>
      <category>Kafka</category>
      <category>Replication</category>
      <author>maruIsCat</author>
      <guid isPermaLink="true">https://maru-itdeveloper.tistory.com/82</guid>
      <comments>https://maru-itdeveloper.tistory.com/82#entry82comment</comments>
      <pubDate>Thu, 7 Apr 2022 16:54:35 +0900</pubDate>
    </item>
    <item>
      <title>[Kafka] Consumer</title>
      <link>https://maru-itdeveloper.tistory.com/81</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Consuming from Kafka:&lt;/b&gt; Partition으로부터 Record를 가져옴(Poll)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Consumer는 각각 고유의 속도로 Commit Log로부터 순서대로 Read(Poll)를 수행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 Consumer Group에 속한 Consumer들은 서로 관련이 없으며, Commit Log에 있는 Event(Message)를 동시에 다른 위치에서 Read할 수 있음&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csmIXY/btryEyCRcyO/dg1o9YIkdRcfuqgAAJaE2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csmIXY/btryEyCRcyO/dg1o9YIkdRcfuqgAAJaE2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csmIXY/btryEyCRcyO/dg1o9YIkdRcfuqgAAJaE2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsmIXY%2FbtryEyCRcyO%2Fdg1o9YIkdRcfuqgAAJaE2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;606&quot; height=&quot;270&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;402&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Consumer Offset: &lt;/b&gt;Consumer Group이 읽은 위치를 표시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Consumer가 자동이나 수동으로 데이터를 읽은 위치를 commit하여 다시 읽음을 방지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;__consumer_offsets&lt;/b&gt;&lt;/span&gt;라는 Internal Topic에서 Consumer Offset을 저장하여 관리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Consuming as a Group:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;span&gt;동일한 group.id로 구성된 모든 Consumer들은 하나의 Consumer Group을 형성&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Partition응 항상 Consumer Group 내의 하나의 Consumer에 의해서만 사용됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Consumer는 주어진 Topic에서 0개 이상의 많은 Partition을 사용할 수 있음&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Consumer Group의 Consumer들은 작업량을 어느 정도 균등하게 분할함&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동일한 Topic에서 consume하는 여러 Consumer Group이 있을 수 있음&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1095&quot; data-origin-height=&quot;477&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eql2bQ/btryEta5TiA/2XCPjMQf0hh1yyHVAtbHX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eql2bQ/btryEta5TiA/2XCPjMQf0hh1yyHVAtbHX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eql2bQ/btryEta5TiA/2XCPjMQf0hh1yyHVAtbHX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feql2bQ%2FbtryEta5TiA%2F2XCPjMQf0hh1yyHVAtbHX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;633&quot; height=&quot;276&quot; data-origin-width=&quot;1095&quot; data-origin-height=&quot;477&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Message Ordering(순서):&lt;/b&gt;&lt;span&gt; Key를 사용하여 Partition별 메시지 순서 보장&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 Key를 가진 메시지는 동일한 Partition에만 전달되어 Key 레벨의 순서 보장 가능&lt;br /&gt;. 멀티 Partition 사용 -&amp;gt; 처리량 증가&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAIlkB/btryDb278Ld/S6RRwdxbfBkuknbSSnDdx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAIlkB/btryDb278Ld/S6RRwdxbfBkuknbSSnDdx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAIlkB/btryDb278Ld/S6RRwdxbfBkuknbSSnDdx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAIlkB%2FbtryDb278Ld%2FS6RRwdxbfBkuknbSSnDdx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;647&quot; height=&quot;256&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;. 운영 중에 Partition 개수를 변경하면? 순서 보장 불가&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;</description>
      <category>~2022/Apache Kafka</category>
      <category>Consumer</category>
      <category>Kafka</category>
      <author>maruIsCat</author>
      <guid isPermaLink="true">https://maru-itdeveloper.tistory.com/81</guid>
      <comments>https://maru-itdeveloper.tistory.com/81#entry81comment</comments>
      <pubDate>Thu, 7 Apr 2022 14:47:32 +0900</pubDate>
    </item>
    <item>
      <title>[Kafka] Producer</title>
      <link>https://maru-itdeveloper.tistory.com/80</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Record(Message) 구조: Header, Key, Value&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;971&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J49vv/btryHLnUKLD/I4zzM3by5TGZ7M3ZPy8RV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J49vv/btryHLnUKLD/I4zzM3by5TGZ7M3ZPy8RV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J49vv/btryHLnUKLD/I4zzM3by5TGZ7M3ZPy8RV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ49vv%2FbtryHLnUKLD%2FI4zzM3by5TGZ7M3ZPy8RV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;370&quot; height=&quot;396&quot; data-origin-width=&quot;971&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Key와 Value는 Avro, Json 등 다양한 형태가 가능&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Serializer/Deserializer&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Kafka는 Record(데이터)를 Byte Array로 저장&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Key와 Value용 Serializer를 각각 설정&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1055&quot; data-origin-height=&quot;451&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/teWyR/btryDQYC5pF/JblHmbYgIf1eScENreuK81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/teWyR/btryDQYC5pF/JblHmbYgIf1eScENreuK81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/teWyR/btryDQYC5pF/JblHmbYgIf1eScENreuK81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FteWyR%2FbtryDQYC5pF%2FJblHmbYgIf1eScENreuK81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;519&quot; height=&quot;451&quot; data-origin-width=&quot;1055&quot; data-origin-height=&quot;451&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Producing to Kafka: High-Level Architecture&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1165&quot; data-origin-height=&quot;553&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmoFsv/btryCOAg6sm/qrPwmDQJ1qjMavwlndkMu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmoFsv/btryCOAg6sm/qrPwmDQJ1qjMavwlndkMu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmoFsv/btryCOAg6sm/qrPwmDQJ1qjMavwlndkMu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmoFsv%2FbtryCOAg6sm%2FqrPwmDQJ1qjMavwlndkMu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;571&quot; height=&quot;553&quot; data-origin-width=&quot;1165&quot; data-origin-height=&quot;553&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Partitioner의 역할: 메시지를 Topic의 어떤 Partition으로 보낼지 결정&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m8nzG/btryzDyFHZg/TmbKe1AKJ0brQKTvHMLyfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m8nzG/btryzDyFHZg/TmbKe1AKJ0brQKTvHMLyfk/img.png&quot; data-alt=&quot;Key가 null일 때, DefaultPartitioner&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m8nzG/btryzDyFHZg/TmbKe1AKJ0brQKTvHMLyfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm8nzG%2FbtryzDyFHZg%2FTmbKe1AKJ0brQKTvHMLyfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;495&quot; height=&quot;247&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;509&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Key가 null일 때, DefaultPartitioner&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>~2022/Apache Kafka</category>
      <category>Kafka</category>
      <category>Producer</category>
      <author>maruIsCat</author>
      <guid isPermaLink="true">https://maru-itdeveloper.tistory.com/80</guid>
      <comments>https://maru-itdeveloper.tistory.com/80#entry80comment</comments>
      <pubDate>Thu, 7 Apr 2022 09:59:24 +0900</pubDate>
    </item>
    <item>
      <title>[Kafka] Broker, Zookeeper</title>
      <link>https://maru-itdeveloper.tistory.com/79</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Kafka Broker: Topic과 Partition을 유지 및 관리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Kafka Broker는 Partition에 대한 Read 및 Write를 관리하는 소프트웨어&lt;br /&gt;. &lt;span style=&quot;color: #ee2323;&quot;&gt;Kafka Server&lt;/span&gt;라고 부르기도 함&lt;br /&gt;. Topic 내의 Partition들을 분산, 유지 및 관리&lt;br /&gt;. 각각의 Broker들은 ID로 식별됨(&lt;span style=&quot;color: #ee2323;&quot;&gt;ID는 숫자&lt;/span&gt;)&lt;br /&gt;&lt;/b&gt;. Topic의 일부 Partition들을 포함 -&amp;gt; Topic 데이터의 일부분(Partition)을 갖을 뿐 데이터 전체를 갖고 있지 않음&lt;br /&gt;. &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Kafka Cluster&lt;/b&gt;&lt;/span&gt;: 여러 대의 Broker들로 구성됨&lt;br /&gt;. Client는 특정&amp;nbsp; Broker에 연결하면 전체 클러스터에 연결됨&lt;br /&gt;. &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;최소 3대 이상&lt;/b&gt;&lt;/span&gt;의 Broker를 하나의 Cluster로 구성해야 함 -&amp;gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt; 4대 이상을 권장&lt;/b&gt;&lt;/span&gt;함&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Zookeeper: Broker를 관리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Zookeeper는 Broker를 관리 (Broker 들의 목록/설정을 관리)하는 소프트웨어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;.&lt;span&gt; 변경사항에 대해 Kafka에게 알림: 토픽 생성/제거, Broker 추가/제거 등&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;. Zookeeper는 홀수의 서버로 작동하게 설계되어 있음(최소 3, 권장 5)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;. Zookeeper에는 Leader(writes)가 있고 나머지 서버는 Follower(reads)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Zookeeper 아키텍쳐: Leader/Follower 기반 Mater/Slave 아키텍쳐&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Zookeeper는 분산형 Configuration 정보 유지, 분산 동기화 서비스를 제공하고&amp;nbsp;&lt;br /&gt;대용량 분산 시스템을 위한 네이밍 레지스트리를 제공하는 소프트웨어&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;분산 작업을 제어하기 위한 Tree 형태의 저장소&lt;br /&gt;-&amp;gt; Zookeeper를 사용하여 멀티 Kafka Broker들 간의 정보(변경 사항 포함) 공유, 동기화 등을 수행&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;.&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;변경사항에 대해 Kafka에게 알림: 토픽 생성/제거, Broker 추가/제거 등&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;. Zookeeper는 홀수의 서버로 작동하게 설계되어 있음(최소 3, 권장 5)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;. Zookeeper에는 Leader(writes)가 있고 나머지 서버는 Follower(reads)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Zookeeper Failover: Quorum 알고리즘 기반&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Quorum(쿼럼)은 &quot;정족수&quot;이며, 합의체가 의사를 진행시키거나 의결을 하는데 필요한 최소 인원을 뜻함&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;분산 코디네이션 환경에서 예상치 못한 장애가 발생해도 분산 시스템의 일관성을 유지시키기 위해서 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>~2022/Apache Kafka</category>
      <category>broker</category>
      <category>Kafka</category>
      <category>zookeeper</category>
      <author>maruIsCat</author>
      <guid isPermaLink="true">https://maru-itdeveloper.tistory.com/79</guid>
      <comments>https://maru-itdeveloper.tistory.com/79#entry79comment</comments>
      <pubDate>Thu, 7 Apr 2022 09:39:25 +0900</pubDate>
    </item>
    <item>
      <title>[Kafka] Topic, Partition, Segment</title>
      <link>https://maru-itdeveloper.tistory.com/78</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Apache Kafka Clients&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Producer&lt;/b&gt;: 메시지를 생산해서 Kafka의 Topic으로 메시지를 보내는 애플리케이션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Consumer&lt;/b&gt;: Topic의 메시지를 가져와서 소비하는 애플리케이션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Consumer&lt;/b&gt; &lt;b&gt;Group&lt;/b&gt;: Topic의 메시지를 사용하기 위해 협력하는 Consumer들의 집합&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 하나의 Consumer는 하나의 Consumer Group에 포함되며, &lt;br /&gt;&amp;nbsp; Consumer Group 내의 Consumer들은 협력하여 Topic의 메시지를 분산 병렬 처리함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Producer와 Consumer의 분리(Decoupling)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. Producer와 Consumer는 서로 알지 못하며, 각각 고유의 속도로 Commit Log에 Write 및 Read를 수행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 다른 Consumer Group에 속한 Consumer들은 서로 관련이 없으며, Commit Log에 있는 Event를 동시에 다른 위치에서 Read할 수 있음&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kRflj/btryyXw9hEp/9IWSTrY2otvk4aKIGbzMqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kRflj/btryyXw9hEp/9IWSTrY2otvk4aKIGbzMqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kRflj/btryyXw9hEp/9IWSTrY2otvk4aKIGbzMqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkRflj%2FbtryyXw9hEp%2F9IWSTrY2otvk4aKIGbzMqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;554&quot; height=&quot;280&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;444&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Kafka Commit Log&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Commit Log&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 추가만 가능하고 변경 불가능한 데이터 스트럭쳐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Offset&lt;/b&gt;&lt;br /&gt;. Commit Log에서 Event의 위치&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;891&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMt3Dx/btryzpmD1Z8/HpbiNChNJ3XqzKiCZOJZCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMt3Dx/btryzpmD1Z8/HpbiNChNJ3XqzKiCZOJZCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMt3Dx/btryzpmD1Z8/HpbiNChNJ3XqzKiCZOJZCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMt3Dx%2FbtryzpmD1Z8%2FHpbiNChNJ3XqzKiCZOJZCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;254&quot; data-origin-width=&quot;891&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Kafka Offset: Commit Log에서 Event의 위치&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Producer가 Write하는 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;LOG-END-OFFSET&lt;/span&gt;&lt;/b&gt;과 Consumer Group의 Consumer가 Read하고 처리한 후에 Commit한 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;CURRENT-OFFSET&lt;/span&gt;&lt;/b&gt;과의 차이(&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Consumer Lag&lt;/span&gt;&lt;/b&gt;)가 발생할 수 있음&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;469&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zOjTm/btryCECYvoI/19bORdXkqzq5XxcKdkgAwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zOjTm/btryCECYvoI/19bORdXkqzq5XxcKdkgAwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zOjTm/btryCECYvoI/19bORdXkqzq5XxcKdkgAwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzOjTm%2FbtryCECYvoI%2F19bORdXkqzq5XxcKdkgAwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;588&quot; height=&quot;301&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;469&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Topic, Partition, Segment: Logical View&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Topic&lt;/b&gt;&lt;/span&gt;: Kafka 안에서 메시지가 저장되는 장소, 논리적인 표현&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Partition&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;. Commit Log, 하나의 Topic은 하나 이상의 Partition으로 구성&lt;br /&gt;. 병렬 처리(Throughput 향상)를 위해서 다수의 Partition을 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Segment&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;. 메시지(데이터)가 저장되는 실제 물리 File&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. Segment File이 지저된 크기보다 크거나 지정된 기간보다 오래되면 새 파일이 열리고 메시지는 새 파일에 추가됨&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwKefZ/btryDP400Z5/8vfMdDJF1EeabkY2rY5Gbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwKefZ/btryDP400Z5/8vfMdDJF1EeabkY2rY5Gbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwKefZ/btryDP400Z5/8vfMdDJF1EeabkY2rY5Gbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwKefZ%2FbtryDP400Z5%2F8vfMdDJF1EeabkY2rY5Gbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;485&quot; height=&quot;323&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Topic, Partition, Segment: Physical View&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Topic 생성 시 Partition 개수를 지정&lt;/span&gt;&lt;/b&gt;하고, 각 Partition은 Broker들에 분산되며 Segment File들로 구성됨&lt;br /&gt;Rolling Strategy: 특정 용량이나 시간에 따라 Segment 파일을 분리&amp;nbsp;&lt;br /&gt;. &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;log.segment.bytes&lt;/span&gt;&lt;/b&gt;(default 1GB)&lt;br /&gt;. &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;log.roll.hours&lt;/b&gt;&lt;/span&gt;(default 168 hours)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yH98U/btryy1lmocY/2c6kAqzRSGlMVTdqff8CfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yH98U/btryy1lmocY/2c6kAqzRSGlMVTdqff8CfK/img.png&quot; data-alt=&quot;파일을 Rolling하여 분리/생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yH98U/btryy1lmocY/2c6kAqzRSGlMVTdqff8CfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyH98U%2Fbtryy1lmocY%2F2c6kAqzRSGlMVTdqff8CfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;329&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;파일을 Rolling하여 분리/생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Active Segment: Partition당 하나의 Active Segment&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Partition당 오직 하나의 Segment가 &lt;b&gt;활성화(Active)&lt;/b&gt; 되어 있음&lt;br /&gt;-&amp;gt; 데이터가 계속 쓰여지고 있는 중&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1525&quot; data-origin-height=&quot;537&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crdw43/btryx3xfd9T/pZFXUgu0Dp2HdkZH2BK2yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crdw43/btryx3xfd9T/pZFXUgu0Dp2HdkZH2BK2yk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crdw43/btryx3xfd9T/pZFXUgu0Dp2HdkZH2BK2yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcrdw43%2Fbtryx3xfd9T%2FpZFXUgu0Dp2HdkZH2BK2yk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;614&quot; height=&quot;216&quot; data-origin-width=&quot;1525&quot; data-origin-height=&quot;537&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Topic, Partition, Segment의 특징&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Topic 생성 시 Partition 개수를 지정 - &lt;/b&gt;&lt;b&gt;개수 변경 가능하나 운영 시에는 변경 권장하지 않음&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Partition 번호는 0부터 시작하고 오름차순&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Topic 내의 Partition들은 서로 독립적임&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Event(Message)의 위치를 나타내는 Offset 이 존재&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Offset은 하나의 Partition에서만 의미를 가짐 - &lt;/b&gt;&lt;b&gt;Partition 0 의 offset 1은 Partition 1의 offset 1과 연관 없음&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Offset값은 계속 증가하고 0으로 돌아가지 않음&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Event(Message)의 순서는 하나의 Partition 내에서만 보장&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Partition에 저장된 데이터(Message)는 변경이 불가능(Immutable)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Partition에 Write되는 데이터는 맨 끝에 추가되어 저장됨&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Partition은 Segment File들로 구성됨 - &lt;/b&gt;&lt;b&gt;Rolling 정책: &lt;span style=&quot;color: #ee2323;&quot;&gt;log.segment.bytes&lt;/span&gt;, &lt;span style=&quot;color: #ee2323;&quot;&gt;log.roll.hours&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;</description>
      <category>~2022/Apache Kafka</category>
      <category>Kafka</category>
      <category>partition</category>
      <category>segment</category>
      <category>topic</category>
      <author>maruIsCat</author>
      <guid isPermaLink="true">https://maru-itdeveloper.tistory.com/78</guid>
      <comments>https://maru-itdeveloper.tistory.com/78#entry78comment</comments>
      <pubDate>Wed, 6 Apr 2022 15:36:29 +0900</pubDate>
    </item>
    <item>
      <title>[Kafka] server.properties</title>
      <link>https://maru-itdeveloper.tistory.com/77</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Kafka 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;brocker id &lt;/b&gt;: 브로커 인스턴스마다 고유한 값을 가짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;linteners &lt;/b&gt;: 브로커에서 참조하는 엔드포인트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;advertised.listeners&lt;/b&gt;&lt;br /&gt;. 컨슈머/프로듀서에서 참조하는 엔드포인트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 설정하지 않으면 linteners에 설정된 기본값 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;** linteners/advertised.listeners가 따로 있는 이유? 내부와 외부 트래픽을 나누기 위해&lt;/b&gt;&lt;br /&gt;&amp;nbsp; . 예를 들어 replication 트래픽은 client의 트래픽을 방해해선 안되기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; . 외부 트래픽은 프록시나 로드밸런서를 타고 올 텐데, 내부 트래픽은 성능 이점 때문에 직접 브로커로 붙어도 괜찮다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; . 외부 트래픽은 SSL을 적용, 내부 트래픽은 SSL을 미적용할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;num.network.threads&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 서버가 요청을 받거나 응답을 내보내는 스레드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;num.io.threads&lt;/b&gt;&lt;br /&gt;. 서버가 클라이언트의 &lt;span&gt;Disk I/O같은 &lt;/span&gt;요청을 처리하는 스레드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;socket.send.buffer.bytes&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;socket.receive.buffer.bytes&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 소켓 사이즈를 바이트 단위로 설정 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;===================================================================&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;log.dirs&lt;/b&gt;&lt;br /&gt;. 브로커가 데이터를 저장하는 디렉토리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;num.partitions&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 파티션 개수를 지정하지 않았을 때 기본적으로 사용되는 파티션 수===================================================================&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;log.flush.interval.messages&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 데이터를 디스크에 쓰기 전에 몇 개 까지의 메시지를 가지고 있을 것인지 설정하는 옵션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;log.flust.interval.ms&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 플러시를 하기 전에 얼마까지 적재를 할 지 시간으로 설정 (n초마다 flush)&lt;br /&gt;===================================================================&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;log.flush.interval.ms&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 리텐션 policy에 따라서 삭제할 수 있는지 여부를 확인하기 위해 log segment를 확인하는 간격&lt;br /&gt;===================================================================&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;auto.create.topics.enable&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;compression.type&lt;/b&gt;&lt;br /&gt;. 프로듀서가 해당 타입으로 메시지를 압축해서 전송, 브로커가 그대로 디스크에 저장하고 컨슈머가 압축한 상태로 consume&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;delete.topic.enable&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. topic을 삭제하는 것을 활성화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;message.max.bytes&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 메시지 payloads 제한&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;replica.lag.time.max.ms&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. follower가 leader한테 이 시간동안 fetch request를 보내지 않는다면, 그리고 leader의 log를 다 소모하지 않았다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; leader는 ISR에서 해당 follower를 제거&lt;/p&gt;</description>
      <category>~2022/Apache Kafka</category>
      <category>Kafka</category>
      <category>server.properties</category>
      <author>maruIsCat</author>
      <guid isPermaLink="true">https://maru-itdeveloper.tistory.com/77</guid>
      <comments>https://maru-itdeveloper.tistory.com/77#entry77comment</comments>
      <pubDate>Wed, 6 Apr 2022 14:43:33 +0900</pubDate>
    </item>
    <item>
      <title>[Camel-Quarkus] Exception Handling &amp;amp; Branch Statement (feat. Slack Component)</title>
      <link>https://maru-itdeveloper.tistory.com/73</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;** &lt;a href=&quot;https://maru-itdeveloper.tistory.com/category/Camel%20with%20Quarkus&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Camel-Quarkus] Shop&amp;nbsp;Example&amp;nbsp;using&amp;nbsp;Kafka,&amp;nbsp;JPA&amp;nbsp;Component&lt;/a&gt;&amp;nbsp;예제에서 이어지는 내용입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1031&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d3NTsZ/btryvvsvbSc/RcQnoH5NqG0slKqKmi4lOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d3NTsZ/btryvvsvbSc/RcQnoH5NqG0slKqKmi4lOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d3NTsZ/btryvvsvbSc/RcQnoH5NqG0slKqKmi4lOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd3NTsZ%2FbtryvvsvbSc%2FRcQnoH5NqG0slKqKmi4lOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;272&quot; data-origin-width=&quot;1031&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Orderform 정보(주문자 Id, 주문한 Item Id, 주문 수량)를 가지고 Order를 생성하는 과정은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) Member Id로 주문자 정보를 조회 &amp;rarr; &lt;u&gt;주문자(회원) 정보가 없으면 Exception&lt;/u&gt;&lt;br /&gt;2) Item Id로 상품 정보를 조회 &amp;rarr; &lt;u&gt;Item 정보가 없으면 Exeption&lt;/u&gt;&lt;br /&gt;3) Member.address 정보로 Delivery(배송정보) 생성&lt;br /&gt;4) 주문 수량과 Item.price 정보로 OrderItem(주문상품 정보) 생성&lt;br /&gt;5) Order 생성 &amp;rarr; &lt;u&gt;이 때 주문 수량만큼 재고를 줄이는데, 재고가 부족하면 Exception&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 프로세스에서 &lt;u&gt;밑줄 친 부분&lt;/u&gt;은 예외 case에 해당한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는, 위 과정에서 발생할 수 있는 Exception에 대한 처리가 되어있지 않은 상태이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Exception이 발생하면 Camel Route가 Exception이 발생한 시점에 동작을 멈추게 되고, 당연히 DB가 Update되지 않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1648786672332&quot; class=&quot;reasonml&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from(&quot;kafka:orders?brokers=localhost:9092&amp;amp;valueDeserializer=org.acme.json.OrderformDeserializer&quot;)
	.process(new Processor() {
    	@Override
        public void process(Exchange exchange) throws Exception {
        	Message message = exchange.getIn();
            System.out.println(&quot;[message header] -- &quot; + message.getHeaders());
            System.out.println(&quot;[message body] -- &quot; + message.getBody());

            Orderform orderform = message.getBody(Orderform.class);

            // Message Transformation
            Long memberId = orderform.getMemberId();
            Long itemId = orderform.getItemId();
            Long orderCount = orderform.getOrderCount();

            Map&amp;lt;String, Object&amp;gt; orderFormMap = Map.of(
            	&quot;memberId&quot;, memberId,&quot;itemId&quot;, itemId, &quot;orderCount&quot;, orderCount
            );
            message.setHeaders(orderFormMap);

            System.out.println(&quot;[message header(changed)] -- &quot; + message.getHeaders());
            System.out.println(&quot;[message body] -- &quot; + message.getBody());
        }
    })
    // Search Member Info. (주문 회원 정보를 DB에서 select)
    .toD(&quot;jpa://&quot; + Member.class.getName() + &quot;?query=select b from &quot; + Member.class.getName() + &quot; b where b.id = ${header.memberId}&quot;)
    .to(&quot;bean:orderProcessingBean?method=checkMember&quot;)
    // Search Item Info. (주문상품 정보를 DB에서 select)
    .toD(&quot;jpa://&quot; + Item.class.getName() + &quot;?query=select b from &quot; + Item.class.getName() + &quot; b where b.id = ${header.itemId}&quot; )
    .to(&quot;bean:orderProcessingBean?method=checkItem&quot;)
    // Make Order (주문 회원, 배송정보, 주문상품의 정보로 실제 주문 엔티티를 생성)
    .to(&quot;bean:orderProcessingBean?method=makeOrder&quot;)
    // JPA persist (주문 정보를 DB에 저장, 연관 table들이 같이 update됨)
    .to(&quot;jpa://&quot; + Order.class.getName() + &quot;?usePersist=true&quot;)
    .log(&quot;--- ${body} ---&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Make Order(주문 Entity 생성) 부분에서 재고가 없는 Exception이 발생할 경우,&lt;br /&gt;위에 정의된 Camel Route를 따라 processing이 진행되는 중에&amp;nbsp; NotEnoughStockExeption이 발생한다.&lt;/p&gt;
&lt;pre id=&quot;code_1648786900675&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//package org.acme.routes.beans;
public void makeOrder(Message message) {
    Map&amp;lt;String, Object&amp;gt; orderFormMap = message.getHeaders();
    Long orderCount = (Long) orderFormMap.get(&quot;orderCount&quot;);

    OrderItem orderItem = OrderItem.createOrderItem(this.item, this.item.getPrice(), orderCount.intValue());
    Order order = Order.createOrder(this.member, this.delivery, orderItem);

    message.setBody(order);
}

//package org.acme.domain;
public static OrderItem createOrderItem(Item item, int orderPrice, int count) {
    OrderItem orderItem = new OrderItem();
    orderItem.setItem(item);
    orderItem.setOrderPrice(orderPrice);
    orderItem.setCount(count);

    item.removeStock(count);
    return orderItem;
}

//package org.acme.domain.Item;
public void removeStock(int quantity) {
    int restStock = this.stockQuantity - quantity;
    if (restStock &amp;lt; 0) {
        throw new NotEnoughStockException(&quot;need more stock&quot;);
    }
    this.stockQuantity = restStock;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Camel onException() 메소드를 사용한 Exception Handling&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시와 같이 onException() 메소드를 사용해서 간단히 Exception Handling이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, onException() 메소드는 해당 라우트에서 onException 구문이 나오기 전까지 발생한 exception만 handling하므로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 Exception이 발생할 것으로 예상되는 DSL문 다음에 사용해야 한다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;from(&quot;kafka:orders?brokers=localhost:9092&amp;amp;valueDeserializer=org.acme.json.OrderformDeserializer&quot;)
        .process(new Processor() {
            @Override
            public void process(Exchange exchange) throws Exception {
                Message message = exchange.getIn();
                System.out.println(&quot;[message header] -- &quot; + message.getHeaders());
                System.out.println(&quot;[message body] -- &quot; + message.getBody());

                Orderform orderform = message.getBody(Orderform.class);

                // Message Transformation
                Long memberId = orderform.getMemberId();
                Long itemId = orderform.getItemId();
                Long orderCount = orderform.getOrderCount();

                Map&amp;lt;String, Object&amp;gt; orderFormMap = Map.of(
                        &quot;memberId&quot;, memberId, &quot;itemId&quot;, itemId, &quot;orderCount&quot;, orderCount
                );
                message.setHeaders(orderFormMap);

                System.out.println(&quot;[message header(changed)] -- &quot; + message.getHeaders());
                System.out.println(&quot;[message body] -- &quot; + message.getBody());
            }
        })
        // Search Member Info. (주문자 정보를 DB에서 select)
        .toD(&quot;jpa://&quot; + Member.class.getName() + &quot;?query=select b from &quot; + Member.class.getName() + &quot; b where b.id = ${header.memberId}&quot;)
        .to(&quot;bean:orderProcessingBean?method=checkMember&quot;)
        // Search Item Info (주문상품 정보를 DB에서 select)
        .toD(&quot;jpa://&quot; + Item.class.getName() + &quot;?query=select b from &quot; + Item.class.getName() + &quot; b where b.id = ${header.itemId}&quot;)
        .to(&quot;bean:orderProcessingBean?method=checkItem&quot;)
        // Make Order (주문 회원, 배송정보, 주문상품 정보로 실제 주문 엔티티를 생성)
        .to(&quot;bean:orderProcessingBean?method=makeOrder&quot;)
        // JPA persist (주문 정보를 DB에 저장, 연관 table들이 같이 update됨)
        .to(&quot;jpa://&quot; + Order.class.getName() + &quot;?usePersist=true&quot;)
        .onException(NotEnoughStockException.class)
            .transform(simple(&quot;exception&quot;))
            .to(&quot;slack:@hslee09?webhookUrl=https://hooks.slack.com/services/xxx...&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Camel Branch 구문(choice ~ when)을 사용한 예외 처리&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 camel branch를 활용하여 exception을 처리할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 message body나 header의 내용에 따라 분기를 나눌 것이다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// Search Member Info. (주문자 정보를 DB에서 select)
.toD(&quot;jpa://&quot; + Member.class.getName() + &quot;?query=select b from &quot; + Member.class.getName() + &quot; b where b.id = ${header.memberId}&quot;)
.to(&quot;bean:orderProcessingBean?method=checkMember&quot;)
// Search Item Info (주문상품 정보를 DB에서 select)
.toD(&quot;jpa://&quot; + Item.class.getName() + &quot;?query=select b from &quot; + Item.class.getName() + &quot; b where b.id = ${header.itemId}&quot;)
.to(&quot;bean:orderProcessingBean?method=checkItem&quot;)
// Make Order (주문 회원, 배송정보, 주문상품 정보로 실제 주문 엔티티를 생성)
.to(&quot;bean:orderProcessingBean?method=makeOrder&quot;)
// JPA persist (주문 정보를 DB에 저장, 연관 table들이 같이 update됨)
.choice()
.when(simple(&quot;${body} is 'org.acme.domain.Order'&quot;))
    .to(&quot;jpa://&quot; + Order.class.getName() + &quot;?usePersist=true&quot;)
.otherwise() // Exception이 발생한 경우, message body가 Order type이 아니므로 slack으로 메시지 송신 
    .to(&quot;slack:@hslee09?webhookUrl=https://hooks.slack.com/services/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&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Camel Route에서 message header나 body의 내용을 보고 Exception 여부를 확인할 수 있도록,&lt;br /&gt;processing bean에서 exception 처리를 해준다&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// package org.acme.routes.beans;
public void makeOrder(Message message) {
    try {
        Map&amp;lt;String, Object&amp;gt; orderFormMap = message.getHeaders();
        Long orderCount = (Long) orderFormMap.get(&quot;orderCount&quot;);

        OrderItem orderItem = OrderItem.createOrderItem(this.item, this.item.getPrice(), orderCount.intValue());
        Order order = Order.createOrder(this.member, this.delivery, orderItem);
		
        // 정상적으로 order을 생성한 경우, message body는 order 객체가 들어가게 된다
        message.setBody(order);
    } catch (RuntimeException e) {
    	// 예외가 발생한 경우, message body를 &quot;exception&quot; string으로 transform 한다
        message.setBody(&quot;exception&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>~2022/Camel with Quarkus</category>
      <category>Camel</category>
      <category>Camel Exception Handling</category>
      <category>Camel-Kafka</category>
      <category>Camel-Slack</category>
      <category>Quarkus</category>
      <author>maruIsCat</author>
      <guid isPermaLink="true">https://maru-itdeveloper.tistory.com/73</guid>
      <comments>https://maru-itdeveloper.tistory.com/73#entry73comment</comments>
      <pubDate>Fri, 1 Apr 2022 14:15:34 +0900</pubDate>
    </item>
    <item>
      <title>[Camel-Quarkus] Shop Example using Kafka, JPA Component</title>
      <link>https://maru-itdeveloper.tistory.com/72</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;OverView&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clRzqA/btrytj0zpiy/28KKLNj0bErZHyDtwKoFpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clRzqA/btrytj0zpiy/28KKLNj0bErZHyDtwKoFpk/img.png&quot; data-alt=&quot;Shop Example Overview&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clRzqA/btrytj0zpiy/28KKLNj0bErZHyDtwKoFpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclRzqA%2Fbtrytj0zpiy%2F28KKLNj0bErZHyDtwKoFpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;577&quot; height=&quot;277&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;678&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Shop Example Overview&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DataSource&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Adding Dependencies&lt;/p&gt;
&lt;pre id=&quot;code_1648445503454&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;io.quarkus&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;quarkus-hibernate-orm&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;io.quarkus&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;quarkus-jdbc-mysql&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DatasSource Configuration&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1648444863330&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;quarkus.datasource.db-kind=mysql
quarkus.datasource.username=root
quarkus.datasource.password=0103
quarkus.datasource.jdbc.url=jdbc:mysql://localhost:3306/my-database?createDatabaseIfNotExist=true
quarkus.hibernate-orm.database.generation.create-schemas=true
quarkus.hibernate-orm.database.generation=drop-and-create&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;** &lt;u&gt;quarkus.hibernate-orm.database.generation.create-schemas&lt;/u&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;Find the class with @Entity annotation and create a DDL statement at the start of the server and apply it to the DB.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;** &lt;u&gt;quarkus.hibernate-orm.database.generation=drop-and-create&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;If schema exists, delete it and recreate it.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Shop Domain Design&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;925&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nBHPy/btrxwA4imo6/I7uVKMrXThYGumVKo1OGh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nBHPy/btrxwA4imo6/I7uVKMrXThYGumVKo1OGh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nBHPy/btrxwA4imo6/I7uVKMrXThYGumVKo1OGh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnBHPy%2FbtrxwA4imo6%2FI7uVKMrXThYGumVKo1OGh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;452&quot; height=&quot;292&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;925&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Send Order(Publishing) to Kafka from REST Endpoint&lt;/h3&gt;
&lt;pre id=&quot;code_1648447157179&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.acme.kafka;

import org.acme.domain.Orderform;
import org.eclipse.microprofile.reactive.messaging.Channel;
import org.eclipse.microprofile.reactive.messaging.Emitter;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

@ApplicationScoped//-&amp;gt; @Component in Spring
public class OrderformProducer {

    @Inject //-&amp;gt; @Autowired in Spring
    @Channel(&quot;orderform-out&quot;) // messaging channel
    Emitter&amp;lt;Orderform&amp;gt; emitter;

    public void sendOrderformToKafka(Orderform orderForm) {
        emitter.send(orderForm);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application.properties&lt;/p&gt;
&lt;pre id=&quot;code_1648447279349&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mp.messaging.outgoing.orderform-out.connector=smallrye-kafka
mp.messaging.outgoing.orderform-out.topic=orders&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d1c2c;&quot;&gt;Quarkus has built-in support for JSON serialization and deserialization based on Jackson. It will also&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;generate&lt;span style=&quot;background-color: #ffffff; color: #0d1c2c;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;the serializer and deserializer for you, so you do not have to configure anything about 'mp.messaging.outgoing.orderform-out.value.serializer'&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Receiving Order from Kafka -&amp;gt; Camel Route&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Adding Camel Dependencies&lt;/p&gt;
&lt;pre id=&quot;code_1648447761915&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.apache.camel.quarkus&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;camel-quarkus-kafka&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.apache.camel.quarkus&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;camel-quarkus-jdbc&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;
	&amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.apache.camel.quarkus&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;camel-quarkus-jpa&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.apache.camel.quarkus&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;camel-quarkus-bean&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Define Route&lt;/p&gt;
&lt;pre id=&quot;code_1648448041198&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from(&quot;kafka:orders?brokers=localhost:9092&amp;amp;valueDeserializer=org.acme.json.OrderformDeserializer&quot;)
	.process(new Processor() {
    	@Override
        public void process(Exchange exchange) throws Exception {
        	Message message = exchange.getIn();
            System.out.println(&quot;[message header] -- &quot; + message.getHeaders());
            System.out.println(&quot;[message body] -- &quot; + message.getBody());

            Orderform orderform = message.getBody(Orderform.class);

            // Message Transformation
            Long memberId = orderform.getMemberId();
            Long itemId = orderform.getItemId();
            Long orderCount = orderform.getOrderCount();

            Map&amp;lt;String, Object&amp;gt; orderFormMap = Map.of(
            	&quot;memberId&quot;, memberId,&quot;itemId&quot;, itemId, &quot;orderCount&quot;, orderCount
            );
            message.setHeaders(orderFormMap);

            System.out.println(&quot;[message header(changed)] -- &quot; + message.getHeaders());
            System.out.println(&quot;[message body] -- &quot; + message.getBody());
        }
    })
    // Search Member Info. (주문 회원 정보를 DB에서 select)
    .toD(&quot;jpa://&quot; + Member.class.getName() + &quot;?query=select b from &quot; + Member.class.getName() + &quot; b where b.id = ${header.memberId}&quot;)
    .to(&quot;bean:orderProcessingBean?method=checkMember&quot;)
    // Search Item Info. (주문상품 정보를 DB에서 select)
    .toD(&quot;jpa://&quot; + Item.class.getName() + &quot;?query=select b from &quot; + Item.class.getName() + &quot; b where b.id = ${header.itemId}&quot; )
    .to(&quot;bean:orderProcessingBean?method=checkItem&quot;)
    // Make Order (주문 회원, 배송정보, 주문상품의 정보로 실제 주문 엔티티를 생성)
    .to(&quot;bean:orderProcessingBean?method=makeOrder&quot;)
    // JPA persist (주문 정보를 DB에 저장, 연관 table들이 같이 update됨)
    .to(&quot;jpa://&quot; + Order.class.getName() + &quot;?usePersist=true&quot;)
    .log(&quot;--- ${body} ---&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;By processing the Message Processing process using Bean, &lt;br /&gt;you can organize the Route concisely and reuse the code for the same processing later.&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648449747562&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.acme.routes.beans;

import io.quarkus.runtime.annotations.RegisterForReflection;
import org.acme.domain.*;
import org.acme.domain.Item.Item;
import org.apache.camel.Message;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import java.util.ArrayList;
import java.util.Map;

@ApplicationScoped
@Named(&quot;orderProcessingBean&quot;)
@RegisterForReflection
public class OrderProcessingBean {

    private static Logger logger = LoggerFactory.getLogger(OrderProcessingBean.class);
    private Member member;
    private Delivery delivery;
    private Item item;

    public void checkMember(ArrayList&amp;lt;Member&amp;gt; memberList) {
        if (ObjectUtils.isEmpty(memberList)) {
            logger.error(&quot;member가 존재하지 않습니다.&quot;);
        } else if (memberList.size() &amp;gt; 1) {
            logger.error(&quot;하나의 member_id key에 두 명 이상의 member가 조회되었습니다.&quot;);
        } else {
            Member member = memberList.get(0);
            this.member = member;
            this.delivery = new Delivery(member.getAddress(), DeliveryStatus.READY);
        }
    }

    public void checkItem(ArrayList&amp;lt;Item&amp;gt; itemList) {
        if (ObjectUtils.isEmpty(itemList)) {
            logger.error(&quot;item이 존재하지 않습니다.&quot;);
        } else if (itemList.size() &amp;gt; 1) {
            logger.error(&quot;하나의 item_id에 두 개 이상의 item이 조회되었습니다.&quot;);
        } else {
            Item item = itemList.get(0);
            this.item = item;
        }
    }

    public void makeOrder(Message message) {
        Map&amp;lt;String, Object&amp;gt; orderFormMap = message.getHeaders();
        Long orderCount = (Long) orderFormMap.get(&quot;orderCount&quot;);

        OrderItem orderItem = OrderItem.createOrderItem(this.item, this.item.getPrice(), orderCount.intValue());
        Order order = Order.createOrder(this.member, this.delivery, orderItem);

        message.setBody(order);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;To receive JSON data from Camel Kafka Component, you must define a custom deserializer.&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648451384533&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.acme.json;

import io.quarkus.kafka.client.serialization.ObjectMapperDeserializer;
import org.acme.domain.Orderform;

public class OrderformDeserializer extends ObjectMapperDeserializer&amp;lt;Orderform&amp;gt; {
    public OrderformDeserializer() {
        super(Orderform.class);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Run the Application and Check Results&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Send Post Request] The Application will publish orderform message.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1195&quot; data-origin-height=&quot;335&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boiSmN/btrxHxxUekl/quBg9059CScqhcDC4Qf2V0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boiSmN/btrxHxxUekl/quBg9059CScqhcDC4Qf2V0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boiSmN/btrxHxxUekl/quBg9059CScqhcDC4Qf2V0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboiSmN%2FbtrxHxxUekl%2FquBg9059CScqhcDC4Qf2V0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;607&quot; height=&quot;335&quot; data-origin-width=&quot;1195&quot; data-origin-height=&quot;335&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[Message Transformation] header contents is changed.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1671&quot; data-origin-height=&quot;89&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SEW6W/btrxD2kAi8s/bfYcKpXQbVICXL9Z58KbqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SEW6W/btrxD2kAi8s/bfYcKpXQbVICXL9Z58KbqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SEW6W/btrxD2kAi8s/bfYcKpXQbVICXL9Z58KbqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSEW6W%2FbtrxD2kAi8s%2FbfYcKpXQbVICXL9Z58KbqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1671&quot; height=&quot;89&quot; data-origin-width=&quot;1671&quot; data-origin-height=&quot;89&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[After JPA persist] Order, Delivery, OrderItem table is updated.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1085&quot; data-origin-height=&quot;610&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brWQbn/btrxr4cuUp9/DkhDNREVNrG8XP645EjtK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brWQbn/btrxr4cuUp9/DkhDNREVNrG8XP645EjtK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brWQbn/btrxr4cuUp9/DkhDNREVNrG8XP645EjtK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrWQbn%2Fbtrxr4cuUp9%2FDkhDNREVNrG8XP645EjtK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;490&quot; height=&quot;610&quot; data-origin-width=&quot;1085&quot; data-origin-height=&quot;610&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://quarkus.io/guides/hibernate-orm&quot;&gt;https://quarkus.io/guides/hibernate-orm&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://medium.com/nerd-for-tech/apache-camel-crud-with-jpa-fa9603430ff5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://medium.com/nerd-for-tech/apache-camel-crud-with-jpa-fa9603430ff5&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://quarkus.io/guides/kafka#kafka-serialization&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://quarkus.io/guides/kafka#kafka-serialization&lt;/a&gt;&lt;/p&gt;</description>
      <category>~2022/Camel with Quarkus</category>
      <category>Camel</category>
      <category>Camel-JPA</category>
      <category>Camel-Kafka</category>
      <category>Camel-Quarkus</category>
      <category>Quakus</category>
      <author>maruIsCat</author>
      <guid isPermaLink="true">https://maru-itdeveloper.tistory.com/72</guid>
      <comments>https://maru-itdeveloper.tistory.com/72#entry72comment</comments>
      <pubDate>Mon, 28 Mar 2022 15:44:14 +0900</pubDate>
    </item>
    <item>
      <title>[Quarkus-Example] Reactive Messaging with Kafka</title>
      <link>https://maru-itdeveloper.tistory.com/71</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Adding dependencies&lt;/h3&gt;
&lt;pre id=&quot;code_1648441338885&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
   &amp;lt;groupId&amp;gt;io.quarkus&amp;lt;/groupId&amp;gt;
   &amp;lt;artifactId&amp;gt;quarkus-smallrye-reactive-messaging-kafka&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
   &amp;lt;groupId&amp;gt;io.quarkus&amp;lt;/groupId&amp;gt;
   &amp;lt;artifactId&amp;gt;quarkus-resteasy-jackson&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;What are we going to exchange?&lt;/h3&gt;
&lt;pre id=&quot;code_1648441548947&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Getter @ToString
public class Employee {
    @Id
    private Integer empId;
    private String empName;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Configure the Application&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d1c2c;&quot;&gt;When you use Reactive Messaging, &lt;u&gt;you send messages to a channel&lt;/u&gt; and &lt;u&gt;receive them from another channel&lt;/u&gt;. These channels are mapped to the underlying messaging technology by configuration. In our application, we must indicate that our reception and publication channels will use the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;movies&lt;span style=&quot;background-color: #ffffff; color: #0d1c2c;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Kafka channel&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648441763750&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#Kafka Server
kafka.bootstrap.servers=localhost:9092
#Kafka Topic Name
kafka.topic.name=employees

# Configuring the incoming channel (reading from Kafka)
mp.messaging.incoming.employees-in.connector=smallrye-kafka
mp.messaging.incoming.employees-in.topic=employees
mp.messaging.incoming.employees-in.key.deserializer=org.apache.kafka.common.serialization.IntegerDeserializer
mp.messaging.incoming.employees-in.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer

# Configuring the outgoing channel (writing to Kafka)
mp.messaging.outgoing.employees-out.connector=smallrye-kafka
mp.messaging.outgoing.employees-out.topic=employees
mp.messaging.outgoing.employees-out.key.serializer=org.apache.kafka.common.serialization.IntegerSerializer
mp.messaging.outgoing.employees-out.value.serializer=org.apache.kafka.common.serialization.StringSerializer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #0d1c2c;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;No configuration required in case of String type(default configuration), but in Integer Type, data will be lost if the serial/deserializer is not set properly.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #0d1c2c;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #0d1c2c;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;In case of Object Type (JSON), the serial/deserializer built into Quarkus automatically operates, so there is no need to set the key/value type.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #7a869a;&quot;&gt;String Type의 경우 Default이기 때문에 설정이 필요 없으나, Integer Type으로 key나 value를 consume/produce 할 경우 serializer를 알맞게 설정하지 않으면 data가 소실된다.&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;color: #7a869a;&quot;&gt;Object Type(JSON)의 경우 Quarkus에 built-in 된 serial/deserializer가 자동으로 동작하게 되어 key/value type 설정이 필요 없다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Publishing to Kafka&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d1c2c;&quot;&gt;Create the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;org.acme.domain.Employee&lt;span style=&quot;background-color: #ffffff; color: #0d1c2c;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;class with the following content:&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648441991402&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.acme.kafka;

import io.smallrye.reactive.messaging.kafka.Record;
import org.acme.domain.Employee;
import org.eclipse.microprofile.reactive.messaging.Channel;
import org.eclipse.microprofile.reactive.messaging.Emitter;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

@ApplicationScoped//-&amp;gt; @Component in Spring
public class EmployeeProducer {

    @Inject //-&amp;gt; @Autowired in Spring
    @Channel(&quot;employees-out&quot;) // messaging channel
    Emitter&amp;lt;Record&amp;lt;Integer, String&amp;gt;&amp;gt; emitter;

    public void sendEmployeeToKafka(Employee employee) {
        emitter.send(Record.of(employee.getEmpId(), employee.getEmpName()));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In this class, we inject an&lt;span&gt;&amp;nbsp;&lt;/span&gt;Emitter, i.e., an object responsible for sending a message to a channel. This emitter is attached to the&lt;span&gt;&amp;nbsp;&lt;/span&gt;movies-out&lt;span&gt;&amp;nbsp;&lt;/span&gt;channel (and so will send messages to Kafka).&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So, the rest of our application can simply use the&lt;span&gt;&amp;nbsp;&lt;/span&gt;sendMovieToKafka&lt;span&gt;&amp;nbsp;&lt;/span&gt;method to send a Employee info. to our Kafka topic.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Consuming from Kafka&lt;/h3&gt;
&lt;pre id=&quot;code_1648442321277&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.acme.kafka;

import io.smallrye.reactive.messaging.kafka.Record;
import org.eclipse.microprofile.reactive.messaging.Incoming;
import org.jboss.logging.Logger;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped //-&amp;gt; @Component in Spring
public class EmployeeConsumer {

    private final Logger logger = Logger.getLogger(EmployeeConsumer.class);

    @Incoming(&quot;employees-in&quot;) // messaging channel
    public void receive(Record&amp;lt;Integer, String&amp;gt; record) {
        logger.infof(&quot;Got an employee: %s - %s&quot;, record.key(), record.value());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0d1c2c;&quot;&gt;Here, we use the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@Incoming&lt;span style=&quot;background-color: #ffffff; color: #0d1c2c;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;annotation to indicate to Quarkus to call the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;receive&lt;span style=&quot;background-color: #ffffff; color: #0d1c2c;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;method for every received record.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;step-6-sending-movies-from-a-rest-endpoint&quot; data-ke-size=&quot;size26&quot;&gt;Sending employees from a REST endpoint&lt;/h2&gt;
&lt;pre id=&quot;code_1648442471726&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.acme.kafka.resource;

import org.acme.kafka.EmployeeProducer;
import org.acme.domain.Employee;

import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path(&quot;/&quot;)
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class EmployeeResource {

    @Inject//-&amp;gt; @Autowired in Spring
    EmployeeProducer producer;

    @POST
    public Response send(Employee employee) {
        producer.sendEmployeeToKafka(employee);
        // Return an 202 - Accepted response.
        return Response.accepted().build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Run the application&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;635&quot; data-origin-height=&quot;27&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N7eE1/btrxEEKQNIl/bxvafkpn16T9fC6nEmvJik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N7eE1/btrxEEKQNIl/bxvafkpn16T9fC6nEmvJik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N7eE1/btrxEEKQNIl/bxvafkpn16T9fC6nEmvJik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN7eE1%2FbtrxEEKQNIl%2Fbxvafkpn16T9fC6nEmvJik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;635&quot; height=&quot;27&quot; data-origin-width=&quot;635&quot; data-origin-height=&quot;27&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Send Post Request&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZaVvM/btrxyiu2WkR/fHHIIaRYlze88DI8ZlK290/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZaVvM/btrxyiu2WkR/fHHIIaRYlze88DI8ZlK290/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZaVvM/btrxyiu2WkR/fHHIIaRYlze88DI8ZlK290/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZaVvM%2Fbtrxyiu2WkR%2FfHHIIaRYlze88DI8ZlK290%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;842&quot; height=&quot;422&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;Check the producer's logs when receiving messages&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;101&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rmem3/btrxyj8zWAl/edrTwTzasp5MG51hGSrBQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rmem3/btrxyj8zWAl/edrTwTzasp5MG51hGSrBQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rmem3/btrxyj8zWAl/edrTwTzasp5MG51hGSrBQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frmem3%2Fbtrxyj8zWAl%2FedrTwTzasp5MG51hGSrBQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;101&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;101&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>~2022/Quarkus</category>
      <category>Quarkus</category>
      <category>Quarkus Kafka</category>
      <category>Reactive Messaging</category>
      <author>maruIsCat</author>
      <guid isPermaLink="true">https://maru-itdeveloper.tistory.com/71</guid>
      <comments>https://maru-itdeveloper.tistory.com/71#entry71comment</comments>
      <pubDate>Mon, 28 Mar 2022 14:10:52 +0900</pubDate>
    </item>
    <item>
      <title>[Kafka] 기본 개념</title>
      <link>https://maru-itdeveloper.tistory.com/70</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Kafka의 종류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache Kafka&lt;br /&gt;&amp;nbsp; . 오픈 소스, 자유롭게 수정과 배포 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Confluent Kafka&lt;br /&gt;&amp;nbsp; . Community License: 소프트웨어 수정, 재배포 가능하지만 Saas 형태로 서비스 제공하는 것은 금지됨&lt;br /&gt;&amp;nbsp; . Enterprise License: 연간 구독형&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;데이터 파이프라인(Data Pipeline)이란?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간에 사람의 개입 없이&lt;br /&gt;데이터를 오염, 중복, 유실과 같은 결함 없이&lt;br /&gt;수집, 저장, ETL(Extract, Transform, Load)이 가능하도록&lt;br /&gt;일련의 흐름을 만들어 주는 과정&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Event는 비즈니스에서 일어나는 모든 일(데이터)를 의미&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 웹사이트에서 무언가를 클릭하는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 청구서 발행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 송금&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 배송 물건의 위치 정보&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 택시의 GPS 좌표&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 센서의 온도/압력 데이터&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Event Stream은 연속적인 많은 이벤트들의 흐름&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. BigData의 특징을 가짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 비즈니스의 모든 영역에서 광범위하게 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 대용량의 데이터(Big Data 발생)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Apache Kafka의 3가지 주요 특징&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 이벤트 스트림을 안전하게 전송: Publish &amp;amp; Subscribe&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 이벤트 스트림을 디스크에 저장: Write to Disk&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 이벤트 스트림을 분석 및 처리: Processing &amp;amp; Analysis&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Apache Kafka의 사용 사례: Event(메시지/데이터)가 사용되는 모든 곳&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. Messaging Syetem&lt;br /&gt;. IOT 디바이스로부터 데이터 수집&lt;br /&gt;. 애플리케이션에서 발생하는 로그 수집&lt;br /&gt;. Realtime Event Stream Processing (Fraud Detection, 이상 감지 등)&lt;br /&gt;. DB 동기화(MSA 기반의 분리된 DB간 동기화)&lt;br /&gt;. 실시간 ETL(Extract-Transform-Loda)&lt;br /&gt;. Spark, Flink, Strom, Hadoop과 같은 빅데이터 기술과 같이 사용&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1127&quot; data-origin-height=&quot;283&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciCQo2/btrwb53Fbme/vJhs2GxRfH9fZsV3XqUyOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciCQo2/btrwb53Fbme/vJhs2GxRfH9fZsV3XqUyOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciCQo2/btrwb53Fbme/vJhs2GxRfH9fZsV3XqUyOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciCQo2%2Fbtrwb53Fbme%2FvJhs2GxRfH9fZsV3XqUyOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1127&quot; height=&quot;283&quot; data-origin-width=&quot;1127&quot; data-origin-height=&quot;283&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. Apache Kafka는 흐르는 데이터를 처리하기 위한 플랫폼(Event-Streaming Platform)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;497&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpzqqZ/btrv8FrATNU/NCJdfjOkqNKwR4TVZRkn5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpzqqZ/btrv8FrATNU/NCJdfjOkqNKwR4TVZRkn5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpzqqZ/btrv8FrATNU/NCJdfjOkqNKwR4TVZRkn5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpzqqZ%2Fbtrv8FrATNU%2FNCJdfjOkqNKwR4TVZRkn5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;865&quot; height=&quot;497&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;497&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>~2022/Apache Kafka</category>
      <category>Apache kafka</category>
      <author>maruIsCat</author>
      <guid isPermaLink="true">https://maru-itdeveloper.tistory.com/70</guid>
      <comments>https://maru-itdeveloper.tistory.com/70#entry70comment</comments>
      <pubDate>Wed, 16 Mar 2022 20:41:29 +0900</pubDate>
    </item>
  </channel>
</rss>