呼叫中心MRCP Server的負載均衡解決方案,看這篇就夠了


 
  虎年開工第一周,放眼望去,到處都是虎虎生威的好景象,咱也不能閑着,本想吟詩一首助助興,奈何俺沒那文化底蘊!
  思來想去,還是寫個關於ASR的技術文檔吧,即可以總結過去的經驗,也可放眼未來不是……

一、背景介紹

  開始聊MRCP Server的負載均衡解決方案前,我們先簡單看看為啥要做這件事。
  隨着人工智能技術的發展,在呼叫中心業務中,傳統的IVR(電話導航)按鍵導航模式在逐步向IVR智能客服轉變(客戶與IVR機器人進行語音對話),而這離不開ASR(語音識別)服務的使用。呼叫中心系統底層(如FreeSWITCH)調用實時ASR的方式通常是基於MRCP協議實現。
  呼叫中心使用ASR功能,其實包含下面3種組件:
  • MRCP客戶端:發送RTP和SIP/MRCP的發起者,如FreeSWITCH(下文簡稱FS)
  • MRCP服務端:處理MRCP/SIP信令,接收並轉發RTP
  • ASR引擎  :解析RTP,將語音轉換成文本,並返回給MRCP Server
 
  從上圖可以發現,對於呼叫中心ASR調用者而言,只需要關心怎么對接MRCP Server即可,無需關注ASR Engine部分。
  在實際使用過程中,如果你采購第三方ASR系統進行私有化部署的話(比如科大訊飛ASR、百度ASR),通常MRCP Server和ASR Engine是打包在一起,並部署在同一機器上。但無論你采購哪家的ASR產品進行集群化部署,廠商都沒有提供ASR的負載均衡解決方案,需要客戶自行解決。

二、方案分析

       調用MRCP Server其實包含SIP(UDP/TCP)、MRCP(TCP)、RTP(UDP)三部分,然而MRCP和RTP的服務端地址是由SIP INVITE的響應 200 OK中SDP指定(如下圖),所以只要完成對SIP負載均衡就能解決另外兩個,那么要給MRCP Server做負載均衡就變成了給 SIP(UDP/TCP)做負載均衡了。
       我們期望負載均衡的效果是:只要服務端集群下有多台機器,即使客戶端只有一個,負載均衡設備也能將請求均勻分發給服務端的每一個成員。
      
       常規的負載均衡方案,無外乎基於硬件負載均衡設備實現,如A10(即AX)、F5、NetScaler等;或者基於軟負載實現,如LVS、Nginx等。但這些常規方法,都無法真正做到給MRCP Server實現負載均衡。 
       我們以FS作為MRCP Client,AX作為負載均衡設備為例,其他方案也存在跟AX一樣的問題。
       FS通過AX設備與MRCP Server之間進行SIP交互時,首先,對於FS和AX設備相對固定的情況下,SIP請求的IP四元組(Source IP、Source port、Destination IP、Destination port)就不會發生變化,因為FS對接MRCP Server時,會在MRCP配置文件中指定客戶端和服務端的IP/Port,所以AX每次分配給FS的MRCP Server都是同一台,這顯然不符合負載均衡的預期;其次,AX設備默認的會話保持時長為120秒,但電話場景,在收到200 OK后,可能長達半小時不會再有SIP交互,這會導致后續的SIP無法送達。
 
       既然此路不通,我們自然要考慮其他解決方案,咱堂堂七尺男兒,也不能在一棵樹上吊死不是……
       在這我給出兩個解決方案,以及各自的優缺點:
  • 方案A:通過FreeSWITCH的distributor模塊實現
  • 方案B:通過OpenSIPs實現
 
