การทำธุรกรรมทั่ว REST microservices?


195

สมมติว่าเรามี Microservices ผู้ใช้ Wallet REST และเกตเวย์ API ที่จับคู่สิ่งต่าง ๆ เข้าด้วยกัน เมื่อ Bob ลงทะเบียนในเว็บไซต์ของเราเกตเวย์ API ของเราจำเป็นต้องสร้างผู้ใช้ผ่าน microservice ของผู้ใช้และกระเป๋าเงินผ่าน microservice ของ Wallet

ต่อไปนี้คือสถานการณ์บางอย่างที่อาจเกิดความผิดพลาด:

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

  • Bob ของผู้ใช้ถูกสร้างขึ้น แต่ก่อนที่จะสร้าง Wallet ของเราได้ API ของฮาร์ดไดรฟ์จะขัดข้อง ตอนนี้เรามีผู้ใช้ที่ไม่มีกระเป๋าเงิน (ข้อมูลไม่สอดคล้องกัน)

  • Bob ผู้ใช้ถูกสร้างขึ้นและในขณะที่เรากำลังสร้าง Wallet การเชื่อมต่อ HTTP จะลดลง การสร้างกระเป๋าเงินอาจสำเร็จหรือไม่ได้

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

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


3
ลิงค์ที่น่าสนใจ: news.ycombinator.com/item?id=7995130
Olivier Lalonde

3
หากผู้ใช้ไม่เข้าใจโดยไม่มีกระเป๋าเงินทำไมถึงต้องสร้าง microservice แยกต่างหาก อาจเป็นสิ่งที่ไม่ถูกต้องกับสถาปัตยกรรมในตอนแรกหรือไม่ ทำไมคุณต้องใช้เกตเวย์ API ทั่วไป btw มีเหตุผลเฉพาะสำหรับมันหรือไม่?
Vladislav Rastrusny

4
@VladislavRastrusny เป็นตัวอย่างสมมติ แต่คุณสามารถนึกถึงบริการ Wallet ที่ถูกจัดการโดย Stripe ได้
Olivier Lalonde

คุณสามารถใช้ตัวจัดการกระบวนการเพื่อติดตามการทำธุรกรรม (รูปแบบตัวจัดการกระบวนการ) หรือให้ microservice แต่ละรู้วิธีการทริกเกอร์การย้อนกลับ (รูปแบบตัวจัดการซะงะ) หรือทำสองเฟสกระทำ ( blog.aspiresys.com/software-product-engineering) / producteering / … )
andrew pate

@VladislavRastrusny "หากผู้ใช้ไม่เข้าใจโดยไม่ต้องใช้กระเป๋าเงินทำไมถึงต้องสร้างบริการไมโครสโคปแยกต่างหากสำหรับมัน" - ตัวอย่างเช่นนอกเหนือจากความจริงที่ว่าผู้ใช้ไม่สามารถอยู่ได้หากไม่มีกระเป๋าเงินพวกเขาไม่มีรหัสทั่วไป ดังนั้นทั้งสองทีมจะพัฒนาและปรับใช้ Microservices ของผู้ใช้และ Wallet โดยอิสระ นี่เป็นจุดเริ่มต้นของการทำ microservices ตั้งแต่แรกใช่มั้ย
Nik

คำตอบ:


148

อะไรที่ไม่สมเหตุสมผล:

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

