วิธีตั้งค่าสถาปัตยกรรมไมโครเซอร์วิสใน Ruby: คำแนะนำทีละขั้นตอน

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

ไมโครเซอร์วิส คืออะไร?

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

Microservices กำลังแทนที่เซิร์ฟเวอร์แบ็คเอนด์เสาหินแบบคลาสสิก
ทวีต

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

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

วิธีการตั้งค่าสถาปัตยกรรมไมโครเซอร์วิส

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

สถาปัตยกรรมของโบรกเกอร์

สถาปัตยกรรมโบรคเกอร์ โดยมีโบรกเกอร์ (B) อยู่ตรงกลาง และไมโครเซอร์วิสสี่ตัวที่อยู่รอบๆ นั้น เรียกพวกมันว่า N, S, E, W เส้นทางคำขอ/การตอบสนองเริ่มต้นที่อินพุตภายนอกสถาปัตยกรรม จากนั้นไปตามเส้นทาง N, B, E, B, S, B, W, B, E, B, N ก่อนที่จะออกไปเป็นเอาต์พุตในที่สุด

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

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

การสื่อสารระหว่างบริการ

ในคู่มือนี้ เราจะใช้ ZeroMQ เพื่อจัดการการสื่อสารระหว่างบริการและนายหน้า

สแต็ค ZeroMQ ที่ด้านบนสุดคือบล็อกที่มีจุดไข่ปลา ตามด้วยสัญลักษณ์ ZeroMQ บล็อกด้านล่างมี จากบนลงล่าง: การขนส่ง เครือข่าย ดาต้าลิงค์ และทางกายภาพ

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

การสร้าง Microservices Suite

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

มาเริ่มสร้างกันเลยดีกว่า

เริ่มต้น

ก่อนอื่น มาตรวจสอบให้แน่ใจว่าคุณมีทุกสิ่งที่จำเป็นในการเปิดโบรกเกอร์และบริการ ขั้นแรก เริ่มต้นด้วยการดาวน์โหลดและติดตั้ง Node.js, ZeroMQ และ Git บนเครื่องของคุณ หากคุณกำลังใช้ OSX มีแพ็คเกจ homebrew สำหรับแต่ละแพ็คเกจ และลีนุกซ์ลีนุกซ์ส่วนใหญ่จะมีแพ็คเกจสำหรับแต่ละแพ็คเกจด้วย ดังนั้นคุณไม่น่าจะมีปัญหากับสิ่งนี้ ผู้ใช้ Windows สามารถใช้ลิงก์ดาวน์โหลดที่ให้ไว้ด้านบนได้ง่ายๆ

ดำเนินการนายหน้า

หลังจากติดตั้งการพึ่งพาที่จำเป็นทั้งหมดแล้ว ให้โบรกเกอร์ของเราดำเนินการ ในคู่มือนี้ เรากำลังใช้ Node.js ของโบรกเกอร์ซึ่งเป็นส่วนหนึ่งของ ZMQ Service Oriented Suite คุณสามารถค้นหารหัสและเอกสารประกอบได้ที่ GitHub ในการเรียกใช้โบรกเกอร์ ก่อนอื่นให้โคลนบูทสแตรปของโบรกเกอร์ไปที่เครื่องของคุณ ที่เก็บนี้เป็นบูตสแตรปสำหรับการใช้ไลบรารีนายหน้าด้านบน หมายเหตุ ขั้นตอนนี้ไม่จำเป็นเนื่องจากไลบรารีดั้งเดิมสามารถรันได้เอง แต่ความแตกต่างระหว่างทั้งสองคือในที่เก็บ bootstrap คุณสามารถเปลี่ยนการกำหนดค่าเริ่มต้นได้

ขั้นแรก ใช้คำสั่ง Git ต่อไปนี้เพื่อดาวน์โหลดโครงการไปยังเครื่องของคุณ:

 $ git clone [email protected]:dadah/zmq-broker-bootstrap.git

หลังจากที่คุณทำเสร็จแล้ว ให้ย้ายไปที่ไดเร็กทอรีที่สร้างขึ้น:

 $ cd zmq-broker-bootstrap

ตอนนี้ติดตั้งการพึ่งพาแพ็คเกจ:

 $ npm install

