Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

การสร้างแอพ Ruby on Rails แบบหลายผู้เช่าด้วยโดเมนย่อย

ตามคำจำกัดความของ multitenancy เมื่อแอพให้บริการผู้เช่าหลายราย หมายความว่ามีผู้ใช้สองสามกลุ่มที่แชร์การเข้าถึงอินสแตนซ์ซอฟต์แวร์ร่วมกัน ตัวอย่างที่ดีของแอปที่สนับสนุนการเช่าหลายพื้นที่คือแพลตฟอร์ม Jira ซึ่งแต่ละบริษัทมีโดเมนย่อยในการเข้าถึงซอฟต์แวร์ เช่น mycompany.atlassian.net .

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

หลังจากอ่านบทความนี้แล้ว คุณจะสามารถ:

  • อภิปรายเกี่ยวกับแอปหลายผู้เช่าประเภทต่างๆ และบอกว่าแนวทางที่กำหนดคือตัวเลือกที่เหมาะสมเมื่อใด
  • สร้างแอปพลิเคชัน Ruby on Rails พร้อมรองรับผู้เช่าหลายรายและ
  • ใช้โดเมนย่อยที่กำหนดเองสำหรับลูกค้าของคุณภายในแอปพลิเคชัน Ruby on Rails

จำเป็นต้องมีความรู้พื้นฐานเกี่ยวกับ Rails เพื่อติดตามบทความนี้และรับประโยชน์สูงสุด

พหุเทนแนนซี่ในทฤษฎี

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

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

ระดับฐานข้อมูล-แถว

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

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

ระดับสคีมา

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

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

ระดับฐานข้อมูล

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

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

การสร้าง Application Skeleton

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

เวอร์ชันล่าสุดของ Ruby คือ 2.7.2 และ 6.1 RC1 ของ Ruby on Rails gem เมื่อเขียนบทความนี้ ตรวจสอบให้แน่ใจว่าคุณได้ติดตั้ง Ruby เวอร์ชันที่ถูกต้องในระบบของคุณและพยายามติดตั้งเวอร์ชันที่ถูกต้องของเฟรมเวิร์ก:

gem install rails -v 6.1.0.rc1

ขณะนี้ เราสามารถสร้างไฟล์โครงการด้วยคำสั่งต่อไปนี้:

rails _6.1.0.rc1_ new tenantapp -d=mysql

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

cd tenantapp/
rails s

แอปพลิเคชัน Rails แบบหลายผู้เช่าพร้อมฐานข้อมูลเดียว

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

แผนมีดังนี้:

  • เราจะสร้างโครงร่างสำหรับแอปพลิเคชัน Rails โดยใช้เฟรมเวิร์ก Ruby และ Ruby on Rails เวอร์ชันล่าสุดที่มีให้บริการ
  • เราจะนั่งร้านโมเดลบางรุ่นเพื่อให้มีข้อมูลที่เราดำเนินการได้
  • สำหรับฟังก์ชันที่เราสร้างในขั้นตอนก่อนหน้านี้ เราจะเพิ่มการรองรับ multitenancy โดยอัปเดตตารางฐานข้อมูลและโค้ดในแอปพลิเคชันของเรา
  • ขั้นตอนสุดท้ายคือการเพิ่มการสนับสนุนสำหรับโดเมนย่อยที่กำหนดเองเพื่อให้ผู้ใช้เข้าถึงผู้เช่าได้อย่างง่ายดาย
  • เราจะสำรวจแนวคิดสำหรับคุณลักษณะบางอย่างที่คุณสามารถนำไปใช้ในภายหลังได้ด้วยตัวเอง

การเตรียมข้อมูลการทดสอบ

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

ฉันจะสร้างโมเดลผู้เขียนก่อน ซึ่งจะเก็บข้อมูลของผู้เขียน:

rails g scaffold author slug:string name:string description:string
rake db:create
rake db:migrate

ขณะนี้คุณสามารถไปที่ https://localhost:3000/authors address และเพิ่มผู้เขียนสองสามคน เพื่อให้เราสามารถกำหนดให้กับบทความได้ในภายหลัง

ขั้นตอนต่อไปคือการสร้างบทความ:

