用python协程设计语音通信类后端程序


开发中的一些总结,有些乱。

主要描述,如果使用Python协程,该如何设计tsapi程序。为最终的调度设计积累一部分的经验,并且,最终形成一个框架,在这个框架上,可以方便的新增新的功能,让其他的员工不再对Python协程望而生畏。


1、整个框架主要的部分是call模块,主要负责呼叫流程的控制。它把对asapi的调用全部集中到一个模块中,可以实现对模块的封装,便于以后的替换。
2、call模块和上层模块的交互对象我本来考虑使用用户号码,但是考虑到方便性,以及这样的话必须基于一个假设:所有的用户号码必须是唯一标识:不利于后期的维护。所以,初步考虑采用asobj模块中的user和meet对象作为交互对象。对User和meet类的使用,建议用组合,然后再考虑继承。
3、poc server使用的是一种callinfo的机制,即所有处于稳定状态的用户都有一个callinfo来记录其当前的状态。本来我不想用这样的一个对象来记录,原因是不同的关系会有不同的callinfo定义,最终callinfo会多起来。不用callinfo就要求协程能够管理一个流程的全部生命周期,包括开始和结束,这样如果两个流程间有相互关系,将会难于处理。所以,还是要使用callinfo,不过,callinfo只有两个,p2p和meetcallinfo。
4、考虑一种业务流程:A,B,C三个用户正在进行一个呼叫流程,比如转接,c正在振铃,还未接通。此时另外一个更高优先级流程需要把C拉入新的流程中。要求最好c能够不用挂机再发起呼叫,并且原有的流程能够正常处理。实现这个功能需要增加一个user_man模块,负责用户管理。所有对用户的呼出都调用此模块的接口进行呼出,每次呼出都申请一个新的协程,这个协程结束后在返回原先的协程进行处理后续操作。如果呼叫未成功时就有新的流程操作此用户,则停止原先的协程,makecall协程结束后直接进入新的协程处理。
5、当一个协程在处理过程中,如何向这个协程发送一个消息,并且让协程进行不同处理?本来考虑使用类似golang的方法,每个协程对外提供一个通道,所有的协程对外通信都使用通道的消息。可以实现,不过实现后,整个协程机制就会复杂起来。因为每个协程都有一个栈,每次的消息处理要停止原先的栈,处理完毕后要返回原先的栈,如果在消息处理时调用了协程接口,则对栈以及消息的处理会陷入混乱。所以,针对类似的需求,采用如下机制: 1)协程处理过程中发生异常,需要终止协程,可以通过协程的throw接口向协程发送不同类型的异常。
2)比较难于处理的是,如果一个用户在流程过程中被其他的协程拉去处理其他事情,而此用户离开后,不影响原有的处理流程,所以也无法终止原有流程(如果要能够终止就好处理了)。当原有流程需要处理此用户是,可能会和用户当前处理的协程造成冲突。比如:强插后一用户保持,强插结束用户要恢复通话,当此时此用户已经被其他用户拉入了其他的处理流程中,如何好好的处理。如下方案: 1、一个用户只运行一个协程进行处理,每次协程开始是,都要设置用户的锁(或者标志),设置锁后,其他的协程不可以在处理此用户,除非是被强制解锁。缺点:提高编程复杂度,一个用户在被协程使用前,一定要设置过才可以,不使用的时候 要解锁。容易出错。确定方案,就用锁的方案:在协程开始的时候加锁,协程结束后解锁。只有对用户加锁的协程才可以处理用户。 2、取消和补偿函数也要调整:目前是没有锁的方案的,现在要增加锁,补偿是人工不是此协程锁定此用户了,那么也就无法再进行对用户的操作了。 3、最终解决方案:一个大的模块,负责维护所有的user,实现user的makecall接口(所有的makecall必须从这里走)。实现所有的as 呼叫控制接口,对用户的呼叫控制等操作前,都要判断用户是否被当前操作协程控制。所有的补偿操作也必须经过协程是否控制用户的判断。用户原先被一个协程控制,如果要被另外一个协程控制,则需要清楚用户相关所有状态:放音则停止放音,录音则停止录音,会场中则离开会场,总之,恢复到呼叫接通原始状态。如果出于稳定状态,没有在携程中,则另外处理。 终极方案:对每调用一个呼叫控制接口则设置一下协程状态,然后,可以在不同的状态下受到不同的事件,进行不同的处理,这是最繁琐的一种方法。不过也是最明显,最简单明了的一种方法。
6、如何对用户进行挂机或呼叫结束操作?用户分两种,主动呼入与呼出,这些用户应该如何挂机那?还是根据业务的需要,由上层业务自己选择,或者在协程的补偿中挂机,或者等待操作员的操作下挂机,或者使用类似垃圾回收机制,如果没有呼叫或者协程在用户上,则自动垃圾回收掉。——根据业务再看看。
拆线操作,第一步流程抢占:把用户从原有流程中拉出,原有流程进行用户拉出操作。第二步,判断用户状态,如果已经通话,则挂机,如果处在新呼叫的协程中,则停止新的协程。
每个流程根据不同的情况,选择对用户的不同处理。同时,考虑增加垃圾回收保护。
7、编码的过程中,发现Greenlet原有的模式:父协程调用子协程,子协程处理完成后,switch到父协程来处理。这种模式非常容易出错,原因是他们在协程交互过程中,如果涉及到交互,很容易出错:父协程想等待,却等不到子协程的运行结果。所以,考虑按照go协程的处理方法来处理协程。
8、重新考虑一个模块管理所有的用户:呼叫,挂机,抢占。
协程的kill有问题:kill时,协程可能已经结束,但是还在协程任务列表中未被处理。
9、用户抢占时要在统一入口中考虑,记录当前正在抢的用户。如果有新的流程对同一个用户抢占,则停止之前的抢占。用户抢占成功后给新的流程。 抢占就是clear user的所有呼叫操作:(录音,放音,连接,会议等),直到刚呼通,没有任何操作的状态。 有一个问题是抢占时如果用户正在进行操作,比如connect,不能做到很好的取消。这是需要取消协程才能够取消的。 协程间发送消息如果要等待响应要能够进行coro操作。 呼叫的抢占:一定要先停止协程,再情况用户操作。因为协程取消是可能会影响新的操作。 所有流程的session在流程创建的时候申请,用于保存流程的相关信息,报个各个用户的抢占处理函数。

总结:使用python协程简化流程开发,比较难以搞定的是状态和数据的同步问题。如果协程间关联不大,则相对比较简单,但是我们公司面临的挑战往往不是性能,而是业务逻辑复杂度,特别是流程间无时无刻都存在的流程交互。这是我用python协程解决类似问题的第二次尝试,目前已经基本解决,不过仍有优化空间,部分模块复杂度还是有些高。


相关内容

    暂无相关文章

评论关闭