優點
缺點
方案A
1、無需依賴第三方負載均衡組件
1、配置繁瑣復雜
2、MRCP Server節點增刪,都需要調整FS配置文件,而且得在無ASR業務時,才能加載生效
3、端口數量消耗大(每個MRCP Server都需要單獨分配端口段)
4、負載均衡策略相對單一,只支持按比例分配
方案B
1、配置簡單
2、MRCP Server節點增刪,只需調整OpenSIPs的DB即可,有ASR調用時,也可更改,實時生效
3、端口數量消耗小(只需要配置一個MRCP Profile文件,多個MRCP Server共用端口段)
4、負載均衡方案多種多樣,支持按比例、輪詢等多種方式
1、需要依賴第三方負載均衡組件OpenSIPs

 三、方案詳解

        接下來,我們詳細看看每種方案的具體實現方式。以下方案運行環境為:CentOS 7.6、FreeSWITCH 1.6.20、OpenSIPs 2.4.2。
        本篇文章中,我們不詳細講解每種方式的實現原理,只介紹解決方法,有興趣的同學可以自行學習FS和OpenSIPs的相關功能點,這里給出幾個鏈接:
        
        假設我們只有一台FS作為MRCP 客戶端,並且MRCP Server 集群中有兩台服務器,分別是 mrcp1 和 mrcp2,希望FS針對每一通電話執行ASR命令時,請求可均勻分配給兩個MRCP Server。

3.1 基於FS的distributor模塊實現MRCP Server的LB

        該方案的核心思路如下:
  1. FS直接與MRCP  Server對接,為MRCP Server集群下每一個成員配置一個profile
  2. 將MRCP Server集群下的所有成員配置成 FS 的網關,並開啟網關的SIP OPTION探測功能,同時確保gateway的name要與mrcp_profile文件中profile的name一致
  3. 通過FS的distributor 模塊為這些MRCP網關配置負載均衡策略
  4. 最后,實際執行ASR命令時,先通過 expand eval ${distributor mrcp ${sofia profile external gwlist down}} 負載均衡分配得到一個可用的 MRCP Server Profile的名稱,然后用該MRCP Profile的名稱作為FS play_and_detect_speech ASR命令的參數即可。
        Are you ready!咱們上配置步驟:
  • 第一步: FS與MRCP Server對接 
在 /usr/local/freeswitch/conf/mrcp_profiles/下配置FS對接MRCP Server的文件
tree /usr/local/freeswitch/conf/mrcp_profiles
├── mrcp1.xml
└── mrcp2.xml

下面只給出mrcp1.xml的部分核心配置,只需要確保mrcp1.xml和mrcp2.xml里client-port、rtp-port-min、rtp-port-max配置不同即可
這里可以看到,如果MRCP Server集群有很多機器,那么這里的RTP端口段可能不夠用,一通電話進行ASR解析需要2個端口,一個用來傳輸RTP、一個傳RTCP
<include>
  <profile name="mrcp1" version="2">   【每個MRCP Server這里配置的名稱都不一樣,但一定有一個相同名稱的網關】
    <param name="client-ip" value="192.168.1.99"/>
    <param name="client-port" value="client-port-1"/>
    <param name="server-ip" value="server-ip-1"/>
    <param name="server-port" value="8060"/>
    <param name="sip-transport" value="tcp"/>   【也可以是UDP哦】
    <param name="rtp-ip" value="192.168.1.99"/>
    <param name="rtp-port-min" value="min-port-1"/>
    <param name="rtp-port-max" value="max-port-1"/>
    <param name="ua-name" value="FreeSWITCH"/>
  </profile>
</include>
  • 第二步:配置FS網關
在 /usr/local/freeswitch/conf/sip_profiles/external/下配置網關對接文件
/usr/local/freeswitch/conf/sip_profiles/external
├── mrcp1.xml
└── mrcp2.xml

mrcp1.xml 的詳細配置如下:
<gateway name="mrcp1">  【gateway的name要與mrcp_profile文件中profile的name一致,或可以按照某種規則轉換】
        <param name="username" value=""/>
        <param name="proxy" value="mrcp1-server-ip:8060"/>  【當然這里端口可能是其他值】
        <param name="realm" value="mrcp1-server-ip"/>
        <param name="register" value="false"/>
        <param name="rtp-autofix-timing" value="false"/>
        <param name="caller-id-in-from" value="true"/>
        <param name="ping" value="10"/>    【FS給proxy對應地址發送探測的周期】
        <param name="ping-max" value="5"/>
        <param name="ping-min" value="2"/>
</gateway>
  • 第三步:配置FS的distributor模塊
vim /usr/local/freeswitch/conf/autoload_configs/distributor.conf.xml
<configuration name="distributor.conf" description="Distributor Configuration">
  <lists>
    <list name="mrcp">  【權重配置成一樣,相當於兩個MCRP server 按 1:1 分配】
      <node name="mrcp1" weight="5"/>  【node name值與sip gateway 名稱相同】
      <node name="mrcp2" weight="5"/>
    </list>
  </lists>
