一、前言:为什么稳定与安全如此重要
作为一名软件工程师,我曾经坚信"功能为王"。功能够强,界面够炫,用户自然会来。然而,真正上线运营一款客服系统后我才深刻明白:
功能可以让用户试用,但稳定性 和安全性才是让用户留下来的根本。
客服系统的特殊性在于:它与客户业务直接挂钩,是一线成交和售后的桥梁。你永远无法预知用户什么时候打开网页,发出一句"请问在吗?" 你也无法承受这种情况下系统突然宕机、消息延迟、甚至数据泄露的代价。
我曾遇到过这样的真实场景:
- 某次凌晨系统崩溃了,第二天早上一查,客户错失了上百条访客咨询;
- 有访客反馈聊天记录丢失,客服追踪不到历史对话;
- 更危险的是,有脚本试图通过 WebSocket 枚举接口,尝试篡改聊天内容。
这些事故带来的不只是客户的信任危机,更是我自己作为工程师的羞耻感。那一刻我意识到:
客服系统的"核心竞争力"不在于功能列表,而在于可靠的连接能力 与构筑用户信任的系统安全性。
于是,我开始了漫长的"偏执工程"之路,从通信底层,到系统架构,再到攻击防护,逐步将这款客服系统打磨成一个能够支撑海量并发、全年无故障运行的系统。今天的这篇文章,我将拆解我在构建这套系统时的关键思路、架构演进与安全防御策略,并分享核心代码逻辑。
如果你也正在开发客服系统,或任何面向公众的实时在线系统,我相信这篇文章能为你带来一些启发。
二、整体架构设计:分布式、模块化、可私有化部署
在线客服系统的底层复杂度,远比"页面上弹个对话框"要高得多。它必须支持高并发访问、低延迟消息传输、实时状态同步、聊天历史归档、AI协同处理......这些能力若全部耦合在一个服务中,只会变得臃肿不堪、难以维护。
因此,我一开始就以模块化、可插拔的方式构建整套系统,同时将**"极简私有化部署"**作为第一优先级考虑。
1. 服务划分与通信机制
系统核心划分为以下几个服务模块:
服务模块 | 说明 |
---|---|
Gateway 网关服务 | 统一入口,负责 WebSocket/HTTP 接入与转发 |
ChatService | 核心会话处理逻辑,消息存储、分发、状态维护 |
AgentService | 客服端处理逻辑,身份验证、消息调度等 |
AdminService | 后台管理模块(租户管理、配置等) |
AIService(可选) | 智能客服接口(支持对接 GPT、本地模型等) |
模块之间通过 HTTP / gRPC 调用,通信协议清晰分离,便于部署与扩展。
✅ 每个模块都可以单独运行,支持最小部署、增量升级。
2. 私有化部署,从未如此简单
客户部署的场景千差万别:有的公司要求内网运行,完全离线 ,有的则希望运行在自有服务器上并对接内部系统。因此,我在系统设计上做了这些关键设计:
✅ 一键安装脚本
我提供了一个一键安装脚本,客户只需在一台干净的 Ubuntu 服务器上运行以下命令:
bash
curl -sSLO https://files.shengxunwei.com/kf/installscript/install.sh && chmod +x install.sh && bash install.sh
该脚本将自动完成:
- 所有依赖组件安装(.NET、MySQL、Redis、Nginx 等)
- Web 管理后台 + 访客端 + 客服端程序部署
- 数据库初始化与默认配置
- 启动服务并输出访问地址
👨💻 无需懂容器、无需配置环境,5分钟即可在自己服务器跑起来。
✅ 支持 宝塔面板/aaPanel 部署
为了迎合国内外广泛使用的运维面板,我还提供了非常详细的在线部署说明书。
3. 数据隔离与多租户支持
我们的系统原生支持多租户架构,每个租户(站点)拥有:
- 独立的访客数据、聊天记录、设置项
- 独立的 API 密钥与访问权限
- 独立的部署入口(支持分域名绑定)
这样部署出来的系统将自动启用"仅一个租户运行"模式,适用于 SaaS 客户转为私有部署的迁移场景。
4. 数据存储结构与安全设计
为了确保聊天数据的安全性和可控性,系统采用以下策略:
- 关系型数据库存储结构化数据(MySQL/PostgreSQL)
- Redis 缓存在线状态、聊天室映射等高频读写数据
- 可选启用 Elasticsearch 存储全文聊天记录,支持搜索功能
- 所有聊天记录、用户信息默认加密存储(可选择开启透明加解密模块)
数据库结构也保持高度解耦,例如:
sql
CREATE TABLE ChatMessage (
Id BIGINT PRIMARY KEY,
SessionId BIGINT,
SenderType ENUM('Visitor', 'Agent', 'AI'),
Message TEXT,
Timestamp DATETIME,
TenantId VARCHAR(64)
);
🔒 多租户隔离,支持单租户数据导出与迁移。
5. 私有部署与SaaS共存:一套代码,双模式运行
最后一个关键点是,我们的架构支持"SaaS 与私有化统一代码、统一接口、统一架构"。
只需在配置文件中切换运行模式:
json
{
"DeploymentMode": "SelfHosted", // 或 "SaaS"
"TenantId": "yourcompany"
}
整个系统会自动切换为私有化部署逻辑,例如跳过租户切换、中控平台接口等。
好的,以下是第三章节:极致稳定性背后的关键策略的完整正文草稿,突出工程实践与技术细节,适合在技术社区中引发共鸣与转发:
三、极致稳定性背后的关键策略
系统是否稳定,从不取决于"正常情况下是否运行良好",而在于 "极端情况下是否还能稳住"。
我们在设计客服系统时,将"极致稳定性"视为第一优先目标,围绕"消息可靠性、服务可用性、系统自愈能力"三个维度,做了大量工程实践。
1. 断线重连与会话续接机制
客服系统天然基于WebSocket建立持久连接,而用户的网络环境往往并不稳定:
- 4G 网络频繁切换基站
- 公司 WiFi 会定期断流
- 浏览器崩溃或刷新页面
如果处理不好,就会出现:
- 消息丢失
- 会话中断
- 客服误以为访客离开
✅ 我的设计思路:
- WebSocket 心跳检测 + 客户端 reconnect 重连机制
- 每条消息带唯一 MessageId
💡 C# 示例:确认机制简化代码
csharp
// 接收消息
public async Task OnMessageReceived(string tenantId, string sessionId, string messageId, string content)
{
SaveToDatabase(sessionId, messageId, content);
await _messageCache.Cache(sessionId, messageId); // 缓存确认
}
// 客户端断线后重连
public async Task ResumeSession(string sessionId, string id)
{
var messagesToResend = await _messageCache.GetMessages(sessionId, id);
foreach (var msg in messagesToResend)
{
await SendToClient(sessionId, msg);
}
}
这种机制在极端情况下(如重连10秒后)依然可以精准恢复未读消息,零丢失。
2. 服务节点可水平扩展,支持高并发
我们使用了以下方式支持高并发与动态扩展能力:
- 所有状态(连接、消息、队列)都存于 Redis,服务节点无状态化,支持随时横向扩容
- WebSocket连接统一由 Gateway 层接入,转发至后端节点
- 消息发送支持"推拉结合":关键节点用 Push,非活跃会话用 Pull 延迟拉取,节省资源
高可用架构部署图(简图):
css
┌────────────┐
│ Nginx LB │
└────┬───────┘
│
┌───▼─────┐ ┌──────────┐
│ Gateway │ --> │ Redis Bus│
└───┬─────┘ └──────────┘
│
┌───▼────┐ ┌──────────┐ ┌──────────┐
│ Node A │ │ Node B │ │ Node C │
└────────┘ └──────────┘ └──────────┘
部署上完全支持 Kubernetes自动弹性扩容 或本地服务进程手动部署。
3. 灰度发布与无感升级能力
在 SaaS 模式下,任何一次"闪断"都可能导致客服断线、用户投诉。为此,我们设计了多种升级方式:
- 蓝绿部署策略:A组节点跑旧版本,B组跑新版,新访客逐步切流
- 热更新机制:聊天逻辑支持局部热加载,不重启整个服务
- 零停机升级脚本:数据库结构变更使用 EF Core Migrations + 向后兼容设计
示例:灰度流量控制策略
csharp
public bool IsInNewVersionGroup(string visitorId)
{
// 对访客哈希值做分组
return visitorId.GetHashCode() % 100 < CurrentGrayPercent;
}
我们甚至为每个租户设计了独立版本配置,允许某些重要客户暂缓升级,以确保稳定性优先。
4. 全链路健康检测与熔断机制
稳定性从来不是"上线后就不管了",而是要持续监测与自我修复。
我们的监控体系包含:
- Prometheus 采集:连接数、请求耗时、异常率、WebSocket延迟等
- Grafana 可视化:关键KPI预警、趋势图
- 熔断策略:某服务响应异常率连续高于阈值,则从集群中摘除(基于 Polly)
- 自动重启机制:服务僵死时,Supervisor/NSSM 会自动拉起进程
示例:Polly 熔断器
csharp
var policy = Policy
.Handle<Exception>()
.CircuitBreakerAsync(
handledEventsAllowedBeforeBreaking: 5,
durationOfBreak: TimeSpan.FromSeconds(30)
);
通过这些机制,即便某个节点挂掉,系统整体依然可服务;即便出现慢请求,系统也能降级继续运行。
四、安全性设计:从通信到权限的全方位加固
客服系统本质上就是一个"通道":连接访客和企业、连接浏览器和数据库、连接公网和内网。而每一个连接,都是潜在的攻击入口。
在没有安全设计的系统中,攻击者可以轻松做到:
- 篡改访客身份冒充他人
- 拦截并伪造聊天内容
- 扫描接口暴力探测敏感数据
- 越权调用后台接口,访问他人数据
我们采取了 端到端全链路安全防护策略,从通信加密、签名验证、权限管理、到系统隔离,确保客服系统能真正做到"数据不泄露,接口不越权,攻击不生效"。
1. 所有通信必须加密:HTTPS + WSS 强制开启
我们默认强制启用 HTTPS 与 WebSocket Secure (WSS),并在服务端检测是否通过加密访问。部署时提供免费 Let's Encrypt 证书自动签发机制:
bash
certbot --nginx -d chat.example.com
服务端校验:
csharp
if (!context.Request.IsHttps)
{
context.Response.StatusCode = 403;
return;
}
✅ 无论是 API 接口还是 WebSocket 通信,均不允许明文传输。
2. 接口签名验证机制:防篡改、防重放
访客端初始化连接或提交数据时,必须带上签名参数。签名逻辑如下:
- 使用访问密钥
AppSecret
对参数进行 HMAC-SHA256 签名 - 客户端提交
timestamp
+signature
参数 - 服务端校验签名合法性,并拒绝过期/伪造请求
示例:C# 签名校验逻辑
csharp
public bool VerifySignature(IQueryCollection query, string secret)
{
var timestamp = query["timestamp"];
var signature = query["signature"];
if (string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(timestamp))
return false;
var raw = $"timestamp={timestamp}";
var expected = HmacSha256(raw, secret);
return expected.Equals(signature, StringComparison.OrdinalIgnoreCase);
}
JavaScript 生成签名(访客端):
js
const raw = `timestamp=${timestamp}`;
const signature = CryptoJS.HmacSHA256(raw, appSecret).toString();
✅ 该机制有效阻止了 URL 参数篡改、重放攻击、伪造请求等问题。
3. 多级权限模型:访客、客服、管理员分权隔离
我们定义了 3 类身份角色:
角色 | 权限范围 |
---|---|
访客 Visitor | 仅能访问本人会话、消息发送 |
客服 Agent | 查看/管理其所属租户内会话 |
管理员 Admin | 管理租户信息、配置、系统参数等 |
所有接口均基于 ClaimsIdentity 进行权限授权。例如:
csharp
[Authorize(Roles = "Agent,Admin")]
[HttpPost("/api/message/send")]
public IActionResult SendMessage(MessageDto dto) { ... }
🔒 即使知道 API 地址,访客也无法访问客服功能;客服也无法访问非本租户数据。
4. 防御常见攻击:XSS、CSRF、WebSocket滥用
1)XSS 防护
- 服务端统一对 HTML 内容进行清洗
- 客户端聊天框内容使用 DOMPurify 或 Vue 插件过滤
2)CSRF 防护
- 所有状态写操作必须带 CSRF Token 或 Authorization Header
- 管理后台启用双 Cookie 签名机制(防钓鱼)
3)WebSocket 滥用防护
攻击者可能使用爬虫或脚本持续发起 WebSocket 连接,造成资源耗尽。我们通过以下策略防御:
- 限制每个 IP 每分钟连接数(如使用 nginx + limit_conn)
- 每个连接建立时必须验证有效
siteCode
和签名 - 非法连接直接
context.Response.StatusCode = 400
并关闭
示例代码片段:
csharp
if (!IsValidSiteCode(siteCode) || !VerifySignature(...))
{
context.Response.StatusCode = 400;
return;
}
await context.AcceptWebSocketAsync();
5. 多租户数据隔离 + 防横向越权
系统默认运行于多租户模式,所有数据访问逻辑必须携带 TenantId
,并通过租户授权验证。
服务端数据库层:
sql
SELECT * FROM ChatMessage WHERE TenantId = @TenantId AND SessionId = @SessionId
代码中使用拦截器强制注入:
csharp
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries())
{
if (entry.Entity is ITenantOwned owned)
owned.TenantId = CurrentTenantId;
}
return base.SaveChanges();
}
即使开发者忘记写 where,也不会跨租户访问他人数据。
五、实战部署策略:让每一个用户都用得放心
一个优秀的客服系统,不能只停留在"线上跑得好"。它还必须:
- 部署简单 ------ 让用户能快速上线使用;
- 运行安全 ------ 能长期稳定运行在客户自己的服务器上;
- 数据可控 ------ 所有聊天数据、配置都在客户手中,企业"可见、可管、可控"。
这也是为什么我们一直坚持把**"私有化部署"作为一等公民支持**。
1. 为什么坚持做"真正可控"的私有化部署
市面上很多号称支持私有部署的系统,实际上:
- 要求公网访问控制台或依赖云服务
- 核心模块仍在云端(如AI、账号中心等)
- 数据依旧回传到云服务器,用户无感知
而我们提供的私有化部署方案,是"纯净离线"的:
✅ 不依赖任何第三方云服务 ✅ 可部署在 内网服务器、虚拟机、国产主机 上 ✅ 全部数据、日志、配置、密钥 存储在本地机器中 ✅ 可选择 开源部分模块 供客户安全审计
🧠 对于政企客户、金融、医疗等对数据安全有高要求的行业,这是基本底线。
2. 部署方式:图形界面 vs 命令行一键包
我们提供两种部署方式,覆盖不同技术水平的用户:
🟢 方式一:aaPanel 图形化一键部署
适合中小企业或不具备 DevOps 能力的客户。
- 可通过"插件市场"在线安装客服系统
- 完整支持服务初始化、数据库配置、SSL 证书签发
- 所有运行服务均通过 Supervisor 守护,异常自动重启
🟢 方式二:Shell 脚本一键部署(离线包支持)
适合 DevOps/IT 运维团队,具备脚本部署能力的场景。
bash
curl -sSO https://files.shengxunwei.com/kf/installscript/install.sh
chmod +x install.sh
./install.sh --selfhost --port 8080 --no-cloud
- 离线环境只需提前准备安装包(或内网镜像源)
- 支持指定安装目录、端口、租户ID等参数
- 安装完毕后自动输出访问地址和登录账号密码
3. 安全隔离策略:保障本地部署不被"污染"
在私有部署环境下,我们额外做了以下安全加固:
✅ 完全屏蔽公网回传
- 所有自动上报、升级检查、遥测逻辑 默认禁用
- 所有 API 调用目标域名均可配置,默认仅限本机访问
- 部署包内含完整静态资源,不依赖 CDN
✅ 进程级隔离 + 网络分区建议
- 每个服务模块运行在独立进程(非子线程)
- 支持使用 Docker / systemd / NSSM 等运行方式
- 强烈建议将服务运行在 独立 VLAN 或 DMZ 区域,通过反向代理转发
✅ 管理员登录日志 + 访问审计
- 所有登录尝试均记录 IP、UA、来源信息
- 系统后台内置"操作审计日志"模块,支持导出归档
示例:登录日志结构
json
{
"username": "admin",
"ip": "192.168.1.15",
"success": true,
"timestamp": "2025-07-09T10:24:31Z",
"source": "WebAdmin"
}
4. 部署后运行保障:自检机制 + 异常提醒
我们内置了一套轻量级自检机制,部署后首次运行将自动检测:
- 数据库连接是否可用
- Redis 是否正常连接
- WebSocket 接入是否通畅
- 系统关键目录读写权限是否正确
此外,提供可选的运行时健康检查 API:
http
GET /api/healthz
返回示例:
json
{
"redis": "OK",
"database": "OK",
"websocket": "OK",
"version": "2.6.4-selfhost"
}
该接口可对接监控平台(如 Zabbix、UptimeRobot、Prometheus)实现异常自动告警。
5. 数据导出 + 全量备份支持
客户随时可以导出:
- 所有会话记录(JSON / CSV / SQL)
- 客服账号配置
- 系统参数备份文件
并且提供完整数据迁移脚本,支持:
- SaaS → 私有化
- 私有化 → 新服务器迁移
- 私有化恢复历史快照
独立者的产品成果
可全天候 7 × 24 小时挂机运行,网络中断,拔掉网线,手机飞行模式,不掉线不丢消息,欢迎实测。
访客端:轻量直观、秒级响应的沟通入口
访客端是客户接触企业的第一窗口,我们精心打磨每一处交互细节,确保用户无需任何学习成本即可发起对话。无论是嵌入式聊天窗口、悬浮按钮,还是移动端自适应支持,都实现了真正的"即点即聊"。系统支持智能欢迎语、来源识别、设备类型判断,可自动记录访客路径并呈现于客服端,帮助企业更好地理解用户意图。在性能方面,访客端采用异步加载与自动重连机制,即使网络波动也能保障消息顺畅送达,真正做到------轻量不失稳定,简单不失智能。

