Django, şirketiniz için bir uygulama geliştirmek amacıyla seçebileceğiniz popüler bir çerçevedir. Peki ya birden fazla istemcinin kullanacağı bir SaaS uygulaması oluşturmak istiyorsanız? Hangi mimariyi seçmelisiniz? Bu göreve nasıl yaklaşılabileceğini görelim. Tek Kiracılı Mimari En basit yaklaşım, sahip olduğunuz her müşteri için ayrı bir örnek oluşturmaktır. Diyelim ki bir Django uygulamamız ve bir veritabanımız var. Daha sonra her istemci için kendi veritabanını ve uygulama örneğini çalıştırmamız gerekir. Bu, her uygulama örneğinin yalnızca bir kiracıya sahip olduğu anlamına gelir. Bu yaklaşımın uygulanması basittir: sahip olduğunuz her hizmetin yeni bir örneğini başlatmanız yeterlidir. Ancak aynı zamanda bir soruna da neden olabilir: Her müşteri altyapı . Yalnızca birkaç müşteriye sahip olmayı planlıyorsanız veya her bir örnek küçükse bu çok da önemli olmayabilir. maliyetini önemli ölçüde artıracaktır Ancak 100.000 kuruluşa kurumsal mesajlaşma hizmeti sağlayan büyük bir şirket kurduğumuzu varsayalım. Her yeni müşteri için tüm altyapıyı kopyalamanın ne kadar pahalı olabileceğini hayal edin! Ayrıca, uygulama sürümünü güncellememiz gerektiğinde, bunu her istemci için dağıtmamız gerekir, böylece . dağıtım da yavaşlar Çok Kiracılı Mimari Uygulama için çok sayıda istemcimizin olduğu bir senaryoda yardımcı olabilecek başka bir yaklaşım daha var: çok kiracılı mimari. Bu, olarak adlandırdığımız birden fazla istemcimiz olduğu, ancak hepsinin uygulamanın yalnızca bir örneğini kullandığı anlamına gelir. kiracı Bu mimari, her istemci için özel örneklerin yüksek maliyeti sorununu çözerken, yeni bir sorunu da beraberinde getiriyor: Müşteri verilerinin diğer istemcilerden güvenli bir şekilde izole edildiğinden nasıl emin olabiliriz? Aşağıdaki yaklaşımları tartışacağız: kullanma: Her veritabanı tablosuna eklememiz gereken yabancı anahtarla verilere hangi kiracının sahip olduğunu tespit edebiliriz. Paylaşılan veritabanı ve paylaşılan veritabanı şeması kullanma: Bu şekilde, birden fazla veritabanı örneğini korumamıza gerek kalmayacak, ancak iyi düzeyde kiracı veri yalıtımı elde edeceğiz. Paylaşılan bir veritabanı ancak ayrı veritabanı şemaları kullanma: tek kiracılı örneğe benzer görünüyor ancak aynı olmayacak çünkü yine de paylaşılan bir uygulama örneği kullanacağız ve kiracıyı kontrol ederek hangi veritabanının kullanılacağını seçeceğiz. Ayrı veritabanları Bu fikirlerin derinliklerine inelim ve bunları Django uygulamasıyla nasıl entegre edeceğimizi görelim. Paylaşılan Şema ile Paylaşılan Veritabanı Bu seçenek akla ilk gelen olabilir: tablolara bir Yabancı Anahtar eklemek ve bunu her kiracı için uygun verileri seçmek amacıyla kullanmak. Ancak çok büyük bir dezavantajı var: kiracıların verileri hiçbir şekilde izole edilmiyor, bu nedenle küçük bir programlama hatası, kiracının verilerinin yanlış istemciye sızması için yeterli olabilir. veritabanı yapısına bir örnek alalım: Django belgelerinden from django.db import models class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField("date published") class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) Hangi kayıtların hangi kiracıya ait olduğunu belirlememiz gerekecek. Bu nedenle, mevcut her tabloya bir tablosu ve bir yabancı anahtar eklememiz gerekiyor: Tenant class Tenant(models.Model): name = models.CharField(max_length=200) class Question(models.Model): tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE) question_text = models.CharField(max_length=200) pub_date = models.DateTimeField("date published") class Choice(models.Model): tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE) question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) Kodu biraz basitleştirmek için oluşturduğumuz her modelde tekrar kullanılacak soyut bir temel model oluşturabiliriz. class Tenant(models.Model): name = models.CharField(max_length=200) class BaseModel(models.Model): tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE) class Meta: abstract = True class Question(BaseModel): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField("date published") class Choice(BaseModel): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) Gördüğünüz gibi burada en az iki büyük risk var: Bir veya geliştirici yeni modele kiracı alanı eklemeyi unutabilir geliştirici verileri filtrelerken bu alanı kullanmayı unutabilir. Bu örneğin kaynak kodu GitHub'da bulunabilir: . https://github.com/bp72/django-multitenancy-examples/tree/main/01_shared_database_shared_schema Ayrı Şemalarla Paylaşılan Veritabanı Paylaşılan şemanın risklerini akılda tutarak başka bir seçeneği değerlendirelim: veritabanı hâlâ paylaşılacak ancak her kiracı için özel bir şema oluşturacağız. Uygulama için popüler bir kütüphane olan ( ) bakabiliriz. Django-tenants'a belgeler Küçük projemize ekleyelim (resmi kurulum adımları bulunabilir). django-tenants burada İlk adım aracılığıyla kütüphane kurulumudur: pip pip install django-tenants Modelleri değiştirin: modeli artık ayrı bir uygulamada olacak ve modellerinin artık kiracıyla bağlantısı olmayacak. Farklı kiracıların verileri ayrı şemalarda olacağından, artık bireysel kayıtları kiracı satırlarına bağlamamız gerekmeyecek. Tenant Question Choice dosyası Tenants/models.py from django.db import models from django_tenants.models import TenantMixin, DomainMixin class Tenant(TenantMixin): name = models.CharField(max_length=200) # default true, schema will be automatically created and synced when it is saved auto_create_schema = True class Domain(DomainMixin): # a required table for django-tenants too ... dosyası anketler/models.py from django.db import models class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField("date published") class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) Soru ve Seçimin artık Kiracı için yabancı bir anahtara sahip olmadığına dikkat edin! Değişen diğer şey ise Kiracının artık ayrı bir uygulamada olmasıdır: Bu yalnızca etki alanlarını ayırmak için değil, aynı zamanda tablosunu paylaşılan şemada saklamamız gerekeceğinden ve her kiracı için tabloları oluşturulacağından da önemlidir. şema. tenants polls Birden fazla şema ve kiracıyı desteklemek için dosyasında değişiklikler yapın: settings.py DATABASES = { 'default': { 'ENGINE': 'django_tenants.postgresql_backend', # .. } } DATABASE_ROUTERS = ( 'django_tenants.routers.TenantSyncRouter', ) MIDDLEWARE = ( 'django_tenants.middleware.main.TenantMainMiddleware', #... ) TEMPLATES = [ { #... 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.request', #... ], }, }, ] SHARED_APPS = ( 'django_tenants', # mandatory 'tenants', # you must list the app where your tenant model resides in 'django.contrib.contenttypes', # everything below here is optional 'django.contrib.auth', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.admin', ) TENANT_APPS = ( # your tenant-specific apps 'polls', ) INSTALLED_APPS = list(SHARED_APPS) + [app for app in TENANT_APPS if app not in SHARED_APPS] TENANT_MODEL = "tenants.Tenant" TENANT_DOMAIN_MODEL = "tenants.Domain" Şimdi geçişleri oluşturup uygulayalım: python manage.py makemigrations python manage.py migrate_schemas --shared Sonuç olarak public şemanın oluşturulacağını ve sadece paylaşılan tabloları içereceğini göreceğiz. şema için varsayılan bir kiracı oluşturmamız gerekecek: public python manage.py create_tenant --domain-domain=default.com --schema_name=public --name=default_tenant Sorulursa değerini olarak ayarlayın. is_primary True Daha sonra hizmetin gerçek kiracılarını oluşturmaya başlayabiliriz: python manage.py create_tenant --domain-domain=tenant1.com --schema_name=tenant1 --name=tenant_1 python manage.py create_tenant --domain-domain=tenant2.com --schema_name=tenant2 --name=tenant_2 Artık veritabanında tablolarını içeren 2 şema daha olduğuna dikkat edin: polls Artık kiracılar için kurduğunuz etki alanlarındaki API'leri çağırdığınızda farklı şemalardan Sorular ve Seçenekler alacaksınız - hepsi bu kadar! Mevcut uygulamayı taşırsanız kurulum daha karmaşık ve belki daha da zor görünse de, yaklaşımın kendisi hala verilerin güvenliği gibi birçok avantaja sahiptir. Örneğin kodunu bulabilirsiniz. burada Ayrı Veritabanları Bugün tartışacağımız son yaklaşım daha da ileri giderek kiracılar için ayrı veri tabanlarına sahip olmaktır. Bu sefer birkaç veri tabanımız olacak: Kiracının veritabanlarının adlarıyla eşleşmesi gibi paylaşılan verileri saklayacağız ve her kiracı için ayrı bir veritabanı oluşturacağız. default_db Daha sonra settings.py dosyasında veritabanlarının yapılandırmasını ayarlamamız gerekecek: DATABASES = { 'default': { 'NAME': 'default_db', ... }, 'tenant_1': { 'NAME': 'tenant_1', ... }, 'tenant_2': { 'NAME': 'tenant_2', ... }, } Artık her kiracının verilerini QuerySet yöntemini çağırarak alabileceğiz: using Questions.objects.using('tenant_1')… Yöntemin dezavantajı, tüm geçişleri her bir veritabanına aşağıdakileri kullanarak uygulamanız gerekmesidir: python manage.py migrate --database=tenant_1 Ayrıca, her kiracı için yeni bir veritabanı oluşturmak, kullanımına veya paylaşılan şema yaklaşımında olduğu gibi yalnızca yabancı anahtar kullanımına kıyasla daha az uygun olabilir. django-tenants Öte yandan, kiracının verilerinin izolasyonu gerçekten iyidir: veritabanları fiziksel olarak ayrılabilir. Diğer bir avantajımız ise gerektirdiği gibi sadece Postgresql kullanmakla sınırlı kalmayacağız, ihtiyaçlarımıza uygun herhangi bir motoru seçebiliyoruz. django-tenants Çoklu veritabanı konusu hakkında daha fazla bilgiyi Django bulabilirsiniz. belgelerinde Karşılaştırmak Tek kiracılı Paylaşılan şemayla MT Ayrı şemaya sahip MT Ayrı veritabanlarına sahip MT Veri izolasyonu ✅Yüksek ❌En düşük ✅Yüksek ✅Yüksek Yanlışlıkla veri sızıntısı riski ✅Düşük ❌Yüksek ✅Düşük ✅Düşük Altyapı maliyeti ❌Her kiracıyla daha yüksek ✅Alt ✅Alt ✅❌ Tek kiracılıya göre daha düşük Dağıtım hızı ❌Her kiracıyla daha düşük ✅ ✅❌ Her şema için yürütülmesi gerektiğinden taşıma işlemleri daha yavaş olacaktır ✅❌ Her veritabanı için yürütülmesi gerektiğinden taşıma işlemleri daha yavaş olacaktır Uygulaması kolay ✅ ❌ Hizmet zaten tek kiracılı bir uygulama olarak uygulanmışsa çok sayıda değişiklik gerektirir ✅ ✅ Çözüm Yukarıdakilerin tümünü özetlemek gerekirse, Görünüşe göre sorun için sihirli bir değnek yok, her yaklaşımın artıları ve eksileri var, bu nedenle hangi ödünleşimi yapabileceklerine karar vermek geliştiricilere kalmış. Ayrı veritabanları, kiracının verileri için en iyi yalıtımı sağlar ve uygulanması kolaydır, ancak bakım için size daha yüksek bir maliyet getirir: n veritabanının güncellenmesi, veritabanı bağlantı sayıları daha yüksektir. Uygulanması gereken ayrı bir şema biti kompleksine sahip paylaşılan bir veritabanıdır ve geçişle ilgili bazı sorunlar yaşayabilir. Tek kiracı, uygulanması en basit olanıdır ancak kiracı başına hizmetinizin tam bir kopyasına sahip olduğunuz için kaynak aşırı tüketimi nedeniyle size maliyet getirir.