เปลี่ยนมาใช้ Apache Kafka กันเถอะ

NV4RE
True e-Logistics
4 min readJul 2, 2019

--

Apache Kafka คืออะไร ?

Apache Kafka คือ คนกลางในการกระจายข้อมูลระหว่างผู้ส่ง (producers) ไปหาผู้รับ (consumers) ที่ติดตามในหัวข้อนั้นๆ (topic)

Apache Kafka vs RabbitMQ ?

ผมเชื่อว่ามีหลายคนที่เข้ามาอ่านบทความนี้ คงจะกำลังตัดสินใจว่าจะใช้ message queue ตัวไหนดี หรือไม่ก็เปลี่ยนจาก RabbitMQ มาเป็น Apache Kafka

ซึ่งถ้าหากเรามองในด้าน performance, always availability, scalability หรือ reliability ก็ต้องบอกว่า Apache Kafka กินขาดแน่นอนอยู่แล้ว

เฮ้!!!! พวกเราเปลี่ยนมาใช้ Apache Kafka กันเร็ว, ณ ตอนนี้ต้องขอเบรคทุกคนไว้ก่อนครับ แน่นอนว่า RabbitMQ มันมีข้อดีของมันอยู่แล้ว และมีหลายๆงานที่เจ้า RabbitMQ สามารถใช้งานได้ดีกว่า และง่ายกว่าที่ต้องใช้ Apache Kafka ด้วย

RabbitMQ strong point

สิ่งแรกที่ RabbitMQ เจ๋งกว่า Apache Kafka คือ มันสามารถ ack/nack ราย message ได้ (หากเป็น Apache Kafka จะต้อง Commit offset) ซึ่งมันจะมีประโยชน์ตรงที่ เราสามารถ process message ไปโดยที่ไม่ต้องสนใจ order ขอ message (สมมุติว่าเรามี 3 messages เข้ามาพร้อมกัน แต่เรา process message 2,3 เสร็จแล้วเราสามารถ commit message ไปได้เลยโดยที่ไม่ต้องรอ message 1 เสร็จแล้วค่อย commit ทีเดียว) ซึ่งมันก็ไม่ใช่ practices ที่ดีนักสำหรับ message queue แต่เหล่า programmers ของเรานั้นชอบวิธีการนี้กันซะเหลือเกิน ทั้งที่มันทำให้เกิด side effects อย่างเช่น read-before-write และอื่นๆอีกมากมาย แล้วก็ต้องมาปวดหัวตอนแก้บัคกัน

อีกเรื่องที่เจ๋งมากของ RabbitMQ ที่ Apache Kafka ไม่มีคือ Routing key ใน RabbitMQ เราสามารถให้ queue ของเรา bind ไว้กับ routing key ใน exchange ได้ (ตามแต่ประเภทของ Exchange เรา) ซึ่งทำให้สามารถ map message เข้า queue ของเราโดยที่ฝ่ายส่งไม่ต้องส่งไปหลายๆ queue สามารถส่งเข้ามาที่ Exchange เดียวแล้ว routing key จะพา message ขอเรามาเสริฟถึงที่เอง แต่ช้าก่อน ไม่ใช้ว่า Apache Kafka จะทำะอะไรแบบนี้ไม่ได้นะ เราสามารถทำอะไรแบบนี้บน Apache Kafka โดยใช้ Kafka Stream เพื่อ filter message และส่งไปยัง topics อื่นๆที่เราต้องการก็ได้เหมือนกัน

อ่าวววว ถ้าบอกมาแบบนี้ก็แปลว่า Apache kafka ดีกว่าเห็นๆเลยสิ เราเปลี่ยนมาใช้ Apache kafka กันเร็ววววววว!!!

ก่อนจะหันมาใช้ Apache Kafka เป็น Black-bone กัน มันมีเรื่องนึ่งที่ RabbitMQ ทำได้ดีก็คือออออออ RPC การทำ RPC บน Apache Kafka เป็นอะไรที่ยุ่งยากมาก และไม่ใช่งานที่เหมาะกับมันเท่าไร

แต่หากคุณมีความจำเป็นต้องใช้ RPC คุณก็สามารถมีทั้ง RabbitMQ และ Apache Kafka ก็ได้หนิ

Kafka Architecture

Kafka Architecture

1). Kafka Producer API

ใช้สำหรับกระจาย messages ไปยังแต่ละ topic

2). Kafka Consumer API

ใช้สำหรับ subscribe กับ topic เพื่อคอยรับ messages

3). Kafka Streams API

เพื่อให้ application สามารถทำ stream processing โดยจะรับมากจาก topic เดียวหรือหลาย topic และเอาไปกระจายต่อไปยัง topic(s) อื่น (Consumer + Producer)

4). Kafka Connector API

ใช้สำหรับสร้าง producer (Connect source) หรือ consumer (connect sink) ที่เป็นงานพื้นฐานหรือใช้งานบ่อยๆ อย่างเช่น เอาข้อมูลจาก topic เข้า Elasticsearch (อารมณ์คล้ายๆ Docker)

Kafka Components

1). Producer

คนส่ง message เข้า topic

2). Consumer

คนที่ subscribes topic(s) เพื่อที่เอา message ไปประมวลผลต่อ

3). Topic

คือที่รวบรวม message ที่ส่งเข้ามา และเราสามารถตั้งชื่อให้ topic ได้ โดย message จะถูกเก็บไว้ในแต่ละ partition

