You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决Asterisk AMI获取活跃呼叫与通道数的异常问题?

Asterisk AMI 获取活跃呼叫与通道数问题解决方案

问题分析

  1. 事件未订阅:默认登录AMI后,服务端不会主动推送ChannelStateChange等通道相关事件,必须显式发送订阅指令。
  2. 事件监听目标错误:当前代码监听的CoreShowChannel是主动查询CoreShowChannels后的返回条目,而非实时通道状态变更事件;实时状态变更需监听ChannelStateChange事件。
  3. 事件解析逻辑缺陷:AMI事件以「多行内容+空行」为分隔单位,逐行扫描的方式无法完整捕获事件上下文,导致ChannelStateDesc字段解析失败。

解决方案

方案1:实时事件监听(推荐)

通过订阅ChannelStateChange事件实现实时状态更新:

步骤1:订阅通道事件

登录AMI成功后,立即发送事件订阅指令,指定监听通道相关事件:

// 登录后添加事件订阅
fmt.Fprintf(conn, "Action: Events\r\nEventMask: Channel\r\n\r\n")

EventMask: Channel会订阅所有通道生命周期相关事件,包括ChannelStateChange。

步骤2:修正事件收集逻辑

修改server.go的扫描逻辑,将多行事件内容收集完整后再解析:

func ConnectToAMI(address, username, password string) error {
    conn, err := net.Dial("tcp", address)
    if err != nil {
        return err
    }
    defer conn.Close()

    // 登录请求
    fmt.Fprintf(conn, "Action: Login\r\nUsername: %s\r\nSecret: %s\r\n\r\n", username, password)
    // 订阅通道事件
    fmt.Fprintf(conn, "Action: Events\r\nEventMask: Channel\r\n\r\n")

    peerStatus := &data.PeerStatus{}
    callStatus := &data.CallStatus{}
    var eventBuffer []string

    scanner := bufio.NewScanner(conn)
    for scanner.Scan() {
        line := scanner.Text()
        fmt.Println(line)

        if line == "" {
            // 空行代表一个事件结束,开始解析
            if len(eventBuffer) > 0 {
                eventContent := strings.Join(eventBuffer, "\r\n")
                switch {
                case strings.Contains(eventContent, "Event: PeerStatus"):
                    data.GetPeerStatus(eventContent, peerStatus)
                    fmt.Printf("Active peers: %d | Inactive peers: %d\n", peerStatus.Active, peerStatus.Inactive)
                case strings.Contains(eventContent, "Event: ChannelStateChange"):
                    data.GetChannelStatus(eventContent, callStatus)
                    fmt.Printf("Active calls: %d | Active channels: %d\n", callStatus.ActiveCalls, callStatus.ActiveChannels)
                }
                eventBuffer = nil
            }
        } else {
            eventBuffer = append(eventBuffer, line)
        }
    }

    return scanner.Err()
}

步骤3:优化通道状态解析

修正data.GetChannelStatus逻辑,确保正确提取ChannelStateDesc,同时建议通过Uniqueid跟踪通道避免重复计数:

func GetChannelStatus(event string, callStatus *CallStatus) {
    var state string
    var uniqueID string
    for _, line := range strings.Split(event, "\r\n") {
        switch {
        case strings.HasPrefix(line, "ChannelStateDesc: "):
            state = strings.TrimSpace(strings.TrimPrefix(line, "ChannelStateDesc: "))
        case strings.HasPrefix(line, "Uniqueid: "):
            uniqueID = strings.TrimSpace(strings.TrimPrefix(line, "Uniqueid: "))
        }
    }

    // 建议维护map记录每个通道的当前状态,避免重复加减
    // 示例:if state == "Up" && !channelMap[uniqueID] { ... }
    callStatus.UpdateCallStatus(state)
}

方案2:主动查询(CoreShowChannelsComplete)

若需要一次性获取当前通道/呼叫总数,可使用CoreShowChannels指令,通过CoreShowChannelsComplete事件直接获取统计值:

步骤1:发送查询指令

在登录后或定时发送CoreShowChannels请求:

fmt.Fprintf(conn, "Action: CoreShowChannels\r\n\r\n")

步骤2:解析返回结果

在事件处理逻辑中添加CoreShowChannelsComplete解析:

// 在事件解析的switch中添加
case strings.Contains(eventContent, "Event: CoreShowChannelsComplete"):
    var totalChannels, activeChannels int
    for _, line := range strings.Split(eventContent, "\r\n") {
        switch {
        case strings.HasPrefix(line, "TotalChannels: "):
            fmt.Sscanf(strings.TrimSpace(strings.TrimPrefix(line, "TotalChannels: ")), "%d", &totalChannels)
        case strings.HasPrefix(line, "ActiveChannels: "):
            fmt.Sscanf(strings.TrimSpace(strings.TrimPrefix(line, "ActiveChannels: ")), "%d", &activeChannels)
        }
    }
    callStatus.ActiveChannels = activeChannels
    // 活跃呼叫数通常为活跃通道数的一半(双向呼叫场景),需根据实际业务调整
    callStatus.ActiveCalls = activeChannels / 2
    fmt.Printf("Active calls: %d | Active channels: %d\n", callStatus.ActiveCalls, callStatus.ActiveChannels)

关键注意事项

  • AMI权限配置:确保manager.conf中给当前用户配置了足够权限,例如read = channel,core
  • 重复计数规避:实时事件监听时,必须通过Uniqueid跟踪每个通道的状态,避免同一通道多次触发导致计数偏差
  • 版本兼容性:不同Asterisk版本的AMI事件字段可能存在差异,建议参考对应版本官方文档

内容的提问来源于stack exchange,提问作者Vedo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 05:25:00