Просмотр исходного кода

修复JSSIP网关异常占用并发数未释放问题,新增日志输出

lmx 6 дней назад
Родитель
Сommit
5619ec9ea8

+ 8 - 2
src/main/java/com/telerobot/fs/wshandle/impl/CallApi.java

@@ -1318,15 +1318,18 @@ public class CallApi extends MsgHandlerBase {
                         gatewayConfig.getGatewayAddr(),
                         gatewayConfig.getGwName());
 
+                // 先把网关引用挂到 customerChannel 上,保证后续任何失败路径都能通过 listener.releaseGateway() 归还许可;
+                customerChannel.setGatewayConfig(gatewayConfig);
                 String originationStr = genCallPhoneString(gatewayConfig, projectId, caseNo, uuidInner,
                         uuidOuter, phone, callType, videoLevel);
-                customerChannel.setGatewayConfig(gatewayConfig);
                 // 反向注册 Contact 解析失败时 dialstring 为空,换下一网关重试
                 if(StringUtils.isNullOrEmpty(originationStr)){
                     logger.warn("{} 软电话外呼 dialstring 为空(多为 register=2 Contact 未找到),跳过 originate,标记网关已尝试:uuid={}, register={}",
                             getTraceId(), gatewayConfig.getUuid(), gatewayConfig.getRegister());
                     triedList.add(gatewayConfig);
-                    SipGatewayLoadBalance.releaseGateway(gatewayConfig);
+                    // 统一走 listener.releaseGateway() 归还:释放后会把 customerChannel 上的网关引用置 null,
+                    // 避免后续 CHANNEL_HANGUP 事件对同一许可重复释放;
+                    listener.releaseGateway();
                     continue;
                 }
 
@@ -1350,6 +1353,9 @@ public class CallApi extends MsgHandlerBase {
                     logger.warn("{} 软电话外呼 originate 失败:uuid={}, register={}, sipCode={},加入 triedList",
                             getTraceId(), gatewayConfig.getUuid(), gatewayConfig.getRegister(),
                             customerChannel.getHangupSipCode());
+                    // 切换下一个网关前先归还当前网关许可,防止 setGatewayConfig 覆盖引用后许可永久丢失;
+                    // 若 CHANNEL_HANGUP 或 BACKGROUND_JOB 失败分支已释放过,releaseGateway 内部判空后不会重复释放;
+                    listener.releaseGateway();
                     triedList.add(gatewayConfig);
                 } else {
                     logger.info("{} 软电话外呼 originate 成功:uuid={}, register={}, 已尝试网关数={}",

+ 4 - 0
src/main/java/com/telerobot/fs/wshandle/impl/CallListener.java

@@ -400,6 +400,10 @@ public class CallListener implements IEslEventListener {
 			boolean callFailed = customerChannel.getAnsweredTime() == 0L &&
 					!customerChannel.testFlag(ChannelFlag.RECV_RING_MEDIA);
 			if (!responseOK && callFailed) {
+				// originate 失败且客户腿未建立时,可能不会再收到 CHANNEL_HANGUP 事件,
+				// 必须在此归还网关许可,否则许可永久丢失;
+				// releaseGateway 内部有判空保护并在释放后置 null,即使后续仍收到挂机事件也不会重复释放;
+				releaseGateway();
 				releaseSignal();
 				ProcessDialedResultCase(apiResponse);
 			}

+ 15 - 0
src/main/java/com/telerobot/fs/wshandle/impl/SipGatewayLoadBalance.java

@@ -177,6 +177,21 @@ public class SipGatewayLoadBalance {
                 return gw;
             }
         }
+        // 未获取到任何网关许可时,打印每个候选网关的剩余许可数,
+        // 便于运维区分“并发真满”与“许可泄漏”两种情况;
+        StringBuilder permitsInfo = new StringBuilder();
+        for (int i = 0; i < gatewayGroup.getGatewayList().size(); i++) {
+            GatewayConfig gw = gatewayGroup.getGatewayList().get(i);
+            boolean tried = triedList != null && triedList.contains(gw);
+            permitsInfo.append(String.format("[uuid=%s, gwName=%s, 配置并发=%d, 剩余许可=%d, 已尝试=%s] ",
+                    gw.getUuid(),
+                    gw.getGwName(),
+                    gw.getConcurrency(),
+                    gw.getAvailableConcurrency().availablePermits(),
+                    tried
+            ));
+        }
+        logger.warn("未获取到可用网关,groupId={}, 各网关许可情况:{}", gatewayGroupId, permitsInfo);
         return null;
     }