เราสามารถเปรียบ topic เป็นเสมือนเรือบันทุกสินค้า และ partition เป็นเหมือนตู้คอนเทนเนอร์ และ message เป็นเสมือนสินค้าที่อยู่ในตู้คอนเทนเนอร์ (partition) จะเห็นว่าเวลาเราจะขนส่งสินค้า เราจะไม่เอาสินค้าไว้บนเรือเฉยๆ จะเอาสินค้าไว้ในตู้คอนเทนเนอร์ และในแต่ละ สินค้า (message) จะมีการ sort ตามที่อยู่ด้วย ว่าสินค้าชินนี้ควรจะอยู่ตู้ไหนเพื่อจะได้ง่ายเวลาขนส่ง Kafka ก็มี concept นี้เหมือนกันคือ หากเราตั้ง key ของ message เป็นบ้านเลขที่ของเรา เราจะการันตีได้ว่า ของที่ส่งมาบ้านเลขที่เราก็จะมาลง container (partition) เดิมเสมอ

4). Partition

ที่ๆ message จะถูกเก็บ สามารถมีได้เท่าที่ต้องการต่อ topic (คุณสามารถมี 1000 partition ใน 1 topic ได้)

โดย message ที่อยู่ก่อนในแต่ละ partition(offset x) จะเก่ากว่า message ที่ตามมา(offset x+1) เสมอ เช่น message ที่ offset 5 ของ partition 0 จะเก่ากว่า offset 6 ของ partition 0

แต่จะไม่สามารถการันตีข้าม partition ได้ เช่น เราไม่สามารถบอกได้ว่า message offset 5 ของ partition 0 เก่าหรือใหม่กว่า message offset 8 ของ partition 2

5). Broker

คือที่ๆข้อมูลจะถูกเก็บ (เปรียบเสมือน server เก็บข้อมูล) โดยมันจะเก็บ โดยมันจะแยก topic/partition เป็นส่วนย่อยๆเพื่อให้สามารถกระจายกันเก็บได้ (ในกรณีที่มีหลาย Broker)

Kafka cluster

เราสามารถทำ replication ในแต่ละ topic ได้โดยเราสามารถมีได้แค่ 1 leader ในแต่ละ partition และตัวอื่นๆจะกลายเป็น ISR(in-sync replica) หากเราตั้ง replication factor ของ topic1 และ topic2 เป็น 2 และ 3 ตามลำดับ

topic1: 2 replication factor, topic2: 3 replication factor

โดย replication factor จะช่วยให้ Kafka สามารถทำงานได้อยู่หาก broker บางตัวตายไป (N-1 หาก N คือ replication factor) เห็นว่า topic1 ยังจะมีข้อมูลอยู่ครบ หาก 1 broker ตายไป (ลองเอามือปิด broker 101–103 ทีละตัวแล้วจะเห็นว่ามี partition ครบ) แต่หากเป็น topic2 ที่มี replication factor เป็น 3 ตัว kafka จะสามารถให้บริการ topic2 ได้อย่างสมบรูณ์ ถึงแม้ broker ดับไปถึง2ตัว

6). Zookeeper

หน้าที่หลังๆคือคอยจัดการ broker(s) โดยจะเก็บข้อมูล metadata ต่างๆของ broker เอาไว้ ทำ health check ตัว broker และทำการจัดตั้งการเลือก leader broker

How Apache Kafka solve our problem

ทุกวันนี้หลายๆคนคงทำ clustering กันหมดแล้ว ซึ่งก็อาจจะเจอปัญหาแย่งกันเขียนข้อมูลหรือ อ่านข้อมูลที่ยังเขียนไม่เสร็จกันบ้าง

สมมุติว่าเป็น app ธนาคาร แล้วมี messages 3 อันเข้ามา ฝาก 500บาท, ถอน 400บาท และ ถอน 300บาท ตามลำดับ แล้ว app เรามี 2 pod ก็จะเกิดเหตุการณ์ ตัวแรกจะ consume message ฝาก 500 บาทไปและก็เอาไปประมวลผลนู้นนี้นั้น แล้วตอนนี้ pod ที่ 2 ของเราก็ consume ถอน 400 บาทไปซึ่งมันจะไม่สำเร็จเพราะ message ฝาก 500 บาทของเรายังประมวลผล อยู่ยังไม่ได้เอาไปเขียนใน DB เลย

ซึ่งผมจะมาแสดงให้ดูว่าเราจะแก้ปัญหานี้ใน Apache Kafka กันได้ยังไง

ถ้าใครได้อ่านจะเห็นแว๊บๆว่าง หาก message ที่มี key นั้นจะเข้ามายัง partition เดิมเสมอ (ยกเว้นจำนวน partition เปลี่ยน) งั้นเราก็ตั้ง key ของ message เป็น account กันเลย

ก่อนอื่นให้เราสร้าง topic1 ที่มี 3partition ขึ้นมา และ run code ด้านล่างนี้

topic1's messages (Lense)

เมื่อเราดู messages ข้างในจะเห็นว่า message ที่มี account เดียวกันจะเข้า partition เดียวกันเสมอ เราก็แค่ให้ในแต่ละ worker ทำงานเป็น synchronized และ consume กันคนละ partition (ใช้ consumer group) คราวนี้เราก็สามารถ ทำได้หลาย message พร้อมกันได้แล้ว

ในแต่ละ worker เราก็สามารถ group ด้วย account_id ได้อีกเพื่อจะได้ทำแต่ละ account พร้อมกันเลย

References

--

--