ปรับปรุงการผสานรวมซอฟต์แวร์: บทช่วยสอน Apache Camel

เผยแพร่แล้ว: 2022-03-11

ซอฟต์แวร์แทบไม่มีอยู่ในสุญญากาศของข้อมูล อย่างน้อย นั่นคือสมมติฐานที่เราวิศวกรซอฟต์แวร์สามารถสร้างขึ้นสำหรับแอปพลิเคชันส่วนใหญ่ที่เราพัฒนา

ในทุกขนาด ทุกชิ้นส่วนของซอฟต์แวร์—ไม่ทางใดก็ทางหนึ่ง—สื่อสารกับซอฟต์แวร์อื่นด้วยเหตุผลหลายประการ: เพื่อรับข้อมูลอ้างอิงจากที่ใดที่หนึ่ง เพื่อส่งสัญญาณตรวจสอบ ติดต่อกับบริการอื่น ๆ ในขณะที่เป็นส่วนหนึ่งของการกระจาย ระบบ และอื่นๆ

ในบทช่วยสอนนี้ คุณจะได้เรียนรู้ว่าความท้าทายที่ใหญ่ที่สุดในการรวมซอฟต์แวร์ขนาดใหญ่คืออะไร และวิธีที่ Apache Camel แก้ปัญหาเหล่านี้ได้อย่างง่ายดาย

ปัญหา: การออกแบบสถาปัตยกรรมเพื่อการบูรณาการระบบ

คุณอาจทำสิ่งต่อไปนี้อย่างน้อยหนึ่งครั้งในชีวิตวิศวกรรมซอฟต์แวร์ของคุณ:

  • ระบุส่วนของตรรกะทางธุรกิจของคุณที่ควรเริ่มต้นการส่งข้อมูล
  • ในชั้นแอปพลิเคชันเดียวกัน ให้เขียนการแปลงข้อมูลตามสิ่งที่ผู้รับคาดหวัง
  • ล้อมข้อมูลในโครงสร้างที่เหมาะสำหรับการถ่ายโอนและกำหนดเส้นทางผ่านเครือข่าย
  • เปิดการเชื่อมต่อกับแอปพลิเคชันเป้าหมายโดยใช้ไดรเวอร์ที่เหมาะสมหรือ SDK ของไคลเอ็นต์
  • ส่งข้อมูลและจัดการการตอบสนอง

เหตุใดจึงเป็นแนวปฏิบัติที่ไม่ดี

แม้ว่าคุณจะมีการเชื่อมต่อประเภทนี้เพียงไม่กี่รายการ แต่ก็ยังสามารถจัดการได้ ด้วยจำนวนความสัมพันธ์ที่เพิ่มขึ้นระหว่างระบบ ตรรกะทางธุรกิจของแอปพลิเคชันจึงปะปนกับตรรกะการรวม ซึ่งเกี่ยวกับการปรับข้อมูล การชดเชยความแตกต่างทางเทคโนโลยีระหว่างสองระบบ และการถ่ายโอนข้อมูลไปยังระบบภายนอกด้วย SOAP, REST หรือคำขอที่แปลกใหม่กว่า .

หากคุณกำลังรวมแอปพลิเคชันหลายตัวเข้าด้วยกัน คงจะเป็นเรื่องยากอย่างยิ่งที่จะย้อนดูภาพการพึ่งพาทั้งหมดในโค้ดดังกล่าว: ข้อมูลอยู่ที่ไหนและใช้บริการใด คุณจะมีสถานที่หลายแห่งที่ทำซ้ำตรรกะการรวมเพื่อบูต

ด้วยวิธีการดังกล่าว แม้ว่างานจะสำเร็จในทางเทคนิค แต่เราก็จบลงด้วยปัญหาใหญ่เกี่ยวกับความสามารถในการบำรุงรักษาและการปรับขนาดของการผสานรวม การจัดระเบียบการไหลของข้อมูลใหม่อย่างรวดเร็วในระบบนี้แทบจะเป็นไปไม่ได้ ยังไม่รวมถึงปัญหาที่ลึกซึ้งกว่านั้น เช่น การขาดการตรวจสอบ วงจรแตก การกู้คืนข้อมูลที่ลำบาก เป็นต้น

ทั้งหมดนี้มีความสำคัญอย่างยิ่งเมื่อรวมซอฟต์แวร์เข้ากับขอบเขตขององค์กรขนาดใหญ่ ในการจัดการกับการผนวกรวมองค์กรหมายถึงการทำงานกับชุดแอปพลิเคชัน ซึ่งทำงานบนแพลตฟอร์มที่หลากหลายและมีอยู่ในสถานที่ต่างๆ การแลกเปลี่ยนข้อมูลในแนวซอฟต์แวร์ดังกล่าวมีความต้องการค่อนข้างมาก ต้องเป็นไปตามมาตรฐานความปลอดภัยระดับสูงของอุตสาหกรรมและให้วิธีที่เชื่อถือได้ในการถ่ายโอนข้อมูล ในสภาพแวดล้อมขององค์กร การรวมระบบจำเป็นต้องมีการออกแบบสถาปัตยกรรมที่แยกออกมาอย่างละเอียดถี่ถ้วน

บทความนี้จะแนะนำคุณเกี่ยวกับปัญหาเฉพาะที่ต้องเผชิญกับการรวมซอฟต์แวร์ ตลอดจนนำเสนอโซลูชันที่ขับเคลื่อนด้วยประสบการณ์สำหรับงานการรวม เราจะทำความคุ้นเคยกับ Apache Camel ซึ่งเป็นเฟรมเวิร์กที่มีประโยชน์ซึ่งสามารถบรรเทาปัญหาที่แย่ที่สุดของนักพัฒนาการรวมระบบได้ เราจะทำตามด้วยตัวอย่างว่า Camel สามารถช่วยสร้างการสื่อสารในกลุ่มไมโครเซอร์วิสที่ขับเคลื่อนโดย Kubernetes ได้อย่างไร

ความยากลำบากในการบูรณาการ

แนวทางที่ใช้กันอย่างแพร่หลายในการแก้ปัญหาคือการแยกเลเยอร์การผสานรวมในแอปพลิเคชันของคุณ มันสามารถมีอยู่ภายในแอพพลิเคชั่นเดียวกันหรือเป็นซอฟต์แวร์เฉพาะที่ทำงานโดยอิสระ ในกรณีหลังที่เรียกว่ามิดเดิลแวร์

