我在业余时间开发了一款自己的独立产品:升讯威在线客服与营销系统。其中有些功能,我一直认为是客服系统应该有的基本功能,应该没必要单独拿出来说。直到总会有客户专门和我说起和他们原来使用的系统的对比,我才知道真的是很多所谓的客服系统,真是一言难尽。
升讯威在线客服与营销系统走的是免费与分享路线,我会通过文章分享客服系统一些重要且基本的功能是如何实现的,以及它们的技术方案,部分代码。希望对您有用。
在本文中,我将详细说明访客端聊天窗口的多窗口数据同步技术。该技术能确保访客端在网页多开,刷新,关闭再打开等复杂情况下,收发的消息完全同步,并且可靠送达:
技术路线
对于多窗口数据同步,一般有三种技术路线:
一、同一浏览器内多个标签页之间的数据同步
1. BroadcastChannel API
随着现代 Web 应用朝着多标签页、多窗口协同的方向演进,如何实现同一浏览器上下文内的高效数据同步机制成为关键技术挑战之一。传统方案多依赖 localStorage 的 storage 事件或 SharedWorker,但存在性能瓶颈、同步延迟大、调试困难等问题。
为此,W3C 推出了 BroadcastChannel API,这是一种浏览器原生支持的同源上下文间广播通信机制,允许不同标签页、iframe、甚至独立的浏览器窗口之间进行高效、即时的数据传输。
- 应用场景: ** 多标签协同编辑:多个标签页打开同一文档时,实时同步编辑状态。 ** 跨页面登录状态共享:在一个页面登录后,其他页面同步刷新用户信息。 ** 在线聊天窗口弹出同步:主窗口与弹窗之间共享消息队列。 ** Web 通知广播:在一个窗口触发操作,其他窗口同步显示提示信息。 ** 数据缓存在标签间共享:避免多标签重复拉取后端资源,减少 API 请求频率。
- 用途:在同一源(域名)下的多个标签页/iframe之间进行通信
- 优点:简单、现代浏览器支持较好
- 用法示例:
js
const channel = new BroadcastChannel('my_channel');
channel.postMessage({ key: 'value' });
channel.onmessage = (event) => {
console.log('Received:', event.data);
};
技术挑战与优化方案 异常页面断联 当某些页面处于挂起状态(如后台标签页被系统冻结),消息广播可能延迟。建议通过 heartbeat ping 机制轮询活跃连接,并在失联时移除通道监听器。
安全性 BroadcastChannel 在同源策略下无加密验证,任何同源页面都可加入通信。为避免数据被恶意监听,建议: 对数据使用结构签名(如 HMAC)。 添加信道协议版本字段,避免协议升级时兼容性问题。 在主通道封装认证层,非认证者拒绝处理消息。
2. LocalStorage + StorageEvent
HTML5 引入了 LocalStorage,它是一种在浏览器本地存储数据的方式,允许网页存储少量的数据,并且数据会持久化存储,即使页面关闭后依然可以访问。 然而,当多个页面需要共享同一份数据时,传统的 JavaScript 变量和函数无法有效地实现跨标签页的数据同步。在此背景下,HTML5 又引入了 StorageEvent,该事件可以侦听 LocalStorage 中的数据变化并实时通知其他窗口或标签页。
LocalStorage LocalStorage 是一种 Web 存储机制,它允许以键值对的形式将数据存储在客户端。每个域名(Origin)都拥有自己独立的 LocalStorage 空间,最大存储容量一般为 5MB 左右,且存储的数据在浏览器关闭后仍然存在,直到被显式删除。
StorageEvent StorageEvent 是一个用于监听 LocalStorage 或 SessionStorage 数据变化的事件。该事件触发时,其他浏览器窗口或标签页能够接收到来自该源的更新,并可以立即响应这些变化。这个特性使得它成为跨页面、跨标签页通信的重要手段。
StorageEvent 触发时,事件对象会包含变化的详细信息,如被修改的存储键、修改前的值、修改后的值、以及修改发生的窗口/标签页等信息。
- 用途 :通过
localStorage
写入触发其他标签页的storage
事件 - 限制:当前标签页写入不会触发自己,无法监听自己写的值
- 用法示例:
js
localStorage.setItem('sync_key', JSON.stringify({ data: 123 }));
window.addEventListener('storage', (e) => {
if (e.key === 'sync_key') {
console.log('Storage updated:', JSON.parse(e.newValue));
}
});
3. SharedWorker
SharedWorker 允许在多个浏览器上下文(如多个标签页、窗口或 iframe)之间共享一个单独的后台线程,这为前端开发提供了一种跨标签页、跨窗口共享计算资源的高效解决方案。 SharedWorker 是一种浏览器 API,允许多个标签页、窗口或 iframe 共享同一个后台线程(Worker)。这意味着,多个页面或标签页可以通过该共享 Worker 执行一些复杂的计算任务、存储数据或执行其他异步操作,从而节省计算资源和提高响应速度。 与传统的 Web Worker 相比,SharedWorker 通过 port 对象让不同页面之间能够通信,多个浏览器上下文可以通过这个共享的 Worker 进行数据交换。这使得 Web 开发者能够更好地管理并发任务和共享资源。
- 用途:多个标签页共享一个Worker线程,可用于持久连接、共享状态
- 优势:适合统一WebSocket连接、集中式状态管理
- 限制:不能在不同域名或跨浏览器窗口间工作
二、同一用户不同设备之间的数据同步
1. WebSocket
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,旨在解决传统 HTTP 协议在实时数据交互中的局限性。它由 IETF 于 2011 年发布,并成为 Web 标准的一部分。与 HTTP 请求-响应模型不同,WebSocket 允许客户端与服务器之间保持长时间的连接,并且可以在任何一方需要时进行实时数据交换。它的工作流程可以分为两个阶段:建立连接和数据传输。首先,客户端通过 HTTP 发起 WebSocket 握手请求,服务器响应确认后,HTTP 连接升级为 WebSocket 连接。此时,客户端和服务器之间的连接将保持开放,可以进行双向数据传输,直到其中一方主动关闭连接。WebSocket 的最大优势在于它提供了低延迟、高效率的双向通信。相比于传统的轮询和长轮询方法,WebSocket 使得数据交换更加实时,避免了频繁的连接建立与断开,从而减少了网络延迟和带宽消耗。适用于需要高频率实时通信的场景,如在线聊天、实时游戏、股票行情推送、社交媒体更新等。在这些应用中,客户端与服务器之间需要频繁交换小数据量,并且需要尽量减少延迟。
- 用途:实现客户端与服务器的长连接,支持服务器主动推送消息
- 适合:聊天室、多人协作、实时数据更新等
- 方案示意 :
- 客户端A发送变更到服务端
- 服务端广播给其他客户端B/C/D
2. WebRTC + Signaling Server
WebRTC(Web Real-Time Communication)是一项允许浏览器和移动应用进行点对点通信的技术,它支持音视频通话、文件共享以及实时数据交换,且无需用户安装插件。WebRTC 的核心优势在于其低延迟、高效的数据传输能力,尤其适用于实时通讯、视频会议、在线游戏等应用场景。 然而,WebRTC 本身仅处理媒体流的传输和点对点通信,但它不提供建立和管理连接的机制。在 WebRTC 中,客户端之间的连接通常需要借助一个信令服务器(Signaling Server)来交换元数据(如网络地址、连接请求、ICE 候选等),以便协商和建立 WebRTC 的点对点连接。 WebRTC 与信令服务器的结合非常适合需要实时音视频通信的应用,如视频会议、在线教育、远程医疗、实时客服等。信令服务器通常使用 WebSocket、HTTP 或其他协议来实时传输信令数据。在这些应用中,信令服务器确保客户端可以快速、有效地建立连接,并在需要时进行协商和更新。
典型的 WebRTC 应用流程如下:
- 用户A和用户B通过浏览器访问应用,双方通过信令服务器交换 SDP 和 ICE 候选。
- 信令服务器传递信息后,WebRTC 在用户A和用户B之间建立点对点连接,直接进行媒体流的传输。
- 连接成功后,双方可以进行实时的音视频交流,数据流的传输不再通过信令服务器,信令服务器仅用于初期的协商和后续的断开通知。
- 用途:浏览器对浏览器之间的点对点通信
- 适合:低延迟协作、文件传输
- 注意:需要中间的 signaling server 来建立连接
三、多个用户、多设备的高频协作场景
1. 服务端事件推送(Server-Sent Events, SSE)
Server-Sent Events (SSE) 是一种用于在客户端和服务器之间进行单向实时通信的技术。通过 SSE,服务器可以主动向客户端发送数据更新,而客户端则无需频繁发送请求。这种技术基于 HTTP 协议,允许服务器将更新数据推送到客户端,使得浏览器能够即时接收到服务器端的最新信息。
工作原理 SSE 的工作原理相对简单,客户端通过建立一个持久的 HTTP 连接,向服务器发送一个请求。服务器接收到请求后,保持这个连接打开,并定期发送新的数据流到客户端。与 WebSocket 不同,SSE 是单向的通信,数据仅从服务器流向客户端,因此适合用于需要实时推送数据但不需要频繁从客户端向服务器发送数据的应用场景。
- 用途:从服务端单向推送消息给客户端
- 优势:比轮询节省资源,兼容性好于 WebSocket(但只支持单向)
- 适合:低频实时通知、数据变动提醒等
应用场景 SSE 适用于各种需要实时推送信息的应用,特别是以下几种场景:
- 实时通知:如社交媒体平台、消息应用、新闻网站等,可以通过 SSE 向用户实时推送新的通知或消息。
- 股票行情与金融数据:SSE 非常适合实时推送股票行情、加密货币价格等更新,这些信息需要快速和持续地发送给客户端。
- 在线游戏:在多人在线游戏中,SSE 可以用来推送游戏状态的变化,如玩家位置、得分更新等。
- 监控系统:用于实时监控系统状态、日志、传感器数据等,可以将实时信息推送到客户端进行展示。
2. 基于 CRDT 或 OT 的协同算法
CRDT 是一种通过设计特殊的数据结构来解决数据冲突的技术,常用于去中心化的系统中。CRDT 确保即使多个用户并发修改同一数据,最终所有节点的状态也会达成一致,且无需额外的冲突解决机制。CRDT 基于数学原理,通过保证操作的交换性和结合性,使得不同节点之间的操作顺序无关,从而自动解决冲突。
CRDT 常见的类型有:
- 计数器:比如增量计数器或可撤销的计数器,支持在不同节点独立递增或递减。
- 集合:如元素集合,可以无冲突地并行添加或删除元素。
- 有序集合:如有序队列,确保不同节点间的顺序一致。
OT 是一种经典的协同算法,广泛应用于在线协作编辑工具中,如 Google Docs 等。OT 通过记录操作并将其转化为独立的变更单元,在并发编辑时进行顺序转换,确保每个用户的编辑操作能够在其他用户的操作基础上无冲突地合并。OT 的核心思想是将用户的操作转换成可以相互转换的"操作单元",并确保每个操作都能被正确地应用在其他操作之前或之后。例如,两个用户同时插入文字时,OT 会根据操作发生的顺序自动调整插入位置,从而避免冲突。优势在于它能够提供高效的实时协作,适用于需要精细控制并发编辑的场景,但它需要中心化的服务器来协调和管理操作。
- 用途:实现高性能多人实时协作
- 方案 :
- CRDT(Conflict-free Replicated Data Types)
- OT(Operational Transformation)
OT 协同算法
升讯威在线客服系统使用了最先进的 OT Operational Transformation)协同算法,来达到页面多开、刷新、飞行模式,聊天不中断,消息不丢失。
OT 算法基本原理详解
OT(Operational Transformation)算法 是一种强一致性的同步模型,最早应用于军事协同文档系统中,其核心思想是通过操作的变换与融合机制 ,来实现多客户端并发编辑下的数据一致性。该算法适用于文本编辑器、代码协作工具、在线聊天系统等需要毫秒级响应和冲突消解能力的场景。
一、核心概念
OT 的核心基于如下三个概念:
- 操作(Operation):对数据执行的原子变更,例如插入、删除或替换。在文本场景中表现为 Insert 或 Delete。
- 版本(Revision):服务器记录的操作版本号。每个客户端操作基于某个版本生成,当多个客户端同时提交变更时,就可能引发版本分叉。
- 变换函数(Transformation Function):用于将两个冲突操作变换为可以连续执行的不冲突版本,确保最终数据一致。
二、原理说明:并发操作的协调
假设两个客户端用户 Alice 和 Bob,分别在同一文本 "Hello"
上并发进行了如下操作:
- Alice:在第 5 位插入
"!"
,操作记为Insert(5, "!")
。 - Bob:在第 0 位删除
"H"
,操作记为Delete(0, 1)
。
由于这两个操作是基于同一个版本发起的,服务器需要将其进行转换,使得它们可以连续作用于同一个数据副本。这就是变换函数的作用:
- 将 Alice 的操作根据 Bob 的操作进行变换(Transform)。
- 更新位置偏移或删除区间,调整 Alice 的操作为:
Insert(4, "!")
。 - 然后按顺序应用:先删,再插。
这样,无论服务器先收到谁的操作,最终都会得到一致的数据状态:"ello!"
。
三、核心算法流程(伪逻辑)
text
Client:
1. 用户在本地执行操作 O。
2. 将操作 O 和当前客户端版本号 R 一并发送到服务器。
Server:
1. 获取历史操作 H = allOps[R:]
2. 对 O 与每个历史操作 h ∈ H 执行变换 O = transform(O, h)
3. 应用 O 到数据模型
4. 将变换后的 O 及新版本号广播给所有客户端
四、OT 算法的变换
在变换过程中,位置的偏移是关键。例如:
Insert(3, "X")
在Delete(0, 2)
之后,插入点变为 1。Delete(4, 1)
与Insert(4, "Y")
同步发生,需调整顺序。
五、在聊天系统中的应用优势
OT 算法不仅适用于文档编辑,在在线聊天系统中也有广泛应用,尤其是在以下模块中:
- 消息内容实时协作:多个客服同时编辑一个回复草稿。
- 会话上下文增强:AI 插入提示建议内容,与人工输入实时融合。
- 聊天记录修改日志:在已发送消息上做更改的权限控制与同步。
- 知识库协作更新:OT 可用于 FAQ 和答案模板的多人共建。
借助 OT 算法,聊天系统可实现如下目标:
- 零冲突编辑:避免因多个用户同时修改造成消息错乱。
- 延迟容忍能力:OT 支持异步网络环境中的最终一致性。
- 无锁协同体验:用户之间无需等待锁释放即可并发编辑。
六、扩展机制与融合策略
现代 OT 实现中通常会结合以下机制:
- 多通道融合:在文本 OT 的基础上,添加结构型操作(如移动节点、合并消息块)。
- 语义层补充:例如在聊天中加入角色权限限制,如只有管理员可以 delete 操作。
- AI 协同增强:将 AI 生成的操作作为特殊操作流插入 OT 队列。
OT 算法通过"以操作为中心"的机制,在保持编辑响应性的同时实现了高一致性,特别适合需要人机协同、低延迟互动的现代通信系统。
Python 实现 OT 协同算法:应用于在线聊天系统的完整示例
下面我们深入构造一个完整的 Python 工程级别示例 ,展示如何实现一个适用于在线聊天系统的 OT(Operational Transformation)核心机制。
一、工程结构概览
plaintext
ot_chat/
├── main.py
├── ot/
│ ├── __init__.py
│ ├── operation.py # 操作结构定义
│ ├── transform.py # 操作变换逻辑
│ ├── history.py # 历史版本跟踪
│ └── session.py # 用户协同会话管理
└── utils/
└── logger.py # 日志记录模块
二、核心代码模块
1. operation.py
python
# 定义基础操作类型:Insert 与 Delete
class Insert:
def __init__(self, position, content, user_id):
self.type = 'insert'
self.position = position
self.content = content
self.user_id = user_id
class Delete:
def __init__(self, position, length, user_id):
self.type = 'delete'
self.position = position
self.length = length
self.user_id = user_id
2. transform.py
python
# 用于处理 Insert 和 Delete 操作的变换逻辑
def transform(op1, op2):
if op1.type == 'insert' and op2.type == 'insert':
if op1.position <= op2.position:
return op1
else:
op1.position += len(op2.content)
return op1
if op1.type == 'delete' and op2.type == 'insert':
if op1.position >= op2.position:
op1.position += len(op2.content)
return op1
if op1.type == 'insert' and op2.type == 'delete':
if op1.position <= op2.position:
return op1
elif op1.position > op2.position + op2.length:
op1.position -= op2.length
else:
op1.position = op2.position
return op1
if op1.type == 'delete' and op2.type == 'delete':
if op1.position >= op2.position + op2.length:
op1.position -= op2.length
elif op1.position + op1.length <= op2.position:
pass
else:
# 删除区域有交集
overlap_start = max(op1.position, op2.position)
overlap_end = min(op1.position + op1.length, op2.position + op2.length)
op1.length -= (overlap_end - overlap_start)
if op1.length < 0:
op1.length = 0
return op1
3. history.py
python
class OperationHistory:
def __init__(self):
self.history = []
self.revision = 0
def add(self, op):
self.history.append(op)
self.revision += 1
def get_since(self, revision):
return self.history[revision:]
4. session.py
python
from ot.history import OperationHistory
from ot.transform import transform
class ChatSession:
def __init__(self, session_id):
self.session_id = session_id
self.document = ""
self.history = OperationHistory()
def apply_operation(self, op, client_revision):
# Step 1: transform op based on history
ops_to_transform = self.history.get_since(client_revision)
for old_op in ops_to_transform:
op = transform(op, old_op)
# Step 2: apply op to document
if op.type == 'insert':
self.document = self.document[:op.position] + op.content + self.document[op.position:]
elif op.type == 'delete':
self.document = self.document[:op.position] + self.document[op.position + op.length:]
# Step 3: store in history
self.history.add(op)
return self.document
5. main.py
python
from ot.session import ChatSession
from ot.operation import Insert, Delete
if __name__ == "__main__":
session = ChatSession("room1")
print("初始内容:", session.document)
op1 = Insert(0, "Hello", user_id="Alice")
result1 = session.apply_operation(op1, client_revision=0)
print("操作1后:", result1)
op2 = Insert(5, " World", user_id="Bob")
result2 = session.apply_operation(op2, client_revision=1)
print("操作2后:", result2)
op3 = Delete(0, 6, user_id="Alice")
result3 = session.apply_operation(op3, client_revision=2)
print("操作3后:", result3)
运行结果(模拟多个客户端并发修改):
makefile
初始内容:
操作1后: Hello
操作2后: Hello World
操作3后: World
三、适配在线聊天系统的方式
这套 OT 框架可以轻松适配在线聊天系统中的以下模块:
- 聊天室介绍编辑:多人同时编辑聊天室信息
- 消息草稿协同编辑:协作生成自动回复内容
- AI 插入建议修改:修改或注入 AI 建议
- 多客服并发沟通记录修订
通过对每条消息视为一个"小文档",可以将消息体嵌入 OT 流程中,实现毫秒级一致性同步。
四、部署与扩展建议
- 可将
ChatSession
封装为微服务,挂载于 Redis 分布式会话管理中 - 每个客户端保留本地
client_revision
,与服务器同步后更新 - 可通过 WebSocket 保持连接,推送变更
结语
在线客服系统的开发并非易事,从前端到后端,从数据库到 AI,从性能优化到安全保障,每一步都需要深思熟虑。如果你在实践中遇到新的问题,或者有更好的解决方案,欢迎在评论区分享你的见解,让我们一起探索更优雅的技术实现方案!
简介下这个 .net 开发的小系统
升讯威在线客服与营销系统是一款客服软件,但更重要的是一款营销利器。
- 可以追踪正在访问网站或使用 APP 的所有访客,收集他们的浏览情况,使客服能够主动出击,施展话术,促进成单。
- 可嵌入网站、手机 APP、公众号、或者通过 URL 地址直接联系客服。
- 支持访客信息互通,可传输访客标识、名称和其它任意信息到客服系统,与您的业务系统对接。
- 可全天候 7 × 24 小时挂机运行,网络中断,拔掉网线,手机飞行模式,不掉线不丢消息,欢迎实测。
希望能够打造: 开放、开源、共享。努力打造 .net 社区的一款优秀开源产品。