อะไรจะทำให้คุณปวดหัว:

  • EJBs กับการทำธุรกรรมการกระจาย มันเป็นหนึ่งในสิ่งเหล่านั้นที่ทำงานได้ในทางทฤษฎี แต่ไม่ใช่ในทางปฏิบัติ ตอนนี้ฉันกำลังพยายามทำธุรกรรมแบบกระจายสำหรับ EJB ระยะไกลผ่านอินสแตนซ์ JBoss EAP 6.3 เราได้คุยกับฝ่ายสนับสนุน RedHat มาหลายสัปดาห์แล้ว แต่ก็ยังใช้งานไม่ได้
  • สองเฟสกระทำการแก้ปัญหาในทั่วไป ฉันคิดว่าโปรโตคอล 2PCเป็นอัลกอริธึมที่ยอดเยี่ยม (หลายปีที่ผ่านมาฉันใช้กับ C กับ RPC) มันต้องการกลไกการกู้คืนความล้มเหลวที่ครอบคลุมพร้อมการลองใหม่พื้นที่เก็บข้อมูลของรัฐและอื่น ๆ ความซับซ้อนทั้งหมดถูกซ่อนอยู่ภายในกรอบการทำธุรกรรม (เช่น JBoss Arjuna) อย่างไรก็ตาม 2PC ไม่ได้เป็นข้อพิสูจน์ที่ผิดพลาด มีบางสถานการณ์ที่ธุรกรรมไม่สามารถทำให้เสร็จสมบูรณ์ได้ จากนั้นคุณต้องระบุและแก้ไขฐานข้อมูลที่ไม่สอดคล้องกันด้วยตนเอง มันอาจเกิดขึ้นเพียงครั้งเดียวในการทำธุรกรรมล้านครั้งหากคุณโชคดี แต่มันอาจเกิดขึ้นเพียงครั้งเดียวในทุก ๆ 100 ธุรกรรมขึ้นอยู่กับแพลตฟอร์มและสถานการณ์ของคุณ
  • โศกนาฏกรรม (การทำธุรกรรมชดเชย) มีค่าใช้จ่ายในการดำเนินการสร้างการชดเชยและกลไกการประสานงานเพื่อเปิดใช้งานการชดเชยในตอนท้าย แต่การชดเชยก็ไม่ได้ล้มเหลวเช่นกัน คุณอาจยังไม่ลงรอยกัน (= ปวดหัวบ้าง)

อาจเป็นทางเลือกที่ดีที่สุด:

  • ความสอดคล้องในที่สุด ธุรกรรมที่มีลักษณะคล้ายกรดและการชดเชยธุรกรรมนั้นไม่สามารถพิสูจน์ได้และทั้งคู่อาจนำไปสู่ความไม่สอดคล้องกัน ความมั่นคงในที่สุดมักจะดีกว่า "ความไม่สอดคล้องกันเป็นครั้งคราว" มีโซลูชั่นการออกแบบที่แตกต่างกันเช่น:
    • คุณอาจสร้างโซลูชันที่แข็งแกร่งยิ่งขึ้นโดยใช้การสื่อสารแบบอะซิงโครนัส ในสถานการณ์ของคุณเมื่อ Bob ลงทะเบียนเกตเวย์ API สามารถส่งข้อความไปยังคิว NewUser และตอบกลับผู้ใช้ทันทีว่า "คุณจะได้รับอีเมลเพื่อยืนยันการสร้างบัญชี" บริการผู้บริโภคคิวสามารถประมวลผลข้อความทำการเปลี่ยนแปลงฐานข้อมูลในธุรกรรมเดียวและส่งอีเมลไปยัง Bob เพื่อแจ้งการสร้างบัญชี
    • MICROSERVICE ผู้ใช้สร้างระเบียนผู้ใช้และบันทึกกระเป๋าสตางค์ในฐานข้อมูลเดียวกัน ในกรณีนี้ที่เก็บ Wallet ใน Microservice ผู้ใช้เป็นแบบจำลองของที่เก็บ Wallet หลักเท่านั้นที่สามารถมองเห็นได้ด้วย Microservice Wallet มีกลไกการซิงโครไนซ์ข้อมูลที่ใช้ทริกเกอร์หรือเตะเป็นระยะเพื่อส่งการเปลี่ยนแปลงข้อมูล (เช่นกระเป๋าใหม่) จากแบบจำลองไปยังต้นแบบและในทางกลับกัน

แต่ถ้าคุณต้องการการตอบสนองแบบซิงโครนัส

  • สร้าง microservices หากโซลูชันที่มีคิวไม่ทำงานเนื่องจากผู้ใช้บริการต้องการการตอบสนองทันทีฉันต้องการสร้างฟังก์ชันการทำงานของผู้ใช้และกระเป๋าเงินใหม่เพื่อจัดวางในบริการเดียวกัน (หรืออย่างน้อยใน VM เดียวกันเพื่อหลีกเลี่ยงการทำธุรกรรมแบบกระจาย ) ใช่มันเป็นขั้นตอนที่ไกลกว่าจากไมโครไซต์และใกล้กับหินใหญ่ก้อนเดียว แต่จะช่วยให้คุณไม่ต้องปวดหัว