โบรกเกอร์พร้อมแล้ว ในการรันโบรกเกอร์ของคุณ ให้รันคำสั่งต่อไปนี้:

 $ bin/zss-broker run

คุณสามารถค้นหาไฟล์การกำหนดค่าสำหรับแต่ละสภาพแวดล้อมใน config/ นี่คือการกำหนดค่าการพัฒนาเริ่มต้น:

 { "broker": { "backend": "tcp://127.0.0.1:7776", "frontend": "tcp://127.0.0.1:7777" }, "log": { "consolePlugin": { "level": "debug" } } }

พารามิเตอร์ backend กำหนดที่อยู่ ip:port ของแบ็คเอนด์และฟรอนต์เอนด์ของโบรกเกอร์ ที่อยู่ส่วนหลังเป็นที่ที่นายหน้ารับคำขอและตอบกลับบริการ และที่อยู่ส่วนหน้าเป็นที่ที่รับและส่งไปยังลูกค้าบริการ คุณยังสามารถตั้งค่าระดับการบันทึกโดยเปลี่ยน log.consolePlugin.level ค่าที่เป็นไปได้คือ trace , debug , info , warn และ error และพวกเขากำหนดจำนวนของกระบวนการบันทึกข้อมูลที่นายหน้าจะส่งออก

เรียกใช้บริการ

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

ไปที่ไดเร็กทอรีบริการของคุณและโคลนที่เก็บ bootstrap:

 $ git clone [email protected]:dadah/zmq-service-suite-ruby-bootstrap.git

ไปที่ไดเร็กทอรีที่สร้างขึ้นใหม่:

 $ cd zmq-service-suite-ruby-bootstrap

ตอนนี้ติดตั้งการพึ่งพาทั้งหมด:

 $ bundle install

ในการเริ่มบริการ ให้รันคำสั่งต่อไปนี้:

 $ bin/zss-service run

ยอดเยี่ยม. คุณมีบริการแรกและเริ่มทำงาน

หากคุณไปที่หน้าต่างคอนโซลที่คุณปล่อยให้โบรกเกอร์ทำงาน คุณจะเห็นผลลัพธ์ต่อไปนี้:

 2015-12-15 16:45:05 | INFO | BROKER - Async Broker is waiting for messages... 2015-12-15 16:45:14 | DEBUG | BACKEND - received from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 76f50741-913a-43b9-94b0-36d8f7bd75b1 2015-12-15 16:45:14 | DEBUG | BACKEND - routing from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 76f50741-913a-43b9-94b0-36d8f7bd75b1 to SMI.UP request... 2015-12-15 16:45:14 | INFO | SMI - SMI register for sid: HELLO-WORD instance: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b! 2015-12-15 16:45:14 | DEBUG | BACKEND - reply to: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 76f50741-913a-43b9-94b0-36d8f7bd75b1 with status: 200 2015-12-15 16:45:15 | DEBUG | BACKEND - received from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 3b3a0416-73fa-4fd2-9306-dad18bc0502a 2015-12-15 16:45:15 | DEBUG | BACKEND - routing from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 3b3a0416-73fa-4fd2-9306-dad18bc0502a to SMI.HEARTBEAT request... 2015-12-15 16:45:15 | DEBUG | BACKEND - reply to: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 3b3a0416-73fa-4fd2-9306-dad18bc0502a with status: 200 2015-12-15 16:45:16 | DEBUG | BACKEND - received from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: b3044c24-c823-4394-8204-1e872f30e909 2015-12-15 16:45:16 | DEBUG | BACKEND - routing from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: b3044c24-c823-4394-8204-1e872f30e909 to SMI.HEARTBEAT request... 2015-12-15 16:45:16 | DEBUG | BACKEND - reply to: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: b3044c24-c823-4394-8204-1e872f30e909 with status: 200

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

การบริโภคจากบริการ

ตอนนี้เรามีบริการแล้ว เราจะใช้งานอย่างไร?

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

 $ bin/zss-client

คุณควรเห็นสิ่งนี้:

 15-49-15 16:49:54 | INFO | ZSS::CLIENT - Request 90a88081-3485-45b6-91b3-b0609d64592a sent to HELLO-WORD:*#HELLO/WORLD with 1.0s timeout 15-49-15 16:49:54 | INFO | ZSS::CLIENT - Received response to 90a88081-3485-45b6-91b3-b0609d64592a with status 200 "Hello World"

