扩展cxf
CXF扩展和调优
扩展CXF
1、??为了能提供一个友好的请求资源路径,对于rest over http和soap rpc over http统一请求地址,资源url为:http://ip:port/conext/ws/sqs/accountId/queueName
soap请求有特殊的http header,如果有SOAPAction则为soap请求,如果Content-Type为text/xml、text-xml-SOAP、application/soap+xml则为soap请求,其他请求为rest请求。请求分为两种,一个是queue级别的,一个是message级别的。比如queue级别的CreateQueue,ListQueues,请求地址为http://ip:port/conext/ws/sqs;比如message级别的SendMessage、deleteMessage,请求地址为http://ip:port/conext/ws/sqs/accountId/queueName2、? soap请求的认证信息存在soap header中,Rest请求的认证信息存在parameter中。
如何突破CXF的限制:
1、??在cxf中,一个endpoint对应于一个服务,rest类型的请求,由于其特殊性,可以在定义endpoint后通过cxf提供的java annotation实现http?绑定,类似如下方法:
@Get
@HttpResource(location="/customers/{first}/{last}") void findCustomer(@WebParam(name="first") String firstName, @WebParam(name="last") String lastName);
但是在soap协议中,endpoint是不能改变的,如果你定义了一个service endpoint为http://ip:port/conext/ws/sqs,你请求地址为http://ip:port/conext/ws/sqs/xxx,cxf会找不到ServletDestination,具体参考org.apache.cxf.transport.servlet. ServletController?类invoke方法。更不要说,不同用户访问的请求地址还不一样,要解决这个问题,必须在请求到达org.apache.cxf.transport.servlet.CXFServlet类之前进行映射处理。首先,我们对内部endpoint进行如下定义:
l??Rest内部endpoint定义:
鉴于rest充分使用到了http语义,rest服务内部endpoint ?address定义为:
/sqs/rest,然后在rest服务实现类中定义不同级别的请求,对于queue级别的,
location为/,对于message级别的location为/{accoutId}/{queueName}
l??Soap内部endpoint定义:
Soap需要实现提供两个实现类,分别处理queue级别的和message级别的。queue级别的address定义为/sqs/soap/queue,message级别的address定义为/sqs/soap/message。
在定义完内部endpoint后,需要把外部客户请求映射到内部endpoint。请求映射最好是使用硬件来实现,采用7层交换设备修改http协议,能高效的处理请求地址映射。不过目前项目不具备这样的设备,我们只能扩展org.apache.cxf.transport.servlet.CXFServlet类,对请求映射处理。
继承org.apache.cxf.transport.servlet.CXFServlet,并自定义invoke方法。
在org.apache.cxf.transport.servlet. ServletController?类invoke方法中,对于请求的判断,是通过request.getPathInfo()来获得请求地址查询请求目的地,需要重新构造request请求,并重写getPathInfo方法。通过继承ServletRequestWrapper,实现对getPathInfo方法的重写。
在请求到达服务实现类后,需要获取accoutId,queueName等参数信息,可以把这些信息存储在Attribute中,通过requset.getAttribute方便的获取这些参数。重写getAttribute(String)方法,可以达到此目的。
2、??对于安全的处理,可以通过cxf提供的Interceptor。cxf默认的提供的Interceptor链有点长,对于不能通过认证的无效请求,要尽量减少处理时间,可以在Phase.READ阶段SoapActionInInterceptor拦截器之前ReadHeadersInterceptor之后对请求进行统一认证处理。因为两种请求的获取认证信息的方式不一样,需要在继承ServletRequestWrapper时,同时提供判断是否是soap请求的getAttribute方法。对于soap请求,从soap header中读取;对于rest请求,从parameter中读取,预留认证接口,认证失败后抛出SoapFault异常,终止inbound拦截器链。为了便于扩展,使用策略模式,提供默认认证实现。
?
性能:
?
1、??启用FastInfoset(快速信息集)
webservice的性能实在是敢恭维。曾经因为webservice吞吐量上不去,对webservice进行了一些性能方面的优化,采用了FastInfoset,效果很明显,极端条件下的大数据量传输,性能提高60%,他可以减少传输成本,序列化成本和xml解析成本。
Cxf提供了FastInfoset协商机制,实现类见org.apache.cxf.feature.FastInfosetFeature,在bus中启用如下配置:
<cxf:features><cxf:fastinfoset force="false"/></cxf:features>
Force=false表示服务端和客户端第一次通信时会协商(通过检查标准的HTTP头的Accept字段,值为MIME类型的application/fastinfoset)是否启用FastInfoset支持,如果客户端不支持,则不启用快速信息集。
需要在pom中添加依赖:
??<dependency>
??? <groupId>com.sun.xml.fastinfoset</groupId>
??? <artifactId>FastInfoset</artifactId>
??? <version>1.2.9</version>
??? <type>jar</type>
??? <scope>compile</scope>
??? </dependency>
FastInfoset参考:http://java.sun.com/developer/technicalArticles/xml/fastinfoset/
2、??启用gzip压缩支持
客户端和服务器端是否使用Gzip压缩,也是基于http协议协商的(检查请求header?中是否有Accept-encoding:gzip)。但是这里需要仔细权衡下。对于小数据量,启用gzip压缩支持是吃力不讨好的行为,数据量很小的时候,gzip压缩结果不明显,还浪费cpu。我们需要权衡数据大小,按照经验设置threshold为10*1024byte。
在bus中启用如下配置:
<bean />并且在依赖中加入:
?? <dependency>
??? <groupId>org.apache.cxf</groupId>
??? <artifactId>cxf-rt-javascript</artifactId>
??? <version>2.4.1</version>
??? <type>jar</type>
??? <scope>compile</scope>
</dependency>
?
?