Apache CXF实现Web Service(5)—— GZIP使用
2015-12-04 17:56
1036 查看
Apache CXF实现Web Service(5)—— GZIP使用
参考来源:CXF WebService整合Spring
Apache CXF实现Web Service(1)——不借助重量级Web容器和Spring实现一个纯的JAX-WS web service
Apache CXF实现Web Service(4)——Tomcat容器和Spring实现JAX-RS(RESTful) web service
首先参照
Apache CXF实现Web Service(4) 创建一个WTP项目,并参照(1) 新建一个测试的Web Service:HelloWorld.java和其实现HelloWorldImpl.javaHelloWorld.java
package com.cnblog.richaaaard.cxftest.spring.ws.helloworld.services; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; import org.apache.cxf.annotations.GZIP; //@GZIP(threshold=128) @GZIP @WebService public interface HelloWorld { @WebMethod @WebResult String sayHi(@WebParam String text); }
HelloWorldImpl.java
package com.cnblog.richaaaard.cxftest.spring.ws.helloworld.services; import javax.jws.WebService; @WebService(endpointInterface="com.cnblog.richaaaard.cxftest.spring.ws.helloworld.services.HelloWorld",serviceName="helloService") public class HelloWorldImpl implements HelloWorld { public String sayHi(String name) { String msg = "Hello " + name + "!"; return msg; } }
在Spring的配置中需要注意的是,我们需要引入jarws的schema
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
完整的配置文件如下:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd" default-lazy-init="true"> <import resource="classpath:META-INF/cxf/cxf.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/> <bean id="helloService" class="com.cnblog.richaaaard.cxftest.spring.ws.helloworld.services.HelloWorldImpl"> </bean> <jaxws:endpoint implementor="#helloService" address="/HelloService"/> </beans>
web.xml文件保持不变
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd" default-lazy-init="true"> <import resource="classpath:META-INF/cxf/cxf.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/> <bean id="helloService" class="com.cnblog.richaaaard.cxftest.spring.ws.helloworld.services.HelloWorldImpl"> </bean> <jaxws:endpoint implementor="#helloService" address="/HelloService"/> </beans>
项目结构如图
下面需要测试几个问题
@GZIP如何工作的?@GZIP有两个属性 force 和 threshold 怎么用?
@GZIP加在接口上是否可行?(我们用Spring实例化bean是用的HelloWorldImpl)
测试
@GZIP如何工作的?
第一步
在Eclipse中Run As... -> Run on Server,然后在浏览器中验证是否发布成功:我们Tomcat本地运行的端口是8080。
第二步
运行TCPMon,新建监听端口8081,目标端口8080:第三步
新建客户端测试代码,并将address设置成"http://localhost:8081/cxf/services/HelloService"package com.cnblog.richaaaard.cxftest.spring.ws.helloworld.client; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; import org.apache.cxf.transport.common.gzip.GZIPInInterceptor; import org.apache.cxf.transport.common.gzip.GZIPOutInterceptor; import com.cnblog.richaaaard.cxftest.spring.ws.helloworld.services.HelloWorld; public class Client { public static void main(String[] args) { JaxWsProxyFactoryBean client = new JaxWsProxyFactoryBean(); // factory.getInInterceptors().add(new LoggingInInterceptor()); // factory.getOutInterceptors().add(new LoggingOutInterceptor()); // factory.getInInterceptors().add(new GZIPInInterceptor()); // factory.getOutInterceptors().add(new GZIPOutInterceptor()); client.setServiceClass(HelloWorld.class); client.setAddress("http://localhost:8081/cxf/services/HelloService"); HelloWorld helloworld = (HelloWorld) client.create(); System.out.println(helloworld.sayHi("Richard")); System.exit(0); } }
第一个测试,运行Client.java
Run As... -> Java Application在TCPMon(关于如何使用TCPMon请查看http://www.cnblogs.com/richaaaard/p/5019438.html)中查看结果
发现并没有像预料中的那样发生GZIP压缩
怀疑出现问题
服务器不支持GZIP?
CXF有BUG?
使用方式有问题?
打开@GZIP Annotation的源码查看
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE }) @Inherited public @interface GZIP { int threshold() default -1; boolean force() default false; }
这个标注有两个属性:threshold 与 force
然后查看Apache CXF 关于@GZIP的文档 (CXF Features http://cxf.apache.org/docs/featureslist.html) 与 (CXF Annotations http://cxf.apache.org/docs/annotations.html)
细心的同学会发现“CXF Features文档中关于GZIPFeature的说明中仍然出现了FastInfoset”这个错误
threshold - the threshold under which messages are not gzipped
force - force GZIP compression instead of negotiating via the Accept-Encoding header
GZIP is a negotiated enhancement. An initial request from a client will not be gzipped, but an Accept header will be added and if the server supports it, the response will be gzipped and any subsequent requests will be.
上面一段话的意思是:第一次请求不会发生GZIP,但是如果服务器支持,会加如到Accept头上,返回的消息会发生GZIP然后,后面发生的请求也会有GZIP。
关于threshold的定义:可以发现我们测试中的请求(request)长度(Content-Length)是232,返回(response)长度是259。
这里猜想
@GZIP应该有一个自己的默认threshold,如果修改默认实现,也就能发生GZIP了我们将threshold修改成256介于232和259之间
@GZIP(threshold=256) //@GZIP @WebService public interface HelloWorld { @WebMethod @WebResult String sayHi(@WebParam String text); }
重启服务器,运行程序
请求没有发生GZIP,而响应端有GZIP
再次运行Client(不重启服务器)
我们发现,请求(request)并没有像Apache官方文档那样说的,也会有GZIP。这是为什么呢?是不是和我们的客户端有关?
将代码增加一次请求试试
发现连续的两次请求仍然没有发生GZIP。再仔细查看关于GZIP的解释,"...如果服务器支持..."package com.cnblog.richaaaard.cxftest.spring.ws.helloworld.client; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; import org.apache.cxf.transport.common.gzip.GZIPInInterceptor; import org.apache.cxf.transport.common.gzip.GZIPOutInterceptor; import com.cnblog.richaaaard.cxftest.spring.ws.helloworld.services.HelloWorld; public class Client { public static void main(String[] args) { JaxWsProxyFactoryBean client = new JaxWsProxyFactoryBean(); // factory.getInInterceptors().add(new LoggingInInterceptor()); // factory.getOutInterceptors().add(new LoggingOutInterceptor()); // factory.getInInterceptors().add(new GZIPInInterceptor()); // factory.getOutInterceptors().add(new GZIPOutInterceptor()); client.setServiceClass(HelloWorld.class); client.setAddress("http://localhost:8081/cxf/services/HelloService"); HelloWorld helloworld = (HelloWorld) client.create(); System.out.println(helloworld.sayHi("Richard")); System.out.println(helloworld.sayHi("Kobe Bryant")); System.exit(0); } }
这里猜想
是不是我们服务器的设置问题,不支持GZIP呢?修改Tomcat的Connector配置,增加
compressionMinSize="256" compression="on" noCompressionUserAgents="gozilla, traviata" compressableMimeType="text/html,text/xml"
然后重启服务器,再运行客户端
发现请求(request)仍然没有发生GZIP
如何才能使请求也提交GZIP格式呢?
我们暂且放下这个问题,先将Tomcat配置文件关于compression的配置还原来看看force如何工作的
在HelloWorld.java的头上修改@GZIP增加force属性,threshold仍然为256@GZIP(force=true, threshold=256) @WebService public interface HelloWorld { @WebMethod @WebResult String sayHi(@WebParam String text); }
请求仍然没有被压缩
[](http://images2015.cnblogs.com/blog/613455/201512/613455-20151204175354393-824266017.png
修改成128呢?因为请求的Content-Length是232
在次运行,发现请求成功压缩了(这是请求压缩的一种情景)
好像漏了什么东西
之前我们反复测试,期望第二次请求(request)可以根据服务端返回的Accept-Encoding header 自行进行GZIP压缩,我们测试代码当时设置的threhold是256,而请求的Content-Length=232。我们将threshold调整到128,去掉force属性,重启服务器再试一下(这时的Tomcat没有配置compression相关属性)。当我们单次运行的时候(每次客户端运行结束,进程退出)
先后运行两次独立的请求,请求(request)没有发生GZIP,这是因为客户端是不同进程的缘故
当我加入一行代码,在统一进程中连续两次请求服务器时,我们会发现第二次请求会自行GZIP压缩,而此时Tomcat上没有对compression进行特别配置
由此可见
Tomcat是内置支持GZIP的服务器
Tomcat上的compression是服务器自己独立的压缩机制,与Apache CXF无关,但是服务器级别的配置会影响我们使用的CXF Web Service
那么问题来了
Tomcat服务器配置的压缩机制是怎么工作的呢?*扩展
StackOverflow上关于GZIPInInterceptor和GZIPOutInterceptor的回答是否正确?
通过上面的所有测试就能得出结论,这个Interceptor并不对服务端响应消息的GZIP起任何作用,读者可以自行测试相关文章推荐
- spring中dataSource的org.apache.commons.pool.impl.GenericObjectPool异常
- Apache Stratos探究:Cartridge Agent的生命周期
- BerkeleyX CS100.1x"Introduction to Big Data with Apache Spark"环境搭建
- LINUX下开启apache时出现Address already in use: make_sock: could not bind to address [::]:80
- Exception in thread "main" java.lang.UnsatisfiedLinkError: org.apache.hadoop.io.nativeio.NativeIO$Wi
- Apache Avro使用入门指南
- Apache BeanUtils 1.9.2 官方入门文档
- Apache----windows下虚拟主机和虚拟目录的认识(Apache配置)
- apache错误:internal server error 500原
- eclipse新建maven项目,提示错误“Could not resolve archetype org.apache.maven.archetypes .”
- org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class
- java.lang.ClassNotFoundException: org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFil
- [Windows Server 2008] 安装Apache+PHP+MySQL
- SEQ!org.apache.hadoop.io.LongWritable"org.apache.hadoop.io.BytesWritable
- LAMP 1.2 Apache编译安装
- Apache CXF 和 Spring 开发 Web Service 1
- 在Ubuntu环境部署Apache Spark集群
- apache服务器的常用功能及设置
- apache服务器的常用功能及设置
- apache中安装edusoho报错Internal Server Error The server encountered an internal error or misconfiguration and was unable to complete your request. ……