龙空技术网

Axis调用webService问题

小心程序猿QAQ 181

前言:

今天同学们对“apacheaxis2的使用”大体比较注意,小伙伴们都想要学习一些“apacheaxis2的使用”的相关文章。那么小编同时在网上网罗了一些对于“apacheaxis2的使用””的相关内容,希望我们能喜欢,同学们快快来学习一下吧!

背景

之前有 一篇文章 专门介绍webservice,只能算是webservice入门,本文则是涉及到调用的客户端,也是在实际使用过程中出现了问题,通过自己的分析以及相关资料的查询才了解到具体原因,记录下来,避免自己以后踩坑。

线上问题复现

在线上应用中,有一个服务端使用了webservice,client也就只能使用wsdl协议进行调用。具体调用过程如下:

创建Service单例。

CLIENT;private Service serviceInstance;ServiceEnum(){    serviceInstance = new Service(getClientEngineConfig());}public Service getServiceInstance() {    return serviceInstance;}private static EngineConfiguration getClientEngineConfig() {    SimpleProvider engineProvider = new SimpleProvider();    engineProvider.deployTransport(HTTPTransport.DEFAULT_TRANSPORT_NAME, new RequestLoggerHandler());    return engineProvider;}复制代码

这里面的RequestLoggerHandler是自定义的HttpSender,继承了CommonsHTTPSender,用来发送Http请求的。

通过call.invoke方法调用服务端服务。