หากคุณไปที่หน้าต่างคอนโซลที่บริการของคุณทำงานอยู่ คุณจะเห็นสิ่งนี้:

 Started hello-word daemon... 15-45-15 16:45:14 | INFO | ZSS::SERVICE - Starting SID: 'HELLO-WORD' ID: 'hello-word#aaa65374-8585-410a-a41d-c8a5b024553b' Env: 'development' Broker: 'tcp://127.0.0.1:7776' 15-49-15 16:49:54 | INFO | ZSS::SERVICE - Handle request for HELLO-WORD:*#HELLO/WORLD 15-49-15 16:49:54 | INFO | ZSS::SERVICE - Reply with status: 200

ดี. คุณเพิ่งเปิดตัวและใช้ไมโครเซอร์วิส “Hello World” ของคุณ อย่างไรก็ตาม นี่ไม่ใช่สิ่งที่เราตั้งใจจะทำ เราต้องการสร้างบริการของเรา มาเริ่มกันเลยดีกว่า

สร้างบริการของคุณ

ขั้นแรก ให้หยุดบริการ “Hello World” ของเราเสียก่อน ไปที่หน้าต่างคอนโซลของบริการแล้วกด Ctrl+C เพื่อหยุดบริการ ต่อไปเราต้องเปลี่ยนบริการ "Hello World" เป็นบริการ "Person"

โครงสร้างรหัส

เริ่มต้นด้วยการดูรหัสทรีของโครงการ ดูเหมือนว่านี้:

ลำดับชั้นของไฟล์/โฟลเดอร์ของตัวอย่างโปรเจ็กต์ zmq-service-suite-ruby-bootstrap มีการอธิบายโดยละเอียดด้านล่าง แต่โปรดทราบว่าไฟล์ .rb สามไฟล์สุดท้ายที่กล่าวถึงนั้นอยู่ภายใต้ lib/repositories ไม่ใช่ภายใต้ lib

  • ไดเร็กทอรี bin คือที่ที่คุณจัดเก็บสคริปต์ที่เปิดบริการของคุณ
  • config เก็บไฟล์การกำหนดค่าทั้งหมด
    • ไฟล์ boot.rb คือที่ที่คุณสามารถเพิ่มการพึ่งพาบริการทั้งหมดของคุณได้ หากคุณเปิดขึ้นมา คุณจะสังเกตได้ว่ามีการขึ้นต่อกันหลายรายการอยู่ในรายการนั้นแล้ว หากคุณต้องการเพิ่มอีก นี่คือสิ่งที่คุณควรทำ
    • ไฟล์ application.yml เก็บการตั้งค่าแอปพลิเคชันทั้งหมดของคุณ เราจะดูที่ไฟล์นี้ในภายหลัง
    • ใน config/initializers คุณสามารถเพิ่มสคริปต์ initializer ได้ ตัวอย่างเช่น คุณสามารถเพิ่มการตั้งค่าสำหรับการเชื่อมต่อ ActiveRecord หรือ Redis ได้ที่นี่ สคริปต์ที่คุณเพิ่มลงในไดเร็กทอรีนี้จะทำงานเมื่อเริ่มต้นบริการ
  • ในไดเร็กทอรี db/migrate คุณสามารถจัดเก็บการย้าย ActiveRecord หรือ Sequel ได้หากมี ในกรณีที่คุณทำไม่ได้ คุณสามารถลบไดเร็กทอรีนี้ทั้งหมด
  • lib คือตำแหน่งที่โค้ดแอปพลิเคชันหลักของคุณอยู่
    • ไฟล์ settings.rb จะโหลดไฟล์ application.yml และทำให้ใช้งานได้ตลอดขอบเขตของบริการ เพื่อให้คุณสามารถเข้าถึงการกำหนดค่าได้จากทุกที่ ตัวอย่างเช่น Settings.broker.backend จะส่งกลับที่อยู่แบ็กเอนด์ของโบรกเกอร์ที่คุณกำหนดไว้ในไฟล์ YML ด้านบน
    • ไฟล์ service_register.rb คือที่ที่คุณลงทะเบียนบริการและเส้นทางบริการของคุณ เราจะอธิบายในภายหลัง
    • ไฟล์ hello_world_service.rb กำหนดปลายทางของบริการ “Hello World”
    • lib/daos คือที่ที่คุณเก็บอ็อบเจ็กต์ ActiveModel ของคุณ หากคุณใช้ ActiveRecord หรืออ็อบเจ็กต์การเข้าถึงข้อมูลอื่นๆ ที่คุณอาจสร้างขึ้นในท้ายที่สุด เช่น โมเดล Sequel ของคุณ
    • lib/dtos เก็บอ็อบเจ็กต์การถ่ายโอนข้อมูลของคุณ ออบเจ็กต์เหล่านี้เป็นออบเจ็กต์ที่ส่งกลับไปยังไคลเอ็นต์ของบริการในที่สุด
    • lib/repositories เก็บของคุณ ที่เก็บคืออ็อบเจ็กต์ที่อนุญาตให้บริการเข้าถึงข้อมูลและเป็นอ็อบเจ็กต์เดียวที่ได้รับอนุญาตให้จัดการ DAO ดังนั้นหากบริการต้องการกลุ่มของอินสแตนซ์ "Hello World" บริการจะถามที่เก็บข้อมูลสำหรับพวกเขา ในทางกลับกัน พื้นที่เก็บข้อมูลใช้ DAO ที่เหมาะสมเพื่อดึงข้อมูลที่เกี่ยวข้องจากฐานข้อมูล ข้อมูลจะถูกแมปเข้ากับคอลเลกชั่น DTO “HelloWorld” หรือ “HelloWorld” ที่เหมาะสม ซึ่งจะถูกส่งกลับไปยังบริการ
    • lib/repositories/mappers คือที่ที่คุณจัดเก็บ mappers ของคุณ Mappers คืออ็อบเจ็กต์ที่แปลง DAO เป็น DTO และในทางกลับกัน

