webman可以动态的链接数据库吗

巴乔

问题描述

webman可以动态的链接数据库吗,我们的业务是每个客户都是一个单独的数据库,我们要批量对每个企业做个数据库业务处理,每个企业都需要单独链接到自己的数据库上。这个webman有什么好的解决方案吗。能不能就是动态的来链接数据库,进行操作处理。

为此你搜索到了哪些方案及不适用的原因

我看到webman的数据库是读取数据库配置文件来链接不同的数据同。我们有好几千的企业,不能都写到配置文件里面。所以有什么好的解决方案没有。配置文件那块也不支持动态的设置。

907 8 5
8个回答

nitron

都几千家了,上个数据库代理吧

  • 巴乔 2023-11-28

    现在数据库维护也是个问题。每次批量更新,都是用的脚本更新。

six

手动连接,看对应orm文档就行了

hon陈烁临

我的动态实现方案你可以参考一下:
1. 设置一个base库 ,database.php配置中只配置 base库的链接,企业客户的数据库连接信息配置到base库的租户表中

2. 根据base库中的配置 动态生成 webman用到的 database配置

3.根据租户域自动切换租户链接

4.所有业务数据库操作使用model进行操作,model中使用动态链接即可

  • 巴乔 2023-11-28

    好的,我试下,我原先用的tp6或swoole,那块和差不多。我们是动态的改变config,然后来达到不同的企业用不同的数据库,和你这个逻辑也很像

  • hon陈烁临 2023-11-28

    我试了其他方式动态,都不理想。因为框架启动的时候依赖 db config ,要动态生成所以开始直接用PDO进行连接

  • 巴乔 2023-11-28

    是的,都是依赖db config,你使用webman自带的orm还是think-orm, webman只能model上进行操作数据库,不能用tp的数据库链接操作,

  • 巴乔 2023-11-28

    你们的项目都是自己用原生链接进行操作数据库吗。用了orm了吗

  • walkor 2023-11-28

    “ webman只能model上进行操作数据库,不能用tp的数据库链接操作“
    这是哪来的说法,用的本来就是tp的orm,怎么可能用法不一样

  • hon陈烁临 2023-11-29

    我这里使用的是自带的orm(Eloquent ORM)和think-orm差不多,没有webman只能model上操作数据库的说法,Db 类一样的可以操作数据库,我这里业务数据库操作统一用model,是因为动态租户数据库我只支持到model这一层和个人习惯

  • doit 2023-12-05

    这样有一个问题吧,就是启动后新增的数据库还是没法使用,必须重启项目

  • hon陈烁临 2023-12-06

    确实是存在启动后新增的数据库无法使用问题,使用Illuminate\Database\Capsule\Manager::addConnection 应该可以动态添加

  • doit 2023-12-07

    如果是这样的话,直接在thinkorm中添加配置循环,每次更改配置文件即可,在model里添加trait设置connection没多大区别了,反而会更简单一些吧?

  • 彭彭 2023-12-07

    确实这个方案是在webman启动时,把存在租户表中的配置信息读出来,作为数据库配置文件加载,然后自动启动里的support\bootstrap\LaravelDb::class会addConnection进去,如果动态添加怎么做呢,请教一下!

  • hon陈烁临 2023-12-08

    参照 support\bootstrap\LaravelDb::class 里面的方法 自行 addConnection

  • 彭彭 2023-12-08

    那addConnection时要不要$capsule->setEventDispatcher,$capsule->setAsGlobal()和$capsule->bootEloquent()重新执行一次呢,要不要像LaravelDb类里把 Paginator相关的加上?

  • hon陈烁临 2023-12-08

    是都要执行一次的,但是我这边应用下来 动态addConnection 上线后事件有问题,目前还没有搞清楚是什么原因导致事件失效

  • hon陈烁临 2023-12-08

    @彭彭 现在我确认了上面说的动态方式不可行,会导致mysql链接数超限

  • 彭彭 2023-12-08

    动态添加时也参照support\bootstrap\LaravelDb::class一样,加一个定时任务的心跳呢,这样会复用这个mysql连接吧

  • hon陈烁临 2023-12-08

    这个我不确定

  • 彭彭 2023-12-08

    webman在启动时,support\bootstrap\LaravelDb::class已经setEventDispatcher,setAsGlobal(),bootEloquent()添加Paginator相关了,动态addConnection时,你刚回复都需要执行一次,我觉得前面已经setEventDispatcher,setAsGlobal(),bootEloquent()和添加Paginator相关,这些做过的动作不需要再做了吧,只在addConnection后,再给这个新的connection加一个定时任务的心跳即可。

  • hon陈烁临 2023-12-08

    可以实验一下看看 我现在没有找到有效的本地测试方式,没法在本地复现上面提到的问题

