XML-RPC

XML-RPC协议: [url]http://xmlrpc.scripting.com/spec.html[/url]
XML-RPC实现: [url]http://ws.apache.org/xmlrpc/[/url]

从基础协议来看,主要支持以下类型:
Integer, Boolean, String, Double, Date, Base64, Array, Map,Exception(仅error code and message)

apache xmlrpc实现做了对Java对象序列化以及异常堆栈的扩展,但使用之前客户端与服务端都必须启用这些扩展,任意一端使用了这些功能但未开启均会抛异常。

代码配置:
config.setEnabledForExtensions(true);
config.setEnabledForExceptions(true);

[b]服务端:[/b]
提供了用于测试的web server实现: org.apache.xmlrpc.webserver.WebServer
只不过一般都是通过servlet的形式部署在成熟的web server上,但有意思地是我们必须把服务注册文件放在classpath
[b]org/apache/xmlrpc/webserver/XmlRpcServlet.properties[/b]下面。

详细请见官方文档: [url]http://ws.apache.org/xmlrpc/server.html[/url]

[b]客户端:[/b]
默认客户端使用的[i]org.apache.xmlrpc.client.XmlRpcSunHttpTransportFactory[/i]进行连接,也就是[i]java.net.HttpURLConnection[/i],这个开启的连接用完之后就会主动关闭且不提供重试功能。

还有一个就是用commons-httpclient的[b]org.apache.commons.httpclient.HttpClient.XmlRpcCommonsTransportFactory[/b]
但是默认情况下每次调用,它会新生成一个HttpClient对象,可以通过从外部传入一个HttpClient对象来进行重用。

再者HttpClient默认情况又使用的是单线程,单连接的连接管理器:[i]org.apache.commons.httpclient.SimpleHttpConnectionManager[/i]
这时需要使用多线程,多连接版本:[b]org.apache.commons.httpclient.MultiThreadedHttpConnectionManager[/b]

以下是一段示例代码:
//多线程版本
MultiThreadedHttpConnectionManager connManager = new MultiThreadedHttpConnectionManager();
//最大连接数设为10
connManager.getParams().setDefaultMaxConnectionsPerHost(10);
HttpClient httpClient = new HttpClient(connManager);

XmlRpcClient rpcClient = new XmlRpcClient();
XmlRpcCommonsTransportFactory transportFactory = new XmlRpcCommonsTransportFactory(rpcClient);
//重用HttpClient对象
transportFactory.setHttpClient(httpClient);
rpcClient.setTransportFactory(transportFactory);

XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setServerURL(new URL("http://192.168.1.6:9000/test/xmlrpc"));
//开启扩展功能以支持Java复杂对象序列化
config.setEnabledForExtensions(true);
rpcClient.setConfig(config);

//使用对象代理,看起来更像RPC点
ClientFactory factory = new ClientFactory(rpcClient);
Calculator calc = (Calculator) factory.newInstance(Thread.currentThread().getContextClassLoader(),
Calculator.class, "Calculator"); //可以指定绑定的服务名,不然默认提交的服务名将是接口的全类名


[b]这里顺带提下commons-httpclient-3.x的连接池以及重试机制。[/b]

HttpClient默认情况下每次从连接池拿到一个连接会进行连接测试,以确保拿到的连接是可用的,未关闭的。

一般的HttpServer实现即使客户端使用HTTP1.1要求Keep-Alive的Connection,也会在一定时间没有数据交互后[b]主动关闭[/b]连接,Tomcat6.0.18直接使用socket的connectionTimeout的设置默认为20秒,你可以在apache-tomcat-6.0.18\conf\server.xml的Connector配置项进行修改测试。

你可以通过以下方式禁用使用连接前测试。
[i]httpClient.getParams().setParameter("http.connection.stalecheck", false);[/i]

[b]但禁用之后,会发生什么状况呢?[/b]
HttpClient会直接使用此连接发送请求,并试图读取响应。
从TCP协议来看,Http Server的主动关闭只是关闭了 server -> client的连接,client仍然可以发送数据给server,但是client将读不到数据。

测试发现如果先client发送数据给server,然后试图读取响应,会抛出如下异常。
[i]java.net.SocketException: Software caused connection abort: recv failed[/i]

而如果不先client发送数据给server,试图读取响应可以得到-1这种EOF连接关闭提示。

恰巧HttpClient默认重试机制将不给与机会给已成功发送数据,却返回java.net.SocketException的情况进行重试,具体可以参考: [i]org.apache.commons.httpclient.DefaultHttpMethodRetryHandler.retryMethod[/i]

[b]这样如果关闭了连接前测试,将可能会出现调用失败的情况。[/b]

关于连接前测试,我们可以参考下HttpClient的代码,使用临时设置socket timeout为1以及BufferedInputStream的mark和reset,这样让读取尝试不会消费掉本应让应用读到的数据。

[i]org.apache.commons.httpclient.HttpConnection.isStale()[/i]

try {
socket.setSoTimeout(1);
inputStream.mark(1);
int byteRead = inputStream.read();
if (byteRead == -1) {
// again - if the socket is reporting all data read,
// probably stale
isStale = true;
} else {
inputStream.reset();
}
} finally {
socket.setSoTimeout(this.params.getSoTimeout());
}


NIO就可以很方便地通过数据读取事件检测到连接关闭,只不过要记得在处理中关闭此连接哦,不然它会不耐其烦地告诉你有数据可读,其实就是一个无聊的-1。

Http Server的主动关闭,如果client未进行相应地close,会造成一大堆长时间的[b]CLOSE_WAIT[/b]状态的TCP连接,比[b]TIME_WAIT[/b]可要久多了,这样也会浪费掉client端的套接字资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值