ไฟล์ application.yml จาก config มีลักษณะดังนี้:

 defaults: &defaults broker: backend: tcp://127.0.0.1:7776 frontend: tcp://127.0.0.1:7777 logging: console: level: info development: <<: *defaults test: <<: *defaults production: <<: *defaults

การตั้งค่านี้เพียงแค่ตั้งค่าที่อยู่ส่วนหลังและส่วนหน้าของโบรกเกอร์ และระดับการบันทึก

หากทั้งหมดนี้ฟังดูน่าสับสน อย่าเพิ่งกังวลไป เพราะมันจะชัดเจนขึ้นเมื่อเราก้าวต่อไป

“บุคคล” บริการ

มาต่อกันที่บริการ "บุคคล" ของเรา เริ่มต้นด้วยการกำหนดค่าการเชื่อมต่อฐานข้อมูล เปิดไฟล์ config/initializers/active_record.rb และยกเลิกหมายเหตุบรรทัดเดียวที่นั่น จากนั้น เพิ่มรายการต่อไปนี้ในการกำหนดค่าการพัฒนาของคุณใน application.yml เพื่อให้มีลักษณะดังนี้:

 defaults: &defaults broker: backend: tcp://127.0.0.1:7776 frontend: tcp://127.0.0.1:7777 logging: console: level: info database: adapter: postgresql database: zss-tutorial-development

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

 $ rake db:create

หากคุณต้องการฐานข้อมูลอื่น คุณต้องเพิ่ม gem ที่เหมาะสมใน gemfile แล้วรวมชุดติดตั้งโปรเจ็กต์

ต่อไปคือการอพยพ สำหรับสิ่งนั้น เพียงแค่สร้างไฟล์ db/migrate ชื่อ 000_creates_persons.rb :

 $ touch db/migrate/000_creates_persons_table.rb

เปิดไฟล์และสร้างการโยกย้ายเช่นเดียวกับที่คุณทำกับการย้ายข้อมูล Rails ปกติ:

 class CreatesPersons < ActiveRecord::Migration def change create_table :persons do |t| t.name t.timestamps end end end

ถัดไป เรียกใช้:

 $ rake db:migrate == 0 CreatesPersons: migrating ================================================ -- create_table(:persons) DEPRECATION WARNING: `#timestamp` was called without specifying an option for `null`. In Rails 5, this behavior will change to `null: false`. You should manually specify `null: true` to prevent the behavior of your existing migrations from changing. (called from block in change at /Users/francisco/Code/microservices-tutorial/db/migrate/000_creates_persons.rb:6) -> 0.0012s == 0 CreatesPersons: migrated (0.0013s) =======================================