public static String getService(String endpoint, String wsName, String xmlBody, WsdlMonitorLogInfoDTO wsdlMonitorLogInfoDTO) {        String rspxml = "";        Call call = (Call) ServiceEnum.CLIENT.getServiceInstance().createCall();        call.setProperty(MessageContext.HTTP_TRANSPORT_VERSION, HTTPConstants.HEADER_PROTOCOL_11);        call.setTargetEndpointAddress(endpoint);        call.setOperationName(wsName);        call.addParameter("userName", XMLType.XSD_DATE, ParameterMode.IN);        call.setReturnType(XMLType.XSD_STRING);        call.setTimeout(15000);        //log.info("cmsc.gateway=>统一认证:call.invoke url=[{}],OperationName=[{}]",endpoint,wsName);        rspxml = (String)call.invoke(new Object[]{xmlBody});}复制代码

本质就是创建一个call对象,设置call对象属性,调用call对象的invoke方法。以上代码在日常使用的时候没有任何问题,但是在调用量增大的时候,有极少接口会调用出错。具体报错类似如下:

2021-08-24 22:00:00.620| INFO|fce3ee0082a67242|5830914be8852ad6|false|1|http-nio-9090-exec-128|c.c.c.u.g.c.e.w.RequestLoggerHandler    |the RequestLoggerHandler endPoint [], respMesssage [<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="; xmlns:xsd="; xmlns:xsi=";><soapenv:Body><soapenv:Fault><faultcode xmlns:ns1=";>ns1:Client</faultcode><faultstring>No such operation 'getAssertInfoByArtifact'</faultstring><detail><ns2:hostname xmlns:ns2=";>ac-webservice-7df55d9dfb-9vbq9</ns2:hostname></detail></soapenv:Fault></soapenv:Body></soapenv:Envelope>]2021-08-24 22:00:00.621|ERROR|fce3ee0082a67242|5830914be8852ad6|false|1|http-nio-9090-exec-128|c.c.c.u.g.c.api.wsdlCall.WsdlService    |[d424df19f29c4166902a93e9198535ed_CA02001],请求统一认证异常:No such operation 'getAssertInfoByArtifact'复制代码

这里的报错提示是AssertionQryUID这个方法的webserviceName应该是AssertionQryUID,但是日志里面提示传入的是getAssertInfoByArtifact,因此服务端报错了。请求参数如下所示:

2021-08-24 22:00:00.501| INFO|fce3ee0082a67242|5830914be8852ad6|false|1|http-nio-9090-exec-128|c.c.c.u.g.c.e.w.RequestLoggerHandler    |the RequestLoggerHandler endPoint [], reqMesssage [<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="; xmlns:xsd="; xmlns:xsi=";> <soapenv:Body>  <getAssertInfoByArtifact soapenv:encodingStyle=";>   <userName xsi:type="xsd:string">0qmwPBkdgvy7WZTW5RMSKde93O3Z86s9iAH3+pE2vnRZcd8eFCbAAK3Y4zuInazzmnL4cJvMmxaCzq9t+lOQHb62uWSXc9nfS+0RAPhPHLV1dRnnviCnP3ngLJabhfGqqrS6E4rPHFAv+8QrTcKHeunPmEZH/AUqsUn3GU0V/bnFdqRHaOJ2ADfjcaCpn34s8AVlB+T5d5t7SLmtF+SNk46xpPLgxW3W0WcTPKKfGFuBGgBtM5MC7D3w7thHP+nO87azJyU202WHbV11waL/qvj1OGPfwtjHnNF/VB5/Km7LUNe4noeoqvWSCHglnSL1oIGOHb5vznt+DSceprItHYcUb8WIDZB/fxM7CUfRia+N5vY4SNvMkHbLy4/jQTKL4bvdxrDt00WPG1Xd795AXmHRuuVhToj5mq/KI2ZH3AOe0I3oYG6L5AAZsWZ7rXJtegfj8RgHctmEE8DDN4LJlE39YVXaCfoUdM4UPM5W0OhszQ7U7y6wwX+o4K41faTSEHbx7gTFhcNpBpLb7eGzCSAiVqsePwtbQOVIFnjZSjmMqekOF25pMY4oLYbHNhcOH4vShUPAUIyLFLQdgxO3L0OnJ50I4PRE2J4EeQLX277hdsQ5+n18dG8Pq/lqPOOyyJvPxiun7EnDZfC24hsbhSSBjJ1u8d+NnWJLHSzFmfh1OvDIVnIVoeizVHdEBdzm8Y5HEdjUqj9/evER95kLK/QLuxE4eAWYnYFNd6qX9nj0N9RvONHE4ckD9Y/BV1U3</userName>  </getAssertInfoByArtifact> </soapenv:Body></soapenv:Envelope>]复制代码
更有意思的是在调整了Http请求类的超时时间等参数之后,接口的识别率提高得很快。
AxisProperties.setProperty("axis.http.client.maximum.total.connections","150");AxisProperties.setProperty("axis.http.client.maximum.connections.per.host","50");AxisProperties.setProperty("axis.http.client.connection.pool.timeout","20000");AxisProperties.setProperty("axis.http.client.connection.default.connection.timeout","10000");AxisProperties.setProperty("axis.http.client.connection.default.so.timeout","5000");复制代码
问题分析

这个问题在调用量低的时候很难复现,在调用量增大之后变得严重,看起来非常像线程安全的问题。查看整个调用过程,虽然getService方法是一个静态方法,但是Call这个对象每次调用都会创建,是一个局部变量,应该不会有线程安全问题。除非Call对象里面有一些共享变量之类的导致线程安全问题。看下Call方法的内部。

果然Call类中Service是一个共享的变量,也就是所有的请求使用的是相同的Service,那么如果Service是非线程安全的,整个调用过程自然也就是非线程安全的。而Http请求参数的调整使得线程安全问题更严重了,整个逻辑上的分析是合理的,那么Service究竟是不是线程安全的呢?答案是否定。

Are Axis2 generated stubs thread-safe

也就是说axis1以及axis2都是线程非安全的。

解决办法stackoverflow上提到了一种解决办法,那就是池化技术。既能解决线程安全问题,同时也提高了性能(需要调用的时候从池子里面获取对象,调用完成之后还回去)。个人不太喜欢这种方式,觉得对资源的消耗还是有点多,因为每个Service都会创建HttpClient,并且在里面还会用到连接池。引入ThreadLocal解决线程安全问题,网上找了一下:threadLocal解决线程安全问题吗?结论是ThreadLocal不能解决共享变量的线程安全问题。切记切记。既然webservice本质上依然是通过Http调用远程服务,那么我完全可以不用框架,自己用Http工具类调用服务,这样不就没有任何线程安全的问题了,而且可以对调用过程进行优化,最终决定采用这种方式实现。小结

看似一个简单client端调用,在使用过程中由于没有注意到线程安全的问题,就会出错。反思一下就是在自己不了解类库的情况下,盲目地使用单例才是引起线程安全问题的根本原因,单例虽好,但在调用过程中一定确认是线程安全的才能使用,对于非线程安全的一定不能使用单例。

作者:hujun8610

链接:

来源:掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

标签: #apacheaxis2的使用