umwerk / predis

适用于 Redis 的灵活且功能齐全的 PHP 客户端库

v1.0.3 2015-07-30 18:34 UTC

README

umwerk/predis 在从节点失败时添加了故障转移,直到 Predis 1.1.0 正式支持此功能。请自行承担风险!

Predis

Latest Stable Version Total Downloads License Build Status HHVM Status

Predis 是一个灵活且功能齐全的 Redis 客户端库,适用于 PHP >= 5.3。

默认情况下,此库不需要任何额外的 C 扩展,但它可以与 phpiredis 一起使用,以降低序列化和解析 Redis RESP 协议 的开销。Predis 的异步实现可通过 Predis\Async 获得(实验性)。

Predis 可以与 HHVM >= 2.3.0 一起使用,但无法保证不会遇到意外问题(尤其是在启用 JIT 编译器时通过 Eval.Jit = true),因为 HHVM 仍在积极开发中,并且与标准 PHP 还不完全兼容。

更多关于此项目的详细信息可以在 常见问题解答wiki 上找到。

主要功能

  • 通过配置文件支持广泛的 Redis 版本(从 2.03.0)。
  • 通过客户端分片和一致性哈希或自定义分发器进行集群。
  • 智能支持 redis-cluster(Redis >= 3.0)。
  • 支持主从复制(在主节点上执行写操作,在从节点上执行读操作)。
  • 对所有已知 Redis 命令使用可自定义的前缀策略进行透明键前缀。
  • 命令管道(适用于单节点和聚合连接)。
  • Redis 事务(Redis >= 2.0)的抽象支持 CAS 操作(Redis >= 2.2)。
  • 通过 Lua 脚本(Redis >= 2.6)的抽象,自动在 EVALSHAEVAL 之间切换。
  • 基于 PHP 迭代器的 SCANSSCANZSCANHSCAN(Redis >= 2.8)的抽象。
  • 客户端在第一次命令时惰性地建立到 Redis 的连接。
  • 支持 TCP/IP 和 UNIX 域套接字以及持久连接。
  • 支持 Webdis(需要 ext-curlext-phpiredis)。
  • 支持自定义连接类,以提供不同的网络或协议后端。
  • 灵活的系统用于定义自定义命令和服务器配置文件。

如何使用 Predis

Predis 可在 Packagist 上找到,这允许使用 Composer 进行快速 安装。或者,您可以在我们的 自己的 PEAR 通道 上找到此库,通过 PEAR 进行更传统的安装。最后,每个版本的存档都可以在 GitHub 上找到。

加载库

Predis依赖于PHP的自动加载功能,在需要时加载其文件,并遵守PSR-4标准。当依赖项通过Composer管理时,自动加载会自动处理,但对于没有自动加载功能的工程或脚本,也可以利用其自身的自动加载器。

// Prepend a base path if Predis is not available in your "include_path".
require 'Predis/Autoloader.php';

Predis\Autoloader::register();

可以通过启动bin/create-phar命令,直接从仓库创建一个phar存档。该phar包含定义其自身自动加载器的stub,因此您只需使用require()来启动使用该库。最终,通过启动bin/create-single-file,可以生成包含所有源代码的单个大型PHP文件,但此做法不推荐。

连接到Redis

在创建客户端实例时不传递任何连接参数时,Predis假定默认主机为127.0.0.1,端口为6379。默认的connect()操作超时时间为5秒。

$client = new Predis\Client();
$client->set('foo', 'bar');
$value = $client->get('foo');

连接参数可以是URI字符串或命名数组的格式。后者是提供参数的首选方式,但URI字符串在从非结构化或部分结构化的来源读取参数时可能很有用。

// Parameters passed using a named array:
$client = new Predis\Client([
    'scheme' => 'tcp',
    'host'   => '10.0.0.1',
    'port'   => 6379,
]);

// Same set of parameters, passed using an URI string:
$client = new Predis\Client('tcp://10.0.0.1:6379');

从Predis v1.0.2开始,客户端也理解URI字符串中的redis方案,如临时IANA注册中定义的那样。

实际支持的连接参数列表可能因每个连接后端而异,因此建议参考它们的特定文档或实现以获取详细信息。

当提供连接参数数组时,Predis会自动以客户端分片的方式在集群模式下运行。在提供每个节点的配置时,可以混合使用命名数组和URI字符串。

$client = new Predis\Client([
    'tcp://10.0.0.1?alias=first-node',
    ['host' => '10.0.0.2', 'alias' => 'second-node'],
]);

有关更多详细信息,请参阅此文档的聚合连接部分。

客户端配置

可以通过向Predis\Client::__construct()的第二个参数传递选项来简单地配置客户端的各个方面。