</configuration>
  • 最后,來看看使用效果
按照上述配置,將mrcp2服務宕機后,執行負載均衡的效果如下:
freeswitch@LPT0596> sofia profile external gwlist down  【獲取宕機的網關】
mrcp2
freeswitch@LPT0596> expand eval ${distributor mrcp ${sofia profile external gwlist down}}   【將宕機的網關排除在外后,獲取分配的SM節點】
mrcp1
freeswitch@LPT0596> expand eval ${distributor mrcp ${sofia profile external gwlist down}}   【如果mrcp2沒有宕機,這里將返回mrcp2】
mrcp1

使用得到的MRCP Server Profile名稱執行ASR命令:play_and_detect_speech
/usr/local/freeswitch/sounds/ivr_prompt_voice.wav detect:unimrcp:mrcp1 {start-input-timers=false,no-input-timeout=10000,recognition-timeout=10000}ahlt_ats

  其他FS相關命令:

  • reload mod_unimrcp : 修改FS與MRCP server對接的文件后,重新加載生效【只有當前沒有正在執行的ASR操作時,才能重加載】
  • sofia profile external rescan : 重新加載FS的網關配置

3.2 基於OpenSIPs實現MRCP Server的LB

3.2.1核心思路

  FS不直接與MRCP Server對接,而是與OpenSIPs進行對接。對接方式是把OpenSIPs配置成一個MRCP profile,文件中的server-ip 和 server-port 地址配置成OpenSIPS 的服務地址即可。
  FS執行ASR命令時,先將SIP請求發送給OpenSIPs,再由OpenSIPs負載均衡到MRCP Server集群中的成員,交互的時序圖如下:
   

3.2.2 方案分析

        通過OpenSIPs來實現對MRCP的負載均衡需要解決下面幾個問題:
  • 問題1、如何判斷收到的INVITE請求是要執行ASR命令,還是普通呼叫命令?
  • 問題2、知道是執行ASR命令后,如何選擇MRCP Server,進行分配?
  • 問題3、如果有多套MRCP Server集群,比如一套百度MRCP,一套阿里MRCP,客戶端希望能指定引擎使用,該如何解決?
 
        既然有問題,那咱們就解決問題,決不能被困難嚇倒。等我大碗喝酒,大口吃肉后,我就能將他們各個擊破,你信不信……
        
  • KO-問題1
        我們來看一條FS發送給OpenSIPs,請求執行MRCP負載均衡的SIP INVITE信息,其中 192.168.1.99是FS,192.168.1.18是OpenSIPs。
INVITE sip:192.168.1.18:5070 SIP/2.0
Via:  SIP/2.0/UDP 192.168.1.99:5102;rport;branch=z9hG4bKQ21yZS46ytrgF
Max-Forwards:  70
From:  <sip:192.168.1.99:5102>;tag=4B8SvQe66FNvc
To:  <sip:192.168.1.18:5070>
Call-ID: ed9f5f6b-0673-123b-199a-fa163e72d95e
CSeq:  47770741 INVITE
Contact:  <sip:192.168.1.99:5102>
User-Agent:  FreeSWITCH
Allow:  INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE
Supported:  timer, 100rel
Content-Type:  application/sdp
Content-Disposition:  session
Content-Length:  306
 