客服端软件:为高效率沟通而生
客服端是客服人员的作战平台,我们构建了一个专注、高效、响应迅速的桌面级体验。系统采用多标签会话设计,让客服可同时处理多组对话;访客轨迹、历史会话、地理位置、设备信息、来源渠道等关键信息一目了然,协助客服快速做出判断。内置快捷回复、常用文件、表情支持和智能推荐功能,大幅降低重复劳动成本。同时,系统还支持智能分配、会话转接、转人工、自定义状态等多种机制,保障团队协作流畅,让客服不仅能应对高峰,更能稳定交付满意度。

Web 管理后台:
Web 管理后台是企业对客服系统的"驾驶舱",从接入配置、坐席管理,到数据统计、权限控制,一切尽在掌握。你可以灵活设置接待策略、工作时间、转接规则,支持按部门/标签/渠道精细分配访客,满足复杂业务场景。系统还内置访问监控、聊天记录检索、客服绩效统计、错失会话提醒等运营级功能,助力管理者洞察服务瓶颈,持续优化资源配置。支持私有化部署、分权限管理、日志记录与数据导出,为追求安全性与高可控性的企业,提供真正"掌握在自己手里的客服系统"。

希望能够打造: 开放、开源、共享。努力打造一款优秀的社区开源产品。
钟意的话请给个赞支持一下吧,谢谢~