hessian 的夸语言使用(PHP服务端 | C#客户端)
本人资深老代码。
用hessian差不多有5个年头了,基本上可以算是出来就在用吧。这玩意做RPC确实方便,而且能跨语言。曾经在企业工作,大部分都是用java、C#做开发。用hessian的时候发现了不少坑,感觉都趟过了。现在突然有个小东西更适合用php做服务端,前端用C#,自然就想起来用hessian了。
于是两边同时开发,php这边就用直接实现了server端,测试用php做了简单的client。完美通过,中文部分用urlencode先保证程序能跑起来。C#这边驾轻就熟一次性撸完。本来很顺溜的进行对接了,结果发现C#这边一直报SystemNetWebException.任何提示都没有。吐老血。。。
怎么办?这时候再换其他的不太合适,用webservice这东西体积太大,安全性也不高。一边不爽一边看了看php的hessian实现代码。发现代码量不大,那应该C#这边也没什么大问题。于是下载了一份最新的C#的hessian实现,一步步调试吧。最后发现C#的实现确实挺简单的,只是用了固定的规则拼接方法名和参数,然后直接发送http请求,并没有做太多的加密解密规则,那就好办。
打开了php的实现部分,用最笨的办法一步步调试。最后发现错误出现在HessianObjectFactory.php的detectVersion方法,简单阅读,这个方法是来检查版本的。先去掉,默认设置为不检查。
随后发现错误依然报,继续调试php。发现在HessianObjectFactory创建的时候会初始化两个默认属性
function __construct(){ $this->protocols = array( '2'=>array($this,'loadVersion2'), '1'=>array($this,'loadVersion1') ); $this->transports = array( 'CURL' => 'HessianCURLTransport', 'curl' => 'HessianCURLTransport', 'http' => 'HessianHttpStreamTransport' ); }
这一看就肯定有问题呢,在php的hessian实现包里有两个文件夹“hessian1”,“hessian2”不用说,肯定有两个不同的标准来实现的。再看transports有CURL和http两种协议,这完全不用考虑肯定是HTTP的流实现,因为在C#端已经看了。 那回过头来就再尝试一下不同版本的不同协议咯。
首先,看了一下 在初始化工厂的时候是可以设置选择哪个版本哪个协议来初始化hessian实例的。如果不填写的话会有默认的HessianOptions对象呗创建。于是看了一下HessianOptions.php的代码。比较简单最上面就出现了
/** * Configuration options for use in Hessian services, both client and server */ class HessianOptions{ public $version = 1;//2; /** * Name of the transport to use * @var string */ public $transport = 'http';//'CURL';
先直接改这部分吧。先把$transport默认值改为http,在C#端测试一下,还是有错误,但是已经变了,错误是hessian相关的,那再把$version换成1。再试C#调用。成功了!
整个过程本人带着怒气调试,基本无脑!试两次后发现确实成功了。好吧!算是经验记录。忘各位同仁在使用hessian少掉坑!
总结一下:只需要修改默认配置即可,HessianOptions.php中修改public $version = 1;public $transport = 'http';即可支持C#进行客户端调用。主要是满足C#的传递方式。
在此,再总结一下hessian的一些特性:
1.在调试C#源码的时候发现hessian在C#的实现上是支持重载的,因为在传递方法名和参数的拼接时发现中间有一段是表示如果支持重载,则在方法名拼接时在方法名后面增加0,1这种下标并且在参数之间增加_。不过在php的server实现里面并没有看见重载配置,所以至少C#用hessian是支持重载的,这和官方前几年的文档不一样,应该是近期新增的功能。
public object DoBurlapMethodCall(object[] arrMethodArgs, MethodInfo methodInfo) { Type[] argumentTypes = GetArgTypes(arrMethodArgs); Stream sInStream = null; Stream sOutStream = null; try { WebRequest webRequest = this.OpenConnection(m_uriBurlapServiceUri); webRequest.ContentType = "text/xml"; webRequest.Method = "POST"; MemoryStream memoryStream = new MemoryStream(2048); CBurlapOutput cBurlapOutput = this.GetBurlapOutput(memoryStream); string strMethodName = methodInfo.Name; if (m_CBurlapProxyFactory.IsOverloadEnabled) {// 可以发现IsOverloadEnabled就是启用是否重载的了 if (arrMethodArgs != null) { strMethodName = strMethodName + "__" + arrMethodArgs.Length; } else { strMethodName = strMethodName + "__0"; } } cBurlapOutput.Call(strMethodName, arrMethodArgs); try { webRequest.ContentLength = memoryStream.ToArray().Length; sOutStream = webRequest.GetRequestStream(); memoryStream.WriteTo(sOutStream); } catch (Exception e) { throw new CBurlapException("Exception by sending request to the service with URI:\n" + this.URI.ToString() + "\n" + e.Message); }
2.在以往的经验里面发现java和C#做互相调用时java的Map类型对应为C#的Hashtable。很多人说java无法用Map这点是不对的。
3.在java中的package名称和C#中的namespace是需要一致的,但是本人在测试C#和C#进行hessian调用时发现两者的namespace并不需要一致,只要保证ClassName相同即可完成调用。
4.很多人说hessian比较稳定,快速;其实hessian也就是用http进行通讯的快速倒是可以,毕竟数据使用了二进制压缩传输。但是稳定就说不上了,曾经有项目在用的时候发现100次调用失败率超过7%。毕竟http协议建立连接是比较慢切耗费资源的。但是在考虑到开发的时间成本上,不大的项目直接用hessian是很不错的选择。
5.hessian的数据体积是有限制的,特别是在PHP、nodejs等较轻量级的语言上,过大的数据会直接传送失败,这和服务容器的配置一定要把握好。
6.在中文传输上,hessian没有太好的去考虑解决,毕竟是老外开发。最偷懒的解决方法就是通讯都用utf8,在发现中文依然处理不了的时候使用urlencode进行编码吧,收到后再解码。