protonlabs / predis
Requires
- php: >=5.3.9
Requires (Dev)
- phpunit/phpunit: ~4.8
Suggests
- ext-curl: Allows access to Webdis when paired with phpiredis
- ext-phpiredis: Allows faster serialization and deserialization of the Redis protocol
This package is auto-updated.
Last update: 2023-01-24 11:52:36 UTC
README
适用于 PHP >= 5.4 的灵活且功能齐全的 Redis 客户端。
Predis 默认不需要任何额外的 C 扩展,但可以选择与 phpiredis 配合使用,以降低 Redis RESP 协议的序列化和解析开销。
更多关于此项目的详细信息,可以在 常见问题解答 中找到。
主要功能
- 支持使用配置文件支持不同版本的 Redis(从 2.0 到 3.2)。
- 支持使用客户端分片和可插拔键空间分配器进行集群。
- 支持 redis-cluster(Redis >= 3.0)。
- 支持主从复制设置和 redis-sentinel。
- 使用可自定义的前缀策略透明地键前缀。
- 在单个节点和集群上执行命令管道(仅客户端分片)。
- 抽象 Redis 事务(Redis >= 2.0)和 CAS 操作(Redis >= 2.2)。
- 抽象 Lua 脚本(Redis >= 2.6)并自动在
EVALSHA
或EVAL
之间切换。 - 基于 PHP 迭代器的抽象
SCAN
、SSCAN
、ZSCAN
和HSCAN
(Redis >= 2.8)。 - 客户端在首次执行命令时建立连接,并且可以持久化。
- 可以通过 TCP/IP(也支持 TLS/SSL 加密)或 UNIX 域套接字建立连接。
- 支持 Webdis(需要
ext-curl
和ext-phpiredis
)。 - 支持自定义连接类,以提供不同的网络或协议后端。
- 定义自定义命令和配置文件的灵活系统,并覆盖默认值。
如何安装和使用 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()
它来开始使用库。
连接到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时,用户认证需要username
和password
。
还可以使用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');
还支持连接方案redis
(tcp
的别名)和rediss
(tls
的别名),区别在于包含这些方案的URI字符串的解析遵循其各自的IANA临时注册文档中描述的规则。
实际支持的连接参数列表可能因每个连接后端而异,因此建议查阅其具体文档或实现以获取详细信息。
当提供连接参数数组时,Predis将自动在集群模式下使用客户端分片工作。提供每个节点的配置时,可以混合使用命名数组和URI字符串。
$client = new Predis\Client([ 'tcp://10.0.0.1?alias=first-node', ['host' => '10.0.0.2', 'alias' => 'second-node'], ]);
有关更多详细信息,请参阅本文件的聚合连接部分。
Redis连接是延迟的,这意味着客户端仅在需要时才连接到服务器。虽然建议让客户端自行处理底层操作,但在某些情况下,可能仍然希望控制何时打开或关闭连接:这可以通过调用$client->connect()
和$client->disconnect()
轻松实现。请注意,这些方法对聚合连接的影响可能因具体实现而异。
客户端配置
可以通过将特定客户端选项传递给Predis\Client::__construct()
的第二个参数来配置客户端的许多方面和行为。
$client = new Predis\Client($parameters, ['profile' => '2.8', 'prefix' => 'sample:']);
选项通过一个类似DI的容器进行管理,其值仅在需要时才会懒加载。Predis默认支持的客户端选项包括:
profile
:指定用于匹配Redis特定版本的配置文件。prefix
:自动应用于命令中找到的键的前缀字符串。exceptions
:客户端在Redis错误时应抛出或返回响应。connections
:连接后端列表或连接工厂实例。cluster
:指定集群后端(predis
、redis
或可调用对象)。replication
:指定一个复制后端(TRUE
、sentinel
或可调用对象)。aggregate
:覆盖cluster
和replication
以提供自定义连接聚合器。parameters
:聚合连接的默认连接参数列表。
用户还可以提供带有值或可调用对象(用于延迟初始化)的自定义选项,这些选项存储在选项容器中,以便稍后通过库使用。
聚合连接
聚合连接是Predis实现集群和复制的基石,它们用于将多个连接分组到单个Redis节点,并隐藏根据上下文正确处理它们的特定逻辑。聚合连接通常在创建新的客户端实例时需要一组连接参数。
集群
默认情况下,当未设置特定的客户端选项,并将连接参数数组传递给客户端构造函数时,Predis会配置自身以集群模式运行,使用传统的客户端端分片方法来创建一个独立的节点集群,并将键空间分布在这些节点之间。这种方法需要节点的外部健康监控,并在通过添加或删除节点更改其配置时需要手动操作以重新平衡键空间。
$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3']; $client = new Predis\Client($parameters);
随着Redis 3.0的推出,一种新型的监督和协调类型的集群以redis-cluster的形式引入。这种方法使用不同的算法来分配键空间,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识别只读命令并将它们发送到随机的从节点,以提供某种形式的负载均衡。一旦检测到执行任何可能修改键空间或键值操作的命令,就切换到主节点。当从节点失败时,客户端不是引发连接错误,而是尝试回退到配置中提供的不同从节点。
在复制模式下使用客户端所需的基本配置需要将一个Redis服务器标识为主节点(这可以通过使用alias
参数设置为master
的连接参数来完成)以及一个或多个作为从节点运行的服务器。
$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3']; $options = ['replication' => true]; $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能够区分执行写操作和只读操作的命令,但EVAL
和EVALSHA
代表了一个特殊情况,即客户端因为无法判断Lua脚本在从节点上执行是否安全而切换到主节点。尽管这确实是默认行为,但当某些Lua脚本不执行写操作时,可以提供提示告诉客户端在从节点上执行这些脚本。
$parameters = ['tcp://10.0.0.1?alias=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\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
目录中包含一些脚本,展示了客户端如何配置和使用这些脚本在基本和复杂场景中利用复制。
命令管道
管道可以帮助提高性能,当需要向服务器发送多个命令时,通过减少网络往返延迟来提高性能。管道也与聚合连接一起工作。客户端可以在可调用的块中执行管道,或者返回一个带有链式命令能力的管道实例,这是由于其流畅的接口。
// 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();
事务
客户端提供了一个基于MULTI
和EXEC
的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();
此抽象可以通过WATCH
和UNWATCH
执行检查和设置操作,并在Redis因WATCH
键被触及而中止事务时提供自动重试。有关使用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']);
脚本命令
虽然可以在Redis 2.6+上使用Lua脚本(直接使用EVAL
和EVALSHA
),但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 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 3.2(Redis的当前稳定版本)的配置文件执行集成测试,但可以通过修改phpunit.xml
并将REDIS_SERVER_VERSION
设置为dev
来选择性地针对从unstable
分支构建的Redis实例。有关测试Predis的更详细信息,请参阅测试README。
Predis使用Travis CI进行持续集成,过去和当前的构建历史可以在其项目页面上找到。
其他
项目相关链接
作者
许可证
Predis的代码在MIT许可证下分发(见LICENSE)。