ตอนนี้เราได้สร้างตารางของเราแล้ว มาสร้างแบบจำลองสำหรับตารางกัน สร้างไฟล์ lib/daos/person.rb :

 $ touch lib/daos/person.rb

แก้ไขดังนี้:

 module DAO class Person < ActiveRecord::Base end end

มีแบบของคุณ ตอนนี้ คุณต้องสร้างแบบจำลอง DTO สำหรับ "บุคคล" เพื่อให้คุณสามารถส่งคืนให้กับลูกค้าได้ สร้างไฟล์ lib/dtos/person.rb :

 $ touch lib/dtos/person.rb

แก้ไขดังนี้:

 module DTO class Person < Base attr_reader :id, :name end end

ถัดไป คุณต้องสร้าง Mapper เพื่อแปลง DAO "บุคคล" เป็น DTO "บุคคล" สร้างไฟล์ lib/repositories/mappers/person.rb และแก้ไขดังนี้:

 module Mapper class Person < Mapper::Base def self.to_dao dto_instance DAO::Person.new id: dto_instance.id, name: dto_instance.name end def self.to_dto dao_instance DTO::Person.new id: dao_instance.id, name: dao_instance.name end end end

ที่นี่ Mapper::Base ต้องการให้คุณใช้งาน self.to_dao และ self.to_dto หากคุณไม่ต้องการทำเช่นนั้น คุณสามารถใช้ self.map แทนและแทนที่ Mapper::Base.map ซึ่งเรียก to_dao หรือ to_dto ขึ้นอยู่กับว่าแอตทริบิวต์ที่ได้รับคือ DAO หรือ DTO

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

มาสร้างที่เก็บกันเถอะ สร้างไฟล์ lib/repositories/person.rb :

 $ touch lib/dtos/person.rb

แก้ไขดังนี้:

 module Repository class Person < Repository::Base def get DAO::Person.all.map do |person| Mapper::Person.map(person) end end end end

ที่เก็บนี้มีเฉพาะเมธอดของอินสแตนซ์ที่ get ซึ่งเพียงรับบุคคลทั้งหมดจากฐานข้อมูล และแมปพวกเขาลงในคอลเล็กชันของ DTO ของบุคคล ซึ่งค่อนข้างง่าย มารวมสิ่งนี้กันตอนนี้เลย สิ่งที่เหลืออยู่ตอนนี้คือการสร้างบริการและปลายทางที่เรียกที่เก็บนี้ ในการทำเช่นนั้น ให้สร้างไฟล์ lib/person_service.rb :

 $ touch lib/person_service.rb

แก้ไขดังนี้:

 class PersonService < BaseService attr_reader :person_repo def initialize @person_repo = Repository::Person.new end def get payload, headers persons = person_repo.get() if persons.empty? raise ZSS::Error.new(404, "No people here") else persons.map &:serialize end end end

