zenstruck/redis
Composer 安装命令:
composer require zenstruck/redis
包简介
Lazy proxy for php-redis with DX helpers, utilities and a unified API.
关键字:
README 文档
README
Lazy proxy for php-redis with DX helpers, utilities and a unified API.
Zenstruck\Redis is a unified proxy for \Redis|\RedisArray|\RedisCluster. With
a few exceptions and considerations, the API is the same no matter the underlying
client. This allows you to use the same API in development, where you are likely
just using \Redis, and production, where you could be using \RedisArray or
\RedisCluster.
The proxy is lazy in that, if created via a DSN, doesn't instantiate the underlying client until a command is executed.
This library integrates well with Symfony and a recipe is available.
Installation
composer require zenstruck/redis
Redis Factory
Creating a Redis client instance is done via a DSN string. The DSN must use the following format:
redis[s]://[pass@][ip|host|socket[:port]][/db-index][?{option}={value}...]
Redis Proxy Factory
It is recommended to use the proxy whenever possible. It has the following benefits over using the real client:
- Lazy: a connection is not established until a Redis command is actually called.
- Encapsulated: for the most part, knowledge of the real client is not required. You don't need to change your usage depending on the client used. There are some exceptions to this.
- Developer Experience (DX): use the fluent sequence and transaction api.
Here are some examples creating the proxy from a DSN.
use Zenstruck\Redis; $proxy = Redis::create('redis://localhost'); // Zenstruck\Redis<\Redis> $proxy = Redis::create('redis://localhost?redis_sentinel=sentinel_service'); // Zenstruck\Redis<\Redis> (using Redis Sentinel) $proxy = Redis::create('redis:?host[host1]&host[host2]'); // Zenstruck\Redis<\RedisArray> $proxy = Redis::create('redis:?host[host1]&host[host2]&redis_cluster=1'); // Zenstruck\Redis<\RedisCluster>
You can also create a Proxy from an exising instance of \Redis|\RedisArray|\RedisCluster:
use Zenstruck\Redis; /** @var \Redis|\RedisArray|\RedisCluster $client */ $proxy = Redis::wrap($client)
Redis Real Client Factory
An instance of \Redis|\RedisArray|\RedisCluster can be created directly:
use Zenstruck\Redis; $client = Redis::createClient('redis://localhost'); // \Redis $client = Redis::createClient('redis://localhost?redis_sentinel=sentinel_service'); // \Redis (using Redis Sentinel) $client = Redis::createClient('redis:?host[host1]&host[host2]'); // \RedisArray $client = Redis::createClient('redis:?host[host1]&host[host2]&redis_cluster=1'); // \RedisCluster
Factory Options
Certain Redis options can be set via your DSN's query parameters or passed
as an array to the second parameter of Zenstruck\Redis::create/createClient().
Prefix
You can set a prefix for all keys:
use Zenstruck\Redis; $proxy = Redis::create('redis://localhost?prefix=app:'); $proxy = Redis::create('redis://localhost', ['prefix' => 'app:']); // equivalent to above
Serializer Option
By default, Redis stores all scalar/null values as strings and objects/arrays as "Array"/"Object". In order to store properly typed values and objects/arrays, you must configure a Redis serializer:
use Zenstruck\Redis; // PHP: serialize/unserialize values $proxy = Redis::create('redis://localhost?serializer=php'); $proxy = Redis::create('redis://localhost', ['serializer' => \Redis::SERIALIZER_PHP]); // equivalent to above // JSON: json_encode/json_decode values (doesn't work for objects) $proxy = Redis::create('redis://localhost?serializer=json'); $proxy = Redis::create('redis://localhost', ['serializer' => \Redis::SERIALIZER_JSON]); // equivalent to above
NOTE: There is a performance trade off when using Redis serialization. Consider creating a separate client for operations/logic that requires serialization.
Redis Proxy API
/** @var Zenstruck\Redis $proxy */ // call any \Redis|\RedisArray|\RedisCluster method $proxy->set('mykey', 'value'); $proxy->get('mykey'); // "value" // get the "real" client $proxy->realClient(); // \Redis|\RedisArray|\RedisCluster
Sequences/Pipelines and Transactions
The proxy has a fluent, auto-completable API for Redis pipelines and transactions:
/** @var Zenstruck\Redis $proxy */ // use \Redis::multi() $results = $proxy->transaction() ->set('x', '42') ->incr('x') ->get('x')->as('value') // alias the result of this command ->del('x') ->execute() // the results of the above transaction as an array (keyed by index of command or alias if set) ; $results['value']; // "43" (result of ->get()) $results[3]; // true (result of ->del()) // use \Redis::pipeline() - see note below about \RedisCluster $proxy->sequence() ->set('x', '42') ->incr('x') ->get('x')->as('value') // alias the result of this command ->del('x') ->execute() // the results of the above sequence as an array (keyed by index of command of alias if set) ; $results['value']; // "43" (result of ->get()) $results[3]; // true (result of ->del())
NOTE: When using sequence() with \RedisCluster, the commands are executed
atomically as pipelines are not supported.
NOTE: When using sequence()/transaction() with a \RedisArray instance, the
first command in the sequence/transaction must be a "key-based command"
(ie get()/set()). This is to choose the node the transaction is run on.
Countable\Iterable
Zenstruck\Redis is countable and iterable. There are some differences when
counting/iterating depending on the underlying client:
\Redis: count is always 1 and iterates over itself once\RedisArray: count is the number of hosts and iterates over each host wrapped in a proxy.\RedisCluser: count is the number of masters and iterates over each master with node parameters pre-set. This enables running node commands on each master without passing node parameters to these commands (when iterating)
/** @var Zenstruck\Redis $proxy */ $proxy->count(); // 1 if \Redis, # hosts if \RedisArray, # "masters" if \RedisCluster foreach ($proxy as $node) { $proxy->flushAll(); // this is permitted even for \RedisCluster (which typically requires a $nodeParams argument) }
NOTE: If running commands that require being run on each host/master it is recommended
to iterate and run even if using \Redis. This allows a seamless transition to
\RedisArray/\RedisCluster later.
Utilities
ExpiringSet
Zenstruck\Redis\Utility\ExpiringSet encapsulates the concept of a Redis expiring
set: a set (unordered list with no duplicates) whose members expire after a time.
Each read/write operation on the set prunes expired members.
/** @var Zenstruck\Redis $client */ $set = $client->expiringSet('my-set'); // redis key to store the set $set->add('member1', 600); // set add "member1" that expires in 10 minutes $set->add('member1', new \DateInterval::createFromDateString('5 minutes')); // can use \DateInterval for the TTL $set->add('member1', new \DateTime('+5 minutes')); // use \DateTimeInterface to set specific expiry timestamp $set->remove('member1'); // explicitly remove a member $set->all(); // array - all unexpired members $set->contains('member'); // true/false $set->clear(); // clear all items $set->prune(); // explicitly "prune" the set (remove expired members) count($set); // int - number of unexpired members foreach ($set as $member) { // iterate over unexpired members } // fluent $set ->add('member1', 600) ->add('member2', 600) ->remove('member1') ->remove('member2') ->prune() ->clear() ;
NOTE: In order to use complex types (arrays/objects) as members, your redis client must be configured with a serializer.
Below is a pseudocode example using this object for tracking active users on a website. When authenticated users login or request a page, their username is added to the set with a 5-minute idle time-to-live (TTL). A user is considered active within this time. On logout, they are removed from the set. If a user has not made a request within their last TTL, they are removed from the set.
/** @var Zenstruck\Redis $client */ $set = $client->expiringSet('active-users'); $ttl = \DateInterval::createFromDateString('5 minutes'); // LOGIN EVENT: $set->add($event->getUsername(), $ttl); // LOGOUT EVENT: $set->remove($event->getUsername()); // REQUEST EVENT: $set->add($event->getUsername(), $ttl); // ADMIN MONITORING DASHBOARD WIDGET $activeUserCount = count($set); $activeUsernames = $set->all(); // [user1, user2, ...] // ADMIN USER CRUD LISTING foreach ($users as $user) { $isActive = $set->contains($user->getUsername()); // bool // ... }
Integrations
Symfony Framework
Add a supported Redis DSN environment variable:
# .env
REDIS_DSN=redis://localhost
Configure services:
# config/packages/zenstruck_redis.yaml services: # Proxy that is autowireable Zenstruck\Redis: factory: ['Zenstruck\Redis', 'create'] arguments: ['%env(REDIS_DSN)%'] # Separate proxy's that have different prefixes redis1: class: Zenstruck\Redis factory: ['Zenstruck\Redis', 'create'] arguments: ['%env(REDIS_DSN)%', { prefix: 'prefix1:' }] redis2: class: Zenstruck\Redis factory: ['Zenstruck\Redis', 'create'] arguments: ['%env(REDIS_DSN)%', { prefix: 'prefix2:' }] # Separate proxy that uses PHP serialization serialization_redis: class: Zenstruck\Redis factory: ['Zenstruck\Redis', 'create'] arguments: ['%env(REDIS_DSN)%', { serializer: php }] # expiring set service active_users: class: Zenstruck\Redis\Utility\ExpiringSet factory: ['@Zenstruck\Redis', 'expiringSet'] arguments: - active_users # redis key # Specific clients that are autowireable Redis: class: Redis factory: ['Zenstruck\Redis', 'createClient'] arguments: ['%env(REDIS_DSN)%'] # note REDIS_DSN must be for \Redis client RedisArray: class: RedisArray factory: ['Zenstruck\Redis', 'createClient'] arguments: ['%env(REDIS_DSN)%'] # note REDIS_DSN must be for \RedisArray client RedisCluster: class: RedisCluster factory: ['Zenstruck\Redis', 'createClient'] arguments: ['%env(REDIS_DSN)%'] # note REDIS_DSN must be for \RedisCluster client
Use Zenstruck\Redis for session storage (see Symfony Docs
for more details/options):
# config/services.yaml # Assumes "Zenstruck\Redis" is available as a service and symfony/expression-language is installed services: redis_session_handler: class: Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler arguments: - "@=service('Zenstruck\\\\Redis').realClient()" # config/packages/framework.yaml framework: # ... session: handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler
Contributing
Running the test suite:
composer install
docker compose up -d # setup redis, redis-cluster, redis-sentinel
vendor/bin/phpunit -c phpunit.docker.xml
Credit
Much of the code to create php-redis clients from a DSN has been taken and modified from the Symfony Framework.
zenstruck/redis 适用场景与选型建议
zenstruck/redis 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 10.62k 次下载、GitHub Stars 达 6, 最近一次更新时间为 2022 年 03 月 18 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「redis」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 zenstruck/redis 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 zenstruck/redis 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 zenstruck/redis 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Microservice RPC through message queues.
The CodeIgniter Redis package
贝嘟分布式缓存扩展
Redis distributed locks for laravel
Redis backed library for creating background jobs and processing them later.
Symfony Queue Bundle
统计信息
- 总下载量: 10.62k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 6
- 点击次数: 6
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2022-03-18