如何监控EWS订阅创建相关SOAP消息?排查连接超限异常
EWS流模式订阅触发Exchange节流限制排查方案
问题背景与现象
- 基于Spring的Java应用,通过EWS连接本地Exchange Server 2016,采用流模式拉取邮件
- 每30分钟通过新线程创建30分钟有效期的订阅,预期旧连接自动过期
- 单实例运行正常,双实例运行一段时间后,某实例抛出节流错误:
You have exceeded the available concurrent connections for your account. Try again once your other requests have completed.
Exchange节流配置
服务器当前节流参数:
EWSMaxConcurrency=27, MaxStreamingConcurrency=10, HangingConnectionLimit=10
已尝试的处理及遗留问题
- 最初未显式关闭连接/取消订阅(单实例无问题),添加关闭逻辑后问题仍存在
- 调用
StreamingSubscriptionConnection.close()时抛出异常:
...[m.e.w.d.n.StreamingSubscriptionConnection.close(349)]: java.lang.Exception: microsoft.exchange.webservices.data.notification.StreamingSubscriptionConnection
- 已开启EWS跟踪:
service.setTraceEnabled(true); service.setTraceFlags(EnumSet.allOf(TraceFlags.class));
但仅邮件到达时输出日志,订阅/连接创建阶段无日志;尝试SOAPUI适配WSDL失败,不熟悉IntelliJ Tunnelij插件配置
监控SOAP消息的排查建议
1. 修复EWS跟踪日志缺失问题
EWS的TraceFlags默认可能未覆盖订阅相关操作,显式指定跟踪类型:
service.setTraceFlags(EnumSet.of( TraceFlags.EwsRequest, TraceFlags.EwsResponse, TraceFlags.Authentication, TraceFlags.SyncMessages ));
同时自定义日志输出器,将跟踪内容写入文件而非仅控制台:
service.setTraceListener(new ITraceListener() { @Override public void trace(String traceType, String traceMessage) { // 将traceMessage写入日志文件,按时间戳区分请求/响应 Files.write(Paths.get("ews_trace.log"), (LocalDateTime.now() + " [" + traceType + "] " + traceMessage + "\n").getBytes(), StandardOpenOption.APPEND, StandardOpenOption.CREATE); } });
这样可以捕获订阅创建(Subscribe请求)、连接启动、取消订阅(Unsubscribe请求)的完整SOAP交互。
2. 本地代理抓包(无需修改代码)
使用本地代理工具(如Charles、Fiddler)捕获EWS的HTTP流量:
- 配置应用的JVM参数,让EWS客户端通过代理发送请求:
-Dhttp.proxyHost=localhost -Dhttp.proxyPort=8888 -Dhttps.proxyHost=localhost -Dhttps.proxyPort=8888
- 在代理工具中添加Exchange服务器的SSL证书信任,解密HTTPS流量
- 过滤路径包含
/EWS/Exchange.asmx的请求,重点关注:Subscribe请求:查看订阅创建的参数(如有效期、文件夹)GetStreamingEvents请求:流连接的生命周期Unsubscribe请求:确认取消订阅的执行情况
3. 排查连接未释放的根源
从SOAP日志重点确认:
- 每次订阅到期后,是否发送了
Unsubscribe请求,且服务器返回成功响应 StreamingSubscriptionConnection.close()调用时,是否触发了连接关闭的SOAP请求,还是仅本地释放资源- 双实例下,两个实例的订阅请求总数是否超过
MaxStreamingConcurrency=10的限制 - 服务器是否存在
Unsubscribe响应延迟,导致旧订阅未及时释放,新订阅持续创建触发节流
4. 代码层面的额外检查
确保订阅管理逻辑线程安全:
- 维护全局的
StreamingSubscriptionConnection实例列表,每次创建新订阅前,显式关闭旧实例并调用unsubscribe() - 捕获
close()方法的异常,确保即使抛出异常,仍执行unsubscribe()操作:
try { if (connection != null && connection.isOpen()) { connection.close(); } } catch (Exception e) { log.error("关闭连接失败", e); } finally { if (subscription != null) { try { service.unsubscribe(subscription); } catch (Exception e) { log.error("取消订阅失败", e); } } }
内容的提问来源于stack exchange,提问作者CaptainObv
相关产品推荐
相关产品推荐

