codeigniter 不支持原生长轮询,因其基于同步阻塞的 php-fpm 模型,无事件循环或 websocket 能力;强行实现需绕过 mvc、禁用缓冲与超时,并配置 nginx 关闭代理缓冲,但高并发下易耗尽进程,推荐改用短轮询+redis 通知方案。

CodeIgniter 本身不支持原生长轮询(Long Polling)服务端推送,它没有内置的事件循环、WebSocket 服务器或异步 HTTP 响应能力。所有请求仍走传统 PHP-FPM/CGI 同步阻塞模型,超时、内存限制、连接保持等都需手动绕过和管控。
为什么直接用 $this->output->set_output() 做长轮询会失败
常见误区是控制器里写个 while 循环等待条件、期间 flush() + ob_flush(),但实际运行中会遇到:
- PHP 的
max_execution_time默认 30 秒,未显式设置会导致连接被强制中断 - FPM 模式下
fastcgi_finish_request()不生效,ob_flush()可能被缓冲层拦截,前端收不到分块响应 - Apache/Nginx 默认启用代理缓冲(如
proxy_buffering on),会攒满 4KB 才发包,破坏“即时推送”语义 - 每个长轮询请求独占一个 PHP 进程,100 个并发 = 100 个阻塞进程,迅速耗尽 FPM worker
index.php 入口层绕过框架做裸响应
若坚持用 CI 环境跑长轮询逻辑,必须跳过整个 MVC 生命周期,直接在 index.php 顶层处理特定路径(如 /lp),避免加载 CI 核心、路由、数据库等开销:
- 判断
$_SERVER['REQUEST_URI'] === '/lp',立即exit后续 CI 流程 - 调用
set_time_limit(0)、ignore_user_abort(true)、ini_set('output_buffering', 'Off') - 禁用所有输出缓冲:
while (ob_get_level()) ob_end_clean(); - 手动设置响应头:
header('Content-Type: text/event-stream');或text/plain,并确保Cache-Control: no-cache - 用
file_get_contents('php://input')解析简单参数,不要依赖 CI 的$this->input
用 CLI + 文件/Redis 通知替代真长轮询
更可行的方案是放弃“一个请求挂住几秒”的模式,改用短请求轮询 + 外部信号通知:
- 前端每 2–5 秒发一次
/api/check-notifications,CI 控制器查 Redis 键(如notif:user_123)是否存在,存在则返回并DEL;否则返回空 - 后台任务(如 Cron 或 CLI 命令)处理完事件后,
SET对应 key 并设 TTL,例如:$redis->setex('notif:user_123', 60, json_encode([...])) - 避免数据库轮询:查表比 Redis
GET慢 10–100 倍,高并发下 DB 成瓶颈 - 注意 Redis 连接复用:CI3 无原生 Redis 类,需自己封装或用
phpredis扩展直连,别每次 new 一个连接
Nginx 配置必须显式关闭缓冲和超时
即使 PHP 层做到实时 flush,Nginx 默认配置仍会吞掉你的流式响应:
- 对应 location 块加:
proxy_buffering off;、proxy_cache off; - 调大超时:
proxy_read_timeout 300;(匹配 PHPset_time_limit) - 禁用压缩:
gzip off;,否则 gzip 流可能延迟输出 - 如果用 HTTPS,确认
ssl_buffer_size 4k;不干扰小包发送
CodeIgniter 里不是“怎么配”,而是“要不要做”。多数业务场景下,用短轮询 + Redis 通知更稳、更易监控、更能水平扩展。硬上长连接,容易在凌晨三点因为 200 个卡死的 PHP 进程把整站拖垮。