4
ความสอดคล้องในที่สุดทำงานสำหรับฉัน ในกรณีนี้คิว "NewUser" ควรสูงและพร้อมใช้งาน
Ram Bavireddi

@RamBavireddi ทำ Kafka หรือ RabbitMQ รองรับคิวยืดหยุ่นหรือไม่
v.oddou

@ v.oddou ใช่พวกเขาทำ
Ram Bavireddi

2
@ พอลลอร์เดอร์ฉันไม่แน่ใจว่าคุณแตกต่างกันอย่างไรในการทำธุรกรรมเพื่อความสอดคล้องในที่สุด ถ้าหากในที่สุดความสอดคล้องของคุณการสร้างกระเป๋าเงินจะล้มเหลว?
balsick

2
@balsick หนึ่งในความท้าทายของการตั้งค่าความสอดคล้องในที่สุดคือความซับซ้อนของการออกแบบที่เพิ่มขึ้น การตรวจสอบความสอดคล้องและเหตุการณ์การแก้ไขมักจะต้อง การออกแบบของการแก้ปัญหาแตกต่างกันไป ในคำตอบฉันขอแนะนำสถานการณ์ที่บันทึก Wallet ถูกสร้างขึ้นในฐานข้อมูลเมื่อประมวลผลข้อความที่ส่งผ่านนายหน้าข้อความ ในกรณีนี้เราสามารถตั้ง Dead Dead Channel ได้นั่นคือหากการประมวลผลข้อความนั้นเกิดข้อผิดพลาดเราสามารถส่งข้อความไปยังคิวจดหมายที่ตายแล้วและแจ้งให้ทีมงานที่รับผิดชอบ "Wallet"
Paulo Merson

66

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

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

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

  • อย่าคิดมากเกินไปเมื่อกำหนดบริการเว็บของคุณ (ฉันไม่มั่นใจเกี่ยวกับ hype บริการไมโครที่เกิดขึ้นวันนี้: ความเสี่ยงมากเกินไปที่จะไปไกลเกินไป);

  • Async เพิ่มประสิทธิภาพให้มากขึ้นเช่นต้องการซิงค์ส่งการแจ้งเตือนทางอีเมลทุกครั้งที่ทำได้

  • สร้างบริการที่ชาญฉลาดมากขึ้นเพื่อให้ "เรียกคืนได้" ไม่ว่าจะกี่ครั้งประมวลผลด้วย uid หรือ taskid ที่จะปฏิบัติตามคำสั่งจากล่างสุดจนถึงสิ้นสุดตรวจสอบกฎเกณฑ์ทางธุรกิจในแต่ละขั้นตอน

  • ใช้คิวข้อความ (JMS หรืออื่น ๆ ) และโอนไปยังข้อผิดพลาดในการจัดการโปรเซสเซอร์ที่จะใช้การดำเนินการกับ "ย้อนกลับ" โดยใช้การดำเนินการตรงข้ามโดยวิธีการทำงานกับคำสั่ง async จะต้องมีคิวบางประเภทในการตรวจสอบสถานะปัจจุบันของกระบวนการ ดังนั้นให้พิจารณาว่า;

  • ในวิธีสุดท้าย (เนื่องจากอาจไม่เกิดขึ้นบ่อย) ให้วางคิวเพื่อประมวลผลข้อผิดพลาดด้วยตนเอง

ลองย้อนกลับไปที่ปัญหาเบื้องต้นที่โพสต์ไว้ สร้างบัญชีและสร้างกระเป๋าเงินและทำให้ทุกอย่างเรียบร้อย

สมมติว่ามีการเรียกใช้บริการเว็บเพื่อจัดการการดำเนินการทั้งหมด