คุณมักประสบปัญหาใดในการพัฒนาและสนับสนุนมิดเดิลแวร์ โดยทั่วไป คุณมีรายการหลักดังต่อไปนี้:

  • ช่องข้อมูลทั้งหมดไม่น่าเชื่อถือในระดับหนึ่ง ปัญหาที่เกิดจากความไม่น่าเชื่อถือนี้อาจไม่เกิดขึ้นในขณะที่ความเข้มของข้อมูลอยู่ในระดับต่ำถึงปานกลาง ระดับการจัดเก็บข้อมูลแต่ละระดับตั้งแต่หน่วยความจำแอปพลิเคชันไปจนถึงแคชและอุปกรณ์ที่อยู่ด้านล่างอาจเกิดความล้มเหลวได้ ข้อผิดพลาดบางอย่างเกิดขึ้นได้ยากกับข้อมูลปริมาณมากเท่านั้น แม้แต่ผลิตภัณฑ์ของผู้จำหน่ายที่พร้อมสำหรับการผลิตที่บรรลุนิติภาวะแล้วก็ยังมีปัญหาในการติดตามจุดบกพร่องที่ยังไม่ได้รับการแก้ไขที่เกี่ยวข้องกับการสูญหายของข้อมูล ระบบมิดเดิลแวร์ควรจะสามารถแจ้งให้คุณทราบถึงการสูญเสียข้อมูลเหล่านี้และให้การส่งข้อความซ้ำได้ทันท่วงที
  • แอปพลิเคชันใช้โปรโตคอลและรูปแบบข้อมูลที่แตกต่างกัน ซึ่งหมายความว่าระบบการรวมเป็นม่านสำหรับการแปลงข้อมูลและอะแดปเตอร์ไปยังผู้เข้าร่วมคนอื่นๆ และใช้เทคโนโลยีที่หลากหลาย สิ่งเหล่านี้รวมถึงการเรียก REST API แบบธรรมดา แต่ยังสามารถเข้าถึงนายหน้าคิว การส่งคำสั่ง CSV ผ่าน FTP หรือการดึงข้อมูลแบทช์ไปยังตารางฐานข้อมูล นี่เป็นรายการยาวและจะไม่มีวันสั้นไปกว่านี้
  • การเปลี่ยนแปลงรูปแบบข้อมูลและกฎการกำหนดเส้นทางเป็นสิ่งที่หลีกเลี่ยงไม่ได้ แต่ละขั้นตอนในกระบวนการพัฒนาแอปพลิเคชัน ซึ่งเปลี่ยนโครงสร้างข้อมูล มักจะนำไปสู่การเปลี่ยนแปลงในรูปแบบข้อมูลการรวมและการแปลง บางครั้ง การเปลี่ยนแปลงโครงสร้างพื้นฐานด้วยโฟลว์ข้อมูลองค์กรที่จัดโครงสร้างใหม่ก็มีความจำเป็น ตัวอย่างเช่น การเปลี่ยนแปลงเหล่านี้อาจเกิดขึ้นเมื่อมีการแนะนำจุดเดียวของการตรวจสอบข้อมูลอ้างอิงที่ต้องประมวลผลรายการข้อมูลหลักทั้งหมดทั่วทั้งบริษัท ด้วยระบบ N เราอาจมีการเชื่อมต่อระหว่างกันสูงสุดเกือบ N^2 ดังนั้นจำนวนสถานที่ที่ต้องใช้การเปลี่ยนแปลงจึงเพิ่มขึ้นอย่างรวดเร็ว มันจะเป็นเหมือนหิมะถล่ม เพื่อรักษาความสามารถในการบำรุงรักษา เลเยอร์มิดเดิลแวร์จะต้องให้ภาพที่ชัดเจนของการพึ่งพาด้วยการกำหนดเส้นทางที่หลากหลายและการแปลงข้อมูล

แนวคิดเหล่านี้ควรคำนึงถึงเมื่อออกแบบการผสานรวมและเลือกโซลูชันมิดเดิลแวร์ที่เหมาะสมที่สุด วิธีหนึ่งที่เป็นไปได้ในการจัดการกับสิ่งนี้คือการใช้ประโยชน์จาก Enterprise Service Bus (ESB) แต่ ESB ที่จัดหาโดยผู้ค้ารายใหญ่มักจะหนักเกินไปและมักจะมีปัญหามากกว่าที่ควร: แทบจะเป็นไปไม่ได้เลยที่จะเริ่มต้นอย่างรวดเร็วกับ ESB มีช่วงการเรียนรู้ที่ค่อนข้างสูงชัน และความยืดหยุ่นลดลงในรายการที่ยาว ของคุณสมบัติและเครื่องมือในตัว ในความเห็นของฉัน โซลูชันการรวมโอเพนซอร์ซน้ำหนักเบานั้นเหนือกว่ามาก—พวกเขายืดหยุ่นกว่า ปรับใช้กับคลาวด์ได้ง่าย และปรับขนาดได้ง่าย

การรวมซอฟต์แวร์ไม่ใช่เรื่องง่าย ทุกวันนี้ ขณะที่เราสร้างสถาปัตยกรรมไมโครเซอร์วิสและจัดการกับบริการขนาดเล็กจำนวนมาก เราก็มีความคาดหวังสูงว่าควรสื่อสารอย่างมีประสิทธิภาพเพียงใด

รูปแบบการรวมองค์กร

ตามที่คาดหวัง เช่นเดียวกับการพัฒนาซอฟต์แวร์โดยทั่วไป การพัฒนาการกำหนดเส้นทางและการแปลงข้อมูลเกี่ยวข้องกับการดำเนินการซ้ำๆ ประสบการณ์ในด้านนี้ได้รับการสรุปและจัดระบบโดยผู้เชี่ยวชาญที่จัดการปัญหาการบูรณาการมาระยะหนึ่งแล้ว ผลลัพธ์ที่ได้คือชุดของเทมเพลตที่แยกออกมาซึ่งเรียกว่ารูปแบบการรวมองค์กรที่ใช้สำหรับการออกแบบโฟลว์ข้อมูล วิธีการรวมเหล่านี้อธิบายไว้ในหนังสือชื่อเดียวกันโดย Gregor Hophe และ Bobby Wolfe ซึ่งคล้ายกับหนังสือ Gang of Four ที่สำคัญ แต่ในด้านซอฟต์แวร์การติดกาว

เพื่อยกตัวอย่าง รูปแบบนอร์มัลไลเซอร์จะแนะนำส่วนประกอบที่แม็พข้อความที่มีความหมายเท่ากันซึ่งมีรูปแบบข้อมูลที่แตกต่างกันกับโมเดลบัญญัติเดียว หรือตัวรวบรวมคือ EIP ที่รวมลำดับของข้อความเป็นหนึ่งเดียว

