codeigniter 不支持原生信号量,因其依赖单机共享内存,无法跨服务器或 php-fpm 进程协同;推荐用 redis 计数器或数据库唯一约束实现分布式限流,redisson 的 rsemaphore 可集成但需手动配置并严格管理租约与续约。

CodeIgniter 本身不提供 Semaphore(信号量)或分布式锁机制,也没有内置的 semaphore 类或 $this->semaphore 组件。直接在 CI 中调用类似 sem_acquire() 或 RSemaphore 会报错——它根本不存在。
为什么不能直接在 CodeIgniter 里用 PHP 原生 semaphore 扩展
PHP 的 sem_acquire()、sem_release() 等函数属于 sysvsem 扩展,它实现的是**单机进程间信号量**,依赖系统共享内存段,完全无法跨服务器、跨 PHP-FPM worker 进程协同。而 CodeIgniter 应用通常部署在多台 Web 服务器 + 负载均衡下,原生 semaphore 在这种场景下彻底失效。
- 即使启用
sysvsem,CI 请求每次由不同 worker 处理,彼此看不到对方创建的信号量 ID -
sem_get()返回的资源句柄只在当前请求生命周期有效,无法持久化或传递 - 没有自动清理机制:worker 异常退出时,信号量可能永久滞留,导致后续所有请求卡死
CodeIgniter 中真正可用的“信号量式”控制方案
若你实际想做的是「限制某项操作并发数」(例如:同一时间最多 3 个用户能触发价格重算),必须借助外部共享存储。推荐路径:
- 用 Redis 实现计数器 + 过期控制:通过
INCR/DECR和EXPIRE原子组合模拟信号量,配合stringRedisTemplate或Predis客户端接入 CI - 用数据库唯一约束模拟轻量信号量:建表
rate_limit,字段key(唯一索引)、created_at;加锁走INSERT IGNORE,释放走DELETE,靠主键冲突判断是否“获取到配额” - 不推荐用
LOCK TABLES模拟:它只能全表互斥,不是 N 选 M 的信号量逻辑,且已知会阻塞整个表,高并发下极易拖垮 DB
Redisson 的 RSemaphore 能否接入 CodeIgniter
可以,但需手动集成,CI 不提供任何封装支持。关键点:
- 必须通过 Composer 加入
redisson/redisson,并确保 PHP 支持ext-redis或ext-pcntl(部分功能依赖) - 初始化
RedissonClient需脱离 CI 生命周期管理,建议放在application/libraries/中单例持有,避免每次请求重建连接 - 调用
$rs->getSemaphore("price_calc")后,acquire(2)表示申请 2 个许可,release(1)归还 1 个——这些方法返回布尔值,失败需主动重试或降级 - 务必设置
leaseTime(租约时间),否则客户端崩溃后许可永不归还;不要依赖try/finally保证release,网络分区时仍可能漏掉
最容易被忽略的坑:锁粒度与业务超时不匹配
很多人把 RSemaphore 的 leaseTime 设为 30 秒,但业务逻辑平均耗时 45 秒。结果是许可提前释放,其他请求误入,数据被覆盖。真实场景中,必须满足:leaseTime > 最大预期执行时间 × 1.5,且在业务代码内定期调用 expire 续约(Redisson 提供 RSemaphore.expire 方法)。没有续约机制的信号量,在长任务中等于没锁。











