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

基于jaxws-rt与CXF的WS请求响应异步日志优化方案咨询

问题背景

需要异步记录WebService(WS)的所有请求/响应数据到关系型数据库,且不能影响WS通信本身的性能,约束条件为必须使用CXF生成的WebService客户端和jaxws-rt。

已尝试方案及问题

  • 直接将SOAPMessageContext放入BlockingQueue异步处理:主线程处理WS响应时调用com.sun.xml.ws.api.message.Message.readPayloadAsJAXB()后,消息体流被读取耗尽,异步线程无法获取有效数据,方案不可行。
  • 使用SOAPMessageContext.getMessage()复制消息:每次调用都会完整解析消息体,大消息场景下性能暴跌,无法接受。
  • 考虑jaxws-rt的Tube扩展:找不到无需自定义jaxws-tubes.xml的简便注册方式,成本过高。
  • 反射获取Packet并复制:通过反射访问SOAPMessageContextImpl父类MessageUpdatableContext的私有字段packet,调用copy(true)复制后丢入队列,性能损耗低,但依赖私有API,存在版本升级后字段变更的隐患。

更优方案:基于CXF Interceptor的标准实现

利用CXF原生的Interceptor扩展机制,在消息流被读取前完成复制,既避免反射依赖,又保证性能,同时符合jaxws-rt+CXF客户端的约束。

步骤1:实现异步日志Interceptor

import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import java.io.ByteArrayOutputStream;
import java.time.Instant;
import java.util.logging.Level;
import java.util.logging.Logger;

public class AsyncWsLoggingInterceptor extends AbstractPhaseInterceptor<Message> {
    private static final Logger LOGGER = Logger.getLogger(AsyncWsLoggingInterceptor.class.getName());
    private final BlockingQueueHolder blockingQueueHandler;

    public AsyncWsLoggingInterceptor(BlockingQueueHolder blockingQueueHandler) {
        // 选择合适的阶段:发送选SEND,接收选RECEIVE,确保消息未被读取
        super(Phase.RECEIVE);
        this.blockingQueueHandler = blockingQueueHandler;
    }

    @Override
    public void handleMessage(Message message) {
        try {
            // 复制消息内容到字节数组,避免流被后续操作耗尽
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            message.getContent(java.io.OutputStream.class).writeTo(baos);
            byte[] messageContent = baos.toByteArray();

            // 判断是请求还是响应
            boolean isOutbound = Boolean.TRUE.equals(message.getExchange().isOutbound());
            // 丢入异步队列
            if (!blockingQueueHandler.offer(
                    new SoapMessageContainer(Instant.now(), messageContent, null, isOutbound)
            )) {
                LOGGER.severe("队列已满,丢弃SOAP消息");
            }
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "异步日志处理异常", e);
        }
    }
}

步骤2:注册Interceptor到CXF客户端

在创建CXF客户端代理后,将Interceptor添加到入站/出站拦截器链:

// 假设port是CXF生成的WebService客户端代理
org.apache.cxf.endpoint.Client client = org.apache.cxf.frontend.ClientProxy.getClient(port);
BlockingQueueHolder queueHolder = new BlockingQueueHolder(); // 你的队列持有者实例

// 注册入站(响应)拦截器
client.getInInterceptors().add(new AsyncWsLoggingInterceptor(queueHolder));
// 注册出站(请求)拦截器
client.getOutInterceptors().add(new AsyncWsLoggingInterceptor(queueHolder));

方案优势

  1. 无私有API依赖:完全使用CXF和jaxws-rt的标准扩展点,兼容性强,版本升级无隐患。
  2. 性能优异:Interceptor是CXF原生处理链路的一部分,消息复制操作仅涉及字节流读写,大消息场景下性能远优于SOAPMessageContext.getMessage()方案。
  3. 避免消息体耗尽:在消息流被业务逻辑读取前完成复制,异步线程可获取完整的请求/响应数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 23:22:34