webman/redis
Redis client for the webman framework, providing connection pooling and coroutine-safe access through ext-redis or Predis. Supports multiple connections, cluster and sentinel setups, lazy connections, and an expressive API for caching, queues, pub/sub and other common Redis workloads.
webman/redis bundles illuminate/redis (the Redis component extracted from Laravel) into webman, wiring it to the Worker lifecycle with an optional connection pool. If you have written Redis::set(...) in a Laravel app, this is the same API surface.
Install and publish config
composer require -W webman/redis
The -W flag matters on older webman skeletons whose pinned dependencies would otherwise block the install. The plugin installer copies config/redis.php into your project; if it does not appear, copy it manually from vendor/webman/redis/src/config/plugin/webman/redis/redis.php.
Look here first
config/redis.php – host, port, password, database, timeout, and the pool block.support\Redis – the static entry point; every static call proxies to the default connection.vendor/webman/redis/README.md – short, but it is the canonical list of supported keys.First use case: cache a hot query
<?php
namespace app\controller;
use support\Redis;
use support\Request;
class UserController
{
public function show(Request $request, $id)
{
$key = "user:{$id}";
$data = Redis::get($key);
if ($data === null) {
$data = json_encode(['id' => $id, 'name' => 'tom']); // replace with a DB call
Redis::setex($key, 300, $data);
}
return response(json_decode($data, true));
}
}
Start with php start.php start and hit the route. If you see Class "support\Redis" not found, the plugin was not installed or the composer autoloader was not regenerated (composer dump-autoload).
Facade for the default, explicit connection objects for everything else. Static calls are convenient for one-liners, but any block of more than two commands should bind the connection once:
$redis = Redis::connection(); // default from config/redis.php
$redis = Redis::connection('cache'); // named key in config/redis.php
$redis->incr('hits:today');
$redis->expire('hits:today', 86400);
Multi-connection layout. Define one entry per concern rather than mixing SELECT calls at runtime: default (business data), cache (throwaway, higher eviction risk), session, queue. Each connection can point to a different host, database, or pool size. Give cache connections a shorter timeout than business connections so a slow cache does not stall requests.
Pipelines and transactions. Both are first-class on the connection:
Redis::pipeline(function ($pipe) use ($ids) {
foreach ($ids as $id) {
$pipe->get("user:{$id}");
}
});
Redis::transaction(function ($tx) use ($key) {
$tx->incr($key);
$tx->expire($key, 60);
});
A pipeline turns N round trips into one; on a per-process worker this is the single biggest latency win for list pages.
Distributed locks. Use SET NX EX plus a Lua compare-and-delete, not SETNX + EXPIRE (non-atomic):
$token = bin2hex(random_bytes(16));
$ok = Redis::set($key, $token, 'EX', 10, 'NX');
// release
Redis::eval(
"if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end",
1, $key, $token
);
Raw client escape hatch. For anything the abstraction hides (SCAN cursors, OBJECT ENCODING, CLIENT LIST), drop to the driver:
$client = Redis::connection()->client(); // Redis (phpredis) or Predis\Client
$iterator = null;
while ($keys = $client->scan($iterator, 'user:*', 500)) { /* ... */ }
Background work. webman/redis-queue, cache stores, and cron tasks all want Redis. Register a dedicated connection per consumer so a queue backlog cannot exhaust the pool the request path depends on. For SUBSCRIBE/PSUBSCRIBE/BLPOP, write a custom Workerman process in config/process.php — never subscribe inside a controller.
Coroutine runtimes. Under Swoole/Swow the pool is real: max_connections should exceed expected concurrent coroutines per process, and wait_timeout bounds how long a coroutine queues for a free connection. Under plain Workerman (no coroutines) one connection per process handles everything, so max_connections => 1 is sufficient and pool mostly provides reconnect hygiene.
Wrap it once. A thin app\support\Cache class that owns key prefixes, TTLs, and JSON (de)serialization keeps key naming consistent and gives you one place to add metrics or fallbacks.
config/redis.php at process start. Editing it and re-running a request does nothing until php start.php reload.Redis server went away. Keep timeout low (2s), let the pool's heartbeat_interval/idle_timeout recycle sockets, and wrap hot paths in a retry-once try/catch.KEYS *, SUBSCRIBE, BLPOP, MONITOR, and huge SMEMBERS/LRANGE calls block the whole event loop for that process — every other request queued on it waits too. Use SCAN with a COUNT hint, and isolate blocking work in a dedicated process.pipeline vs transaction. Pipelines are not atomic; if you need atomicity use transaction() or a Lua script. Do not assume MULTI semantics from a pipeline.phpredis and predis disagree on booleans, nulls, and float formatting. If you might switch drivers, serialize complex values yourself (json_encode) and set the value type explicitly.database is per physical connection. With pooling, every pooled socket issues SELECT n. Mixing logical DBs on one connection at runtime will confuse you under concurrency — use named connections instead.Redis::connection() inside the request/scope.Redis::connection() is cheap (the manager caches instances), so there is no penalty for calling it per method — unlike instantiating your own Illuminate\Redis\RedisManager, which opens a fresh socket each time and will exhaust maxclients.RedisException from phpredis and Predis\Connection\ConnectionException from predis. Catch broadly around Redis calls in user-facing paths and degrade to the database rather than returning a 500.testing connection at a separate database index and flushdb in setUp; never let tests share the production index. Assert on TTLs as well as values — missing expire calls are the most common leak.config/redis.php for any new Redis instance; for custom behaviour (prefixing, tracing, serialization) subclass the connection or wrap support\Redis in a service class rather than patching vendor code, so composer update stays safe.How can I help you explore Laravel packages today?