v=0
o=FreeSWITCH 2480643166757753319 6144298267054033408 IN IP4 192.168.1.99
s=-
c=IN IP4 192.168.1.99
t=0 0
m=application 9 TCP/MRCPv2 1
a=setup:active
a=connection:existing
a=resource:speechrecog
a=cmid:1
m=audio 31799 RTP/AVP 0 8
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=sendonly
a=mid:1
View Code:SIP INVITE信令內容  
        從上面信令可以看到,FS發起的INVITE中,沒有主被叫號碼信息,只有FS和OpenSIPs的IP和端口信息。如果我們的OpenSIPs只用來給MRCP Server做負載均衡,那么就很簡單,收到INVITE請求,都認為是請求執行ASR命令,分配給MRCP Server即可。但是,OpenSIPs只給MRCP Server做負載豈不是大材小用了!所以,實際我們不會這樣使用,我們的OpenSIPs通常還會給其他呼叫中心組件做負載均衡,比如給FreeSWITCH、語音網關、分機注冊服務等做LB。這樣OpenSIPs就會收到來自各種組件的SIP INVITE請求,那么我們該如何判斷收到的 INVITE 是要執行ASR命令,還是要做其他業務呢?
 
        常規思路,自然是OpenSIPs分析INVITE的SIP消息頭,從中進行判斷。可是由於FS的mod_unimrcp模塊的限制,FS執行ASR命令時,發送的SIP INVITE里不支持增加自定義SIP消息頭,所以我們只能從標准SIP 消息頭中進行挖掘。
  • 跟據INVITE請求的源IP:不可行,因為同一個源IP可能發起多種請求的INVITE,比如FS可能是請求執行ASR,也可能是請求呼叫手機;此外,即使可行,源IP也不方便維護。
  • 跟據INVITE請求的目的IP:不可行,所有INVITE請求的該值都一樣
  • 跟據INVITE請求的User-Agent頭:可行,OpenSIPs通過$ua就能獲取該值。雖然不能針對每次INVITE自定義不同的UA頭,但FS對接MRCP Server的Profile中可以指定一個統一的User-Agent頭,默認是FreeSWITCH。
  • 跟據INVITE請求SDP信息中的‘m’頭:可行,OpenSIPs通過$(rb{sdp.line,m})就能獲取該值。如 上面報文中“m=application 9 TCP/MRCPv2 1” 里面有MRCPv2,可跟據這個判斷是執行ASR。
        建議使用User-Agent頭進行區分,取值方便,效率高。所以,FS對接OpenSIPs時,配置的MRCP Profile時,指定一個特別的User-Agent,比如叫ASR_MRCP_CLIENT_FS,OpenSIPs收到INVITE請求,優先判斷UA信息,如果是ASR_MRCP_CLIENT_FS,那么就是要執行ASR命令。
 
  • KO-問題2
        我們可以使用OpenSIPS的load_balancer 或 dispatcher 模塊來實現對 MRCP Server 服務端的負載均衡,兩種方式的特點如下:
  優點 缺點
load_balancer
可控制每個MRCP Server的最大並發量
支持監控分配給每個MRCP Server的實時並發量
分配策略單一:只支持空閑優先策略分配和按比例分配兩種策略,無法支持記憶輪訓,這就導致但MRCP Server集群新增成員時,會將流量全部分配給新增的機器
dispatcher      分配策略多種多樣:如支持記憶輪訓、Hash分配等
不能控制每個MRCP Server的最大並發量,話務量暴漲時,存在雪崩隱患
不能監控分配給每個MRCP Server的實時並發量(但可以自行通過OpenSIPs其他模塊實現)
  • KO-問題3
        在FS上為每一套MRCP Server集群,配置一個MRCP Profile並且都指向OpenSIPs,但User-Agent的值配置成不一樣,OpenSIPs跟據UA的不同,來選擇該給哪個集群做LB。
baidu_mrcp_lb.xml  下面只給出特有配置,其他配置被省略了
<include>
  <profile name="baidu_mrcp_lb" version="2">   【阿里的配置,name為ali_mrcp_lb】
    <param name="server-ip" value="opensips-ip"/>
    <param name="ua-name" value="ASR_MRCP_CLIENT_FS_BAIDU"/>    【阿里的配置,ua-name為ASR_MRCP_CLIENT_FS_ALI】
    <param name="sdp-origin" value="FS_MRCP"/>
  </profile>
</include>
        OpenSIPs給MRCP Server做負載均衡的處理流程圖如下:依賴dialplan模塊進行選擇具體通過哪個模塊來執行LB
        

3.2.3 具體實現

   如果OpenSIPs本身也是集群化部署,那么可以通過本文3.1章節的方法實現對OpenSIPs的負載均衡。
 
        下面代碼涉及OpenSIPs對dialplan、dispatcher、load_balancer幾個模塊的使用,本文不講解這部分的使用方法。
  • 數據庫初始化