บริการ "บุคคล" เริ่มต้นที่เก็บในตัวเริ่มต้น วิธีการอินสแตนซ์สาธารณะทั้งหมดของบริการ "บุคคล" มีเพย์โหลดและส่วนหัวซึ่งคุณสามารถละเว้นได้หากไม่ต้องการ ทั้งสองเป็นอินสแตนซ์ Hashie::Mash และเก็บตัวแปรที่ส่งไปยังปลายทาง ไม่ว่าจะเป็นแอตทริบิวต์หรือส่วนหัว และการตอบกลับจะเลียนแบบการตอบกลับ HTTP เนื่องจากการตอบสนองแต่ละครั้งมีรหัสสถานะที่ลูกค้าสามารถใช้เพื่อค้นหาผลลัพธ์ของคำขอที่ส่งไปยัง บริการพร้อมกับเพย์โหลดการตอบกลับของบริการ รหัสตอบกลับเหมือนกับที่คุณคาดหวังจากเซิร์ฟเวอร์ HTTP ตัวอย่างเช่น คำขอที่ประสบความสำเร็จจะส่งคืนรหัสสถานะ 200 พร้อมกับเพย์โหลดการตอบกลับ หากเกิดข้อผิดพลาดของบริการ รหัสสถานะจะเป็น 500 และหากมีสิ่งผิดปกติกับพารามิเตอร์ที่ส่งไปยังเซิร์ฟเวอร์ รหัสสถานะจะเป็น 400 บริการสามารถตอบกลับด้วยรหัสสถานะ HTTP ส่วนใหญ่พร้อมกับเพย์โหลด ตัวอย่างเช่น หากคุณต้องการให้บริการของคุณบอกลูกค้าว่าเมื่อใดที่พวกเขาไม่ได้รับอนุญาตให้เข้าถึงจุดปลายบางจุด คุณสามารถทำได้โดยตอบกลับด้วยรหัส 403 คุณสามารถดูตัวอย่างรหัสตอบกลับอื่นได้หากคุณมองย้อนกลับไปที่รหัสบริการของเราด้านบน ใน get endpoint เรากำลังส่งคืนรหัสสถานะ 404 พร้อมกับข้อความตัวเลือก "No people here" เมื่อไม่พบบุคคล เช่นเดียวกับเซิร์ฟเวอร์ HTTP จะส่งกลับ 404 หากไม่มีทรัพยากรที่พร้อมใช้งาน หากพื้นที่เก็บข้อมูลส่งคืนผู้คนจริง ๆ บริการจะทำให้ DTO เป็นอนุกรมและส่งกลับไปยังไคลเอนต์ DTO แต่ละรายการมีซีเรียลไลเซอร์เริ่มต้นที่ส่งคืนอ็อบเจ็กต์ JSON ด้วยคีย์และค่าที่เกี่ยวข้องซึ่งกำหนดเป็น attr_reader หรือ attr_accessible ในคำจำกัดความของ DTO คุณสามารถแทนที่ serializer ได้โดยกำหนดวิธีการทำให้เป็นอันดับของคุณในคลาส DTO ของคุณ

ตอนนี้เราได้กำหนดบริการแล้ว เราต้องลงทะเบียน นี่คือขั้นตอนสุดท้าย เปิดไฟล์ lib/service_register.rb และแทนที่ "HelloWorld" ทั้งหมดด้วย "Person" เพื่อให้ไฟล์มีลักษณะดังนี้:

 module ZSS class ServiceRegister def self.get_service config = Hashie::Mash.new( backend: Settings.broker.backend ) service = ZSS::Service.new(:person, config) personInstance = PersonService.new service.add_route(personInstance, :get) return service end end end

ตามที่คุณอาจสังเกตเห็น มีการเปลี่ยนแปลงเล็กน้อยในการเรียก add_route เราลบสตริง “HELLO/WORLD” นั่นเป็นเพราะจำเป็นต้องใช้สตริงก็ต่อเมื่อกริยาบริการไม่ตรงกับวิธีการที่ใช้ ในกรณีของเรา เมื่อเรียกใช้บริการบุคคลด้วยกริยา GET วิธีที่จะเรียกคือ get ดังนั้นเราจึงสามารถละเว้นสตริงได้

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

 config = Hashie::Mash.new( backend: Settings.broker.backend ) service = ZSS::Service.new(:person, config)

จากนั้นจะยกตัวอย่างตัวจัดการบริการ:

 personInstance = PersonService.new

ถัดไป ตัวจัดการบริการถูกผูกไว้กับบริการ:

 service.add_route(personInstance, :get)

สุดท้ายจะต้องส่งคืนอินสแตนซ์บริการ

 return service

เหลือเพียงขั้นตอนสุดท้ายก่อนที่เราจะสามารถเปิดบริการ "บุคคล" ได้ เราจำเป็นต้องสร้างสคริปต์ปฏิบัติการสำหรับมัน เรามีบริการสำหรับ “HelloService” แล้ว ดังนั้นให้เปิดไฟล์ bin/zss-service แทนที่ "hello-word" ด้วย "person" และบันทึกไฟล์ กลับไปที่คอนโซลและเรียกใช้:

 $ bin/zss-service run Starting person: PID: ./log LOGS: ./log Started person daemon... 15-29-15 19:29:54 | INFO | ZSS::SERVICE - Starting SID: 'PERSON' ID: 'person#d3ca7e1f-e229-4502-ac2d-0c01d8c285f8' Env: 'development' Broker: 'tcp://127.0.0.1:7776'

