内容简介:原文链接:在我们我们"邀请"了来自美国的大咖Tom给我们系统讲解了如何用Laravel来构建一个SAAS多租户平台
原文链接: www.pilishen.com/posts/under…
在我们 <<Laravel底层实战兼核心源码解析>> 这个课程的第九章:<Laravel 国际前沿实践探究>
我们"邀请"了来自美国的大咖Tom给我们系统讲解了如何用Laravel来构建一个SAAS多租户平台
这期间Tom系统讲解了什么是SAAS多租户平台,为什么你要使用这种架构,在构建SAAS平台时单数据库方案和多数据库方案之间有何优劣对比,该如何基于情况去选择,当然也探讨了SAAS平台下如何去处理队列\命令行\搜索,以及外部服务等,最后还介绍了几个构建SAAS时的优秀插件.
可以说Tom的专场,已经涵盖了用laravel搞SAAS平台的方方面面,相信看过的小伙伴对SAAS架构已经胸有成竹了.那么这篇文章呢,我们只是关注SAAS平台搭建中的一个方面,当然也是最令人困惑的一个方面,就是多个数据库之间的通信与切换,我们重点关注一下这一点,通过这一点来深入了解laravel背后处理数据库连接的方式和原理.至于SAAS架构其他的方面,如果你想搞,那么还是免不了要仔细研究Tom的专场.
大部分的应用,只有一个数据库,只需要跟单个数据库进行交互.但是啊,也有相当一部分laravel应用,需要处理多个数据库之间的交互.虽然这方面有一些不错的组件,但是深入理解一下laravel里数据库连接的原理,还是非常有帮助的.
创建数据库连接
{id="createConnection"}
当你在laravel里执行一个数据查询,是 Illuminate\Database\DatabaseManager
在具体负责设置好相应的数据库连接.在配置里,不同的数据库连接有不同的名字,你可以选一个作为默认的数据库连接.这样当你没有提供具体连接数据库的名字时,就可以用默认的那个.
// 这样用的是默认的连接 DB::table('users')->all(); // 这里声明了使用"tenant" 这个数据库(连接) DB::connection('tenant')->table('users')->all(); 复制代码
这个数据库连接在一个laravel生命或请求周期里,只会创建一次,也即是一个单例模式,这样整个期间,只用这一个数据库连接就可以了,既保证效率,又避免混乱.
PDO----PHP标准数据对象
{id="pdo"}
PDO是 PHP 里跟数据库进行交互时的一个标准接口,laravel也是使用了PDO来进行各种的数据查询.当然了,你也可以再配置个数据库连接,然后用它来进行独立的PDO读写逻辑,这样就相当于一个数据库是用来查询或读取的,而另一个数据库是专门用来执行写入\删除\更新等的逻辑.当然更多的,可以进一步查看 laravel读写分离的官方文档
大部分的"多租户"应用,都会给每个"租户"或机构单独设置一个数据库,然后再有一个总的\处于中央位置的数据库,这个数据库用来存储一些租户的整体细节信息.那么这样的话,在一个单一的应用里,你就会同时有一个"系统级"的数据库连接,然后还会有一个"租户"或机构级别的数据库连接.
'tenant' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), // ... ], 'system' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), // ... ], 复制代码
这个系统级别的\总的数据库连接,总是连到那同一个数据库,所以它在config文件里的具体配置是不变的,这个连接下的查询也很简单,都可以类似这样来进行:
DB::connection('system')->table('tenants')->all(); 复制代码
但是当你要在一个租户的数据库上进行查询和连接的时候,就会有意思起来了.因为要具体连接到哪一个租户的数据库,取决于系统当前的租户是谁.因为没法提前知道这一点,所以我们也就不可能在 config/database.php
文件里具体设置好或者说"穷尽"租户的数据库连接.所以呢,租户或机构的数据库连接,就必须在运行中进行动态设置了.
config(['database.connections.tenant.database' => 'tenant1']); 复制代码
上面的这行代码,就会将 tenant
这个数据库连接的配置,具体指向到"tenant1" 这个数据库,用同样的方式,你也可以更改其他的数据库连接配置参数,比如username, password, read/write connections等等.
那么现在,当 DatabaseManager
想着创建tenant相应的连接时,就会用你刚才动态设置的配置项.但是呢,假设在这之前,这个tenant的数据库连接已经解析过一次了,也即里面具体的配置已经被laravel缓存(cache)了,那么这个时候新更改的设置就不会生效,也就不会创建一个新的数据库连接.
要解决这个问题啊,你得确保在设置新的数据库配置项之前,系统里没有其它的\已经解析过或生效了的数据库连接:
config(['database.connections.tenant.database' => 'tenant1']); DB::purge('tenant'); DB::reconnect('tenant'); 复制代码
使用purge()和reconnect()方法,可以确保在tenant这个连接通道上,接下来的任何新的数据查询,都会用上面这个最新设置的数据库信息.
当然了,这个地方我们只是关注数据库的重新连接,如果你看过我们的 <<Laravel底层实战兼核心源码解析>> 课程,看过Tom关于Laravel SAAS的专场,那么这个地方其实还可以搞一些其他必要的事情,比如设置 app.name
,设置 app.url
,同时触发一些有用的event什么的.虽然其他的细节不是我们这篇文章的关注点,但是也要提醒你不要限制想象力哦
这些代码具体要写在哪里呢?
{id="whereToPut"}
一个laravel程序,实际上有好几个"入口":
- http请求(HTTP Requests)
- 命令行(Console Commands)
- 队列任务(Queued Jobs)
我们可以创建一个 TenancyServiceProvider
,记得将其添加到 config/app.php
里,那么在其 register
方法里,我们就可以这样来动态设置当前tenant的数据库连接信息了:
public function register(){ if($this->app->runningInConsole()){ return; } if($request->getHttpHost() == 'tenant1.app.com'){ config(['database.connections.tenant.database' => 'tenant1']); DB::purge('tenant'); DB::reconnect('tenant'); } } 复制代码
检查当前http请求的host信息,然后基于此,来将数据库连接,设置成相应租户的.
至于队列job,我们可以把 tenant_id
信息存到所有job的相应payload里.这样当具体执行这个job时,就可以用之前的方式来动态修改数据库连接配置了.所以在service provider里,可以再添加这么一行:
$this->app['queue']->createPayloadUsing(function () { return Tenant::get() ? [ 'tenant_id' => Tenant::get()->id ] : []; }); 复制代码
这里的 Tenant::get()
是自己写的,用来判断或获取当前的tenant是哪个.这样的话,每一个job的payload里都会包含一个tenant_id的信息,这时我们就可以监听 JobProcessing
这个事件,然后来相应地配置数据库连接:
$this->app['events']->listen(\Illuminate\Queue\Events\JobProcessing::class, function($event){ if (isset($event->job->payload()['tenant_id'])) { Tenant::set($event->job->payload()['tenant_id']); } }); 复制代码
至于命令行里,你得声明当前的tenant是谁,比如通过参数的形式.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 【1】JavaScript 基础深入——数据类型深入理解与总结
- 深入理解java虚拟机(1) -- 理解HotSpot内存区域
- 深入理解 HTTPS
- 深入理解 HTTPS
- 深入理解 SecurityConfigurer
- 深入理解 HTTP 协议
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
正则表达式在线测试
正则表达式在线测试
HEX CMYK 转换工具
HEX CMYK 互转工具