开源在线客服系统部署实战:WebSocket长连接与PHP架构解析
简介这是一套面向中小企业及开发者的开源在线客服管理系统源码包定位是帮助团队快速搭建网页端、移动端等多渠道接入的在线服务支持平台解决客户咨询分散、响应不及时、客服协作低效等常见问题。压缩包整体约7.78MB下载信息未单独列出文件总数与类型明细从资源属性看应为完整可部署的源码工程。已有467人学习下载。系统涵盖用户对话界面、客服工作台、聊天机器人、报表分析、CRM/ERP集成和多语言支持等模块开源授权允许按业务需求调整逻辑和界面开发者可进一步定制工单流程、消息通知、统计维度同时能依托活跃社区获取持续更新与安全补丁有效降低商业软件采购成本对技术团队评估自建客服系统、规划功能迭代有明确参考价值。1. 在线客服管理系统源码从 zip 到能接待访客先看清这套开源系统值不值得装第一眼看到这份「源码-开源版在线客服管理系统」zip我下意识做了两件事先翻里面有没有安装说明再看数据库脚本在不在。这两个东西缺一个再漂亮的源码都白搭。这套包两个都齐解压配置就能跑属于拿到手就能落地的项目。它解决的是最实际的问题访客进来有人接、会话记录存自己库里、不用按坐席数给 SaaS 平台年年交钱。适合手里有一台 Linux 服务器、愿意花半小时折腾配置的团队或个人对 PHP 和 MySQL 有基础认知就能上手前端几乎不用动。2. 架构与核心流程消息链路、在线状态与自动分配是怎么协同的这类系统跟普通 CMS 最大的区别是它必须同时处理好「在线状态感知」和「消息实时性」。访客进页面系统得知道他还在不在客服回一句话得在一两秒内推到访客浏览器里而不是等访客刷新页面再拉。轮询也能做但访客一多服务器扛不住所以开源版普遍走长连接方案。我拆这套包的时候常见做法是 GatewayWorker 挂 WebSocketPHP 进程常驻内存来维护长连接。先把这个骨架搞清楚后面部署和二次开发才不会抓瞎。2.1 源码里有什么目录结构与功能模块把 zip 解开后项目结构跟常见的 PHP 工程一致Web 服务根目录指向 public业务代码集中在 application 里install 目录放着数据库初始化脚本。功能按「访客端、坐席端、管理端、底层服务」四块拆各自的活分得很清楚。模块常见所在位置负责的事访客聊天窗口public/static 加一段嵌入 JS生成右下角浮窗、发消息、收消息、排队提示坐席工作台application/agent坐席登录、接单、回复、转接、结束会话后台管理application/admin建坐席账号、看统计报表、配欢迎语与分配策略长连接服务GatewayWorker 相关目录维持访客与坐席的 WebSocket 连接、在线状态心跳数据库脚本install/kefu.sql建表、写入初始账号与基础配置这个分包方式有个明显好处访客端嵌入代码是独立的一份一个静态 JS 加一个生成 iframe 的 HTML直接贴到官网任何一个页面就能用不用改对方网站后端。这也是这类源码能快速落地的关键。消息表的结构一般是会话表session与消息表message分离会话表存 visitor_id、agent_id、status、create_time消息表存会话 id、发送方类型、消息类型、内容、时间戳。拆开存的好处是统计「今天的会话量」「平均响应时长」时不用去翻海量消息记录。我见过不少新手把消息直接堆在会话表里数据量上来后后台报表慢得没法看这就是表结构没设计好。2.2 访客到坐席的完整消息链路先落库再推送一次完整的客服对话在系统里大概走六步访客打开嵌入了代码的页面JS 向服务端注册一个新会话服务端按分配策略找到当前可接待的坐席生成会话记录访客发消息消息先写入 MySQL再通过 WebSocket 推送给坐席坐席端弹新会话提醒坐席回复走同样的「落库加推送」流程访客或坐席关闭会话系统更新会话状态写入结束时间会话归档成为历史记录供后台统计和质检。注意这里是「先落库再推送」。我拆过的几套系统消息通道都是双写MySQL 负责不丢WebSocket 通道负责快。如果先推后写推送成功但数据库没存上访客刷新页面消息就没了如果只写不推客服端就一直等不到新消息。这套源码把「落库」放在「推送」前面顺序是对的。消息实时性靠的是 WebSocket 长连接而访客在线状态靠的是心跳机制。客户端每隔几十秒发一个 ping服务端回 pong超过一定时间没收到心跳就判定离线。源码里在线坐席列表、忙闲状态都是从这套心跳数据里算出来的。所以后面排错时如果坐席明明在线但系统显示离线优先查长连接进程而不是查业务代码。2.3 会话分配策略怎么选轮询、空闲优先与技能组分配策略是客服系统里最影响体验的部分。访客来了一句「在吗」如果没人接好感立刻归零。常见的分配策略有三种源码后台一般默认轮询也就是按坐席列表挨个轮流派单。策略分配逻辑适用场景轮询分配按坐席顺序轮流接单人人有份坐席能力均匀、考核工作量时空闲优先当前在线且处理中会话最少的坐席先接客服数量多、忙闲不均时技能组分配按访客来源或预设关键词分组售前售后分离、多业务线时轮询最大的问题是某个坐席手里已经积了五个会话新单子照样派给他。空闲优先看着合理但实现时依赖在线状态字段也就是长连接心跳得准。如果心跳断了系统会误判坐席离线单子全派给剩下的人那几个人很快就爆掉。技能组分配最灵活但要提前维护分组和坐席的所属关系小团队一般用不上。我建议第一次部署就用默认轮询跑通全流程等真正有人用了再根据实际忙闲调策略。3. 部署与初始化从解压 zip 到客服系统真正上线部署这类源码我的固定顺序是「解压 → 目录归位 → 建库导数据 → 改配置 → 起服务 → 验证」全程大概半小时。下面每一步按这个顺序写命令可以直接抄。开始之前先确认服务器条件建议 PHP 7.4 或 8.0MySQL 5.7 或 8.0Nginx 或 Apache 都行。PHP 需要 pdo_mysql 扩展如果用 GatewayWorker 那套长连接还额外需要 pcntl 和 posix 扩展这两个是常驻进程跑起来的依赖。3.1 环境准备版本选型与目录归位先解压再归位。这里有个小经验Windows 下压出来的 zip 包文件名带中文的话在 Linux 解压容易乱码个别包还会出现「伪加密」——解压时提示要密码其实是文件加密标志位被改过不是真有密码。遇到这种情况unzip 版本支持的话用unzip -O gbk指定编码重解一次大多能解决。# 解压源码包到临时目录 unzip 源码-开源版在线客服管理系统.zip -d /var/www/kefu # 解压后一般是一个和项目同名的目录把它移到 Web 根目录 mv /var/www/kefu/online_kefu /var/www/html/kefu # runtime 和 uploads 目录必须给足写权限否则登录状态和文件上传都会静默失败 chown -R www-data:www-data /var/www/html/kefu/runtime /var/www/html/kefu/uploads chmod -R 755 /var/www/html/kefu/runtime /var/www/html/kefu/uploads解压参数-d指定目标目录如果目录不存在 unzip 会自动创建。移动目录时注意看清楚解压出来的顶层目录名别把整个临时目录都搬过去否则路径会多一层。chown后面的www-data是 Debian 系服务器上 Nginx 和 Apache 的默认运行用户换成 CentOS 系时一般是nginx或apache用ps aux | grep nginx看一眼就知道。runtime 目录存 PHP 生成的日志和 session 文件uploads 存访客发的图片和文件这两个目录不可写最常见的表现是登录成功后马上又弹回登录页。3.2 导入数据库与配置项修改在 install 目录里找到 .sql 文件这套包里是 kefu.sql。导入前先做两件事确认 MySQL 版本确认字符集。5.7 和 8.0 对 SQL 语法要求有差异老项目导到新库里偶尔报错所以我习惯手动建库再导入而不是让脚本直接执行。# 建库必须用 utf8mb4访客消息里带表情3 字节的 utf8 存不下 mysql -uroot -p -e CREATE DATABASE IF NOT EXISTS kefu DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 导入表结构和初始数据 mysql -uroot -p kefu /var/www/html/kefu/install/kefu.sql # 确认表已经建好列表里应该能看到 session、message、agent 这类表 mysql -uroot -p -e USE kefu; SHOW TABLES;字符集这里我用的是utf8mb4_unicode_ci它比默认的utf8mb4_general_ci对中文和特殊符号的排序更友好。注意旧项目的 .sql 文件里可能写死了DEFAULT CHARSETutf8导入后会有部分中文变问号需要手动把表改成 utf8mb4。改配置前先看一下项目用的是哪种框架常见的是 ThinkPHP 风格配置文件名是application/database.php也有新版本放在.env里。改三处即可// application/database.php 里的数据库连接参数 return [ type mysql, // 数据库类型本项目用 MySQL hostname 127.0.0.1, // 同机部署填 127.0.0.1别用 localhost 容易踩 socket 坑 database kefu, // 上一步建的库名 username kefu_user, // 建议单独建一个业务账号别直接塞 root password 改成你自己的强口令, hostport 3306, // 默认端口 charset utf8mb4, ];为什么强调单独建账号客服系统的数据库账号明文写在项目配置里一旦 Web 目录被拖库或者源码泄露root 口令就跟着漏了。安全做法是先CREATE USER kefu_userlocalhost IDENTIFIED BY 强口令;再授权给 kefu 库。改完配置如果开了 PHP-FPM 或 opcache记得systemctl reload php-fpm或重启一下配置才会重新加载。3.3 启动 Web 服务与长连接进程Web 服务用 Nginx 时最容易翻车的是伪静态。这套系统的路由入口是 index.php所有请求都要通过它来分发。Nginx 默认配置直接访问路径会 404必须在 server 块里加一条转发规则server { listen 80; server_name kefu.example.com; root /var/www/html/kefu/public; location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; } } location ~ \.php$ { include fastcgi_params; fastcgi_pass 127.0.0.1:9000; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } }这段配置里root指向的是 public 目录而不是项目根目录原因public 是 Web 可见的入口application 和 runtime 不应该被外部直接访问。rewrite规则把所有不存在的文件请求都转给 index.phps$1是框架路由用的参数。如果用 Apache对应的是 public/.htaccess 文件核心内容一样只是语法不同RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php?s$1 [QSA,PT,L]Apache 下这个文件不生效时先查 vhost 里有没有开AllowOverride All没开的话 .htaccess 会被直接忽略。伪静态配好网页能开了但消息实时推送还没起来。长连接进程得单独启动它以守护进程方式跑在后台# 进入项目根目录启动 worker 服务 cd /var/www/html/kefu php think worker:start --daemon # 确认 gateway 端口在监听常见的默认端口是 8282 和 5700 ss -lntp | grep -E 8282|5700--daemon表示后台运行不会因为 SSH 断开就被杀掉。端口号不是所有版本都一致以项目里config/gateway_worker.php的配置为准。这一步做完整个系统的两大部分——Web 服务和长连接服务——就都起来了。3.4 验证安装成功的四个信号部署完别急着上线按下面四个信号过一遍。第一浏览器打开http://你的域名/页面右下角出现访客聊天浮窗说明 Web 服务和前端静态资源正常。第二用后台初始账号登录坐席工作台能进去说明数据库连接和 session 正常。初始账号一般写在 install 目录的说明文件里或者登录页有默认提示。第三开一个无痕窗口模拟访客发一句「在吗」坐席端一两秒内弹出提醒说明 WebSocket 链路通了。第四查数据库里 message 表能看到刚才那条测试消息说明落库正常。四条全过这套系统才算真正能接待访客。提示长连接进程不会跟着 Nginx 一起开机自启服务器重启后要记得把php think worker:start --daemon加进开机任务或 supervisor否则页面能打开但消息全不实时。4. 避坑指南部署期与接入期的五个翻车现场这套系统本身不复杂但下面五个问题我几乎每次部署都会遇到至少一个。按「现象 → 原因 → 解决」写清楚碰到直接用。4.1 部署期连不上库、登录被弹回现象导入数据库时报语法错误或者导入后一查表是空的。原因多数是 .sql 文件来自老版本 MySQL里面用了 MySQL 8.0 不再兼容的写法比如某些旧的关键字或默认值语法。解决方式是先手动建好 utf8mb4 库再进库执行 .sql如果某一条语句报错用sed -i s/旧写法/新写法/g kefu.sql把对应片段替换后再导入不要硬着头皮重试。现象后台登录成功后跳回登录页或者验证码一直提示不对。原因几乎都是 session 写不进去。最常见是 runtime 目录没给写权限PHP 生成 session 文件失败其次是配置里 cookie_domain 配错导致 session cookie 没种到浏览器。解决方法是先把 runtime 目录chmod -R 777排掉权限问题再检查配置里的 cookie 域名改成你自己的域名或者留空。这两个问题浪费了我当年一下午的时间后来学乖了部署完第一件事就是看 runtime 目录能不能写文件。4.2 接入期样式丢失、消息不实时现象往自己网站贴了嵌入代码聊天窗口弹出来了但样式全乱控制台报「混合内容」错误。原因你自己的站是 https而客服系统是 http浏览器默认拦截 http 的 iframe 和静态资源。解决方式两条路给客服系统也配上 https 证书走反代或者把嵌入代码里的资源地址改成相对协议。注意改 https 后WebSocket 地址也要跟着从 ws:// 换成 wss://不然连接直接被浏览器掐断。这个是 https 站点接入时最容易忽略的坑。现象坐席端登录正常但访客发消息一直不弹。原因优先查长连接进程ps aux | grep worker看进程在不在ss -lntp | grep 8282看端口有没有监听。很多云服务器默认安全组只放行了 80 和 443GatewayWorker 的端口没放行外部连接根本进不来。如果用的是内网穿透或 NAT 环境还得在隧道配置里把 WebSocket 端口一起映射出去。解决方式是先用curl测一下端口连通性确认网络层没问题再看进程。4.3 运维期连接数打满与消息延迟现象访客一多MySQL 连接数被打满消息延迟十几秒。原因长连接模式下每个客户端连接都可能触发落库查询再加上未读会话数和未回复数每次都要做全表 count慢查询把数据库连接占完了。解决方式三步走先给会话表的 agent_id、status、create_time 加联合索引这是成本最低的一步再把历史会话定期归档到独立表减少主表数据量最后打开数据库连接池适当调大 max_connections。但要注意调大连接数只是延缓问题根子在慢查询索引不加调多大会把内存撑爆。5. 进阶用法与验证二次开发、压测与重启恢复的落地技巧5.1 最小二次开发改欢迎语和分配策略访客端欢迎语、排队提示这类文案后台配置里就能改不用动代码。真正需要动代码的通常是分配策略。默认轮询分单在忙闲不均时不好用想改成「空闲优先」核心逻辑是查在线坐席里处理中会话最少的那一个// 分配坐席优先找在线且空闲的找不到再退回轮询 public function findAvailableAgent() { $agent Db::name(agent) -where(status, 1) // 账号是启用状态 -where(online, 1) // 长连接心跳在线 -order(working_count asc) // 当前处理会话数少的排前面 -find(); return $agent ?: $this-getNextByRoundRobin(); // 兜底逻辑 }这里的online字段来自长连接服务的心跳上报如果 worker 进程挂了这个字段会一直停留在 1造成「在线但根本不接单」的假象。所以改分配逻辑的人必须同时确认心跳数据的更新是活的。历史会话沉淀下来之后还可以按关键词把常见问题导入成团队的客服知识库让访客在排队时先自助查答案能分流掉不少重复提问。5.2 稳定性验证压测和重启恢复上线前做一次简单压测能发现大部分隐患# 模拟 50 个并发访客访问客服页面发 2000 个请求 ab -n 2000 -c 50 http://127.0.0.1/kefu/index/visitor # 压测完看应用日志里有没有超时和数据库连接池报错 tail -f /var/www/html/kefu/runtime/log/$(date %Y%m)/$(date %d).logab只能压 Web 层WebSocket 实时链路还得写个小脚本维护几十个连接做收发测试。更关键的是重启恢复演练手动 kill 掉 worker 进程再启动观察在线访客能不能自动重连而不是全部掉线。我从第一次部署线上客服系统起就强制自己走「压测 重启恢复 数据库备份」三连这套流程后来帮我挡过好几次半夜故障。希望帮到你。本文还有配套的精品资源点击获取