แค่นั้นแหละ. คุณเพิ่งเริ่มบริการ "บุคคล" ของคุณเป็นครั้งแรก ทีนี้มาทดสอบกัน เปิดไฟล์ bin/zss-client เปลี่ยนตัวแปร sid เป็น "person" และเปลี่ยนการเรียกไคลเอ็นต์จาก hello_world() เป็น get() เมื่อเสร็จแล้วให้เรียกใช้ไคลเอนต์ในหน้าต่างใหม่:

 $ bin/zss-client /Users/francisco/.rvm/gems/ruby-2.1.2/gems/zss-0.3.4/lib/zss/client.rb:41:in `new': No people here (ZSS::Error) from /Users/francisco/.rvm/gems/ruby-2.1.2/gems/zss-0.3.4/lib/zss/client.rb:41:in `call' from /Users/francisco/.rvm/gems/ruby-2.1.2/gems/zss-0.3.4/lib/zss/client.rb:55:in `method_missing' from bin/zss-client:12:in `<main>'

อย่างที่คุณเห็น คุณจับ ZSS::Error ได้ เนื่องจากเราทำให้เกิดข้อผิดพลาดเมื่อบริการไม่พบบุคคลและเรายังไม่มีบุคคลในฐานข้อมูลของบริการของเรา

ให้เราจัดการกับข้อผิดพลาดนี้แล้ว เปิด zss-client และแก้ไขดังนี้:

 begin client = ZSS::Client.new(sid, config) p client.get() rescue ZSS::Client => e if e.code == 404 p e.message else raise e end end

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

 $ bin/zss-client "No people here"

ยอดเยี่ยม. ตอนนี้ มาเพิ่มบางคนในตารางของเราแล้วดูว่าบริการส่งคืนให้กับลูกค้าของเราหรือไม่ ในการดำเนินการนี้ เพียงเปิดคอนโซลบริการ:

 $ rake service:console

เพิ่มบางคน:

 $ rake service:console [1] pry(main)> DAO::Person.create name: 'John' => #<DAO::Person:0x007fe51bbe9d00 id: 1, name: "John", created_at: 2015-12-16 13:22:37 UTC, updated_at: 2015-12-16 13:22:37 UTC> [2] pry(main)> DAO::Person.create name: 'Mary' => #<DAO::Person:0x007fe51c1dafe8 id: 2, name: "Mary", created_at: 2015-12-16 13:22:42 UTC, updated_at: 2015-12-16 13:22:42 UTC> [3] pry(main)> DAO::Person.create name: 'Francis' => #<DAO::Person:0x007fe51bc11698 id: 3, name: "Francis", created_at: 2015-12-16 13:22:53 UTC, updated_at: 2015-12-16 13:22:53 UTC> [4] pry(main)> exit

เรียกใช้ไคลเอนต์ของคุณอีกครั้ง

 $ bin/zss-client [{"id"=>1, "name"=>"John"}, {"id"=>2, "name"=>"Mary"}, {"id"=>3, "name"=>"Francis"}]

ที่นั่นคุณมีมัน

การพิจารณาขั้นสุดท้าย

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

รูปแบบพื้นที่เก็บข้อมูล ช่องซ้ายสุดคือ "ตรรกะทางธุรกิจของไคลเอ็นต์" ซึ่งคงอยู่และสืบค้นจากกล่องกลาง ซึ่งเป็นสแต็กที่ประกอบด้วย "data mapper" เหนือ "repository" เหนือ "query object" แต่คั่นด้วยเส้นประ Persist และ query ถูกเชื่อมเข้าด้วยกันโดยการเชื่อมต่อจากกล่องภายนอกที่มีป้ายกำกับว่า "business entitiy" สุดท้าย กล่องขวาสุด "แหล่งข้อมูล" มีลูกศรชี้ไปที่ "ตัวแมปข้อมูล" และลูกศรสองทิศทางที่มี "วัตถุที่สืบค้น"

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

ฉันหวังว่านี่จะช่วยให้คุณเริ่มต้นใช้งานไมโครเซอร์วิสได้ หากคุณต้องการตรวจสอบรหัสบริการ GitHub มีเวอร์ชันสมบูรณ์