基于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));
方案优势
- 无私有API依赖:完全使用CXF和jaxws-rt的标准扩展点,兼容性强,版本升级无隐患。
- 性能优异:Interceptor是CXF原生处理链路的一部分,消息复制操作仅涉及字节流读写,大消息场景下性能远优于
SOAPMessageContext.getMessage()方案。 - 避免消息体耗尽:在消息流被业务逻辑读取前完成复制,异步线程可获取完整的请求/响应数据。
内容的提问来源于stack exchange,提问作者Olli
相关产品推荐
相关产品推荐