$client = new Predis\Client($parameters, ['profile' => '2.8', 'prefix' => 'sample:']);

选项通过一个类似DI的容器进行管理,而它们的值只有在需要时才懒加载。以下是默认支持的选项列表:

  • profile:指定用于匹配特定Redis版本的配置文件。
  • prefix:一个前缀字符串,它将自动应用于命令中找到的键。
  • exceptions:客户端在Redis错误时是否抛出或返回响应。
  • connections:客户端要使用的连接后端或连接工厂。
  • cluster:用于集群的后端(predisredis或自定义配置)。
  • replication:用于复制的后端(predis或自定义配置)。
  • aggregate:自定义连接聚合器(覆盖clusterreplication)。

用户可以提供自定义选项及其值,或存储在选项容器中以供以后通过库使用的懒调用初始化器。

聚合连接

Predis能够聚合多个连接,这是集群和复制的基石。默认情况下,客户端使用客户端分片(默认)或Redis支持的解决方案实现节点集群。对于复制,Predis可以通过在从属上进行读取操作并在写入操作时切换到主节点来处理单个主节点和多个从节点的设置。复制行为是完全可配置的。

复制

客户端可以通过在从节点上执行只读命令,并在检测到将执行写操作的命令时自动切换到主节点,来配置为在主从设置下运行。这是与复制一起工作所需的基本配置。

// Parameters require one master node specifically marked with `alias=master`.
$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2?alias=slave-01'];
$options    = ['replication' => true];

$client = new Predis\Client($parameters, $options);

尽管 Predis 可以区分执行写操作和只读操作的命令,但 EVALEVALSHA 表示一个特殊情况,客户端因为无法判断 Lua 脚本何时可以在从节点上安全执行而切换到主节点。虽然这是默认行为,但在某些 Lua 脚本不执行写操作的情况下,可以提供提示告诉客户端在从节点上执行。

$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2?alias=slave-01'];
$options    = ['replication' => function () {
    // Set scripts that won't trigger a switch from a slave to the master node.
    $strategy = new Predis\Replication\ReplicationStrategy();
    $strategy->setScriptReadOnly($LUA_SCRIPT);

    return new Predis\Connection\Aggregate\MasterSlaveReplication($strategy);
}];

$client = new Predis\Client($parameters, $options);
$client->eval($LUA_SCRIPT, 0);             // Sticks to slave using `eval`...
$client->evalsha(sha1($LUA_SCRIPT), 0);    // ... and `evalsha`, too.

examples 目录包含两个完整的脚本,展示了如何为 基本复杂 场景配置复制。

集群

只需将连接参数数组传递给客户端构造函数,即可配置 Predis 以集群模式使用客户端端分片工作。如果您想利用由 redis-cluster 协调的 Redis >= 3.0 节点,则客户端必须初始化如下

$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2'];
$options    = ['cluster' => 'redis'];

$client = new Predis\Client($parameters, $options);

使用 redis-cluster 时,不需要传递构成您的集群的所有节点,您只需指定一些节点,客户端将自动通过联系服务器之一,直接从 Redis 获取完整和更新的槽位图。

注意:我们目前对 redis-cluster 的支持不考虑主/从复制,但该功能将在库的未来版本中添加。

命令管道

当需要向服务器发送许多命令时,管道可以帮助提高性能,因为它可以减少网络往返时间引入的延迟。管道也适用于聚合连接。客户端可以在可调用块中执行管道,或者返回一个具有链式命令能力的管道实例,这得益于其流畅的界面。

// Executes a pipeline inside the given callable block:
$responses = $client->pipeline(function ($pipe) {
    for ($i = 0; $i < 1000; $i++) {
        $pipe->set("key:$i", str_pad($i, 4, '0', 0));
        $pipe->get("key:$i");
    }
});

// Returns a pipeline that can be chained thanks to its fluent interface:
$responses = $client->pipeline()->set('foo', 'bar')->get('foo')->execute();

事务

客户端提供了基于 MULTIEXEC 的 Redis 事务的抽象,其界面与命令管道类似。

// Executes a transaction inside the given callable block:
$responses = $client->transaction(function ($tx) {
    $tx->set('foo', 'bar');
    $tx->get('foo');
});

// Returns a transaction that can be chained thanks to its fluent interface:
$responses = $client->transaction()->set('foo', 'bar')->get('foo')->execute();

此抽象可以执行检查并设置操作,通过 WATCHUNWATCH,并在 WATCHed 键被触摸时自动重试 Redis 中终止的事务。有关使用 CAS 的事务示例,请参阅 以下示例

添加新命令