rails g scaffold article title:string content:text
rake db:migrate

เพิ่มการรองรับหลายผู้เช่า

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

สร้างการโยกย้ายที่เหมาะสมในฐานข้อมูล

rails g migration AddTenantIdToArticle tenant_id:integer
rake db:migrate

การย้ายข้อมูลข้างต้นจะเพิ่มคอลัมน์ใหม่ใน Article . ของเรา แบบอย่าง. คอลัมน์นี้จะใช้ในการค้นหาใดๆ เพื่อดึงการเข้าถึงเฉพาะข้อมูลที่กำหนดให้กับผู้เช่าปัจจุบัน

มอบหมายบทความให้กับผู้เช่า

ในขั้นตอนนี้ ฉันจะอัปเดตโค้ดเพื่อให้เราสามารถกำหนดผู้เขียนที่กำหนดให้กับบทความและแสดงบทความในภายหลังสำหรับผู้เขียนที่เลือกเท่านั้น เปิด app/controllers/articles_controller.rb และเพิ่มการเปลี่ยนแปลงต่อไปนี้:

class ArticlesController < ApplicationController
 before_action :set_article, only: [:show, :edit, :update, :destroy]
 before_action :set_authors, only: [:edit, :update, :new, :create]
 
 # ...
 
 private
   # Only allow a list of trusted parameters through.
   def article_params
     params.require(:article).permit(:title, :content, :tenant_id)
   end
 
   def set_authors
     @authors = Author.all
   end
end

ในมุมมองของเรา ตอนนี้เราสามารถใช้ @authors ตัวแปรซึ่งมีคอลเลกชันของผู้เขียนที่เพิ่มในแอปของเรา ตอนนี้เราสามารถเพิ่มฟิลด์ Select ที่จะมีชื่อผู้เขียนและกำหนด tenant_id ที่เหมาะสม . เปิด app/views/articles/_form.html.erb และเพิ่มส่วนต่อไปนี้:

<div className="field">
 <%= form.label :author %>
 <%= form.select :tenant_id, options_from_collection_for_select(@authors, 'id', 'name', article.tenant_id), include_blank: true %>
</div>

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

การเพิ่มการสนับสนุนสำหรับโดเมนย่อยที่กำหนดเอง

เราสร้างผู้เขียนชื่อ John Doe และตั้งค่ากระสุนเป็น johndoe . เป้าหมายของเราคือไปที่ https://johndoe.localhost:3000/ address และดูข้อมูลที่เกี่ยวข้องกับ John Doe ผู้เช่าที่ระบุเท่านั้นในกรณีนี้

การกำหนดค่าโดเมนย่อย

เราต้องการจัดการและเยี่ยมชมบทความเมื่อมีการกำหนดผู้เช่า เราสามารถทำได้โดยอัปเดต config/routes.rb ไฟล์และห่อคำจำกัดความของทรัพยากรบทความลงในบล็อกข้อจำกัด:

Rails.application.routes.draw do
 constraints subdomain: /.*/ do
   resources :articles
 end
 
 resources :authors
end

ตามค่าเริ่มต้น Rails ตั้งค่าความยาวโดเมนระดับบนสุดเป็น 1 แต่เราต้องการใช้ localhost เพื่อตั้งค่านี้เป็น 0 เราสามารถทำได้โดยเพิ่มบรรทัดต่อไปนี้ในไฟล์ config/environments/development.rb :

config.action_dispatch.tld_length = 0

การทำให้โดเมนย่อยทำงานกับ multitenancy

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

 
class ArticlesController < ApplicationController
 before_action :set_author
 before_action :set_article, only: [:show, :edit, :update, :destroy]
 
 # GET /articles
 # GET /articles.json
 def index
   @articles = Article.where(tenant_id: @author.id)
 end
 
 # ...
 
 private
   def set_article
     @article = Article.find_by!(id: params[:id], tenant_id: @author.id)
   end
 
   # ...
 
   def set_author
     @author = Author.find_by!(slug: request.subdomain)
   end
end