說明:
        (1) 下方配置了百度、阿里兩個MRCP Server集群,並且每個集群都部署在了兩個IDC(IDC_A和IDC_B)
        (2) OpenSIPs跟據dialplan撥號方案來為阿里和百度選擇負載均衡的方式,dialplan表中字段“attrs”配置邏輯是:[MRCP集群第一路由的集群ID:負載均衡實現方式:集群名稱],如“90:DS:ASR_MRCP_SERVER_CTRIP_ALI”代表,阿里MRCP第一路由的集群ID是90,采用dispacher模塊實現LB;"90:DS:ASR_MRCP_SERVER_CTRIP_ALI”代表,百度MRCP第一路由的集群ID是91,采用load_balancer模塊實現LB
        (3) 無論是dispacher,還是load_balancer,都配置了單IDC下負載均衡的基礎上,增加了逃生路由的功能。集群ID為 90/91代表第一路由,10090/10091代表第二路由
dialplan的attrs字段被賦予了特殊用途
INSERT INTO `dialplan`(`dpid`,`pr`,`match_op`,`match_exp`,`match_flags`,`subst_exp`,`repl_exp`,`timerec`,`disabled`,`attrs`) VALUES (90,1000,1,'^ASR_MRCP_CLIENT_CTRIP_FS_ALI$',0,NULL,NULL,NULL,0,'90:DS:ASR_MRCP_SERVER_CTRIP_ALI'), (90,1000,1,'^ASR_MRCP_CLIENT_CTRIP_FS_BAIDU$',0,NULL,NULL,NULL,0,'91:LB:ASR_MRCP_SERVER_BAIDU');
dispatcher的attrs字段沒有實際作用 INSERT INTO `dispatcher` (`setid`, `destination`, `state`, `weight`, `priority`, `attrs`, `description`) VALUES (
90, 'sip:192.168.1.190:8060', 0, 1, 100, 'pstn=100', 'IDC_A:ASR_MRCP_SEVER_ALI'), (90, 'sip:192.168.1.191:8060', 0, 1, 100, 'pstn=100', 'IDC_A:ASR_MRCP_SEVER_ALI'), (10090, 'sip:192.168.2.198:8060', 0, 1, 100, 'pstn=100', 'IDC_B:ASR_MRCP_SEVER_ALI');
load_balancer的resources字段可以控制最大並發數 INSERT INTO `load_balancer`(`group_id`,`dst_uri`,`resources`,`probe_mode`,`description`) VALUES (
91,'sip:192.168.1.180:8060','pstn=50',2,'IDC_A:ASR_MRCP_SEVER_BAIDU'), (91,'sip:192.168.1.181:8060','pstn=50',2,'IDC_A:ASR_MRCP_SEVER_BAIDU'), (10091,'sip:192.168.2.188:8060','pstn=50',2,'IDC_B:ASR_MRCP_SEVER_BAIDU');

      配置好后,可查看集群內MRCP成員的狀態:

 1 sudo /usr/local/opensips/sbin/opensipsctl fifo lb_list
 2 Destination:: sip:192.168.1.180:8060 id=1 group=90 enabled=yes auto-reenable=on
 3         Resources::
 4                 Resource:: pstn max=50 load=50
 5 Destination:: sip:192.168.1.180:8060 id=2 group=90 enabled=no auto-reenable=on
 6         Resources::
 7                 Resource:: pstn max=50 load=0
 8 Destination:: sip:192.168.2.188:8060 id=3 group=90 enabled=yes auto-reenable=on
 9         Resources::
