用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協程解決類似問題的第二次嘗試,目前已經基本解決,不過仍有優化空間,部分模塊複雜度還是有些高。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章