头图

分布式服务,是将多个具有不同或相同功能的服务分散在不同的服务器上,对外提供服务。

那么在分布式服务中,使用 PHP-Casbin 作为权限控制时,不同服务器上的服务的策略要保证是同步的。这里我们主要探讨常驻内存的 PHP 服务,在常驻内存的服务中,PHP-Casbin 一般是单例模式,所有的策略都会加载到内存,性能出色。

PHP-FPM 下每次都重新初始化 Enforcer 并重新加载策略,不需要做额外的策略同步。

Casbin 中提供了 Watcher ,用于多实例间的消息策略同步。

原理

当某个实例的Enforcer中的策略发生变化时,调用 Watcher ,向消息队列(MQ)中推送消息,监听到该消息队列的Enforcer收到消息后,自动刷新当前实例中的策略。

主要是在常驻进程的框架中使用 Casbin ,例如:Swoole、WorkerMan、ReactPHP 等。

第一种是单实例、多进程,进程中的数据相互隔离的;第二种则是多实例的场景。

环境

这里采用 PHP 8.4Swoole 作为演示环境,并且借助 Redis watcher for PHP-Casbin in Swoole 作为 Watcher

使用composer安装一下依赖:

composer require casbin/casbin
composer require casbin/dbal-adapter

composer require casbin/swoole-redis-watcher

编码

初始化 SwooleServer,使用 swoole 启动一个HTTP服务常驻内存。

use Casbin\Enforcer;
use CasbinAdapter\DBAL\Adapter;
use CasbinWatcher\SwooleRedis\Watcher;
use Swoole\Http\Server;

Co::set(['hook_flags' => SWOOLE_HOOK_ALL]);

$serv = new Server('127.0.0.1', 9501);
$serv->set(['worker_num' => 4]);

在服务启动后,注入回调函数,初始化Casbin的决策器。

$serv->on('WorkerStart', function($server, $worker_id) {
    global $enforcer;

    $adapter = Adapter::newAdapter([
        'driver' => 'pdo_mysql',
        'host' => '127.0.0.1',
        'dbname' => 'test',
        'user' => 'root',
        'password' => '',
        'port' => '3306',
    ]);

    $enforcer = new Enforcer('./path/to/model.conf', $adapter);

    // 设置 Watcher
    $watcher = new Watcher([
        'host' => '127.0.0.1',
        'password' => '',
        'port' => 6379,
        'database' => 0,
    ]);
    $enforcer->setWatcher($watcher);
});

$serv->start();

可以看到,在初始化决策器后,又初始化了Watcher,并调用决策器(Enforcer)设置Watcher

Swoole Watcher 是一个通过 redis发布订阅功能实现的消息生产和消费的。在 swoole 中使用协程异步非阻塞订阅 redis 的消息,收到消息后触发回调,随后调用决策器的loadPolicy(),重新加载策略从而实现不同实例的策略更新。

最后

本文介绍了如何在分布式多实例的架构下实现 Casbin 的策略同步,随着新技术的不断涌现,如今的 PHP 已经不再是完全依赖 PHP-FPM 来运行,不只是 Swoole,还有 FrankenPHP RoadRunner ReactPHP WorkerMan 等框架为 PHP 提供了运行环境来为 PHP 加速。

PHP-Casbin 还有非常多的扩展,为各种各样的框架集成提供了便利,可以查看 GitHub 仓库:https://github.com/php-casbin


JonLee
941 声望19 粉丝

作为一个IT职业人,