workerman通过httpserver监听短信网关http回执接口,需手动读取php://input解析json,校验msg_id幂等处理,并异步落库确保不丢不重。

Workerman怎么监听短信网关的HTTP回执接口?
短信网关(比如亿美、创蓝、容联云)通常用HTTP POST方式主动推送回执(如发送成功、失败、送达状态),不是你去轮询。Workerman适合做长连接服务,但接收这种第三方回调,本质是「写一个轻量HTTP服务端」,而不是用其TCP/UDP能力。
Worker::runAll() 启动后,必须注册一个 HttpServer 实例来处理外部POST请求。别误用 WebsocketServer 或裸 TcpConnection —— 网关不会连WebSocket握手,也不会发二进制包。
- 回执地址要填成类似
@#@#@#@#@#@#@#@#@#@0(端口不能被防火墙拦) - Workerman默认不解析POST body,需手动调用
$_POST或读取php://input - 若网关发的是
application/json,$_POST为空,必须用file_get_contents('php://input')+json_decode()
$http_worker = new HttpServer('0.0.0.0', 2346);
$http_worker->onMessage = function ($connection, $request) {
$raw = file_get_contents('php://input');
$data = json_decode($raw, true) ?: [];
// 处理 $data['msg_id'], $data['status'], $data['phone'] 等字段
$connection->send("success");
};
为什么收到回执但$_POST始终为空?
这是最常卡住的地方:网关发的Content-Type决定PHP能否自动填充$_POST。
- 如果是
application/x-www-form-urlencoded或multipart/form-data,$_POST可用 - 如果是
application/json(现在越来越常见),$_POST一定为空,必须读原始体 - 少数网关用
text/plain发JSON字符串,同样不能依赖$_POST
检查方法很简单:在onMessage里加一行日志file_put_contents('/tmp/callback.log', $request->header['content-type'] . "\n", FILE_APPEND);
- 不要试图用
parse_str()硬转JSON字符串 - 不要忽略
Content-Type大小写(有些网关写成Application/Json)
如何保证回执不丢、不重复、不乱序?
Workerman本身不提供消息队列或幂等机制,这部分得自己补:
网关可能重发(超时未收到200响应时),所以必须校验
msg_id或msgid字段是否已处理过(查数据库或Redis)不要在
onMessage里做耗时操作(如写MySQL主库、调远程API),否则连接卡住导致后续回执堆积甚至超时重发建议把解析后的数据投递到Redis List或Beanstalkd,由后台worker异步落库
用
Redis::setex()缓存已处理的msg_id,过期时间设为24小时足够onMessage中只做「校验+入队+立即返回200」,别sleep、别curl、别事务锁表不要依赖Workerman进程生命周期存状态(重启就丢),所有中间态必须落外部存储
用SSL反代后收不到回执怎么办?
很多用户用Nginx反代Workerman(比如套了HTTPS),这时容易出问题:
- Nginx默认限制body大小,网关回执若带长字段(如完整短信内容)会被截断,
php://input读出来是空或不全 - 必须在Nginx配置里加:
client_max_body_size 10M; - 若启用了
proxy_buffering on,可能缓冲响应,导致网关认为超时;建议关掉:proxy_buffering off; - Workerman监听的仍是HTTP(非HTTPS),Nginx负责解密,所以代码里不用管SSL,但
$_SERVER['HTTPS']永远是空的,别靠它判断协议
真正难的不是接收到数据,而是确认每条回执都进了数据库且只进一次——这需要你在onMessage之外建好幂等层和异步消费链路。