โค้ดหลอกของบริการเว็บจะมีลักษณะเช่นนี้:

  1. โทร microservice สร้างบัญชีส่งผ่านข้อมูลบางส่วนและ id งานที่ไม่ซ้ำกัน 1.1 microservice สร้างบัญชีจะตรวจสอบก่อนว่าบัญชีนั้นถูกสร้างขึ้นแล้ว รหัสงานเชื่อมโยงกับบันทึกของบัญชี microservice ตรวจพบว่าบัญชีไม่มีอยู่ดังนั้นจึงสร้างและเก็บ id งาน หมายเหตุ: บริการนี้สามารถเรียกได้ 2,000 ครั้งจะให้ผลลัพธ์เหมือนเดิมเสมอ บริการตอบด้วย "ใบเสร็จรับเงินที่มีข้อมูลน้อยที่สุดในการดำเนินการเลิกทำหากจำเป็น"

  2. เรียกการสร้าง Wallet โดยระบุ ID บัญชีและรหัสงาน สมมติว่าเงื่อนไขไม่ถูกต้องและไม่สามารถสร้างกระเป๋าเงินได้ การโทรกลับมาพร้อมกับข้อผิดพลาด แต่ไม่มีอะไรถูกสร้างขึ้น

  3. orchestrator ได้รับแจ้งถึงข้อผิดพลาด มันรู้ว่ามันจำเป็นต้องยกเลิกการสร้างบัญชี แต่มันจะไม่ทำเอง มันจะขอให้บริการกระเป๋าเงินทำได้โดยผ่าน "ใบเสร็จรับเงินที่เลิกทำน้อยที่สุด" ที่ได้รับเมื่อสิ้นสุดขั้นตอนที่ 1

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

  5. ตอนนี้สมมติว่าบริการเว็บไม่เคยได้รับความสำเร็จหรือล้มเหลว (ในกรณีนี้) ว่าการยกเลิกการสร้างบัญชีได้ดำเนินการแล้ว มันจะเรียกบริการการยกเลิกบัญชีอีกครั้ง และบริการนี้ควรไม่เคยล้มเหลวเพราะเป้าหมายคือเพื่อให้บัญชีไม่มีอยู่อีกต่อไป ดังนั้นจึงตรวจสอบว่ามีอยู่และไม่เห็นสิ่งใดที่สามารถยกเลิกได้ ดังนั้นมันจึงกลับมาที่การดำเนินการประสบความสำเร็จ

  6. บริการเว็บกลับสู่ผู้ใช้ซึ่งไม่สามารถสร้างบัญชีได้

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

ฉันทำการค้นหาแล้วและเว็บไซต์ของ Microsoft มีคำอธิบายรูปแบบสำหรับวิธีการนี้ มันถูกเรียกว่ารูปแบบการทำธุรกรรมชดเชย:

รูปแบบการทำธุรกรรมชดเชย


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

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

2
เพิ่งเพิ่มลิงค์ไปยังรูปแบบการทำธุรกรรมชดเชยตามที่อธิบายโดย Microsoft
user8098437

3
สำหรับฉันนี่คือคำตอบที่ดีที่สุด ง่ายมาก
ออสการ์เนวาเรซ

1
โปรดทราบว่าการชดเชยการทำธุรกรรมอาจเป็นไปไม่ได้ทันทีในบางสถานการณ์ที่ซับซ้อน (ดังที่ได้เน้นใน Microsoft microsoft docs) ในตัวอย่างนี้จินตนาการก่อนการสร้างกระเป๋าเงินอาจล้มเหลวใครบางคนสามารถอ่านรายละเอียดเกี่ยวกับบัญชีที่เกี่ยวข้องโดยทำการโทร GET บนบริการบัญชีซึ่งไม่ควรมีอยู่ในสถานที่แรกตั้งแต่การสร้างบัญชีล้มเหลว สิ่งนี้สามารถนำไปสู่ข้อมูลที่ไม่สอดคล้องกัน ปัญหาการแยกนี้เป็นที่รู้จักกันดีในรูปแบบ SAGAS
Anmol Singh Jaggi

32

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

การโทรครั้งสุดท้ายนี้ควรทำซ้ำได้อย่างปลอดภัย (ในกรณีที่การเชื่อมต่อของคุณลดลง)

สิ่งนี้จะทำให้การเรียกครั้งสุดท้ายทราบเกี่ยวกับตารางทั้งสอง (เพื่อให้สามารถดำเนินการในธุรกรรม JDBC เดียว)

