希望能够打造一个node.
js平台上的tsapi运行环境。构建node_tsapi的目的是让node平台上也可以运行tsapi应用。用途之一是构建一个可以替代lcs的应用。node平台上支持web socket等下一代web标准,这让
提供更便利更满足标准的实时监控系统成为可能。
现在的监控系统实现方式是让浏览器不断地发送请求以便获得更新后的
数据。这种实现方式对系统的压力很大,不是保持长连接状态而是
不断地创建新连接然后立刻又关闭。同时,也让开发一个完全运行在网页内的软电话成为可能。用途之二是lcs的所有其他代码均可以用javascript来实现。node平台提供与很多优秀平台的接口库,这让编写一个利用新技术新框架的软件变得更加便捷。javascript语言的解释性特性让部署和发布新功能变得快速。还可以畅想的是,负载均衡、双机热备等高级特性。之前决定的目标有些大,现在决定降低要求减少需求。希望这个node_tsapi只是个底层运行框架。其实就是将tsapi提供的功能再做一层转换,让node下的应用也可以使用tsapi。
addonA。node里所有都是异步方式的,
这样才能最大可能发挥node的优势。tsapi也是异步的,
发出一个请求,稍后才收到请求的响应。如果要将二者整合,
貌似tsapi非常适合node的环境。
但其实一个tsapi请求也会是阻塞方式的。
一个tsapi请求底层就是发送一个tcp包。
我们知道发送tcp包是阻塞模式的,除非已到达对端,
否则发送函数不会返回。如此的话,
调用tsapi请求也将在工作线程内执行。
获取响应数据或者AES主动发来的数据可以采用单独一个线程来做
。我们假想这么一个场景。一个make call请求来到node,
node解析后将其交给addonA来处理。
addonA发起一个异步的cstaMakeCall请求。
工作线程将调用cstaMakeCall请求,
如果函数返回失败,调用回调函数。此时,
addonA内不删除请求对象baton。待响应消息到来时,
再调用回调函数。感觉tsapi的编程模型很匹配node。
创建addonA时,可以传入连接AES的参数信息。
调用addonA.Open(function(){}),
传入回调函数。再调用addonA.MakeCall(
function(){}),传入回调函数。
应该在Open的回调函数中判断成功了调用addonA.
MakeCall。
这个使用场景需要后台有一个强健的运行逻辑来支撑。
Open成功不仅仅只是成功打开了流通道,
还生成了一个后台线程用来收取事件或响应数据。
我觉得收取acsOpenStream的确认响应消息也可以通过
这个后台线程来做。为了与后台收tsapi数据线程配合,
后台工作线程,
即调用实际csta函数的线程将会在事件来到后再返回,
也就是说如果事件没到,后台工作线程将一直阻塞。
主线程只有一个,也就是说接收请求以及生成请求的只有一个线程。
main线程生成的请求将交给worker线程处理。
我们假定只有一个worker线程。还有一个back线程,
它的作用是收取tsapi数据。当一个请求被生成后,
worker线程执行。
addonA提供一个接口GetEvent,
由js脚本中的setInterval定时调用。
setInterval内的回调函数会调用addonA的Get
Event接口。这样做有用吗?
GetEvent将收到的事件放入队列中,也包括响应事件。
我们再回到之前的那个场景中。主线程内调用了一个csta函数,
实际是在worker线程内被调用。
在主线程内会调用发起这个请求的回调函数,
此时回调函数被调用其实请求还不知道是否真的执行成功,
因为消息将在GetEvent函数内才能取到。
在addonA对象内将此请求放入队列,
并保存请求的invokeID值。
当主线程调用GetEvent时,
如果是个confirmation事件,
则查找队列看是否有invokeid值相同的请求。如果有,
则处理事件,调用请求的回调函数。
setInterval会调用getEvent,使用uv_
queue_
work会在worker线程内执行acsGetEventPo
ll函数。尽可能多的取数据。
然后addonA中有一个函数会在主线程内被调用。
main thread:getEvent(js) - addonA.getEvent(js) - addonA.GetEvent(c) - addonA.Work_BeginGetEvent(c)
worker thread:addonA.Work_GetEvent(c)
main thread:addonA.Work_
AfterGetEvent(c) - getEventCallback(js)
首次实现的addonA将采用这样的方式:
js代码内启动一个interval,
每隔十毫秒调用一次addonA的getEvent函数。
如此就可解决收取事件的问题,不会延误处理事件的时机。
我在想一个问题。就是基于WEB的CTI到底应该是什么样的?
页面内建立一个web socket连接。
我们是否应该在Work_
AfterOpen函数内调用acsGetEventBlock
这个阻塞式的函数。我们在讨论这个前,
先回到js代码中如何打开与AES的连接开始。传入参数,
调用open接口,传入的还有回调函数。
回调函数将告诉应用方打开是否成功。如果失败了,我们如何重连。
使用setTimeout?这是个选择。如果成功了,
可以开始监视设备。好,这就是使用场景。再回到Work_
AfterOpen函数,这个函数将会在主线程内执行。
acsGetEventBlock是个阻塞式的函数,
也就意味着此时主线程将不能处理其他请求。
最理想的实现就是不要使用这个阻塞函数。
那么我们就使用acsGetEventPoll函数。
但如果这个函数取不到事件呢?取不到事件的情况是网络由延迟,
或者对方某种原因无法回送消息。
也就是说立即调用acsGetEventPoll是可能取不到数
据的。如果这样的话,那么就不知道是否以打开成功。
其实我们可以这么做,
当第一次调用acsGetEventPoll时,
如无任何数据则再次将此请求放入工作队列,并记录已被调用次数。
如果有数据但不是我们需要的那个数据则立即回调函数通知已失败。
或者在超过获取数据的上限时仍没收到则回调通知已失败。
只有在未超过获取数据上限前收到需要的数据才能通知应用已成功。
这样的话,打开流通道是个非阻塞方式的过程。打开流通道成功后,
可以打开取数据的定时器。
main thread:makeCall(js) - addonA.makeCall(js) - addonA.MakeCall(c) - addonA.Work_BeginMakeCall(c)
worker thread:addonA.Work_MakeCall(c)
main thread:addonA.Work_
AfterMakeCall(c)
在上述描述过程中的最后一步,Work_
AfterMakeCall(c)。在这里已经是主线程了,
但不能回调函数通知应用。
因为我们还需要获取响应消息才能回调函数。那么此时,
应该将MakeCallBaton放入一个需要等待响应消息的队
列中。
同时放入队列的还有调用cstaMakeCall返回的Invo
keID_t值。我们将在每次取事件的最后一步Work_
AfterGetEvent中处理这个队列。
首先检查当前这些数据(我们可能处理多于一个的消息,
如果我们能取到的话)是否就是响应消息,
并通过响应消息中的InvokeID_t值,
查找队列中是否有InvokeID_t值相同的Baton对象。
如果有,将响应消息中的成功或失败消息通过回调通知应用。
再说点题外话,Work_
AfterGetEvent函数的另一个作用就是检查对列中的请
求是否超过了规定的等待上限,
如果是则回调函数通知应用请求已超时,也就是失败了。
这是一个典型的单请求单响应的处理模型。
TSAPI还有一些单请求多响应的场景。
对于单请求多响应的模式,上述处理模型仍然使用。首先,
在创建XXXBaton时,
我们已经知道它是否是一个单请求多响应模式的请求。
可以为他赋上一个值指明模式类型。在收到第一个响应消息时,
不回调函数,而是继续等待。每次在收到又一个消息时,重置超时。
直到收到最后一个消息才回调函数。