10                 Resource:: pstn max=50 load=10
11 
12 sudo /usr/local/opensips/sbin/opensipsctl fifo ds_list
13 PARTITION:: default
14         SET:: 90
15                 URI:: sip:192.168.1.190:5080 state=Active first_hit_counter=8
16                         attr:: pstn=500
17                 URI:: sip:192.168.1.191:5080 state=Inactive first_hit_counter=0
18                         attr:: pstn=500
19         SET:: 10090
20                 URI:: sip:192.168.2.198:8060 state=Active first_hit_counter=0
21                         attr:: pstn=100
View Code:查看集群節點狀態
  • OpenSIPs代碼實現
 1 route{
 2     #省略N多代碼...
 3     
 4     # check sip INVITE message source ip and port
 5     if (is_method("INVITE")) {
 6         xlog("ua = $ua , callid = $ci, fu = $fu , tu = $tu , ru = $ru , du =$du src:$si, $(rb{sdp.line,m}))");
 7         $var(dlgPingTag) = "Pp";
 8         if ( $ua == "ASR_MRCP_CLIENT_FS" ) { #to_asr_mrcp_server
 9                $var(dlgPingTag) = ""; # ASR 的SIP通道不能做OPTION探測 10         }
11         if ( !create_dialog("$var(dlgPingTag)") ) {
12             route(PRINT_LOG, "create_dialog error :  Internal Server Error");
13             send_reply("500","SM Internal Server Error");
14             exit();
15         }
16 
17         if ( $ua =~ "^ASR_MRCP_CLIENT_CTRIP_FS*" ) { #to_asr_mrcp_server 【需要修改FS mrcp client配置文件,<param name="ua-name" value="ASR_MRCP_CLIENT_FS..."/>】 18                 if ( dp_translate("90", "$ua/$avp(dest)", "$var(attrs)") ) { #撥號方案判斷
19                         route(exeLb, $(var(attrs){s.int}), "pstn", $(var(attrs){s.select, 1,:}), $(var(attrs){s.select, 2,:}));
20                 }
21         } else { #處理其他呼叫類型,如呼叫手機等
22             #省略N多代碼...
23         }
24     }
25     exit();
26 }
27 
28 #usage : route(exeLb, lb_group_id, resource_type, node_type, lb_method)
29 #e.g.  route(exeLb, 90, "pstn", "ASR_MRCP_SERVER_ALI", "LB")
30 #e.g.  route(exeLb, 90, "pstn", "ASR_MRCP_SERVER_ALI", "DS")
31 route[exeLb]{
32     $var(lb_group_id) = $param(1);
33     $var(lb_group_id_bak) = $param(1) + 10000;
34     $var(resource_type) = $param(2);
35     $var(node_type) = $param(3);
36     $var(lb_method) = $param(4);
37     
38     xlog("[$fU->$rU] Route $rU to '$var(node_type)' by load_balancer group_id : '$var(lb_group_id)' [back_group_id:'$var(lb_group_id_bak)'], resource_type : '$var(resource_type)', node_type : '$var(node_type)' [ci:$ci] [xcid:$hdr(X-CID)]");
39 
40     $var(lbRst) = 0;
41     if( $var(lb_method) == "DS" ) {
42         $var(lbRst) = ds_select_dst("$var(lb_group_id)", "4");
43         if($var(lbRst) == -1) {
44             xlog("[exeLb4CM] [$fU->$rU] Failed --->lbRst=$var(lbRst) Route $rU to '$var(node_type)' by dispatcher group_id : '$var(lb_group_id)', resource_type : '$var(resource_type)' [ci:$ci]");
45             $var(lbRst) = ds_select_dst("$var(lb_group_id_bak)", "4");
46             if(!$var(lbRst)) {
47                 xlog("[exeLb4CM] [$fU->$rU] Failed ===>lbRst=$var(lbRst) Route $rU to '$var(node_type)' by dispatcher [back_group_id:'$var(lb_group_id_bak)'], resource_type : '$var(resource_type)' [ci:$ci]");
48                 }
49         }
50 
51     } else {
52         $var(lbRst) = lb_start_or_next("$var(lb_group_id)", "$var(resource_type)", "s");
53         if( $var(lbRst) < 0) {
54             xlog("[$fU->$rU] Failed --->lbRst=$var(lbRst) Route $rU to '$var(node_type)' by load_balancer group_id : '$var(lb_group_id)', resource_type : '$var(resource_type)' [ci:$ci]");
55             $var(lbRst) = lb_start("$var(lb_group_id_bak)", "$var(resource_type)", "s");
56             if( $var(lbRst) < 0) {
57                 xlog("[$fU->$rU] Failed ===>lbRst=$var(lbRst) Route $rU to '$var(node_type)' by load_balancer [back_group_id:'$var(lb_group_id_bak)'], resource_type : '$var(resource_type)' [ci:$ci]");
58             }
59         }
60 
61     }
62 
63     if ( $var(lbRst) > 0) {
64             if ( $rU == null ) {  #對於FS 發起的 MRCP INVITE 請求, $rU 為 null, 而不設置 $rU 將導致 Load balancer 失敗,所以需要初始化一個值
65                     xlog("[$fU->$rU] rU is null, then initialize to 'Null2Sm' [ci:$ci] [xcid:$hdr(X-CID)]");
66                     #$rU = "Null2SM";
67                      $ru = "sip:" + $(du{uri.host}) + ":" + $dp;
68             } else {
69                    $ru = "sip:" + $rU + "@" + $(du{uri.host}) + ":" + $dp;
70             }
71             xlog("[$fU->$rU] Route to '$var(node_type)' --> [$du] [ci:$ci] [xcid:$hdr(X-CID)]");
72             route(relay);
73     } else {
74             xlog("[$fU->$rU] No available '$var(node_type)' now [ci:$ci] [xcid:$hdr(X-CID)]");
75             t_reply("480", "$var(node_type) Unavailable");
76             exit();
77     }
78 
79 }
  如果按照上面腳本執行了 $ru = "sip:" + $rU + "@" + $(du{uri.host}) + ":" + $dp;,但是$rU== null 並且不設置 $rU="Null2SM"或者其他非空值,會爆如下錯誤
  解決辦法:
  1. 設置$rU 為一個非空值
  2. 直接不修改$ru 的值
  3. 修改 $ru = "sip:" + $(du{uri.host}) + ":" + $dp;
