วิธีสร้างสถาปัตยกรรมเว็บแอปพลิเคชั่นบนเว็บแบบเรียลไทม์ที่มีน้ำหนักมาก


17

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

ก่อนที่จะทำความเข้าใจโดยเฉพาะบริบทเพียงเล็กน้อย:

  • webapp เป็นเรียลไทม์ SPA
  • แบ็คเอนด์อยู่ใน Ruby on Rails เหตุการณ์แบบเรียลไทม์ถูกผลักโดย Ruby ไปยังคีย์ Redis จากนั้นเซิร์ฟเวอร์ไมโครโหนดจะดึงข้อมูลกลับมาและส่งไปยัง Socket.Io;
  • Frontend อยู่ใน AngularJS และเชื่อมต่อโดยตรงกับเซิร์ฟเวอร์ socket.io ในโหนด

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

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

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

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

คุณแน่ใจหรือไม่ว่า Rails เป็นตัวเลือกที่ดีที่สุดสำหรับ SPA ของคุณ? Rails นั้นยอดเยี่ยม แต่มันมุ่งเป้าไปที่แอพพลิเคชั่นเสาหิน ... คุณอาจต้องการแบ็กเอนด์แบบแยกส่วนที่มีการแยกข้อกังวล ... ขึ้นอยู่กับความต้องการของคุณฉันจะพิจารณาเฟรมเวิร์ก Ruby อื่น ๆ
Myst

1
ฉันไม่แน่ใจว่า Rails เป็นตัวเลือกที่ดีที่สุด แต่ฉันก็พอใจกับสแต็คอย่างน้อยที่สุดในแบ็กเอนด์ (อาจเป็นเพราะฉันดีกับเฟรมเวิร์คนี้โดยเฉพาะ) ความกังวลของฉันที่นี่คือเพิ่มเติมเกี่ยวกับวิธีการออกแบบสปาในมุมมองของผู้ไม่เชื่อเรื่องพระเจ้าทางเทคโนโลยี และฉันก็ไม่ต้องการทวีคูณจำนวนภาษากรอบงานและไลบรารีในโครงการเดียว
Philippe Durix

เบนช์มาร์กที่เชื่อมโยงอาจมีปัญหา แต่มีจุดอ่อนในการใช้งาน ActiveRecord ในปัจจุบัน ฉันอาจจะลำเอียงเกี่ยวกับplezi.ioแต่ตามที่ชี้ให้เห็นในผลลัพธ์ของมาตรฐานในภายหลังมันให้การปรับปรุงประสิทธิภาพที่สำคัญแม้ก่อนการจัดกลุ่มและ Redis (ซึ่งไม่ได้ทดสอบ) ฉันคิดว่ามันทำงานได้ดีกว่า node.js ... จนกว่าจะมีการเปลี่ยนแปลงฉันจะใช้ plezi.io
Myst

คำตอบ:


10

วิธีจัดโครงสร้างข้อมูลที่ส่งจากเซิร์ฟเวอร์ไปยังผู้ใช้

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

ตัวอย่างเช่นหากคุณอัปเดตตัวเลือกหุ้นและการเปลี่ยนแปลง AAPL คุณจะไม่ต้องการลดราคาหุ้นทั้งหมดหรือแม้แต่ข้อมูลทั้งหมดเกี่ยวกับ AAPL (ชื่อคำอธิบาย ฯลฯ ) คุณจะผลักดัน AAPL เดลต้าและราคาใหม่เท่านั้น บนไคลเอนต์คุณจะต้องอัปเดตเฉพาะราคาหุ้นนั้นในมุมมอง

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

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

วิธีการกำหนดโครงกระดูกที่สอดคล้องกันและปรับขนาดได้กับข้อมูลที่ส่ง? นี่เป็นข้อความอัปเดตโมเดลหรือข้อความ "มีข้อผิดพลาดกับ blahblahblah"

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

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

จะไม่ส่งข้อมูลเกี่ยวกับทุกสิ่งจากที่ใดในแบ็กเอนด์ได้อย่างไร

ใช้เผยแพร่ / สมัครรูปแบบ เมื่อ SPA ของคุณโหลดหน้าใหม่ที่สนใจรับการอัพเดตแบบเรียลไทม์หน้าควรสมัครสมาชิกเฉพาะเหตุการณ์ที่สามารถใช้งานได้และเรียกตรรกะการอัปเดตมุมมองเมื่อมีกิจกรรมเหล่านั้นมาคุณอาจต้องใช้ตรรกะ pub / sub เซิร์ฟเวอร์เพื่อลดโหลดเครือข่าย มีไลบรารีสำหรับ Websocket pub / sub แต่ฉันไม่แน่ใจว่าอะไรอยู่ในระบบนิเวศของ Rails

วิธีการลดความซ้ำซ้อนของตรรกะทางธุรกิจทั้งบนเซิร์ฟเวอร์และฝั่งไคลเอ็นต์ได้อย่างไร

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

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


นี่คือวิธีที่ฉันเห็นสิ่งนั้นมีโครงสร้างที่ดี

มุมมองลูกค้า:

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

คำสั่งของลูกค้า:

  • ร้องขอการเปลี่ยนแปลงข้อมูล (HTTP PUT / POST / DELETE)
    • การตอบสนองเป็นเพียงความสำเร็จหรือล้มเหลว + ข้อผิดพลาด
    • (เหตุการณ์ที่สร้างโดยการเปลี่ยนแปลงจะมาที่ websocket และเปิดการอัปเดตมุมมอง)

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

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

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


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

4

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

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

หลังจากที่ทุกอย่างถูกจัดเป็นทรัพยากรฉันสามารถแนบข้อความเรียลไทม์กับแบบจำลอง

  • การสร้างทริกเกอร์ข้อความที่มีทรัพยากรใหม่ ๆ
  • การปรับปรุงทริกเกอร์ข้อความที่มีเฉพาะคุณสมบัติที่อัพเดท (รวมถึง UUID);
  • การลบจะทริกเกอร์ข้อความการลบ

บน Rest API นั้นทั้งหมดสร้างอัพเดตลบวิธีสร้างการตอบกลับส่วนหัวเท่านั้นโค้ด HTTP แจ้งถึงความสำเร็จหรือความล้มเหลวและข้อมูลจริงที่ถูกพุชผ่าน websockets

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


ฉันพบว่า CQRS + การส่งข้อความและการจัดหากิจกรรมอ่านน่าสนใจมาก แต่รู้สึกว่ามันค่อนข้างซับซ้อนสำหรับปัญหาของฉันและอาจปรับให้เข้ากับแอปพลิเคชั่นที่ใช้งานหนักมากขึ้นซึ่งการยอมรับข้อมูลในฐานข้อมูลส่วนกลาง แต่ฉันจะจำวิธีนี้อย่างแน่นอน

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

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