ฉันทำการเปลี่ยนแปลงบางอย่างกับคอนโทรลเลอร์:

  • แทนที่จะเป็น set_authors วิธี ฉันกำหนด set_author วิธีการซึ่งกำหนดผู้เขียนร้องขอผ่านโดเมนย่อย จำเป็นต้องเรียกวิธีนี้ใน before_filter ก่อน set_article ถูกเรียก. ต้องกำหนดผู้เขียนก่อนที่เราจะพยายามตั้งบทความ
  • ฉันอัปเดต set_article วิธีค้นหาบทความที่มี id ที่กำหนดและผู้เขียนที่ได้รับมอบหมาย เราไม่ต้องการแสดงบทความที่สร้างโดย Tom ในขณะที่ผู้เช่าปัจจุบันของเราคือ John
  • ฉันอัปเดตการดำเนินการดัชนีเพื่อเลือกเฉพาะบทความที่กำหนดให้กับผู้เช่าปัจจุบันของเรา

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

อัปเดตคอนโทรลเลอร์แล้ว ดังนั้นขั้นตอนต่อไปคืออัปเดตมุมมองฟอร์ม เปิด app/views/articles/_form.html.erb ไฟล์และแทนที่ส่วนก่อนหน้าด้วยฟิลด์ที่ซ่อนอยู่อย่างง่าย:

<%= form.hidden_field :tenant_id, value: @author.id %>

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

ตอนนี้คุณสามารถทดสอบรหัสที่เราสร้างขึ้น สร้างผู้เขียนใหม่ เช่น ชื่อ John Doe และ slug johndoe . ไปที่ https://johndoe.localhost:3000/articles/new address และเพิ่มบทความใหม่ หลังจากเพิ่มบทความใหม่แล้ว คุณสามารถดูได้ในรายการที่มีอยู่ภายใต้ https://johndoe.localhost:3000/articles

ยินดีด้วย! คุณเพิ่งสร้างแอปหลายผู้เช่าโดยที่ผู้เขียนแต่ละคนมีโดเมนย่อย

การปรับปรุงเพิ่มเติม

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

คุณสามารถสร้างขอบเขตพิเศษและรวมไว้ในข้อกังวลได้:

module Tenantable
 extend ActiveSupport::Concern
 
 included do
   scope :for_author, -> (author) { where(tenant_id: author.id) }
 end
end

แล้วใช้ในทุกรุ่นที่มีข้อมูลสำหรับผู้เช่า:

class Article < ApplicationRecord
 include Tenantable
end
 
author = Author.find(1)
Article.for_author(author)

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

แอปพลิเคชั่น Rails แบบหลายผู้เช่าพร้อมหลายฐานข้อมูล

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

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

แผนมีดังนี้:

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

การเตรียมข้อมูลการทดสอบ

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

เริ่มต้นด้วยการสร้าง Author โมเดลพร้อมกับคุณสมบัตินั่งร้านที่จะช่วยให้เราดูและสร้างผู้เขียนใหม่:

rails g scaffold author slug:string name:string description:string
rake db:create
rake db:migrate

ตอนนี้คุณสามารถไปที่ https://localhost:3000/authors และดูว่า Rails ได้สร้างอะไรให้เราบ้าง

เพิ่มผู้เขียนใหม่

ฉันสร้างผู้เขียนใหม่ชื่อ John Doe และ slug johndoe . ค่ากระสุนจะถูกใช้ในภายหลังเพื่อตรวจสอบว่าผู้เขียนคนใดที่เราควรแสดงข้อมูลโดยใช้โดเมนย่อย

เนื่องจากผู้เขียนแต่ละคนจะมีฐานข้อมูลแยกต่างหาก เราจึงต้องเพิ่มฐานข้อมูลใหม่ด้วยตนเองสำหรับ John เปิด config/database.yml และเพิ่มการเปลี่ยนแปลงต่อไปนี้:

development:
 primary:
   <<: *default
   database: tenantapp_development
 primary_johndoe:
   <<: *default
   database: tenantapp_johndoe_development
   migrations_paths: db/tenants_migrations

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

rake db:create

เพิ่มบทความ

ตอนนี้เราสามารถนั่งร้าน Article . ได้แล้ว โมเดลพร้อมกับคอนโทรลเลอร์และมุมมอง:

rails g scaffold article title:string content:text --database primary_johndoe

