ตามคำจำกัดความของ 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 ของเราและไม่พลาดแม้แต่โพสต์เดียว!