caylof

几千家的企业,每家一个数据库,岂不是有几千个数据库。。。

Tinywan

使用中间件结合模型连接器即可解决,线上稳定运行一年

  • 彭彭 2023-12-07

    大神有没有源码分享学习一下,谢谢!

  • Tinywan 2023-12-08

    github webman-admin

  • 彭彭 2023-12-11

    谢谢,是不是https://github.com/Tinywan/webman-admin这个,目前里面的sql好像只有两个表的,我想了解的是webman已经启动后,比如又来了一个新的租户,这个新的租户对应一个新的数据库链接,就会有启动后新加一租户数据库无法连接的问题。

darcy

可以采用动态加载config配置文件就可以了,例如:

namespace plugin\SHGminiApi\app\model;

use DateTimeInterface;
use support\Model;

class Base extends Model
{
/* 数据库配置文件 /
protected $connection = '';

/**
 * 初始化架构造函数
 */
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数库config配置文件

  • 暂无评论
awen

我有同样的需求,自己写的下面的方法实现了,动态数据库链接

<?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';
        //从请求头部获取当前租户标识,默认0
        $flag = \request()->$method(config('plugin.yesgooo.dynamic-database-manager.conf.flag'), 0);

        //当前公司数据库名称,如未传公司ID取默认配置数据库名
        $database = $flag ? config('plugin.yesgooo.dynamic-database-manager.conf.database_prefix').$flag : config('plugin.yesgooo.dynamic-database-manager.conf.default_database');

        //当前的数据库连接名称
        $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();

        //通过容器获取全部数据库连接
        $allConnect = $this->capsule->getContainer()['config']['database.connections'];

        //如果当前连接还没有,则新增连接
        if(!in_array($curConnect, array_keys($allConnect))){
            $this->capsule->addConnection($config, $curConnect); // TODO: Change the autogenerated stub
        }

        //如果默认连接不是当前连接,则设置成默认连接
        if($defaultConnect != $curConnect){
            //设置默认连接
            $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();

    }

}

然后实现个中间件,最后请求都走中间件先生成数据库连接

<?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
    {
        //执行动态数据库管理
        new DynamicDataBaseManager();
        return $handler($request);
    }

}

这样基本实现了需求,但最近发现个问题mysql连接好像不会释放,会超限,问题还没找到

  • 彭彭 2023-12-12

    webman启动时support\bootstrap\LaravelDb::class会有一个启动配置文件database.php的myql,每次在中间件里add时,先前默认链接没有关闭就被重置了,可以试试Db::connection('xxx')->disconnect();关闭之前的mysql链接,再重置当前默认的。我的理解是这样,不知道对不对。

  • awen 2023-12-12

    这种方法,msyql连接超限,你遇到了吗?你现在是如何实现多租户多数据库的

z985342160

数据库配置连接

$connections = [
    'tenant_1' => [
    ],
    'tenant_2' => [
    ]
];

开启多进程后,每个进程都有可能维护每个租户的数据库连接。理论上,数据库连接数 = 进程数 * 租户数量;

这样是不是就造成数据库连接浪费呢,可能的解决办法:

  1. 每次请求都重新连接,响应前,手动关闭数据库连接。
  2. 因为有心跳检测,所有租户的数据库连接都将一直处于活动状态,那么可以再加一个租户活动的心跳机制。比如记录每个租户的最后请求时间,然后定时检测,比如10分钟都没有请求,就关闭连接。

这种方案是否可行?

  • 暂无评论
🔝