尽管我们努力将 Predis 更新到与 Redis 中所有可用的命令保持一致,但您可能更喜欢使用库的旧版本或提供不同的方式来过滤特定命令的参数或解析响应。为了实现这一点,Predis 提供了实现新命令类的功能,以定义或覆盖客户端使用的默认服务器配置中的命令。

// Define a new command by extending Predis\Command\Command:
class BrandNewRedisCommand extends Predis\Command\Command
{
    public function getId()
    {
        return 'NEWCMD';
    }
}

// Inject your command in the current profile:
$client = new Predis\Client();
$client->getProfile()->defineCommand('newcmd', 'BrandNewRedisCommand');

$response = $client->newcmd();

还有一个方法可以发送不过滤其参数或不解析响应的原始命令。用户必须将命令的参数列表作为数组提供,并按照 Redis 命令文档 中定义的签名进行。

$response = $client->executeRaw(['SET', 'foo', 'bar']);

脚本命令

虽然可以使用 Lua 脚本 在 Redis 2.6+ 上通过直接使用 EVALEVALSHA 来发挥其作用,但 Predis 通过构建在它们之上提供脚本命令作为一个更高级别的抽象,使得操作变得更加简单。脚本命令可以注册在客户端使用的服务器配置文件中,并且可以像普通 Redis 命令一样访问,但它们定义了将被发送到服务器进行远程执行的 Lua 脚本。内部默认使用 EVALSHA 来节省带宽,但需要时可以使用 EVAL 作为后备。

// Define a new script command by extending Predis\Command\ScriptCommand:
class ListPushRandomValue extends Predis\Command\ScriptCommand
{
    public function getKeysCount()
    {
        return 1;
    }

    public function getScript()
    {
        return <<<LUA
math.randomseed(ARGV[1])
local rnd = tostring(math.random())
redis.call('lpush', KEYS[1], rnd)
return rnd
LUA;
    }
}

// Inject the script command in the current profile:
$client = new Predis\Client();
$client->getProfile()->defineCommand('lpushrand', 'ListPushRandomValue');

$response = $client->lpushrand('random_values', $seed = mt_rand());

可定制的连接后端

Predis 可以使用不同的连接后端来连接 Redis。其中两个后端利用第三方扩展,例如 phpiredis,在处理大型多批量响应时可以获得显著性能提升。其中一个基于 PHP 流,另一个基于由 ext-socket 提供的套接字资源。两者都支持 TCP/IP 和 UNIX 域套接字。

$client = new Predis\Client('tcp://127.0.0.1', [
    'connections' => [
        'tcp'  => 'Predis\Connection\PhpiredisStreamConnection',  // PHP stream resources
        'unix' => 'Predis\Connection\PhpiredisSocketConnection',  // ext-socket resources
    ],
]);

开发者可以创建自己的连接类来支持全新的网络后端,扩展现有类或提供完全不同的实现。连接类必须实现 Predis\Connection\NodeConnectionInterface 或扩展 Predis\Connection\AbstractConnection

class MyConnectionClass implements Predis\Connection\NodeConnectionInterface
{
    // Implementation goes here...
}

// Use MyConnectionClass to handle connections for the `tcp` scheme:
$client = new Predis\Client('tcp://127.0.0.1', [
    'connections' => ['tcp' => 'MyConnectionClass'],
]);

有关如何创建新的连接后端的更深入的了解,可以参考位于 Predis\Connection 命名空间中的标准连接类的实际实现。

开发

报告错误和贡献代码

对 Predis 的贡献非常受赞赏,无论是以新功能、错误修复或仅仅是错误报告的形式。我们只要求你在提交更改或在问题跟踪器中提交错误报告之前遵循一个 基本的规则集,以便使每个人在处理项目时保持一致性。

测试套件

注意:切勿在运行生产环境中的 Redis 实例或包含您感兴趣的数据的实例上运行 Predis 提供的测试套件!

Predis 拥有一个全面的测试套件,涵盖了库的每个方面。这个测试套件会对正在运行的 Redis 实例进行集成测试(需要 >= 2.4.0),以验证每个命令的实现是否正确,并自动跳过在指定 Redis 配置文件中未定义的命令。如果您没有运行 Redis,可以禁用集成测试。默认情况下,测试套件配置为使用 Redis 2.8(Redis 的当前稳定版本)的配置文件来执行集成测试,但可以通过修改 phpunit.xml 并将 REDIS_SERVER_VERSION 设置为 dev 来选择性地针对从 unstable 分支构建的 Redis 实例。您可以参考 测试 README 获取有关测试 Predis 的更详细信息。

Predis 使用 Travis CI 进行持续集成,过去和当前的构建历史可以在其 项目页面 上找到。

其他

项目相关链接

作者

许可证

Predis 代码在 MIT 许可证的条款下分发(见 LICENSE)。