เนื่องจากสิ่งเหล่านี้เป็นนามธรรมเทคโนโลยีที่ไม่เชื่อเรื่องพระเจ้าซึ่งใช้สำหรับการแก้ปัญหาทางสถาปัตยกรรม EIP จึงช่วยในการเขียนการออกแบบสถาปัตยกรรม ซึ่งไม่ได้เจาะลึกถึงระดับโค้ด แต่จะอธิบายการไหลของข้อมูลในรายละเอียดที่เพียงพอ สัญกรณ์ดังกล่าวสำหรับการอธิบายเส้นทางการรวม ไม่เพียงแต่ทำให้การออกแบบกระชับ แต่ยังกำหนดระบบการตั้งชื่อทั่วไปและภาษาทั่วไป ซึ่งมีความสำคัญอย่างมากในบริบทของการแก้ปัญหาการรวมเข้ากับสมาชิกในทีมจากพื้นที่ธุรกิจต่างๆ

ขอแนะนำ Apache Camel

เมื่อหลายปีก่อน ฉันกำลังสร้างการรวมองค์กรในเครือข่ายร้านค้าปลีกขนาดใหญ่ที่มีร้านค้าอยู่ในสถานที่ที่มีการกระจายอย่างกว้างขวาง ฉันเริ่มต้นด้วยโซลูชัน ESB ที่เป็นกรรมสิทธิ์ ซึ่งกลายเป็นว่ายุ่งยากเกินกว่าจะรักษาไว้ จากนั้น ทีมของเราพบ Apache Camel และหลังจากทำงาน "การพิสูจน์แนวคิด" เราก็เขียนโฟลว์ข้อมูลทั้งหมดของเราใหม่ในเส้นทาง Camel อย่างรวดเร็ว

Apache Camel สามารถอธิบายได้ว่าเป็น "เราเตอร์สื่อกลาง" ซึ่งเป็นเฟรมเวิร์กมิดเดิลแวร์เชิงข้อความที่ใช้รายการ EIP ซึ่งฉันคุ้นเคย มันใช้ประโยชน์จากรูปแบบเหล่านี้ รองรับโปรโตคอลการขนส่งทั่วไปทั้งหมด และมีชุดอะแดปเตอร์ที่มีประโยชน์มากมายรวมอยู่ด้วย Camel ช่วยให้สามารถจัดการรูทีนการผสานรวมจำนวนหนึ่งโดยไม่จำเป็นต้องเขียนโค้ดของคุณเอง

นอกเหนือจากนี้ ฉันจะเลือกคุณลักษณะ Apache Camel ต่อไปนี้:

  • เส้นทางการรวมถูกเขียนเป็นไปป์ไลน์ที่ทำจากบล็อก มันสร้างภาพที่โปร่งใสทั้งหมดเพื่อช่วยติดตามกระแสข้อมูล
  • Camel มีอะแดปเตอร์สำหรับ API ยอดนิยมมากมาย ตัวอย่างเช่น รับข้อมูลจาก Apache Kafka, ตรวจสอบอินสแตนซ์ AWS EC2, ผสานรวมกับ Salesforce—งานทั้งหมดเหล่านี้สามารถแก้ไขได้โดยใช้ส่วนประกอบที่พร้อมใช้งานทันที

เส้นทาง Apache Camel สามารถเขียนด้วย Java หรือ Scala DSL (การกำหนดค่า XML ก็มีให้เช่นกัน แต่ใช้รายละเอียดมากเกินไปและมีความสามารถในการดีบักที่แย่กว่านั้น) ไม่ได้กำหนดข้อจำกัดในสแต็กเทคโนโลยีของบริการสื่อสาร แต่ถ้าคุณเขียนใน Java หรือ Scala คุณสามารถฝัง Camel ในแอปพลิเคชันแทน ของการทำงานแบบสแตนด์อโลน

สัญกรณ์การกำหนดเส้นทางที่ใช้โดย Camel สามารถอธิบายได้ด้วย pseudocode ง่าย ๆ ต่อไปนี้:

 from(Source) .transform(Transformer) .to(Destination)

Source Transformer และ Destination คือปลายทางที่อ้างถึงองค์ประกอบการใช้งานโดย URI

อะไรทำให้ Camel สามารถแก้ปัญหาการรวมระบบที่ฉันอธิบายไว้ก่อนหน้านี้ได้ มาดูกันเลย ประการแรก ลอจิกการกำหนดเส้นทางและการแปลงใช้งานได้เฉพาะในการกำหนดค่า Apache Camel โดยเฉพาะ ประการที่สอง ผ่าน DSL ที่กระชับและเป็นธรรมชาติร่วมกับการใช้ EIP รูปภาพของการพึ่งพาระหว่างระบบจะปรากฏขึ้น มันสร้างจากสิ่งที่เป็นนามธรรมที่เข้าใจได้ และตรรกะของการกำหนดเส้นทางก็ปรับเปลี่ยนได้ง่าย และสุดท้าย เราไม่ต้องเขียนโค้ดการแปลงจำนวนมาก เนื่องจากอแด็ปเตอร์ที่เหมาะสมมักจะรวมอยู่ด้วยแล้ว

บูรณาการ

ฉันควรเพิ่ม Apache Camel เป็นเฟรมเวิร์กสำหรับผู้ใหญ่และได้รับการอัปเดตเป็นประจำ มีชุมชนที่ดีและฐานความรู้ที่สะสมจำนวนมาก

มันมีข้อเสียในตัวเอง ไม่ควรใช้อูฐเป็นชุดรวมที่ซับซ้อน เป็นกล่องเครื่องมือที่ไม่มีคุณสมบัติระดับสูง เช่น เครื่องมือการจัดการกระบวนการทางธุรกิจหรือตัวตรวจสอบกิจกรรม แต่สามารถใช้เพื่อสร้างซอฟต์แวร์ดังกล่าวได้

ระบบทางเลือกอาจเป็นเช่น Spring Integration หรือ Mule ESB สำหรับ Spring Integration แม้ว่าจะได้รับการพิจารณาว่ามีน้ำหนักเบา แต่จากประสบการณ์ของผม การรวมเข้าด้วยกันและการเขียนไฟล์การกำหนดค่า XML จำนวนมากอาจกลายเป็นเรื่องที่ซับซ้อนอย่างไม่คาดคิดและแทบจะไม่ง่ายเลย Mule ESB เป็นชุดเครื่องมือที่แข็งแกร่งและใช้งานได้ดีมาก แต่ตามชื่อของมัน มันเป็นบัสบริการสำหรับองค์กร ดังนั้นจึงจัดอยู่ในหมวดหมู่น้ำหนักที่แตกต่างกัน สามารถเปรียบเทียบ Mule กับ Fuse ESB ซึ่งเป็นผลิตภัณฑ์ที่คล้ายคลึงกันซึ่งใช้ Apache Camel พร้อมชุดคุณลักษณะมากมาย สำหรับฉัน การใช้ Apache Camel สำหรับบริการติดกาวนั้นไม่ใช่เรื่องง่ายในปัจจุบัน ใช้งานง่ายและให้คำอธิบายที่ชัดเจนเกี่ยวกับสิ่งที่จะไป ขณะเดียวกันก็ใช้งานได้เพียงพอสำหรับการสร้างการผสานรวมที่ซับซ้อน

