深入理解Laravel如何管理和配置多数据库连接的

栏目: PHP · 发布时间: 5年前

内容简介:原文链接:在我们我们"邀请"了来自美国的大咖Tom给我们系统讲解了如何用Laravel来构建一个SAAS多租户平台

原文链接: www.pilishen.com/posts/under…

在我们 <<Laravel底层实战兼核心源码解析>> 这个课程的第九章:<Laravel 国际前沿实践探究>

深入理解 <a href='https://www.codercto.com/topics/5169.html'>Laravel</a> 如何管理和配置多数据库连接的
深入理解Laravel如何管理和配置多数据库连接的

我们"邀请"了来自美国的大咖Tom给我们系统讲解了如何用Laravel来构建一个SAAS多租户平台

深入理解Laravel如何管理和配置多数据库连接的
深入理解Laravel如何管理和配置多数据库连接的

这期间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程序,实际上有好几个"入口":

  1. http请求(HTTP Requests)
  2. 命令行(Console Commands)
  3. 队列任务(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是谁,比如通过参数的形式.


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法设计手册

算法设计手册

斯基恩纳 / 清华大学出版社 / 2009-9 / 69.00元

《算法设计手册(第2版)》是算法设计畅销书的最新版本,是设计实用且高效算法的最全面指导书。《算法设计手册(第2版)》揭密了算法的设计与分析,以简单易懂的写作风格,介绍了各种算法技术,着重强调了算法分析,全书包括两大部分,“技术”部分介绍了设计和分析计算机算法的各种方法,“资源”部分给出了大量的参考资源,以及算法实现的各种资源,此外,在作者的个人网址http://www.CS.sunysb.edu/......一起来看看 《算法设计手册》 这本书的介绍吧!

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具