不依赖第三方服务实现Flutter与Laravel推送通知方案咨询
无第三方依赖的Flutter+Laravel推送实现方案
完全不依赖Firebase、OneSignal等第三方推送服务的前提下,采用自托管WebSocket长连接+Postgres原生事件监听+多策略兜底的架构,能稳定实现服务端主动推送能力,完全适配Flutter+Laravel+Postgres12技术栈。
后端(Laravel + Postgres 12)实现
- 搭建自托管WebSocket服务
直接在Laravel项目中部署纯本地运行的WebSocket服务端,所有流量完全走自有服务器,无任何外部服务调用。安装完成后发布配置文件,设置服务端口、心跳间隔、鉴权规则即可,单台2核4G服务器可稳定支撑10w+并发长连接。 - 配置Postgres数据变更实时监听
利用Postgres 12原生的NOTIFY/LISTEN异步消息机制实现数据库更新的无轮询感知,不需要定时扫库,性能损耗极低。
首先创建触发器函数与业务表绑定触发器,示例代码:
然后编写Laravel自定义Artisan常驻命令,启动时建立Postgres长连接监听对应通道,收到变更事件后直接触发Laravel业务事件,核心逻辑:CREATE OR REPLACE FUNCTION notify_new_notification() RETURNS trigger AS $$ BEGIN PERFORM pg_notify('new_notification_channel', row_to_json(NEW)::text); RETURN NEW; END; $$ LANGUAGE plpgsql; -- 绑定到业务通知表,插入新数据时自动触发 CREATE TRIGGER notification_insert_trigger AFTER INSERT ON notifications FOR EACH ROW EXECUTE FUNCTION notify_new_notification();public function handle() { // 保持数据库长连接不超时 DB::connection('pgsql')->listen('new_notification_channel', function($event) { $notifyData = json_decode($event->payload, true); // 触发Laravel端的新通知事件 event(new NewNotificationEvent($notifyData)); }); } - 配置事件广播与通道鉴权
将自定义通知事件配置为通过本地WebSocket服务广播,按用户ID划分私有推送通道,通过Laravel自带的广播路由做通道鉴权,校验客户端连接携带的用户token,避免越权接收消息。 - 服务可靠性配置
将WebSocket服务、Postgres监听进程全部交给Supervisor托管,配置进程异常退出后1秒内自动重启;开启服务端心跳检测,每60秒向所有在线连接发送ping包,120秒未收到pong回复自动清理僵死连接。
前端(Flutter)实现
- 基础连接逻辑
使用官方维护的长连接类库建立WebSocket连接,用户登录成功后携带鉴权token作为连接参数,连接到自有服务器的wss加密地址(必须配置SSL证书,否则会被Android、iOS系统默认拦截)。连接建立后自动订阅对应用户的私有通知通道。 - 消息展示逻辑
收到服务端推送的消息后,首先基于服务端生成的消息唯一ID做去重,然后调用本地通知插件弹出系统级通知栏提醒,同时将消息存入本地数据库供APP内通知列表加载。 - 连接保活与重连机制
- 客户端每50秒向服务端发送一次心跳包,连续3次未收到pong回复判定为连接断开,采用指数退避策略自动重连(首次重连间隔1秒,后续逐次翻倍,最长间隔30秒,避免打满服务端带宽)
- 监听系统网络状态变化、APP前后台切换事件,网络恢复、APP切回前台时主动校验连接状态,连接断开则立即重连
- 离线消息补全
每次连接重连成功后,立即调用后端提供的「离线未读消息拉取」接口,拉取连接断开时间段内当前用户的所有未读通知,补弹本地通知,避免漏消息。iOS系统限制APP后台挂起超过30秒后会主动冻结WebSocket连接,不需要做违规的后台保活(会被App Store驳回),靠重连后的离线消息拉取完全可以覆盖后台场景的消息触达。
兜底可靠性策略
不要完全依赖长连接单通道,加两层兜底覆盖极端场景:
- APP在前台运行时,每5分钟轻量拉取一次未读消息接口,和本地已存消息做差集,存在未收到的新消息则补弹通知,该逻辑对服务端性能影响可忽略
- 服务端给每一条推送消息记录投递状态,客户端收到消息后向服务端发送ACK回执,服务端标记为已送达;未收到ACK的消息会在客户端下次拉取时优先返回
- 所有推送消息绑定全局唯一ID,客户端收到消息后先判断是否已处理,避免同一条消息重复弹出。
内容的提问来源于stack exchange,提问作者Depressedgirlcode
相关产品推荐
相关产品推荐

