predis/predis

一个灵活且功能齐全的PHP Redis客户端。

资助包维护!
tillkruss

安装量: 220,531,011

依赖项: 2,526

建议者: 434

安全: 0

星标: 7,590

关注者: 214

分支: 980

开放问题: 80

v3.0.0-alpha1 2024-01-19 16:57 UTC

README

Software license Latest stable Latest development Monthly installs Build status Coverage Status

A flexible and feature-complete Redis client for PHP 7.2 and newer.

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

主要特性

  • 支持从 3.07.2 的 Redis。
  • 支持使用客户端分片和可插拔键空间分配器的集群。
  • 支持 redis-cluster (Redis >= 3.0)。
  • 支持主从复制设置和 redis-sentinel
  • 使用可自定义的前缀策略透明地键前缀键。
  • 单节点和集群(仅客户端分片)上的命令流水线。
  • Redis事务(Redis >= 2.0)和CAS操作(Redis >= 2.2)的抽象。
  • Lua脚本(Redis >= 2.6)的抽象,并自动在 EVALSHAEVAL 之间切换。
  • 基于PHP迭代器的 SCANSSCANZSCANHSCAN(Redis >= 2.8)的抽象。
  • 客户端在第一次命令时懒加载连接,并且可以持久化。
  • 可以通过TCP/IP(也支持TLS/SSL加密)或UNIX域套接字建立连接。
  • 支持自定义连接类,以提供不同的网络或协议后端。
  • 定义自定义命令的灵活系统,并覆盖默认命令。

如何安装和使用Predis

此库可以在Packagist上找到,以更轻松地管理项目的依赖项使用Composer。每个发布的压缩归档都在GitHub上提供。

composer require predis/predis

加载库

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();

连接到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');

可以通过在参数集合中添加password来访问受密码保护的Redis服务器。当Redis >= 6.0上启用了ACL时,用户认证需要usernamepassword

还可以使用UNIX域套接字连接到本地的Redis实例,在这种情况下,参数必须使用unix方案,并指定套接字文件的路径。

$client = new Predis\Client(['scheme' => 'unix', 'path' => '/path/to/redis.sock']);
$client = new Predis\Client('unix:/path/to/redis.sock');

客户端可以利用TLS/SSL加密连接到安全的远程Redis实例,无需配置像stunnel这样的SSL代理。当连接到运行在各个云托管提供商上的节点时,这可能很有用。可以通过使用tls方案以及通过ssl参数传递的适当选项来启用加密。

// Named array of connection parameters:
$client = new Predis\Client([
  'scheme' => 'tls',
  'ssl'    => ['cafile' => 'private.pem', 'verify_peer' => true],
]);

// Same set of parameters, but using an URI string:
$client = new Predis\Client('tls://127.0.0.1?ssl[cafile]=private.pem&ssl[verify_peer]=1');

支持的连接方案有redistcp的别名)和redisstls的别名),它们的区别在于包含这些方案的URI字符串的解析遵循各自在IANA临时注册文件中描述的规则。

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

Predis在提供连接参数数组以及适当的选项以指导客户端如何聚合它们(集群、复制或自定义聚合逻辑)时可以聚合多个连接。在为每个节点提供配置时,可以混合使用命名数组和URI字符串。

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

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

Redis连接是懒加载的,意味着只有在需要时客户端才会连接到服务器。虽然建议让客户端在幕后自行处理,但在某些情况下,可能仍然希望控制连接何时打开或关闭:这可以通过调用$client->connect()$client->disconnect()轻松实现。请注意,这些方法对聚合连接的影响可能因具体实现而异。

客户端配置

可以通过将特定客户端选项传递给Predis\Client::__construct()的第二个参数来配置客户端的许多方面和行为。

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

选项通过一个类似DI的小型容器来管理,并且它们的值只有在需要时才会懒加载。Predis默认支持的客户端选项有

  • prefix:应用于命令中找到的每个键的前缀字符串。
  • exceptions:客户端在Redis错误时是抛出异常还是返回响应。
  • connections:连接后端列表或连接工厂实例。
  • cluster:指定集群后端(predisredis或可调用对象)。
  • replication:指定复制后端(predissentinel或可调用对象)。
  • aggregate:使用自定义聚合连接(可调用对象)配置客户端。
  • parameters:聚合连接的默认连接参数列表。
  • commands:指定通过库使用的命令工厂实例。

用户还可以提供自定义选项,包括值或可调用对象(用于懒加载),它们存储在选项容器中供库稍后使用。