ฉันส่ง --database พารามิเตอร์เพื่อให้ Rails รู้ว่าไม่ควรวางการโยกย้ายใน db/migrations ที่เป็นค่าเริ่มต้น ไดเร็กทอรีที่ใช้โดยฐานข้อมูลหลัก ขณะนี้เราสามารถเรียกใช้คำสั่ง migrate:

rake db:migrate

ตอนนี้ เรามีสคีมาสองแบบ:db/schema.rb และ db/primary_johndoe_schema.rb . หากคุณต้องการสร้างตารางที่แตกต่างกันสำหรับผู้เช่า คุณสามารถทำได้โดยการตั้งค่า migrations_path ที่ไม่ซ้ำกัน ค่าใน config/database.yml ไฟล์สำหรับผู้เช่าที่กำหนด ในบทความนี้ เราต้องการให้มีตารางเดียวกันสำหรับผู้เช่าทั้งหมด ดังนั้นเส้นทางสู่การย้ายข้อมูลจะเหมือนกัน

เพิ่มการรองรับหลายผู้เช่า

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

Rails 6.1 มาพร้อมกับการรองรับในตัวสำหรับการแบ่งส่วนแนวนอน ชาร์ดเป็นพาร์ติชั่นข้อมูลแนวนอนที่มีชุดย่อยของชุดข้อมูลทั้งหมด เราสามารถจัดเก็บบทความสำหรับผู้เขียนทุกคนในตารางเดียวในฐานข้อมูลเดียว แต่ด้วยการแบ่งส่วนข้อมูล เราสามารถแบ่งข้อมูลออกเป็นหลายตารางที่มีโครงสร้างเดียวกันแต่วางในฐานข้อมูลแยกกัน

มากำหนดชาร์ดของเราในคลาสพาเรนต์สำหรับทุกรุ่นกัน จัดเก็บอยู่ภายใต้ app/models/application_record.rb :

ActiveRecord::Base.connected_to(role: :reading, shard: :johndoe) do
  Article.all # get all articles created by John
end

โดยไม่ต้องใส่สายของเราลงใน connected_to บล็อก ชาร์ดเริ่มต้นจะถูกใช้ ก่อนที่เราจะก้าวไปข้างหน้า เราต้องแนะนำการเปลี่ยนแปลงอีกครั้งหนึ่ง เนื่องจากชาร์ดทั้งหมดใช้โครงสร้างข้อมูลเดียวกัน เราจึงสามารถลบ app/models/primary_johndoe_record.rb โมเดลที่สร้างขึ้นโดยอัตโนมัติเมื่อเราเป็นบทความนั่งร้าน

เรายังต้องแก้ไข app/models/article.rb โมเดลและเปลี่ยนคลาสหลักจาก PrimaryJohndoeRecord ไปที่ ApplicationRecord :

class Article < ApplicationRecord
end

เพิ่มผู้เช่าเพิ่มเติม

ขณะนี้ เรามีผู้เขียนเพียงคนเดียวในฐานข้อมูลของเรา เพื่อทดสอบการทำงานของการสลับไปมาระหว่างผู้เช่าของเรา (ฐานข้อมูล) เราต้องเพิ่มผู้เขียนอีกหนึ่งราย เปิด https://localhost:3000/authors/new address และเพิ่มผู้เขียนใหม่ ฉันเพิ่มผู้เขียนชื่อ Tim Doe และกระสุน timdoe .

เรามีบันทึกสำหรับผู้แต่งใหม่ ดังนั้นเราต้องกำหนดฐานข้อมูลใหม่:

development:
 primary:
   <<: *default
   database: tenantapp_development
 primary_johndoe:
   <<: *default
   database: tenantapp_johndoe_development
   migrations_paths: db/tenants_migrations
 primary_timdoe:
   <<: *default
   database: tenantapp_timdoe_development
   migrations_paths: db/tenants_migrations

ตอนนี้สร้างฐานข้อมูลใหม่และเรียกใช้การย้ายข้อมูล:

rake db:create
rake db:migrate

ขั้นตอนสุดท้ายคือการอัพเดท ApplicationRecord model และกำหนดชาร์ดใหม่:

class ApplicationRecord < ActiveRecord::Base
 self.abstract_class = true
 
 connects_to shards: {
   default: { writing: :primary, reading: :primary },
   johndoe: { writing: :primary_johndoe, reading: :primary_johndoe },
   timdoe: { writing: :primary_timdoe, reading: :primary_timdoe },
 }
end

ตอนนี้คุณสามารถสร้างบทความสำหรับผู้แต่งแต่ละคน:

ActiveRecord::Base.connected_to(role: :writing, shard: :johndoe) do
  Article.create!(title: 'Article from John', content: 'content')
end
 
ActiveRecord::Base.connected_to(role: :writing, shard: :timdoe) do
  Article.create!(title: 'Article from Tim', content: 'content')
end

การเพิ่มการสนับสนุนสำหรับโดเมนย่อยที่กำหนดเอง

เรายังไม่สามารถเข้าชมรายการบทความสำหรับผู้แต่งที่ระบุได้ เนื่องจากเราไม่ทราบว่าควรแสดงบทความจากจอห์นและทิมเมื่อใด เราจะแก้ปัญหานี้โดยใช้โดเมนย่อยที่กำหนดเอง เมื่อไปที่ https://johndoe.localhost:3000/articles เราควรเห็นบทความจาก John และเมื่อไปที่ https://timedoe.localhost:3000/articles บทความจาก Tim

การกำหนดค่าโดเมนย่อย

เราต้องการจัดการและเยี่ยมชมบทความเมื่อมีการกำหนดผู้เช่า เราสามารถทำได้โดยอัปเดต config/routes.rb ไฟล์และห่อคำจำกัดความของทรัพยากรบทความลงในบล็อกข้อจำกัด:

Rails.application.routes.draw do
 constraints subdomain: /.*/ do
   resources :articles
 end
 
 resources :authors
end

ตามค่าเริ่มต้น Rails ตั้งค่าความยาวโดเมนระดับบนสุดเป็น 1 แต่เราต้องการใช้ localhost เพื่อตั้งค่านี้เป็น 0 เราสามารถทำได้โดยเพิ่มบรรทัดต่อไปนี้ในไฟล์ config/environments/development.rb :

config.action_dispatch.tld_length = 0

การทำให้โดเมนย่อยทำงานกับ multitenancy

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

module Tenantable
 extend ActiveSupport::Concern
 
 private
 
 def read_with_tenant(&block)
   author = Author.find_by!(slug: request.subdomain)
 
   ActiveRecord::Base.connected_to(role: :reading, shard: author.slug.to_sym) do
     block.call
   end
 end
end

บันทึกไฟล์นี้เป็น app/controllers/concerns/tenantable.rb และรวมไว้ใน ArticlesController :

class ArticlesController < ApplicationController
 include Tenantable
 
 before_action :set_article, only: [:show, :edit, :update, :destroy]
 
 def index
   read_with_tenant do
     @articles = Article.all
   end
 end
 # ...
end

ตอนนี้คุณสามารถไปที่ https://johndoe.localhost:3000/articles หรือ https://timdoe.localhost:3000/articles และคุณจะเห็นบทความต่างๆ ปรากฏขึ้นในรายการ

หากคุณต้องการสร้างบทความใหม่โดยใช้แบบฟอร์ม คุณต้องกำหนดวิธีการใหม่ที่เรียกว่า write_with_tenant และอัปเดตข้อกังวลและวิธีการของผู้เช่าภายใน ArticlesController ตามลำดับ

การปรับปรุงเพิ่มเติม

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

ActiveRecord::Base.establish_connection(:primary_timdoe)

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

สรุป

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

เรามาสรุปสิ่งที่เราได้เรียนรู้ในบทความนี้กันอย่างรวดเร็ว:

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

ฉันหวังว่าคุณจะสนุกกับการอ่านบทความนี้และสร้างแอปพลิเคชัน Ruby on Rails แบบหลายผู้เช่า

ป.ล. หากคุณต้องการอ่านโพสต์ Ruby Magic ทันทีที่ออกจากสื่อ สมัครรับจดหมายข่าว Ruby Magic ของเราและไม่พลาดแม้แต่โพสต์เดียว!