หรือคุณอาจต้องการคิดว่าทำไมคุณถึงกังวลเกี่ยวกับผู้ใช้ที่ไม่มีกระเป๋าเงิน คุณเชื่อว่าสิ่งนี้จะทำให้เกิดปัญหาหรือไม่? ถ้าเป็นเช่นนั้นการแยกสายที่เหลือเป็นการคิดที่ไม่ดี หากผู้ใช้ไม่ควรมีกระเป๋าเงินอยู่คุณอาจจะเพิ่มกระเป๋าเงินให้กับผู้ใช้ (ในการโทร POST ดั้งเดิมเพื่อสร้างผู้ใช้)


ขอบคุณสำหรับคำแนะนำ บริการผู้ใช้ / กระเป๋าเงินเป็นตัวละครเพียงเพื่อแสดงจุด แต่ฉันยอมรับว่าฉันควรออกแบบระบบเพื่อหลีกเลี่ยงความจำเป็นในการทำธุรกรรมให้มากที่สุด
Olivier Lalonde

7
ฉันเห็นด้วยกับมุมมองที่สอง ดูเหมือนว่าสิ่งที่ microservice ของคุณซึ่งสร้างผู้ใช้ควรสร้างกระเป๋าเงินด้วยเนื่องจากการดำเนินการนี้แสดงถึงหน่วยงานของอะตอม นอกจากนี้คุณสามารถอ่านeaipatterns.com/docs/IEEE_Software_Design_2PC.pdf
Sattar Imamov

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

10

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

ในตัวอย่างปัจจุบันการสร้างผู้ใช้จะเป็นธุรกรรมของตัวเอง การสร้างผู้ใช้จะผลักดันเหตุการณ์ USER_CREATED ลงในคิวเหตุการณ์ บริการ Wallet จะสมัครใช้งาน USER_CREATED และทำการสร้าง Wallet


1
สมมติว่าเราต้องการหลีกเลี่ยง 2PC ใด ๆ และทั้งหมดและสมมติว่าบริการผู้ใช้เขียนลงในฐานข้อมูลจากนั้นเราไม่สามารถส่งข้อความไปยังคิวเหตุการณ์โดยผู้ใช้ที่จะทำธุรกรรมซึ่งหมายความว่ามันอาจไม่เคยทำ บริการ Wallet
Roman Kharkovski

@ RomanKharkovski จุดสำคัญแน่นอน วิธีหนึ่งในการแก้ไขปัญหาอาจเป็นการเริ่มต้นธุรกรรมบันทึกผู้ใช้เผยแพร่เหตุการณ์ (ไม่ใช่ส่วนหนึ่งของธุรกรรม) จากนั้นส่งมอบธุรกรรม (กรณีที่เลวร้ายที่สุดไม่น่าเป็นไปได้อย่างมากการกระทำผิดพลาดและผู้ที่ตอบสนองต่อเหตุการณ์จะไม่สามารถหาผู้ใช้ได้)
Timo

1
จากนั้นเก็บเหตุการณ์ไว้ในฐานข้อมูลรวมถึงเอนทิตี มีงานที่กำหนดเวลาไว้เพื่อประมวลผลกิจกรรมที่เก็บไว้และส่งไปยังนายหน้าข้อความ stackoverflow.com/a/52216427/4587961
Yan Khonski

7

หากกระเป๋าเงินของฉันเป็นเพียงบันทึกอีกกลุ่มหนึ่งในฐานข้อมูล sql เดียวกับผู้ใช้ฉันอาจจะวางรหัสผู้ใช้และการสร้างกระเป๋าเงินไว้ในบริการเดียวกันและจัดการกับการใช้ฐานข้อมูลธุรกรรมปกติ

ฟังดูเป็นเรื่องที่คุณถามว่าเกิดอะไรขึ้นเมื่อรหัสการสร้าง Wallet ต้องการให้คุณแตะระบบอื่นหรือระบบอื่น? Id บอกว่าทั้งหมดขึ้นอยู่กับความซับซ้อนและความเสี่ยงของกระบวนการสร้าง

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

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

ณ จุดนี้คุณอาจแนะนำคิวข้อความพร้อมกับแนวคิดของผู้ใช้และ / หรือกระเป๋าที่สร้างขึ้นบางส่วน

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