1 Feb 12 22:27:35 fat5410 /usr/local/opensips/sbin/opensips[3710]: ERROR:core:parse_uri: bad char '@' in state 0 parsed: <sip:> (4) / <sip:@192.168.1.190:8060> (20)
2 Feb 12 22:27:35 fat5410 /usr/local/opensips/sbin/opensips[3710]: ERROR:core:parse_sip_msg_uri: bad uri <sip:@192.168.1.190:8060>
3 Feb 12 22:27:35 fat5410 /usr/local/opensips/sbin/opensips[3710]: ERROR:core:pv_get_ruri_attr: failed to parse the R-URI
View Code:OpenSIPs 報錯日志

3.2.4 信令記錄:

  • FS 發送INVITE給 OpenSIPs
 1 2022-02-13 13:50:53 +0800 : 192.168.1.99:5221 -> 192.168.1.18:5070
 2 INVITE sip:192.168.1.18:5070 SIP/2.0   你可以看到,這里沒有被叫號碼,所以到了OpenSIPs 后  $rU是null
 3 Via: SIP/2.0/UDP 192.168.1.99:5221;rport;branch=z9hG4bKFUXeX5r0Q0gXp
 4 Max-Forwards: 70
 5 From: <sip:192.168.1.99:5221>;tag=3pU7FrrQBQ1NS
 6 To: <sip:192.168.1.18:5070>
 7 Call-ID: 5c97aeaf-64b4-123a-02b4-fa163ea03f01
 8 CSeq: 38878534 INVITE
 9 Contact: <sip:192.168.1.99:5221>
10 User-Agent: ASR_MRCP_CLIENT_FS_ALI
11 Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE
12 Supported: timer, 100rel
13 Content-Type: application/sdp
14 Content-Disposition: session
15 Content-Length: 299
16  
17 v=0
18 o=FS_MRCP 1321904497415698834 1659019553944433241 IN IP4 192.168.1.99
19 s=-
20 c=IN IP4 192.168.1.99
21 t=0 0
22 m=application 9 TCP/MRCPv2 1
23 a=setup:active
24 a=connection:new
25 a=resource:speechrecog
26 a=cmid:1
27 m=audio 16416 RTP/AVP 0 8
28 a=rtpmap:0 PCMU/8000
29 a=rtpmap:8 PCMA/8000
30 a=sendonly
31 a=mid:1
View Code:FS發送INVITE
  • OpenSIPS 轉發INVITE給 MRCP server
2022-02-13 13:50:53 +0800 : 192.168.1.18:5070 -> 192.168.1.190:8060
INVITE sip:192.168.1.18:5070 SIP/2.0    [如果修改$rU, 這里就是  INVITE sip:Null2SM@192.168.1.18:5070 SIP/2.0]
Record-Route: <sip:192.168.1.18:5070;lr;did=de7.2517abb1>
Via: SIP/2.0/UDP 192.168.1.18:5070;branch=z9hG4bK9443.c1265465.0
Via: SIP/2.0/UDP 192.168.1.99:5221;received=192.168.1.99;rport=5221;branch=z9hG4bKFUXeX5r0Q0gXp
Max-Forwards: 69
From: <sip:192.168.1.99:5221>;tag=3pU7FrrQBQ1NS
To: <sip:192.168.1.18:5070>
Call-ID: 5c97aeaf-64b4-123a-02b4-fa163ea03f01
CSeq: 38878534 INVITE
Contact: <sip:192.168.1.99:5221>
User-Agent:ASR_MRCP_CLIENT_FS_ALI
Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE
Supported: timer, 100rel
Content-Type: application/sdp
Content-Disposition: session
Content-Length: 299
X-UUI: &XCID=0dc0196031626864653EXCIDEND
X-CID: 0dc0196031626864653EXCIDEND
 
