webman可以動態(tài)的鏈接數(shù)據(jù)庫嗎,我們的業(yè)務(wù)是每個(gè)客戶都是一個(gè)單獨(dú)的數(shù)據(jù)庫,我們要批量對每個(gè)企業(yè)做個(gè)數(shù)據(jù)庫業(yè)務(wù)處理,每個(gè)企業(yè)都需要單獨(dú)鏈接到自己的數(shù)據(jù)庫上。這個(gè)webman有什么好的解決方案嗎。能不能就是動態(tài)的來鏈接數(shù)據(jù)庫,進(jìn)行操作處理。
我看到webman的數(shù)據(jù)庫是讀取數(shù)據(jù)庫配置文件來鏈接不同的數(shù)據(jù)同。我們有好幾千的企業(yè),不能都寫到配置文件里面。所以有什么好的解決方案沒有。配置文件那塊也不支持動態(tài)的設(shè)置。
我的動態(tài)實(shí)現(xiàn)方案你可以參考一下:
1. 設(shè)置一個(gè)base庫 ,database.php配置中只配置 base庫的鏈接,企業(yè)客戶的數(shù)據(jù)庫連接信息配置到base庫的租戶表中。
2. 根據(jù)base庫中的配置 動態(tài)生成 webman用到的 database配置
3.根據(jù)租戶域自動切換租戶鏈接
4.所有業(yè)務(wù)數(shù)據(jù)庫操作使用model進(jìn)行操作,model中使用動態(tài)鏈接即可
好的,我試下,我原先用的tp6或swoole,那塊和差不多。我們是動態(tài)的改變config,然后來達(dá)到不同的企業(yè)用不同的數(shù)據(jù)庫,和你這個(gè)邏輯也很像
我試了其他方式動態(tài),都不理想。因?yàn)榭蚣軉拥臅r(shí)候依賴 db config ,要動態(tài)生成所以開始直接用PDO進(jìn)行連接
是的,都是依賴db config,你使用webman自帶的orm還是think-orm, webman只能model上進(jìn)行操作數(shù)據(jù)庫,不能用tp的數(shù)據(jù)庫鏈接操作,
“ webman只能model上進(jìn)行操作數(shù)據(jù)庫,不能用tp的數(shù)據(jù)庫鏈接操作“
這是哪來的說法,用的本來就是tp的orm,怎么可能用法不一樣
我這里使用的是自帶的orm(Eloquent ORM)和think-orm差不多,沒有webman只能model上操作數(shù)據(jù)庫的說法,Db 類一樣的可以操作數(shù)據(jù)庫,我這里業(yè)務(wù)數(shù)據(jù)庫操作統(tǒng)一用model,是因?yàn)閯討B(tài)租戶數(shù)據(jù)庫我只支持到model這一層和個(gè)人習(xí)慣
確實(shí)是存在啟動后新增的數(shù)據(jù)庫無法使用問題,使用Illuminate\Database\Capsule\Manager::addConnection 應(yīng)該可以動態(tài)添加
如果是這樣的話,直接在thinkorm中添加配置循環(huán),每次更改配置文件即可,在model里添加trait設(shè)置connection沒多大區(qū)別了,反而會更簡單一些吧?
確實(shí)這個(gè)方案是在webman啟動時(shí),把存在租戶表中的配置信息讀出來,作為數(shù)據(jù)庫配置文件加載,然后自動啟動里的support\bootstrap\LaravelDb::class會addConnection進(jìn)去,如果動態(tài)添加怎么做呢,請教一下!
那addConnection時(shí)要不要$capsule->setEventDispatcher,$capsule->setAsGlobal()和$capsule->bootEloquent()重新執(zhí)行一次呢,要不要像LaravelDb類里把 Paginator相關(guān)的加上?
是都要執(zhí)行一次的,但是我這邊應(yīng)用下來 動態(tài)addConnection 上線后事件有問題,目前還沒有搞清楚是什么原因?qū)е率录?/p>
動態(tài)添加時(shí)也參照support\bootstrap\LaravelDb::class一樣,加一個(gè)定時(shí)任務(wù)的心跳呢,這樣會復(fù)用這個(gè)mysql連接吧
webman在啟動時(shí),support\bootstrap\LaravelDb::class已經(jīng)setEventDispatcher,setAsGlobal(),bootEloquent()添加Paginator相關(guān)了,動態(tài)addConnection時(shí),你剛回復(fù)都需要執(zhí)行一次,我覺得前面已經(jīng)setEventDispatcher,setAsGlobal(),bootEloquent()和添加Paginator相關(guān),這些做過的動作不需要再做了吧,只在addConnection后,再給這個(gè)新的connection加一個(gè)定時(shí)任務(wù)的心跳即可。
使用中間件結(jié)合模型連接器即可解決,線上穩(wěn)定運(yùn)行一年
可以采用動態(tài)加載config配置文件就可以了,例如:
namespace plugin\SHGminiApi\app\model;
use DateTimeInterface;
use support\Model;
class Base extends Model
{
/* 數(shù)據(jù)庫配置文件 /
protected $connection = '';
/**
* 初始化架構(gòu)造函數(shù)
*/
public function __construct()
{
$config = request()->config;
$sandbox = $config['sandbox'] ?? false;
if ($sandbox) {
$this->connection = 'plugin.SHGminiApi.sandbox';
} else {
$this->connection = 'plugin.SHGminiApi.mysql';
}
}
/**
* 格式化日期
* @param DateTimeInterface $date
* @return string
*/
protected function serializeDate(DateTimeInterface $date)
{
return $date->format('Y-m-d H:i:s');
}
}
注意,我在中間件里面加了請求是否為sandbox模式,如果是sandbox模式,加載sandbox數(shù)庫config配置文件
我有同樣的需求,自己寫的下面的方法實(shí)現(xiàn)了,動態(tài)數(shù)據(jù)庫鏈接
<?php
namespace Yesgooo\DynamicDatabaseManager\DataBaseManager;
use Illuminate\Container\Container as IlluminateContainer;
use Illuminate\Database\Capsule\Manager as Capsule;
use Illuminate\Events\Dispatcher;
class DynamicDataBaseManager
{
protected $capsule;
public function __construct($config = [])
{
$method = config('plugin.yesgooo.dynamic-database-manager.conf.method') ?? 'header';
//從請求頭部獲取當(dāng)前租戶標(biāo)識,默認(rèn)0
$flag = \request()->$method(config('plugin.yesgooo.dynamic-database-manager.conf.flag'), 0);
//當(dāng)前公司數(shù)據(jù)庫名稱,如未傳公司ID取默認(rèn)配置數(shù)據(jù)庫名
$database = $flag ? config('plugin.yesgooo.dynamic-database-manager.conf.database_prefix').$flag : config('plugin.yesgooo.dynamic-database-manager.conf.default_database');
//當(dāng)前的數(shù)據(jù)庫連接名稱
$curConnect = $flag ? config('plugin.yesgooo.dynamic-database-manager.conf.conn_prefix') . $flag : 'default';
$config = $config ?: [
'driver' => 'mysql',
'host' => config('plugin.yesgooo.dynamic-database-manager.conf.host'),
'port' => config('plugin.yesgooo.dynamic-database-manager.conf.port'),
'database' => $database,
'username' => config('plugin.yesgooo.dynamic-database-manager.conf.username'),
'password' => config('plugin.yesgooo.dynamic-database-manager.conf.password'),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
];
$this->capsule = new Capsule(IlluminateContainer::getInstance());
$defaultConnect = $this->capsule->getDatabaseManager()->getDefaultConnection();
//通過容器獲取全部數(shù)據(jù)庫連接
$allConnect = $this->capsule->getContainer()['config']['database.connections'];
//如果當(dāng)前連接還沒有,則新增連接
if(!in_array($curConnect, array_keys($allConnect))){
$this->capsule->addConnection($config, $curConnect); // TODO: Change the autogenerated stub
}
//如果默認(rèn)連接不是當(dāng)前連接,則設(shè)置成默認(rèn)連接
if($defaultConnect != $curConnect){
//設(shè)置默認(rèn)連接
$this->capsule->getDatabaseManager()->setDefaultConnection($curConnect);
}
if (class_exists(Dispatcher::class) && !$this->capsule->getEventDispatcher()) {
$this->capsule->setEventDispatcher(\support\Container::make(Dispatcher::class, [IlluminateContainer::getInstance()]));
}
$this->capsule->setAsGlobal();
$this->capsule->bootEloquent();
}
}
然后實(shí)現(xiàn)個(gè)中間件,最后請求都走中間件先生成數(shù)據(jù)庫連接
<?php
namespace Yesgooo\DynamicDatabaseManager\middleware;
use Yesgooo\DynamicDatabaseManager\DataBaseManager\DynamicDataBaseManager;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
class DynamicDataBase implements MiddlewareInterface
{
public function process(Request $request, callable $handler) : Response
{
//執(zhí)行動態(tài)數(shù)據(jù)庫管理
new DynamicDataBaseManager();
return $handler($request);
}
}
這樣基本實(shí)現(xiàn)了需求,但最近發(fā)現(xiàn)個(gè)問題mysql連接好像不會釋放,會超限,問題還沒找到
webman啟動時(shí)support\bootstrap\LaravelDb::class會有一個(gè)啟動配置文件database.php的myql,每次在中間件里add時(shí),先前默認(rèn)鏈接沒有關(guān)閉就被重置了,可以試試Db::connection('xxx')->disconnect();關(guān)閉之前的mysql鏈接,再重置當(dāng)前默認(rèn)的。我的理解是這樣,不知道對不對。
數(shù)據(jù)庫配置連接
$connections = [
'tenant_1' => [
],
'tenant_2' => [
]
];
開啟多進(jìn)程后,每個(gè)進(jìn)程都有可能維護(hù)每個(gè)租戶的數(shù)據(jù)庫連接。理論上,數(shù)據(jù)庫連接數(shù) = 進(jìn)程數(shù) * 租戶數(shù)量;
這樣是不是就造成數(shù)據(jù)庫連接浪費(fèi)呢,可能的解決辦法:
這種方案是否可行?