ฉันจะคิดนานและหนักว่าทำไมฉันถึงมีความล้มเหลวในขั้นตอนการจัดเตรียมของฉัน


4

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


3

มีวิธีแก้ไขปัญหาใดบ้างที่ป้องกันไม่ให้เกิดความไม่สอดคล้องกันของข้อมูล

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

มีรูปแบบที่อนุญาตให้ธุรกรรมครอบคลุมหลายคำขอ REST หรือไม่

สำหรับ SOAP (ok ไม่ใช่ REST) ​​มีข้อกำหนดของWS-ATแต่ไม่มีบริการใดที่ฉันเคยรวมเข้าด้วยกันได้ให้การสนับสนุน สำหรับ REST นั้น JBoss มีบางอย่างในท่อบางสิ่งบางอย่างในท่อมิฉะนั้น "รูปแบบ" คือการค้นหาผลิตภัณฑ์ที่คุณสามารถเสียบเข้ากับสถาปัตยกรรมของคุณหรือสร้างโซลูชันของคุณเอง (ไม่แนะนำ)

ฉันได้เผยแพร่ผลิตภัณฑ์ดังกล่าวสำหรับ Java EE: https://github.com/maxant/genericconnector

จากเอกสารที่คุณอ้างอิงมีรูปแบบลองยกเลิก / ยืนยันและผลิตภัณฑ์ที่เกี่ยวข้องจาก Atomikos

เครื่องยนต์ BPEL จัดการความสอดคล้องกันระหว่างบริการที่ปรับใช้จากระยะไกลโดยใช้การชดเชย

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

มีหลายวิธีของการผูกมัดทรัพยากรที่ไม่ใช่ธุรกรรมลงในธุรกรรม:

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

หรือฉันควรบังคับใช้ความสอดคล้องกันในรหัสแอปพลิเคชันของฉัน (ตัวอย่างเช่นโดยมีงานพื้นหลังที่ตรวจจับความไม่สอดคล้องกันและแก้ไขได้หรือมีแอตทริบิวต์ "สถานะ" ในรูปแบบผู้ใช้ของฉันด้วยค่า

การสนับสนุนผู้เล่นปีศาจ: ทำไมต้องสร้างบางสิ่งเช่นนั้นเมื่อมีผลิตภัณฑ์ที่ทำเพื่อคุณ (ดูด้านบน) และอาจทำได้ดีกว่าที่คุณสามารถทำได้เพราะพวกเขาได้รับการทดลองและทดสอบแล้วหรือไม่


2

โดยส่วนตัวแล้วฉันชอบความคิดของ Micro Services โมดูลที่กำหนดโดยกรณีการใช้งาน แต่ตามคำถามของคุณพวกเขามีปัญหาการปรับตัวสำหรับธุรกิจคลาสสิกเช่นธนาคารประกันภัยโทรคมนาคม ฯลฯ

การทำธุรกรรมแบบกระจายดังที่ได้กล่าวมาแล้วนั้นไม่ใช่ทางเลือกที่ดีผู้คนจะใช้ระบบที่สอดคล้องกันในที่สุด แต่ฉันไม่แน่ใจว่าสิ่งนี้จะใช้ได้กับธนาคารประกันภัยและอื่น ๆ

ฉันเขียนบล็อกเกี่ยวกับโซลูชันที่เสนอของฉันอาจเป็นสิ่งนี้สามารถช่วยคุณได้ ....

https://mehmetsalgar.wordpress.com/2016/11/05/micro-services-fan-out-transaction-problems-and-solutions-with-spring-bootjboss-and-netflix-eureka/


0

ความมั่นคงในที่สุดคือกุญแจสำคัญที่นี่

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

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

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

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

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

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


-2

ทำไมไม่ใช้แพลตฟอร์มการจัดการ API (APIM) ที่รองรับการเขียนสคริปต์ / การเขียนโปรแกรม? ดังนั้นคุณจะสามารถสร้างบริการแบบรวมใน APIM โดยไม่รบกวนบริการขนาดเล็ก ฉันออกแบบโดยใช้ APIGEE เพื่อจุดประสงค์นี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.