聚合连接

聚合连接是Predis实现集群和复制的基石,它们用于将多个连接分组到单个Redis节点,并根据上下文隐藏处理它们的特定逻辑。聚合连接通常在创建新客户端实例时需要数组形式的连接参数以及适当的客户端选项。

集群

Predis可以被配置为以传统的客户端分片方法工作在集群模式下,创建一个由独立节点组成的集群并将键空间分配给它们。这种方法需要某种外部节点健康监控,并在节点添加或删除时需要手动重新平衡键空间。

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

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

随着Redis 3.0的发布,引入了一种新的监督和协调的集群类型,即redis-cluster。这种方法的算法不同,用于分配key空间,Redis节点通过gossip协议进行通信来处理健康状态、重新平衡、节点发现和请求重定向。要连接到由redis-cluster管理的集群,客户端需要一个节点列表(不一定是完整的,因为如果需要,它将自动发现新的节点),并将cluster客户端选项设置为redis

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

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

复制

客户端可以配置为在单主/多从设置下运行,以提供更好的服务可用性。当使用复制时,Predis识别只读命令并将它们发送到随机从节点,以提供某种形式的负载均衡。一旦检测到执行任何可能修改key空间或键值的值的命令,它就切换到主节点。当从节点失败时,客户端尝试在配置中提供的从节点中回退到不同的从节点。

在复制模式下使用客户端所需的基本配置需要一个Redis服务器被标识为主节点(这可以通过通过设置连接参数中的role参数为master来完成)和一个或多个从节点(在这种情况下,对于从节点设置roleslave是可选的)

$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options    = ['replication' => 'predis'];

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

上述配置具有静态服务器列表,完全依赖于客户端的逻辑,但可以依赖于redis-sentinel来提供更健壮的HA环境,其中sentinel服务器充当客户端的服务发现权威来源。客户端与redis-sentinel一起工作所需的最小配置是一个指向sentinel实例的连接参数列表,replication选项设置为sentinel,以及service选项设置为服务的名称

$sentinels = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options   = ['replication' => 'sentinel', 'service' => 'mymaster'];

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

如果主从节点配置为要求客户端进行身份验证,则必须通过全局parameters客户端选项提供密码。此选项还可以用于指定不同的数据库索引。客户端选项数组将如下所示

$options = [
    'replication' => 'sentinel',
    'service' => 'mymaster',
    'parameters' => [
        'password' => $secretpassword,
        'database' => 10,
    ],
];

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

$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$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\Replication\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目录包含一些脚本,展示了如何配置和使用客户端在基本和复杂场景中利用复制。

命令管道

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

// 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 来执行检查-设置操作,并在 WATCH 的键被触摸时自动重试 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 command factory:
$client = new Predis\Client($parameters, [
    'commands' => [
        'newcmd' => 'BrandNewRedisCommand',
    ],
]);

$response = $client->newcmd();

还有一个方法可以发送原始命令,而不筛选它们的参数或解析响应。用户必须提供命令的参数列表作为数组,按照 Redis 命令文档 中定义的签名。

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

脚本命令

虽然可以直接使用 Lua 脚本(使用 EVALEVALSHA)利用 Redis 2.6+,但 Predis 提供了脚本命令作为建立在它们之上的高级抽象,使事情变得简单。脚本命令可以注册到客户端使用的命令工厂中,并像普通 Redis 命令一样访问,但它们定义了传输到服务器的 Lua 脚本以进行远程执行。内部它们默认使用 EVALSHA 并通过其 SHA1 哈希标识脚本来节省带宽,但在需要时使用 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 command factory:
$client = new Predis\Client($parameters, [
    'commands' => [
        'lpushrand' => 'ListPushRandomValue',
    ],
]);

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

可自定义的连接后端

Predis 可以使用不同的连接后端连接到 Redis。内置的 Relay 集成利用了 PHP 的 Relay 扩展来实现主要性能提升,通过在 PHP 共享运行时内存中缓存 Redis 数据集的部分副本。

$client = new Predis\Client('tcp://127.0.0.1', [
    'connections' => 'relay',
]);

开发者可以创建自己的连接类来支持全新的网络后端,扩展现有类或提供完全不同的实现。连接类必须实现 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,可以禁用集成测试。有关此库的测试的更多详细信息,请参阅 测试 README。)

Predis 使用 GitHub Actions 进行持续集成,过去和当前的构建历史可以在其操作页面找到。

许可证

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