วิธีตั้งค่าสถาปัตยกรรมไมโครเซอร์วิสใน Ruby: คำแนะนำทีละขั้นตอน
เผยแพร่แล้ว: 2022-03-11ไมโครเซอร์วิส คืออะไร?
Microservices เป็นหนึ่งในแนวโน้มล่าสุดในการออกแบบซอฟต์แวร์ที่บริการอิสระหลายรายการสื่อสารกันเองและมีกระบวนการและทรัพยากรของตนเอง แนวทางนี้แตกต่างจากการออกแบบแอปพลิเคชันไคลเอนต์เซิร์ฟเวอร์ทั่วไป แอปพลิเคชันไคลเอ็นต์-เซิร์ฟเวอร์ปกติประกอบด้วยไคลเอ็นต์ตั้งแต่หนึ่งตัวขึ้นไป แบ็คเอนด์แบบเสาหินซึ่งรวมถึงข้อมูลโดเมนและตรรกะทั้งหมด และ API ที่ช่วยให้ไคลเอ็นต์เข้าถึงแบ็คเอนด์และฟังก์ชันการทำงานได้
ในสถาปัตยกรรมไมโครเซอร์วิส แบ็กเอนด์แบบเสาหินที่อธิบายจะถูกแทนที่ด้วยชุดบริการแบบกระจาย การออกแบบนี้ช่วยให้แยกความรับผิดชอบได้ดีขึ้น บำรุงรักษาง่ายขึ้น มีความยืดหยุ่นมากขึ้นในการเลือกเทคโนโลยีสำหรับแต่ละบริการ และความสามารถในการปรับขนาดและความทนทานต่อข้อผิดพลาดได้ง่ายขึ้น ในขณะเดียวกัน ระบบการกระจายที่ซับซ้อนก็มีชุดของความท้าทาย พวกเขามีโอกาสมากขึ้นที่จะต้องจัดการกับสภาพการแข่งขัน และยากต่อการแก้ไขจุดบกพร่อง เนื่องจากปัญหาไม่ได้ระบุได้ง่ายที่บริการเดียว แต่กระจายผ่านหลายบริการแทน หากไม่มีความพยายามในการปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดในขณะที่สร้างระบบดังกล่าว คุณอาจพบว่าตัวเองรายล้อมไปด้วยไฟที่คุณไม่รู้ว่าจะดับอย่างไร สัญญาเพย์โหลดของบริการต้องใช้ความระมัดระวังเป็นพิเศษ เนื่องจากการเปลี่ยนแปลงในบริการหนึ่งอาจส่งผลกระทบต่อลูกค้าทั้งหมด และด้วยเหตุนี้ชุดบริการของแบ็กเอนด์ทั้งหมด
ข้อควรพิจารณาทั้งหมดนี้มีความสำคัญ แต่สมมติว่าคุณได้พิจารณาแล้ว ตอนนี้สิ่งที่คุณต้องการคือการหาวิธีสร้างไมโครเซอร์วิสแบ็คเอนด์ด้วยตัวคุณเอง มาดำดิ่งลงไปในสิ่งนั้นกัน
วิธีการตั้งค่าสถาปัตยกรรมไมโครเซอร์วิส
ขณะนี้มีหลายวิธีที่คุณสามารถตั้งค่าไมโครเซอร์วิสของคุณได้ และในคู่มือนี้ เราจะเน้นที่สถาปัตยกรรมของโบรกเกอร์
สถาปัตยกรรมของโบรกเกอร์
สถาปัตยกรรมของนายหน้าเป็นวิธีหนึ่งที่คุณจะได้รับบริการเพื่อสื่อสารระหว่างกัน ในนั้น บริการทั้งหมดล้อมรอบเซิร์ฟเวอร์การส่งข้อความ นายหน้า และทั้งหมดเชื่อมต่อกับมัน บริการส่งข้อความไปยังนายหน้าซึ่งรู้ว่าบริการหรือบริการอื่นใดที่เขาต้องการส่งต่อข้อความเหล่านี้ วิธีนี้ทำให้บริการต่างๆ ไม่จำเป็นต้องเก็บข้อมูลเกี่ยวกับบริการอื่นๆ แต่พวกเขาพึ่งพานายหน้าในการดูแลข้อความทั้งหมดและช่วยให้พวกเขาสามารถแยกและเน้นเฉพาะโดเมนเฉพาะของพวกเขาเท่านั้น นายหน้าอาจจัดเก็บข้อความเมื่อผู้รับไม่ทำงาน ทำให้ผู้ส่งและผู้รับไม่ต้องถูกบังคับให้ขึ้นพร้อมกัน ดังนั้นจึงทำให้เกิดการแยกตัวที่ดียิ่งขึ้น แน่นอนว่ามีข้อเสียสำหรับโซลูชันนี้ เนื่องจากนายหน้าสามารถกลายเป็นคอขวดได้อย่างรวดเร็ว เนื่องจากการสื่อสารทั้งหมดต้องผ่านมันไป และอาจกลายเป็นจุดล้มเหลวเพียงจุดเดียวสำหรับแบ็กเอนด์ของคุณ อย่างไรก็ตาม มีสองสามวิธีในการลดปัญหาเหล่านี้ วิธีหนึ่งคือการมีหลายอินสแตนซ์ของโบรกเกอร์ที่ทำงานแบบคู่ขนานกัน ซึ่งจะทำให้ระบบยอมรับความผิดพลาดได้ดีขึ้น อีกวิธีหนึ่งก็คือการใช้สถาปัตยกรรมอื่นๆ สถาปัตยกรรมทางเลือกแตกต่างจากสถาปัตยกรรมที่เราจะนำไปใช้ในคู่มือนี้ โดยไม่ใช้นายหน้า หรือโดยการใช้สถาปัตยกรรมนายหน้าอื่น หรือโดยการใช้โปรโตคอลการส่งข้อความอื่น เช่น HTTP
การสื่อสารระหว่างบริการ
ในคู่มือนี้ เราจะใช้ 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"
โครงสร้างรหัส
เริ่มต้นด้วยการดูรหัสทรีของโครงการ ดูเหมือนว่านี้:
- ไดเร็กทอรี
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 เกี่ยวข้องกับการรับข้อมูลจากที่จัดเก็บเท่านั้น หลักการและเทคนิคที่อธิบายไว้ในคู่มือนี้เรียกว่ารูปแบบที่เก็บ ซึ่งคุณสามารถดูได้จากภาพด้านล่าง
ฉันต้องการปิดท้ายด้วยการขอให้ใครก็ตามที่พบว่าสิ่งนี้มีประโยชน์ในการมีส่วนร่วมในชุดบริการ SOA โดยขยายและปรับปรุงในทางใดทางหนึ่ง ยินดีต้อนรับทุกส้อมและคำขอดึงของคุณ
ฉันหวังว่านี่จะช่วยให้คุณเริ่มต้นใช้งานไมโครเซอร์วิสได้ หากคุณต้องการตรวจสอบรหัสบริการ GitHub มีเวอร์ชันสมบูรณ์