การเขียนเส้นทางตัวอย่าง

มาเริ่มเขียนโค้ดกันเลย เราจะเริ่มต้นจากโฟลว์ข้อมูลแบบซิงโครนัสซึ่งกำหนดเส้นทางข้อความจากแหล่งเดียวไปยังรายชื่อผู้รับ กฎการกำหนดเส้นทางจะถูกเขียนใน Java DSL

เราจะใช้ Maven เพื่อสร้างโครงการ ขั้นแรกเพิ่มการพึ่งพาต่อไปนี้ใน pom.xml :

 <dependencies> ... <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-core</artifactId> <version>2.20.0</version> </dependency> </dependencies>

อีกทางหนึ่ง แอปพลิเคชันสามารถสร้างขึ้นบน camel-archetype-java

คำจำกัดความเส้นทางอูฐถูกประกาศในวิธี RouteBuilder.configure

 public void configure() { errorHandler(defaultErrorHandler().maximumRedeliveries(0)); from("file:orders?noop=true").routeId("main") .log("Incoming File: ${file:onlyname}") .unmarshal().json(JsonLibrary.Jackson, Order.class) // unmarshal JSON to Order class containing List<OrderItem> .split().simple("body.items") // split list to process one by one .to("log:inputOrderItem") .choice() .when().simple("${body.type} == 'Drink'") .to("direct:bar") .when().simple("${body.type} == 'Dessert'") .to("direct:dessertStation") .when().simple("${body.type} == 'Hot Meal'") .to("direct:hotMealStation") .when().simple("${body.type} == 'Cold Meal'") .to("direct:coldMealStation") .otherwise() .to("direct:others"); from("direct:bar").routeId("bar").log("Handling Drink"); from("direct:dessertStation").routeId("dessertStation").log("Handling Dessert"); from("direct:hotMealStation").routeId("hotMealStation").log("Handling Hot Meal"); from("direct:coldMealStation").routeId("coldMealStation").log("Handling Cold Meal"); from("direct:others").routeId("others").log("Handling Something Other"); }

ในคำจำกัดความนี้ เราสร้างเส้นทางที่ดึงบันทึกจากไฟล์ JSON แยกเป็นรายการ และกำหนดเส้นทางไปยังชุดตัวจัดการตามเนื้อหาข้อความ

ลองใช้ข้อมูลทดสอบที่เตรียมไว้ เราจะได้ผลลัพธ์:

 INFO | Total 6 routes, of which 6 are started INFO | Apache Camel 2.20.0 (CamelContext: camel-1) started in 10.716 seconds INFO | Incoming File: order1.json INFO | Exchange[ExchangePattern: InOnly, BodyType: com.antongoncharov.camel.example.model.OrderItem, Body: OrderItem{, type='Drink', name='Americano', qty='1'}] INFO | Handling Drink INFO | Exchange[ExchangePattern: InOnly, BodyType: com.antongoncharov.camel.example.model.OrderItem, Body: OrderItem{, type='Hot Meal', name='French Omelette', qty='1'}] INFO | Handling Hot Meal INFO | Exchange[ExchangePattern: InOnly, BodyType: com.antongoncharov.camel.example.model.OrderItem, Body: OrderItem{, type='Hot Meal', name='Lasagna', qty='1'}] INFO | Handling Hot Meal INFO | Exchange[ExchangePattern: InOnly, BodyType: com.antongoncharov.camel.example.model.OrderItem, Body: OrderItem{, type='Hot Meal', name='Rice Balls', qty='1'}] INFO | Handling Hot Meal INFO | Exchange[ExchangePattern: InOnly, BodyType: com.antongoncharov.camel.example.model.OrderItem, Body: OrderItem{, type='Dessert', name='Blueberry Pie', qty='1'}] INFO | Handling Dessert

ตามที่คาดไว้ Camel ส่งข้อความไปยังจุดหมายปลายทาง

ตัวเลือกการถ่ายโอนข้อมูล

ในตัวอย่างข้างต้น การโต้ตอบระหว่างส่วนประกอบเป็นแบบซิงโครนัสและดำเนินการผ่านหน่วยความจำของแอปพลิเคชัน อย่างไรก็ตาม มีหลายวิธีในการสื่อสารเมื่อเราจัดการกับแอปพลิเคชันที่แยกจากกันซึ่งไม่ใช้หน่วยความจำร่วมกัน:

  • การแลกเปลี่ยนไฟล์. แอปพลิเคชั่นหนึ่งสร้างไฟล์ข้อมูลที่ใช้ร่วมกันเพื่อให้อีกโปรแกรมหนึ่งใช้ เป็นที่ที่จิตวิญญาณของโรงเรียนเก่าอาศัยอยู่ วิธีการสื่อสารนี้มีผลกระทบมากมาย: ขาดธุรกรรมและความสม่ำเสมอ ประสิทธิภาพต่ำ และการประสานงานแยกระหว่างระบบ นักพัฒนาหลายคนลงเอยด้วยการเขียนโซลูชันการรวมแบบโฮมเมดเพื่อให้กระบวนการสามารถจัดการได้ไม่มากก็น้อย
  • ฐานข้อมูลทั่วไป ให้แอปพลิเคชันจัดเก็บข้อมูลที่ต้องการแชร์ในสคีมาทั่วไปของฐานข้อมูลเดียว การออกแบบสคีมาแบบรวมและการจัดการการเข้าถึงตารางพร้อมกันเป็นความท้าทายที่โดดเด่นที่สุดของแนวทางนี้ เช่นเดียวกับการแลกเปลี่ยนไฟล์ มันง่ายที่จะกลายเป็นคอขวดถาวร
  • การเรียก API ระยะไกล จัดเตรียมอินเทอร์เฟซเพื่อให้แอปพลิเคชันสามารถโต้ตอบกับแอปพลิเคชันอื่นที่ทำงานอยู่ได้ เช่น การเรียกใช้เมธอดทั่วไป แอปพลิเคชันใช้ฟังก์ชันร่วมกันผ่านการเรียกใช้ API แต่เชื่อมโยงเข้าด้วยกันอย่างแน่นหนาในกระบวนการ
  • การส่งข้อความ ให้แต่ละแอปพลิเคชันเชื่อมต่อกับระบบการส่งข้อความทั่วไป และแลกเปลี่ยนข้อมูลและเรียกใช้การทำงานแบบอะซิงโครนัสโดยใช้ข้อความ ทั้งผู้ส่งและผู้รับจะต้องทำงานพร้อมกันเพื่อส่งข้อความ

มีหลายวิธีในการโต้ตอบ แต่เราควรจำไว้เสมอว่าโดยทั่วไปแล้ว การโต้ตอบมีสองประเภท: แบบซิงโครนัสและอะซิงโครนัส อย่างแรกเหมือนกับการเรียกใช้ฟังก์ชันในโค้ดของคุณ—โฟลว์การดำเนินการจะรอจนกว่าจะดำเนินการและส่งกลับค่า ด้วยวิธีการแบบอะซิงโครนัส ข้อมูลเดียวกันจะถูกส่งผ่านคิวข้อความกลางหรือหัวข้อการสมัครรับข้อมูล การเรียกใช้ฟังก์ชันรีโมตแบบอะซิงโครนัสสามารถนำไปใช้เป็น EIP ที่ร้องขอการตอบกลับ

การส่งข้อความแบบอะซิงโครนัสไม่ใช่ยาครอบจักรวาล มันเกี่ยวข้องกับข้อจำกัดบางอย่าง คุณไม่ค่อยเห็น API การส่งข้อความบนเว็บ บริการ REST แบบซิงโครนัสเป็นวิธีที่ได้รับความนิยมมากขึ้น แต่มิดเดิลแวร์การส่งข้อความนั้นใช้กันอย่างแพร่หลายในอินทราเน็ตขององค์กรหรือโครงสร้างพื้นฐานแบ็คเอนด์ของระบบแบบกระจาย

การใช้คิวข้อความ

ลองทำตัวอย่างของเราแบบอะซิงโครนัส ระบบซอฟต์แวร์ที่จัดการคิวและหัวข้อการสมัครสมาชิกเรียกว่านายหน้าข้อความ มันเหมือนกับ RDBMS สำหรับตารางและคอลัมน์ คิวทำหน้าที่เป็นการรวมแบบจุดต่อจุด ในขณะที่หัวข้อมีไว้สำหรับการสื่อสารแบบเผยแพร่และสมัครสมาชิกกับผู้รับจำนวนมาก เราจะใช้ Apache ActiveMQ เป็นนายหน้าข้อความ JMS เพราะมันมั่นคงและฝังได้

เพิ่มการพึ่งพาต่อไปนี้ บางครั้งการเพิ่ม activemq-all ซึ่งมี ActiveMQ jars ทั้งหมดลงในโปรเจ็กต์ก็มากเกินไป แต่เราจะรักษาการพึ่งพาแอปพลิเคชันของเราให้ไม่ซับซ้อน

 <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-all</artifactId> <version>5.15.2</version> </dependency>

จากนั้นเริ่มต้นนายหน้าโดยทางโปรแกรม ใน Spring Boot เราได้รับการกำหนดค่าอัตโนมัติสำหรับสิ่งนี้โดยเสียบการพึ่งพา Maven ของ spring-boot-starter-activemq

เรียกใช้ตัวรับส่งข้อความใหม่โดยใช้คำสั่งต่อไปนี้ โดยระบุเฉพาะปลายทางของตัวเชื่อมต่อ:

 BrokerService broker = new BrokerService(); broker.addConnector("tcp://localhost:61616"); broker.start();

และเพิ่มข้อมูลโค้ดการกำหนดค่าต่อไปนี้ไปยังเนื้อหาวิธี configure :

 ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616"); this.getContext().addComponent("activemq", ActiveMQComponent.jmsComponent(connectionFactory));

ตอนนี้เราสามารถอัปเดตตัวอย่างก่อนหน้านี้โดยใช้คิวข้อความ คิวจะถูกสร้างขึ้นโดยอัตโนมัติในการส่งข้อความ

 public void configure() { errorHandler(defaultErrorHandler().maximumRedeliveries(0)); ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616"); this.getContext().addComponent("activemq", ActiveMQComponent.jmsComponent(connectionFactory)); from("file:orders?noop=true").routeId("main") .log("Incoming File: ${file:onlyname}") .unmarshal().json(JsonLibrary.Jackson, Order.class) // unmarshal JSON to Order class containing List<OrderItem> .split().simple("body.items") // split list to process one by one .to("log:inputOrderItem") .choice() .when().simple("${body.type} == 'Drink'") .to("activemq:queue:bar") .when().simple("${body.type} == 'Dessert'") .to("activemq:queue:dessertStation") .when().simple("${body.type} == 'Hot Meal'") .to("activemq:queue:hotMealStation") .when().simple("${body.type} == 'Cold Meal'") .to("activemq:queue:coldMealStation") .otherwise() .to("activemq:queue:others"); from("activemq:queue:bar").routeId("barAsync").log("Drinks"); from("activemq:queue:dessertStation").routeId("dessertAsync").log("Dessert"); from("activemq:queue:hotMealStation").routeId("hotMealAsync").log("Hot Meals"); from("activemq:queue:coldMealStation").routeId("coldMealAsync").log("Cold Meals"); from("activemq:queue:others").routeId("othersAsync").log("Others"); }

เอาล่ะ ตอนนี้การโต้ตอบกลายเป็นแบบอะซิงโครนัส ผู้มีโอกาสเป็นลูกค้าของข้อมูลนี้อาจเข้าถึงได้เมื่อพร้อม นี่คือตัวอย่างของการมีเพศสัมพันธ์แบบหลวม ๆ ซึ่งเราพยายามทำให้สำเร็จในสถาปัตยกรรมแบบรีแอกทีฟ ความไม่พร้อมใช้งานของบริการใดบริการหนึ่งจะไม่บล็อกบริการอื่นๆ นอกจากนี้ ผู้บริโภคอาจปรับขนาดและอ่านจากคิวแบบคู่ขนาน คิวเองอาจปรับขนาดและแบ่งพาร์ติชันได้ คิวถาวรสามารถจัดเก็บข้อมูลบนดิสก์เพื่อรอการประมวลผล แม้ว่าผู้เข้าร่วมทั้งหมดจะลงไปก็ตาม จึงทำให้ระบบนี้มีความทนทานต่อข้อผิดพลาดมากกว่า

ข้อเท็จจริงที่น่าประหลาดใจคือ CERN ใช้ Apache Camel และ ActiveMQ เพื่อตรวจสอบระบบของ Large Hadron Collider (LHC) นอกจากนี้ยังมีวิทยานิพนธ์ระดับปริญญาโทที่น่าสนใจซึ่งอธิบายถึงทางเลือกของโซลูชันมิดเดิลแวร์ที่เหมาะสมสำหรับงานนี้ ดังที่พวกเขากล่าวในประเด็นสำคัญว่า "ไม่มี JMS— ไม่มีฟิสิกส์ของอนุภาค!"

การตรวจสอบ

ในตัวอย่างก่อนหน้านี้ เราได้สร้างช่องข้อมูลระหว่างสองบริการ มันเป็นจุดที่อาจเกิดความล้มเหลวเพิ่มเติมในสถาปัตยกรรม ดังนั้นเราจึงต้องดูแลมัน มาดูกันว่าคุณลักษณะการตรวจสอบที่ Apache Camel มีให้คืออะไร โดยพื้นฐานแล้ว จะเปิดเผยข้อมูลทางสถิติเกี่ยวกับเส้นทางผ่าน MBeans ที่ JMX เข้าถึงได้ ActiveMQ เปิดเผยสถิติคิวในลักษณะเดียวกัน

มาเปิดเซิร์ฟเวอร์ JMX ในแอปพลิเคชันกัน เพื่อเปิดใช้งานให้ทำงานด้วยตัวเลือกบรรทัดคำสั่ง:

 -Dorg.apache.camel.jmx.createRmiConnector=true -Dorg.apache.camel.jmx.mbeanObjectDomainName=org.apache.camel -Dorg.apache.camel.jmx.rmiConnector.registryPort=1099 -Dorg.apache.camel.jmx.serviceUrlPath=camel

ตอนนี้ให้เรียกใช้แอปพลิเคชันเพื่อให้เส้นทางทำงานได้สำเร็จ เปิดเครื่องมือ jconsole มาตรฐานและเชื่อมต่อกับขั้นตอนการสมัคร เชื่อมต่อกับ service:jmx:rmi:///jndi/rmi://localhost:1099/camel ไปที่โดเมน org.apache.camel ในแผนผัง MBeans

ภาพหน้าจอ 1

เราจะเห็นได้ว่าทุกอย่างเกี่ยวกับการกำหนดเส้นทางอยู่ภายใต้การควบคุม เรามีจำนวนข้อความบนเครื่องบิน จำนวนข้อผิดพลาด และจำนวนข้อความในคิว ข้อมูลนี้สามารถส่งไปยังชุดเครื่องมือตรวจสอบที่มีฟังก์ชันมากมาย เช่น Graphana หรือ Kibana คุณสามารถทำได้โดยใช้ ELK stack ที่รู้จักกันดี

นอกจากนี้ยังมีเว็บคอนโซลแบบเสียบได้และขยายได้ซึ่งมี UI สำหรับจัดการ Camel, ActiveMQ และอื่นๆ อีกมากมายที่เรียกว่า hawt.io

ภาพหน้าจอ2

เส้นทางการทดสอบ

Apache Camel มีฟังก์ชันค่อนข้างกว้างสำหรับการเขียนเส้นทางการทดสอบด้วยส่วนประกอบจำลอง เป็นเครื่องมือที่มีประสิทธิภาพ แต่การเขียนเส้นทางแยกต่างหากสำหรับการทดสอบเป็นกระบวนการที่ต้องใช้เวลามาก การดำเนินการทดสอบบนเส้นทางการผลิตโดยไม่แก้ไขไปป์ไลน์จะมีประสิทธิภาพมากกว่า Camel มีคุณสมบัตินี้และสามารถใช้งานได้โดยใช้องค์ประกอบ AdviceWith

มาเปิดใช้งานตรรกะการทดสอบในตัวอย่างของเราและเรียกใช้การทดสอบตัวอย่าง

 <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-test</artifactId> <version>2.20.0</version> <scope>test</scope> </dependency>

คลาสทดสอบคือ:

 public class AsyncRouteTest extends CamelTestSupport { @Override protected RouteBuilder createRouteBuilder() throws Exception { return new AsyncRouteBuilder(); } @Before public void mockEndpoints() throws Exception { context.getRouteDefinition("main").adviceWith(context, new AdviceWithRouteBuilder() { @Override public void configure() throws Exception { // we substitute all actual queues with mock endpoints mockEndpointsAndSkip("activemq:queue:bar"); mockEndpointsAndSkip("activemq:queue:dessertStation"); mockEndpointsAndSkip("activemq:queue:hotMealStation"); mockEndpointsAndSkip("activemq:queue:coldMealStation"); mockEndpointsAndSkip("activemq:queue:others"); // and replace the route's source with test endpoint replaceFromWith("file://testInbox"); } }); } @Test public void testSyncInteraction() throws InterruptedException { String testJson = "{\"id\": 1, \"order\": [{\"id\": 1, \"name\": \"Americano\", \"type\": \"Drink\", \"qty\": \"1\"}, {\"id\": 2, \"name\": \"French Omelette\", \"type\": \"Hot Meal\", \"qty\": \"1\"}, {\"id\": 3, \"name\": \"Lasagna\", \"type\": \"Hot Meal\", \"qty\": \"1\"}, {\"id\": 4, \"name\": \"Rice Balls\", \"type\": \"Hot Meal\", \"qty\": \"1\"}, {\"id\": 5, \"name\": \"Blueberry Pie\", \"type\": \"Dessert\", \"qty\": \"1\"}]}"; // get mocked endpoint and set an expectation MockEndpoint mockEndpoint = getMockEndpoint("mock:activemq:queue:hotMealStation"); mockEndpoint.expectedMessageCount(3); // simulate putting file in the inbox folder template.sendBodyAndHeader("file://testInbox", testJson, Exchange.FILE_NAME, "test.json"); //checks that expectations were met assertMockEndpointsSatisfied(); } }

ตอนนี้รันการทดสอบสำหรับแอปพลิเคชันด้วย mvn test เราจะเห็นได้ว่าเส้นทางของเราดำเนินการสำเร็จด้วยคำแนะนำในการทดสอบ ไม่มีข้อความที่ส่งผ่านคิวจริงและผ่านการทดสอบแล้ว

 INFO | Route: main started and consuming from: file://testInbox <...> INFO | Incoming File: test.json <...> INFO | Asserting: mock://activemq:queue:hotMealStation is satisfied

การใช้ Apache Camel กับ Kubernetes Cluster

ปัญหาการรวมระบบอย่างหนึ่งในปัจจุบันคือแอปพลิเคชันไม่คงที่อีกต่อไป ในโครงสร้างพื้นฐานระบบคลาวด์ เราจัดการกับบริการเสมือนที่ทำงานบนหลายโหนดพร้อมกัน เปิดใช้งานสถาปัตยกรรมไมโครเซอร์วิสด้วยบริการขนาดเล็กและน้ำหนักเบาที่โต้ตอบกันเอง บริการเหล่านี้มีอายุการใช้งานที่ไม่น่าเชื่อถือ และเราต้องค้นหาบริการเหล่านี้แบบไดนามิก

การรวมบริการคลาวด์เข้าด้วยกันเป็นงานที่สามารถแก้ไขได้ด้วย Apache Camel เป็นเรื่องที่น่าสนใจเป็นพิเศษเนื่องจากรสชาติของ EIP และความจริงที่ว่า Camel มีอะแดปเตอร์มากมายและรองรับโปรโตคอลที่หลากหลาย เวอร์ชันล่าสุด 2.18 เพิ่มคอมโพเนนต์ ServiceCall ซึ่งแนะนำคุณลักษณะของการเรียก API และการแก้ไขที่อยู่ผ่านกลไกการค้นหาคลัสเตอร์ ปัจจุบันรองรับกงสุล Kubernetes Ribbon ฯลฯ ตัวอย่างของรหัสที่กำหนดค่า ServiceCall ด้วยกงสุล สามารถพบได้ง่าย เราจะใช้ Kubernetes ที่นี่เพราะเป็นโซลูชันการทำคลัสเตอร์ที่ฉันชอบ

สคีมาการรวมจะเป็นดังนี้:

สคีมา

บริการ Order และบริการ Inventory จะเป็นแอปพลิเคชัน Spring Boot เล็กน้อยที่ส่งคืนข้อมูลคงที่ เราไม่ได้ผูกติดอยู่กับกลุ่มเทคโนโลยีเฉพาะที่นี่ บริการเหล่านี้สร้างข้อมูลที่เราต้องการประมวลผล

ผู้ควบคุมบริการสั่งซื้อ:

 @RestController public class OrderController { private final OrderStorage orderStorage; @Autowired public OrderController(OrderStorage orderStorage) { this.orderStorage = orderStorage; } @RequestMapping("/info") public String info() { return "Order Service UU/orders") public List<Order> getAll() { return orderStorage.getAll(); } @RequestMapping("/orders/{id}") public Order getOne(@PathVariable Integer id) { return orderStorage.getOne(id); } }

มันสร้างข้อมูลในรูปแบบ:

 [{"id":1,"items":[2,3,4]},{"id":2,"items":[5,3]}]

ตัวควบคุมบริการ Inventory มีความคล้ายคลึงกับบริการ Order :

 @RestController public class InventoryController { private final InventoryStorage inventoryStorage; @Autowired public InventoryController(InventoryStorage inventoryStorage) { this.inventoryStorage = inventoryStorage; } @RequestMapping("/info") public String info() { return "Inventory Service UU/items") public List<InventoryItem> getAll() { return inventoryStorage.getAll(); } @RequestMapping("/items/{id}") public InventoryItem getOne(@PathVariable Integer id) { return inventoryStorage.getOne(id); } }

InventoryStorage เป็นพื้นที่เก็บข้อมูลทั่วไปที่เก็บข้อมูล ในตัวอย่างนี้ จะส่งคืนอ็อบเจ็กต์ที่กำหนดไว้ล่วงหน้าแบบสแตติก ซึ่งมีการจัดวางให้อยู่ในรูปแบบต่อไปนี้

 [{"id":1,"name":"Laptop","description":"Up to 12-hours battery life","price":499.9},{"id":2,"name":"Monitor","description":"27-inch, response time: 7ms","price":200.0},{"id":3,"name":"Headphones","description":"Soft leather ear-cups","price":29.9},{"id":4,"name":"Mouse","description":"Designed for comfort and portability","price":19.0},{"id":5,"name":"Keyboard","description":"Layout: US","price":10.5}]

มาเขียนเส้นทางเกตเวย์ที่เชื่อมต่อกัน แต่ไม่มี ServiceCall ในขั้นตอนนี้:

 rest("/orders") .get("/").description("Get all orders with details").outType(TestResponse.class) .route() .setHeader("Content-Type", constant("application/json")) .setHeader("Accept", constant("application/json")) .setHeader(Exchange.HTTP_METHOD, constant("GET")) .removeHeaders("CamelHttp*") .to("http4://localhost:8082/orders?bridgeEndpoint=true") .unmarshal(formatOrder) .enrich("direct:enrichFromInventory", new OrderAggregationStrategy()) .to("log:result") .endRest(); from("direct:enrichFromInventory") .transform().simple("${null}") .setHeader("Content-Type", constant("application/json")) .setHeader("Accept", constant("application/json")) .setHeader(Exchange.HTTP_METHOD, constant("GET")) .removeHeaders("CamelHttp*") .to("http4://localhost:8081/items?bridgeEndpoint=true") .unmarshal(formatInventory);

ลองนึกภาพว่าแต่ละบริการไม่ใช่อินสแตนซ์เฉพาะอีกต่อไป แต่เป็นคลาวด์ของอินสแตนซ์ที่ทำงานเป็นหนึ่งเดียว เราจะใช้ Minikube เพื่อลองใช้คลัสเตอร์ Kubernetes ในเครื่อง

กำหนดค่าเส้นทางเครือข่ายเพื่อดูโหนด Kubernetes ในเครื่อง (ตัวอย่างที่กำหนดสำหรับสภาพแวดล้อม Mac/Linux):

 # remove existing routes sudo route -n delete 10/24 > /dev/null 2>&1 # add routes sudo route -n add 10.0.0.0/24 $(minikube ip) # 172.17.0.0/16 ip range is used by docker in minikube sudo route -n add 172.17.0.0/16 $(minikube ip) ifconfig 'bridge100' | grep member | awk '{print $2}' # use interface name from the output of the previous command # needed for xhyve driver, which I'm using for testing sudo ifconfig bridge100 -hostfilter en5

รวมบริการในคอนเทนเนอร์ Docker ด้วยการกำหนดค่า Dockerfile ดังนี้:

 FROM openjdk:8-jdk-alpine VOLUME /tmp ADD target/order-srv-1.0-SNAPSHOT.jar app.jar ADD target/lib lib ENV JAVA_OPTS="" ENTRYPOINT exec java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar

สร้างและพุชอิมเมจบริการไปยังรีจิสตรี Docker ตอนนี้ให้รันโหนดในคลัสเตอร์ Kubernetes ในเครื่อง

การกำหนดค่าการปรับใช้ Kubernetes.yaml:

 apiVersion: extensions/v1beta1 kind: Deployment metadata: name: inventory spec: replicas: 3 selector: matchLabels: app: inventory template: metadata: labels: app: inventory spec: containers: - name: inventory image: inventory-srv:latest imagePullPolicy: Never ports: - containerPort: 8081

เปิดเผยการปรับใช้เหล่านี้เป็นบริการในคลัสเตอร์:

 kubectl expose deployment order-srv --type=NodePort kubectl expose deployment inventory-srv --type=NodePort

ตอนนี้ เราสามารถตรวจสอบว่ามีการส่งคำขอโดยโหนดที่สุ่มเลือกจากคลัสเตอร์หรือไม่ เรียกใช้ curl -X http://192.168.99.100:30517/info ตามลำดับหลายครั้งเพื่อเข้าถึง minikube NodePort สำหรับบริการที่เปิดเผย (โดยใช้โฮสต์และพอร์ตของคุณ) ในผลลัพธ์ เราเห็นว่าเราได้บรรลุความสมดุลของคำขอแล้ว

 Inventory Service UUID = 22f8ca6b-f56b-4984-927b-cbf9fcf81da5 Inventory Service UUID = b7a4d326-1e76-4051-a0a6-1016394fafda Inventory Service UUID = b7a4d326-1e76-4051-a0a6-1016394fafda Inventory Service UUID = 22f8ca6b-f56b-4984-927b-cbf9fcf81da5 Inventory Service UUID = 50323ddb-3ace-4424-820a-6b4e85775af4

เพิ่ม camel-kubernetes และ camel-netty4-http ให้กับ pom.xml ของโปรเจ็กต์ จากนั้นกำหนดค่าคอมโพเนนต์ ServiceCall เพื่อใช้การค้นพบโหนดหลัก Kubernetes ที่แชร์สำหรับการเรียกใช้บริการทั้งหมดระหว่างการกำหนดเส้นทาง:

 KubernetesConfiguration kubernetesConfiguration = new KubernetesConfiguration(); kubernetesConfiguration.setMasterUrl("https://192.168.64.2:8443"); kubernetesConfiguration.setClientCertFile("/Users/antongoncharov/.minikube/client.crt"); kubernetesConfiguration.setClientKeyFile("/Users/antongoncharov/.minikube/client.key"); kubernetesConfiguration.setNamespace("default”); ServiceCallConfigurationDefinition config = new ServiceCallConfigurationDefinition(); config.setServiceDiscovery(new KubernetesClientServiceDiscovery(kubernetesConfiguration)); context.setServiceCallConfiguration(config);

ServiceCall EIP ช่วยเสริม Spring Boot ได้เป็นอย่างดี ตัวเลือกส่วนใหญ่สามารถกำหนดค่าได้โดยตรงในไฟล์ application.properties

เสริมพลังเส้นทาง Camel ด้วยองค์ประกอบ ServiceCall:

 rest("/orders") .get("/").description("Get all orders with details").outType(TestResponse.class) .route() .hystrix() .setHeader("Content-Type", constant("application/json")) .setHeader("Accept", constant("application/json")) .setHeader(Exchange.HTTP_METHOD, constant("GET")) .removeHeaders("CamelHttp*") .serviceCall("customer-srv","http4:customer-deployment?bridgeEndpoint=true") .unmarshal(formatOrder) .enrich("direct:enrichFromInventory", new OrderAggregationStrategy()) .to("log:result") .endRest(); from("direct:enrichFromInventory") .transform().simple("${null}") .setHeader("Content-Type", constant("application/json")) .setHeader("Accept", constant("application/json")) .setHeader(Exchange.HTTP_METHOD, constant("GET")) .removeHeaders("CamelHttp*") .serviceCall("order-srv","http4:order-srv?bridgeEndpoint=true") .unmarshal(formatInventory);

นอกจากนี้เรายังเปิดใช้งาน Circuit Breaker ในเส้นทาง เป็นเบ็ดรวมที่ช่วยให้หยุดการเรียกระบบระยะไกลชั่วคราวในกรณีที่มีข้อผิดพลาดในการจัดส่งหรือผู้รับไม่พร้อมใช้งาน ซึ่งได้รับการออกแบบมาเพื่อหลีกเลี่ยงความล้มเหลวของระบบคาสเคด ส่วนประกอบ Hystrix ช่วยให้บรรลุเป้าหมายนี้โดยใช้รูปแบบ Circuit Breaker

เรียกใช้และส่งคำขอทดสอบ เราจะได้รับคำตอบจากทั้งสองบริการ

 [{"id":1,"items":[{"id":2,"name":"Monitor","description":"27-inch, response time: 7ms","price":200.0},{"id":3,"name":"Headphones","description":"Soft leather ear-cups","price":29.9},{"id":4,"name":"Mouse","description":"Designed for comfort and portability","price":19.0}]},{"id":2,"items":[{"id":5,"name":"Keyboard","description":"Layout: US","price":10.5},{"id":3,"name":"Headphones","description":"Soft leather ear-cups","price":29.9}]}]

ผลลัพธ์เป็นไปตามคาด

กรณีการใช้งานอื่นๆ

ฉันแสดงให้เห็นว่า Apache Camel สามารถรวมไมโครเซอร์วิสในคลัสเตอร์ได้อย่างไร กรอบนี้มีประโยชน์อะไรอีกบ้าง? โดยทั่วไป จะมีประโยชน์ในทุกที่ที่การกำหนดเส้นทางตามกฎอาจเป็นวิธีแก้ปัญหา For instance, Apache Camel can be a middleware for the Internet of Things with the Eclipse Kura adapter. It can handle monitoring by ferrying log signals from various components and services, like in the CERN system. It can also be an integration framework for enterprise SOA or be a pipeline for batch data processing, although it doesn't compete well with Apache Spark in this area.

บทสรุป

You can see that systems integration isn't an easy process. We're lucky because a lot of experience has been gathered. It's important to apply it correctly to build flexible and fault-tolerant solutions.

To ensure correct application, I recommend having a checklist of important integration aspects. Must-have items include:

  • Is there a separate integration layer?
  • Are there tests for integration?
  • Do we know the expected peak data intensity?
  • Do we know the expected data delivery time?
  • Does message correlation matter? What if a sequence breaks?
  • Should we do it in a synchronous or asynchronous way?
  • Where do formats and routing rules change more frequently?
  • Do we have ways to monitor the process?

In this article, we tried Apache Camel, a lightweight integration framework, which helps save time and effort when solving integration problems. As we showed, it can serve as a tool, supporting the relevant microservice architecture by taking full responsibility for data exchange between microservices.

If you're interested in learning more about Apache Camel, I highly recommend the book “Camel in Action” by the framework's creator, Claus Ibsen. Official documentation is available at camel.apache.org.