v=0
o=FS_MRCP 1321904497415698834 1659019553944433241 IN IP4 192.168.1.99
s=-
c=IN IP4 192.168.1.99
t=0 0
m=application 9 TCP/MRCPv2 1
a=setup:active
a=connection:new
a=resource:speechrecog
a=cmid:1
m=audio 16416 RTP/AVP 0 8
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=sendonly
a=mid:1
View Code:OpenSIPs轉INVITE
  • MRCP Server 回復200 OK,返回后續接收RTP的真實地址
 1 2022-02-13 13:50:53 +0800 : 192.168.1.190:8060 -> 192.168.1.18:5070
 2 SIP/2.0 200 OK
 3 Via: SIP/2.0/UDP 192.168.1.18:5070;branch=z9hG4bK9443.c1265465.0
 4 Via: SIP/2.0/UDP 192.168.1.99:5221;received=192.168.1.99;rport=5221;branch=z9hG4bKFUXeX5r0Q0gXp
 5 Record-Route: <sip:192.168.1.18:5070;lr;did=de7.2517abb1>
 6 From: <sip:192.168.1.99:5221>;tag=3pU7FrrQBQ1NS
 7 To: <sip:192.168.1.18:5070>;tag=45D4K1DvpQQXK
 8 Call-ID: 5c97aeaf-64b4-123a-02b4-fa163ea03f01
 9 CSeq: 38878534 INVITE
10 Contact: <sip:192.168.1.190:8060>
11 User-Agent: BaiduSpeech SofiaSIP 1.5.0
12 Accept: application/sdp
13 Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE
14 Supported: timer, 100rel
15 Session-Expires: 600;refresher=uac
16 Min-SE: 120
17 Content-Type: application/sdp
18 Content-Disposition: session
19 Content-Length: 303
20  
21 v=0
22 o=BaiduSpeechServer 8512797916186481341 4497985761629564802 IN IP4 192.168.1.190    【接收RTP的IP】
23 s=-
24 c=IN IP4 192.168.1.190
25 t=0 0
26 m=application 1544 TCP/MRCPv2 1
27 a=setup:passive
28 a=connection:new
29 a=channel:b250b76cea1011eb@speechrecog
30 a=cmid:1
31 m=audio 18380 RTP/AVP 0    【接收RTP的端口】
32 a=rtpmap:0 PCMU/8000
33 a=recvonly
34 a=mid:1
View Code:MRCP Server回200 OK
  • FS發送ACK給OpenSIPs
 1 2022-02-13 13:50:53 +0800 : 192.168.1.99:5221 -> 192.168.1.18:5070
 2 ACK sip:192.168.1.190:8060 SIP/2.0
 3 Via: SIP/2.0/UDP 192.168.1.99:5221;rport;branch=z9hG4bKFUXeX5r0Q0gXp
 4 Route:  <sip:192.168.1.18:5070;lr;did=355.53f8e331>
 5 Max-Forwards:  70
 6 From: <sip:192.168.1.99:5221>;tag=3pU7FrrQBQ1NS
 7 To: <sip:192.168.1.18:5070>
 8 Call-ID: 5c97aeaf-64b4-123a-02b4-fa163ea03f01
 9 CSeq:  38878534 ACK
10 Contact: <sip:192.168.1.99:5221>
11 Content-Length:  0
View Code:FS發送ACK
  • 最后,OpenSIPs將ACK轉發給MRCP Server
 
后記:
        Congratulations!這么長的文章,你居然看完啦。
        不管你是否接受,我都必須送你一朵小紅花……
     
        拿走不謝,哥就是這么大氣,我浪里個浪,浪里個浪……
        

 

 

 
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM