XCC API
XSwitch 集群
本章讨论 XSwitch 集群组网。
来话处理
来话处理如下图所示。假设中继侧来话可以以一定算法分配置到 3 个不同的 XSwitch Node 节点。XNode 收到呼叫后,向 NATS 广播来话消息(Event.Channel(state = START)
),Ctrl 收到后进行处理。
所有 XNode 都订阅至少两个 Subject:
cn.xswitch.node
:所有 XNode 都以同一个 Queue 订阅,发往该 Subject 的消息只有一个 Node 能收到,也就是说接收消息是互斥的。cn.xswitch.node.n
:其中n = 1,2,3 ...
,每个 Node 独立订阅,发往该 Subject 的消息只有一个能收到,也就是说,可以通过它定向发消息。
同理,所有 Ctrl 都订阅两个 Subject:
cn.xswitch.ctrl
:以同一个 Queue 订阅,互斥接收。cn.xswitch.ctrl.n
:其中n = 1,2,3 ...
,每个 Ctrl 独立订阅。
当有来话时,不失一般性,假设该来话分发到了node.1
,则它会构造一个消息,通过 NATS 发送到cn.xswitch.ctrl
上,右侧两个 Ctrl 都有可能收到该消息,但只有一个收到。不失一般性,假设ctrl.1
收到了这个消息,这时候,它从消息的内容中取出node_uuid
,知道了该消息是来自node.1
,因此,它往node.1
上回复一个XNode.Accept
指令,表示它想接管这一路呼叫。node.1
收到后,向ctrl.1
回复200 OK
,表示指令执行成功。至此,Node 和 Ctrl 通过 NATS 建立了一个“虚连接”(一对一对应关系),在这路通话生存期间都是如此。
时序图如下所示:
去话处理
去话逻辑与来话差不多,区别只是控制首先从 Ctrl 发起。假设ctrl.1
发起呼叫,它首先将XNode.Dial
请求发到cn.xswitch.node
这个 Subject 上,不失一般性,假设node.1
收到了这个请求,在后续的返回结果中,它会告诉ctrl.1
这个呼叫是在node.1
上处理了,因而也可以建立虚连接。
如果 Ctrl 想将 Dial 请求发到指定的 Node 上,也可以直接指定 Node 对应的 Subject,如cn.xswitch.node.1
。至于 Ctrl 如何知道有哪几个 Node,参见下一节节点管理。
节点管理
- 每个 Node 在上线时都会主动发
Event.NodeRegister
消息,Ctrl 侧可以订阅该消息以便感知节点上线。 - 每个 Node 在下线时会发
Event.NodeUnregister
消息,请求注销。 - 为了防止 Node 崩溃时来不及发送注销消息,Node 每 20 秒发一个
Event.NodeUpdate
消息,该消息可以做为心跳保活消息使用。此外,该消息还携带节点当前的活动 Channel 数以及负载信息。Ctrl 可以根据该信息向“负载最轻”的 Node 发送新任务。
注意:防止优先级反转。分布式系统的一个难点或者说一个误区就是无条件地往“负载最轻”的节点上发消息。假设我们做了一个自动外呼系统,有三个 Ctrl 和三个 Node。在某一时间,三个 Ctrl 都发现node.1
负载最轻,然后分别向它发送了10
个外呼任务,node.1
就会在同一时刻收到 30 个外呼任务,有可能立即成为“负载最重”的节点,甚至会过载。
总之,分布式系统从来都不是很简单就可以实现的,在实际使用时需要考虑各种边界情况。在实际使用时,简单的轮循算法基于就可以做到“足够好”,如果能配合一些反馈补偿机制,就能做到“更好”。
这些消息默认会发到cn.xswitch.ctrl
上,但是在 ctrl 侧存在多个实例的情况下,由于cn.xswitch.ctrl
一般是队列方式订阅的,会导致一个消息只有其中一个节点能收到。因此,在这种情况下应该开启status-ctrl-subject
参数,它的默认值是cn.xswitch.node.status
。
双机热备
集群需要比较多的物理机或虚拟机。在规模不大的情况下,可以使用双机热备,只需要两台机器。
双机热备与集群不同,双机热备是两台当一台用,浮动 IP 绑在主用 XSwitch 节点上对外提供服务。如下图:
双机热备模式下,两个节点配置如下:
switch.conf.xml
中配置的switch_name
要一致。- XCC 节点名要一致,配置
node-name
参数(该参数如果不配则可选)。 cn.xswitch.node
这个如果使用,则只能用普通订阅模式,而不能用队列订阅。- 如果不使用
cn.xswitch.node
这个通用主题,可以使用cn.xswitch.node.$node-name
主题 - 将备机配置为
standby
模式(目前仅支持命令切换xcc <active|standby>
),在standby
模式下,XCC 正常订阅消息,但不做处理。 - 总之,主备两台机器都会订阅
cn.xswitch.node.$node-name
,但只有主机会处理。
上图中,node-name
被配成了x
,会影响事件消息中的node_uuid
值(也等于x
),时时 XCC Node 节点全名为cn.xswitch.node.x
。在应用中可以直接对这个 Subject 发消息。
需要说明的是,双机热备也需要 Ctrl 侧的支持。下面是一些切换逻辑:
总体逻辑
当发生切换时,系统会向cn.xswitch.ctrl
或cn.xswitch.ctrl.status
(可配置)上发送Event.Recover
事件消息,消息中包含成功恢复的 Channel 数量。
当发生切换时,已经桥接的呼叫会继续通话。正在放音的呼叫会中断(业务侧应该重放),已进入会议的呼叫可以继续会议。
单腿通话
来话
当发生主备切换时,Ctrl 将重新收到START
消息,里面有recovered: true
参数;Ctrl 应先执行Accept
,然后执行后面的动作。在 IVR 类应用中,为了避免重放以前的放音,Ctrl 侧应该是有状态的。
去话
当发生主备切换时,只能通过Event.Recover
事件知道已发生了切换,这时,所有已发送的请求命令将会超时(因为 XNode 无法回应消息),在超时后,Ctrl 侧可以重试上一个命令(这时可能还没有收到Event.Recover
消息),或全局等待收到Event.Recover
消息后再重试。
会议
会议应该可以正常恢复。
桥接的通话
桥接的通话理论上不受影响,但是,主备切换后会丢失一些状态,桥接后的处理可能会不正常(如转 IVR、转评价等)。
主备切换后,如果是已桥接的通话,会重新收到BRIDGE
消息,并带有recovered: true
标志。如果 Ctrl 侧依赖于该事件处理逻辑,则应该检查recovered
标志。
桥接的通话在解除桥接时(如一方挂机),也能收到UNBRIDGE
消息,并带有recovered: true
标志。
小结
总之,双机热备状态下可以恢复处于“稳定”状态的通话,如桥接的通话或会议,但正在接续的电话可能无法正常恢复(不过由于没有正常接通,应该也可以接受)。
另外,为了防止从头重复放音(playback
、speak
、say
等),放音会中断,这时,业务侧应该感知到主备切换并重新放音。如果应用希望恢复后重放所有声音,则可以设置通道变量recovery_skip_announcement_type_applications=false
。