PHP中使用XML-RPC构造Web Service简单入门

[  Web Service介绍 ]

Web Service就是为了异构系统的通信而产生的,它基本的思想就是使用基于XML的HTTP的远程调用提供一种标准的机制,而省去建立一种新协议的需求。 目前进行Web Service通信有两种协议标准,一种是XML-RPC,另外一种是SOAP。XML-RPC比较简单,出现时间比较早,SOAP比较复杂,主要是一些 需要稳定、健壮、安全并且复杂交互的时候使用。

PHP中集成了XML-RPC和SOAP两种协议的访问,都是集中在xmlrpc扩展当 中。另外,在PHP的PEAR中,不管是PHP 4还是PHP 5,都已经默认集成了XML-RPC扩展,而且该扩展跟xmlrpc扩展无关,能够独立实现XML-RPC的协议交互,如果没有xmlrpc扩展,建议使 用PEAR::XML-RPC扩展。

我们这里主要是以XML-RPC来简单描述Web Service的交互过程,部分内容来自PHP手册,更详细内容,建议参考手册。

[  安装xmlrpc扩展 ]

如果你的系统中没有安装xmlrpc的php扩展,那么请正确安装。

在 Windows平台下,首先把PHP安装目录下的扩展php_xmlrpc.dll放到C:\Windows或者C:\Winnt目录下,(PHP4的扩 展在C:\php\extensions目录中,PHP5的扩展在C:\php\ext目录中),同时在C:\Windows\php.ini或者C: \Winnt\php.ini中把extension=php_xmlrpc.dll前面的分号";"去掉,然后重启Web服务器后查看 phpinfo()有没有XML-RPC项目就能够确定是否已经正确安装xmlrpc扩展。

在Unix/Linux平台下,如果没有安装xmlrpc扩展,请在重新编译PHP,在configure的时候请加入 --with-xmlrpc 选项,然后查看phpinfo()看是否正常安装xmlrpc。

(注意:以下操作都是建立在xmlrpc扩张正常安装前提下,请务必正确安装。)

[  XML-RPC工作原理 ]

XML-RPC大致就是整个过程就是使用XML来进行通信。首先构造一个RPC 服务器端用来出来从RPC客户端传递过来的使用XML封装的请求,并且把处理结果通过XML的形式返回给RPC客户端,客户端就去分析XML获取自己需要的数据。

XML-RPC的服务器端必须有现成的函数提供给客户端调用,并且客户端提交的请求中的函数和方法必须和服务器端的一致,否则将无法获取所需要的结果。

下面我进行简单的代码来描述整个过程。


[  XML-RPC实践 ]

服务器端使用xmlrpc_server_create函数产生一个服务器端,然后把需要需要暴露的RPC调用接口进行注册,接受RPC客户端POST过来的XML数据,然后进行处理,处理结果通过XML的形式显示给客户端。

代码如下: rpc_server.php

  1. <?php  
  2. /** 
  3. * 函数:提供给RPC客户端调用的函数 
  4. * 参数: 
  5. * $method 客户端需要调用的函数 
  6. * $params 客户端需要调用的函数的参数数组 
  7. * 返回:返回指定调用结果 
  8. */  
  9. function rpc_server_func($method$params) {  
  10. $parameter = $params[0];  
  11.    if ($parameter == "get")  
  12.    {  
  13.        $return = ''This data by get method'';  
  14.    }  
  15.    else  
  16.    {  
  17.        $return = ''Not specify method or params'';  
  18.    }  
  19.    return $return;  
  20. }  
  21.   
  22. //产生一个XML-RPC的服务器端  
  23. $xmlrpc_server = xmlrpc_server_create();  
  24.   
  25. //注册一个服务器端调用的方法rpc_server,实际指向的是rpc_server_func函数  
  26. xmlrpc_server_register_method($xmlrpc_server"rpc_server""rpc_server_func");  
  27.   
  28. //接受客户端POST过来的XML数据  
  29. $request = $HTTP_RAW_POST_DATA;  
  30.   
  31. //执行调用客户端的XML请求后获取执行结果  
  32. $xmlrpc_response = xmlrpc_server_call_method($xmlrpc_server$request, null);  
  33.   
  34. //把函数处理后的结果XML进行输出  
  35. header(''Content-Type: text/xml'');  
  36. echo $xmlrpc_response;  
  37.   
  38. //销毁XML-RPC服务器端资源  
  39. xmlrpc_server_destroy($xmlrpc_server);  
  40. ?> 

服务器端构造好了,那么再构造我们的RPC客户端。客户端大致通过Socket访问XML-RPC服务器端的80端口,然后把需要调用的RPC接口封装到XML里,通过POST请求提交给RPC服务器端,最后获取服务器端返回结果。

代码如下:rpc_client.php

  1. <?php  
  2. /** 
  3. * 函数:提供给客户端进行连接XML-RPC服务器端的函数 
  4. * 参数: 
  5. * $host  需要连接的主机 
  6. * $port  连接主机的端口 
  7. * $rpc_server XML-RPC服务器端文件 
  8. * $request  封装的XML请求信息 
  9. * 返回:连接成功成功返回由服务器端返回的XML信息,失败返回false 
  10. */  
  11. function rpc_client_call($host$port$rpc_server$request) {  
  12.   
  13.    //打开指定的服务器端  
  14.    $fp = fsockopen($host$port);  
  15.   
  16.    //构造需要进行通信的XML-RPC服务器端的查询POST请求信息  
  17.    $query = "POST $rpc_server HTTP/1.0\nUser_Agent: XML-RPC Client\nHost: ".$host."\nContent-Type: text/xml\nContent-Length: ".strlen($request)."\n\n".$request."\n";  
  18.   
  19.    //把构造好的HTTP协议发送给服务器,失败返回false  
  20.    if (!fputs($fp$querystrlen($query)))  
  21.    {  
  22.        $errstr = "Write error";  
  23.        return false;  
  24.    }  
  25.      
  26.    //获取从服务器端返回的所有信息,包括HTTP头和XML信息  
  27.    $contents = '''';  
  28.    while (!feof($fp))  
  29.    {  
  30.        $contents .= fgets($fp);  
  31.    }  
  32.   
  33.    //关闭连接资源后返回获取的内容  
  34.    fclose($fp);  
  35.    return $contents;  
  36. }  
  37.   
  38. //构造连接RPC服务器端的信息  
  39. $host  = ''localhost'';  
  40. $port  = 80;  
  41. $rpc_server = ''/~heiyeluren/rpc_server.php'';  
  42.   
  43. //把需要发送的XML请求进行编码成XML,需要调用的方法是rpc_server,参数是get  
  44. $request = xmlrpc_encode_request(''rpc_server''''get'');  
  45.   
  46. //调用rpc_client_call函数把所有请求发送给XML-RPC服务器端后获取信息  
  47. $response = rpc_client_call($host$port$rpc_server$request);  
  48.   
  49. //分析从服务器端返回的XML,去掉HTTP头信息,并且把XML转为PHP能识别的字符串  
  50. $split = ''<?xml version="1.0" encoding="iso-8859-1"?>'';  
  51. $xml =  explode($split$response);  
  52. $xml = $split . array_pop($xml);  
  53. $response = xmlrpc_decode($xml);  
  54.   
  55. //输出从RPC服务器端获取的信息  
  56. print_r($response);  
  57.   
  58. ?>  

大致我们上面的例子就是提交一个叫做rpc_server的方法过去,参数是get,然后获取服务器端的返回,服务器端返回的XML数据是:

  1. <?xml version="1.0" encoding="iso-8859-1"?>  
  2. <methodResponse>  
  3. <params>  
  4. <param>  
  5.   <value>  
  6.    <string>This data by get method</string>  
  7.   </value>  
  8. </param>  
  9. </params>  
  10. </methodResponse> 

那么我们再通过xmlrpc_decode函数把这个XML编码为PHP的字符串,我们就能够随意处理了,整个Web Service交互完成。

[  结束语 ]

不 管是XML-RPC也好,SOAP也罢,只要能够让我们稳定、安全的进行远程过程的调用,完成我们的项目,那么就算整个Web Service就是成功的。另外,如果可以的话,也可以尝试使用PEAR中的XML-RPC来实现上面类似的操作,说不定会更简单,更适合你使用。

简单的使用XML-RPC进行Web Service交互就完成了,部分代码参考PHP手册,想获取详细信息建议参考手册,如果文章有不正确,请指正。

PHPRPC for JavaScript

PHPRPC for JavaScript
 
PHPRPC 对 JavaScript 的支持是非常早的,最初协议的设计中对数据的编码方式,返回数据的格式以及回调参数这些内容都充分考虑了 JavaScript 的特性。大部分 PHPRPC 用户最初也是把 PHPRPC 作为一个优秀的 Ajax 方案来使用的。但是 PHPRPC for JavaScript 不同于其它那些专门用于 Ajax 的 RPC 方案(如 DWR、xajax 等),PHPRPC for JavaScript 客户端与服务器是松散耦合的,它不依赖于某种特定语言编写的服务器。并且它还可以同目前许多优秀的 Ajax 框架(如 JQuery、YUI、MooTools 等)一起使用,而不会有任何冲突。并且它还有一个最大的好处,那就是可以让你轻松实现跨域调用。
 
 
PHPRPC for JavaScript 的安装
 

PHPRPC for JavaScript 有 2 个版本的实现,一个是纯 JavaScript 实现的版本(js 版本),另一个是 Flash 与 JavaScript 混合实现的版本(ajs 版本1)。

PHPRPC for JavaScript 两个版本的安装使用方法略有不同,我们下面分别来介绍一下。

js 版本的安装

js 目录下是纯 JavaScript 版本的源代码,其中的 compressed 目录下是压缩后的版本,压缩后的版本有 2 个,直接存放在 compressed 目录下的是不兼容 Windows IE 5.0 的版本(但兼容 Windows IE 5.5 及其以上版本),ie5compat 目录下是兼容 Windows IE 5.0 浏览器的版本。通常情况下,您不需要使用 ie5compat 的版本,这并不是因为 ie5compat 版本更大一些,运行效率更低一些。而是因为现在还在使用 IE 5.0 的用户基本上已经不存在了。另一个更重要的原因是你页面中的 HTML 部分可能根本就无法在 IE 5.0 上正常显示,其它的(除了 PHPRPC 以外的)脚本也无法在 IE 5.0 上面正常运行。因为 IE 5.0 对 HTML 的显示和 JavaScript 的支持实在是相当差劲。

你如果要在你的页面中引用 PHPRPC for JavaScript,只需要把 compressed 下的 phprpc_client.js 复制到你的 Web 目录的脚本目录下,然后在你的页面中像引用其它外部脚本一样引用该脚本文件就可以了,引用的代码看上去像下面这样:

  1. <script type="text/javascript" src="scripts/phprpc_client.js"></script> 

上面这句,你可以放在 head 中,也可以放在 body 中,只要放在你创建 PHPRPC_Client 对象之前就可以了。

ajs 版本的安装

ajs 目录下是 JavaScript 和 Flash 混合实现版本的源代码。同样,你只需要关心 compressed 下的文件(如果你不打算修改源代码的话)。你需要做的同样是将 phprpc_client.js 复制到你的 Web 目录的脚本目录下,同时把 phprpc_flash.js 也复制过去,而 flashrequest.swf 文件你可以放到 Web 目录下的任何你觉得合适的目录下。然后你需要将 phprpc_flash.js 中的 flashrequest.swf 路径修改为你实际的 Web 路径,修改后的代码可能会像这样:

  1. document.write(['<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" ',  
  2.                 'type="application/x-shockwave-flash" ',  
  3.                 'codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" ',  
  4.                 'width="0" height="0" id="flashrequest_as3">',  
  5.                 '<param name="movie" value="/scripts/flashrequest.swf" />',  
  6.                 '<param name="allowScriptAccess" value="always" />',  
  7.                 '<param name="quality" value="high" />',  
  8.                 '<embed src="/scripts/flashrequest.swf" type="application/x-shockwave-flash" ',  
  9.                 'width="0" height="0" name="flashrequest_as3" allowScriptAccess="always" />',  
  10.                 '</object>'].join('')); 

最后,在你的页面中引用这两个脚本,其中 phprpc_client.js 可以放在 head 或者 body 中,而 phproc_flash.js 需要放到 body 中,但不要放到 form 中。这里 ajs 的意思是 Another JavaScript 的意思,你也可以理解为 ActionScript & JavaScript 的意思。

 
 
 
 
PHPRPC for JavaScript 客户端

PHPRPC for JavaScript 的 js 版本和 ajs 版本除了前面安装部分所介绍的引入方法有所不同之外,在用法上基本没有区别。下面我们先来看一下基本用法。

如何调用 PHPRPC 服务

我们先通过一个简单的例子,来介绍如何调用 PHPRPC 服务。

  1. var client = new PHPRPC_Client('http://localhost:8080/index.aspx', ['add''sub']);  
  2. client.setKeyLength(256);  
  3. client.setEncryptMode(2);  
  4. client.add(1, 2, function (result, args, output, warning) {  
  5.     alert(result);  
  6. });  
  7. client.sub(1, 2, function (result, args, output, warning) {  
  8.     alert(result);  
  9. }); 

PHPRPC_Client 对象的 <span style="color: #008000;">setKeyLength</span> <span style="color: #008000;">setEncryptMode</span> 这两个方法是跟加密传输有关的。

<span style="color: #008000;">setKeyLength</span> 方法用于设置密钥长度

<span style="color: #008000;">setEncryptMode</span> 方法用于设置加密模式

上面设置密钥长度、加密模式都是可选项,如果你不需要这些功能,可以直接忽略它们。

PHPRPC 3.0 for JavaScript 客户端与 Java、.NET 客户端不同,它不需要使用 <span style="color: #008000;">useService</span> 来返回指定接口的远程代理对象,JavaScript 客户端本身就是一个代理对象。所以,上面例子中 <span style="color: #008000;">client.add</span> <span style="color: #008000;">client.sub</span> 这两个调用实际上调用的就是远程方法,对于 JavaScript 客户端来说,远程方法名虽然可以不事先声明,但这样只能在 <span style="color: #008000;">client.onready</span> 事件发生后或者 <span style="color: #008000;">client.getReady()</span> 等于 true 时才能进行调用,但这种做法是为了保持与旧版本兼容而提供的,属于过时的方法,所以不推荐这种做法,而是强烈建议像上面那样直接在客户端代码中指定所需要调用的远程方法名。

回调函数有四个参数,你可以认为它们是服务器端方法执行之后返回的内容。

第一个参数 <span style="color: #008000;">result</span> 是服务器端方法(函数)的返回值,它可以是任意类型。

第二个参数 <span style="color: #008000;">args</span> 是方法调用的参数,如果这个调用是一个引用参数传递的调用,参数也有可能被修改,这时候,你可以通过 args 来获得修改后的参数,关于引用参数传递的调用我们后面会做进一步说明。

第三个参数 <span style="color: #008000;">output</span> 是服务器端输出的内容,它是字符串类型的。

第四个参数 <span style="color: #008000;">warning</span> 是服务器端发生的警告错误(目前只有 PHP 服务器会产生警告错误),一般只调试过程中可能会用到。

通过这个例子,我想你已经可以掌握 PHPRPC for JavaScript 客户端的基本使用方法了。

如何在调用 PHPRPC 服务时,进行引用参数传递?

引用参数传递实际上非常简单,看下面这个例子,首先来看 PHP 的服务器端:

  1. <?php  
  2. include('phprpc_server.php');  
  3. function inc(&$n) {  
  4.     $n++;  
  5. }  
  6. $phprpc_server = new PHPRPC_Server();  
  7. $phprpc_server->add('inc');  
  8. $phprpc_server->start();  
  9. ?> 

这个服务器发布了一个 <span style="color: #008000;">inc</span> 方法,该方法是将参数值加一。这个方法是需要引用参数传递的。下面我们来看看如何在 JavaScript 中调用这个远程过程:

  1. var client = new PHPRPC_Client('http://localhost/index.php', ['inc']);  
  2. client.inc(1, function (result, args, output, warning) {  
  3.     alert(args[0]);  
  4. }, true); 

其实很简单,只要在回调函数之后跟一个 true 参数就可以了。这个 true 就是表示启用引用参数传递。

如何来得到远程过程执行的错误信息?

PHPRPC 把错误分为两种,一种是致命错误,一种是警告性错误。

当远程过程执行发生致命错误时,远程过程调用的返回值是一个 PHPRPC_Error 类型的对象,它其中包含了远程过程执行时发生的致命错误信息。

当远程过程执行发生警告性错误时,你可以通过回调函数的第四个参数 warning 得到警告错误, warning 的值也是 PHPRPC_Error 类型的对象。如果没有发生警告错误,warning 为 null

 

PHPRPC for PHP

Install PHPRPC for PHP

Uncompress PHPRPC for PHP releases on the fly,

  • bigint.php
  • compat.php
  • phprpc_date.php
  • xxtea.php

These 4 files are common files which are required by both server and client side.

  • phprpc_client.php

This is the client code, if you need PHPRPC for PHP client only, common files and this file are enough for you, include this file in your client code only, common files are included implicitly.

  • dhparams
  • dhparams.php
  • phprpc_server.php

These files are required by PHPRPC server.
dhparams folder includes parameters which will be used to generate private key in encrypted mode, if you need detailed explaination, see Encrypted transfer.

dhparams.php includes a class to access dhparams directory.
phprpc_server.php is PHPRPC for PHP server, if you are going to publish services by PHP, you should include this file, common files and dhparams.php will be included implicitly.

Runtime environmonet

  • PHP 4.3+, PHP 5, PHP 6
  • PHPRPC client requires socket extension
  • PHPRPC server requires HTTP server with PHP support, such as IIS, apache, lighttpd
  • Session must be configured properly, if encrypted mode is enabled

bigint extenstion is recommeneded to enable when encrypt is enabled, gmp, big_int, bcmath are supported by PHPRPC (ordered by performance). If your host doesn't support these extensions, key exchange will be done by php script, it is a bit slower.

For better encryption performance, xxtea PECL extension should be enabled, it is written in C programming language, this extension can significantly improve encryption performance.

How to compile xxtea extension

There are three popular ways to do this:

  1. Compiled with PHP source code
  2. Use phpize
  3. Use Microsfot Visual C (.net or 6.0) on Windows

1. compile with PHP source code

  1. Create ext/xxtea in PHP source code root path, move all files to this folder
  2. Run <span style="font-family: Arial;">./buildconf</span> to create PHP configure script
  3. Compile PHP with <span style="font-family: Arial;">--enable-xxtea</span> flag, this will create a xxtea internal module, using <span style="font-family: Arial;">--enable-xxtea=shared</span> flag instead will create a dymanical extension.

2. phpize utility

  1. Uncompress xxtea extension source code achive
  2. Run <span style="font-family: Arial;">phpize</span>, this will create configure script and other stuff required
  3. Run <span style="font-family: Arial;">./configure --enable-xxtea=share</span> to generate a proper makefile
  4. Run <span style="font-family: Arial;">make</span> to compile xxtea extension
  5. <span style="font-family: Arial;">make install</span>, this will install xxtea extension to PHP

3. Use Microsoft Visual C (.net or 6.0)

  1. Create ext/xxtea in PHP source code root path, move all files to this folder
  2. Copy php4ts.lib (for PHP 4) or php5ts.lib (for PHP 5) from current working PHP directory to ext/xxtea.
  3. Open php_xxtea.sln or php_xxtea.dsw, compile Release_php4 or release_php5
  4. Copy php_xxtea.dll from ext/xxtea/Release_php4 or ext/xxtea/Release_php5 to PHP extension directory, the path of extension directory can be found in php.ini
  5. add this line to php.ini: <span style="font-family: Arial;">extension=php_xxtea.dll</span>
 
PHPRPC for PHP Server

Publish functions

In Quick start chapter, we already knew how to register a php function, in the following text, we are going to know which functions can be published as PHPRPC services.
Most functions can be published, even php internal functions, except the functions which have resource type parameters or return values (such as mysql_connect, mysql_query).
You can even publish several functions at one time, no matter your own functions or php internal ones. For example:


  1. <?php  
  2. include('php/phprpc_server.php');  
  3. function hello($name) {  
  4.     return 'Hello ' . $name;  
  5. }  
  6. $server = new PHPRPC_Server();  
  7. $server->add(array('hello''md5''sha1'));  
  8. $server->add('trim');  
  9. $server->start();  
  10. ?> 

In this case, we published 4 functions, hello is the one we created, the other three are php internal functions. We can place the function names into an array, add method can accept function names as string and array both.

Publish methods

Class static methods and instance methods are supported as well, for example:

  1. <?php  
  2. include('php/phprpc_server.php');  
  3. class Example1 {  
  4.     static function foo() {  
  5.         return 'foo';  
  6.     }  
  7.     function bar() {  
  8.         return 'bar';  
  9.     }  
  10. }  
  11. $server = new PHPRPC_Server();  
  12. $server->add('foo''Example1');  
  13. $server->add('bar'new Example1());  
  14. $server->start();  
  15. ?> 

foo is a class method, the second parameter is the class name. bar is an instance method, so the second parameter should be an instance of class. If you publish an instance method by the way of class method, you will get a warning error when calling. 1

Maybe we want to ask how to publish 2 methods with the names belonging to different classes? Will they conflict? How to avoid conflict?

Alias

It does, this happens usally, In some cases, the functions registered later may overwrite the one registered before, PHPRPC provide alias mechanism to solve this problem, see following code:

  1. <?php  
  2. include('php/phprpc_server.php');  
  3. function hello($name) {  
  4.     return 'Hello ' . $name;  
  5. }  
  6. class Example1 {  
  7.     static function foo() {  
  8.         return 'foo';  
  9.     }  
  10.     function bar() {  
  11.         return 'bar';  
  12.     }  
  13. }  
  14. class Example2 {  
  15.     function foo() {  
  16.         return 'foo, too';  
  17.     }  
  18.     function bar() {  
  19.         return 'bar, too';  
  20.     }  
  21. }  
  22. $server = new PHPRPC_Server();  
  23. $server->add('hello', NULL, 'hi');  
  24. $server->add('foo''Example1''ex1_foo');  
  25. $server->add('bar'new Example1(), 'ex1_bar');  
  26. $server->add(array('foo''bar'), new Example2(), array('ex2_foo''ex2_bar'));  
  27. $server->start();  
  28. ?> 

Both functions and methods can be registered with alias. To register a function, the second parameter should be set as NULL. To add several methods/functions at one time, the number and order of alias should match the functions/methods list.

NOTE, the original name of the functions/methods are not callable, only alias can be used.

Use Session

In the above example, we did publish an instance method, but we didn't use $this to access instance members in code. It is unusual actaully, it looks like a class static method except it will get warning by called as static methods". You should find it don't work as expected when trying to access instance members in it, for example:

  1. <?php  
  2. include('php/phprpc_server.php');  
  3. class ExampleCounter {  
  4.     var $_count = 0;  
  5.     function inc() {  
  6.         $this->_count += 1;  
  7.     }  
  8.     function count() {  
  9.         return $this->_count;  
  10.     }  
  11. }  
  12. $server = new PHPRPC_Server();  
  13. $server->add(array('inc''count'), new ExampleCounter());  
  14. $server->start();  
  15. ?> 

We published two instance methods of ExampleCounter, but no matter how many times inc called by clients, count always returns 0.

It is caused by the way of PHP execution. The server side pages are loaded again everytime you call them. So, a new ExampleCount instance will be created everytime, and destoried with all page content.

If you want to use instances like local resources, you can add instances to sessions, for example:

  1. <?php  
  2. include('php/phprpc_server.php');  
  3. class ExampleCounter {  
  4.     var $_count = 0;  
  5.     function inc() {  
  6.         $this->_count += 1  
  7.     }  
  8.     function count() {  
  9.         return $this->_count;  
  10.     }  
  11. }  
  12. if (!isset($_SESSION['counter'])) {  
  13.     $_SESSION['counter'] = new ExampleCounter();  
  14. }  
  15. $server = new PHPRPC_Server();  
  16. $server->add(array('inc''count'), $_SESSION['counter']);  
  17. $server->start();  
  18. ?> 

By this way, you will get expected result when calling from clients, you can operate session in instances as well.

  1. <?php  
  2. include('php/phprpc_server.php');  
  3. class ExampleCounter {  
  4.     function ExampleCounter() {  
  5.         if (!isset($_SESSION['count'])) {  
  6.             $_SESSION['count'] = 0;  
  7.         }  
  8.     }  
  9.     function inc() {  
  10.         $_SESSION['count'] += 1;  
  11.     }  
  12.     function count() {  
  13.         return $_SESSION['count'];  
  14.     }  
  15. }  
  16. $server = new PHPRPC_Server();  
  17. $server->add(array('inc''count'), new ExampleCounter());  
  18. $server->start();  
  19. ?> 

That's exactly what we do when developing PHP dynamic pages. If you need application scope instead of session scope, database and Memcache can help, they are out of topic, we won't talk about them here.

Publishment options

Services publishment has several options, in most cases, you don't need to worry about them, the default values should be the best. Here is an example of options:

  1. <?php  
  2. include('php/phprpc_server.php');  
  3. function hello($name) {  
  4.     return 'Hello ' . $name;  
  5. }  
  6. $server = new PHPRPC_Server();  
  7. $server->add('hello');  
  8. $server->setCharset('UTF-8');  
  9. $server->setDebugMode(true);  
  10. $server->setEnableGZIP(true);  
  11. $server->start();  
  12. ?> 

setCharset is a method used to set charset, the default value is 'UTF-8', if you need other charsets, you need to call this method, UTF-8 is one of the best choice for internationalization.

setDebugMode decide whether your servre is running in debug mode. In debug mode, the published methods will return more detailed information, including filenames, lines. Generally speaking, in production site, this options should be turned off, it is secure for your web server, so the default value is false.

setEnableGZIP is used to turn on/off gzip compression. GZIP compresson can reduce the number of bytes transmitted, but it costs more memory and CPU, so the default value is off. Actually, many web servers (such as Apache, lighttpd, nginx) provide GZIP compression, we don't need to turn it on in general.
You won't see the warning error, because it won't popup like a dialog box, it is visible in programming level only, you will find it when you check the error object.

 
 
 
PHPRPC for PHP Client
 
 

In Quick start chapter, we have got to know how to use PHPRPC client to access PHPRPC services, let us have a look at a more complicated example:

  1. <?php  
  2. include ("php/phprpc_client.php");  
  3. $client = new PHPRPC_Client();  
  4. $client->setProxy(NULL);  
  5. $client->useService('http://127.0.0.1/server.php');  
  6. $client->setKeyLength(1000);  
  7. $client->setEncryptMode(3);  
  8. $client->setCharset('UTF-8');  
  9. $client->setTimeout(10);  
  10. echo $client->hi('PHPRPC'), "\r\n";  
  11. echo $client->getKeyLength(), "\r\n";  
  12. echo $client->getEncryptMode(), "\r\n";  
  13. echo $client->getCharset(), "\r\n";  
  14. echo $client->getTimeout(), "\r\n";  
  15. ?> 

This is the hi method we used before, maybe you have found out there are no parameters when we initialize PHPRPC_Client, so we need to call useService to indicate a service URL.

setProxy is provided to set HTTP proxy, proxies willn't be used if NULL is set. You can set proxies by two ways:

  1. By URL: http://username:password@host:port, username, password and port are optional
  2. Passing 4 parameters, they are host, port, username and password by order, again, port, username and password are optional.

<span style="color: #008000;">useService</span> is used to set service URL, if it is set when initializing, it is not necessary to call it anymore. But you can change service URL anytime you want by this method .

<span style="color: #008000;">setKeyLength</span> sets key length.

<span style="color: #008000;">setEncryptMode</span> sets encrypted mode.

<span style="color: #008000;">setCharset</span> sets client charset.

<span style="color: #008000;">setTimeout</span> sets timeout period, use the number of seconds as parameter.

<span style="color: #008000;">getKeyLength</span> gets key length, it is a negotiated value you got from the first remote call, otherwise, it is the value you set by setkeyLength (or default value 128).

<span style="color: #008000;">getEncryptMode</span> gets encrypted mode, after the first call, if the server doesn't support encryption, this method will return 0, otherwise return the value you set.

<span style="color: #008000;">getCharset</span> return charset, after the first call, this method will return server side charset, otherwise, it will return the charset you set.

<span style="color: #008000;">getTimeout</span> return timeout period you set or default value 30s.

Reference parameters

When calling PHPRPC service by method names, it is not reference passing, because it is completely different between remote calling and local calling. For local, Reference parameters is actuall passing memory address (pointer), local calling is possible to modify the value of parameters (not modifying pointer). It cost less then passing value. But remote calling will serialize local value, then pass to remote sever, this is passing value, if passing reference is needed, the server side need to serialize parameters, the return to clients. It is costly, that why PHPRPC client don't use reference parameters by default.

It is not difficult to use reference parameters in PHPRPC, but you need invoke to call remote services. Let use see an example. The server published a PHP internal function: sort, below is how we call it from client:

  1. <?php  
  2. include ("php/phprpc_client.php");  
  3. $client = new PHPRPC_Client('http://127.0.0.1/server.php');  
  4. $fruits = array("lemon""orange""banana""apple");  
  5. $args = array(&$fruits, SORT_STRING);  
  6. print_r($fruits);  
  7. $client->invoke('sort'$args, true);  
  8. print_r($fruits);  
  9. ?> 

We will see the result:

Well, success.

Three things need to be aware:

First, the second parameter of invoke is reference parameter, it must be a variable, so we cannot say:

  1. $client->invoke('sort', array(&$fruits, SORT_STRING), true);  

You will get an error like this:

Second, when assigning $args, if $fruits is not a reference, you won't see the changed value after calling invoke by print_r($fruits);, but if you call print_r($args);, you will find out the first element is changed, it successed.

Third, most important one, the third parameter of invoke decide whether use reference parameters, if it is true, then pass reference, if false, pass value. The default value is false, you are able to use invoke to pass value, but it is more complicated, but if you need string type function name to call remote service, it is a good deal for you.

 

 
PHPRPC for PHP utilities

PHPRPC for PHP Date Class

PHP provides many date and time functions, but they can process string or number format only. In PHP, there is no class to present date and time until PHP 5.2, ~Datetime is shipped with PHP 5.2, but there are no detailed documentation of it.

To exchange date type with the other language, PHPRPC use PHPRPC_Date to present date type. When serializing or unserializing, all date class will be processed by PHPRPC_Date, different languages can exchange date type data directly by this class.

Create PHPRPC_Date object

To get a PHPRPC_Date object, 4 ways are available:

  1. PHPRPC_Date construction function
  2. PHPRPC_Date static method now
  3. PHPRPC_Date static method today
  4. PHPRPC_Date static method parse

They are different and relative, when call PHPRPC_Date constructor without parameters, it returns the same result as now. today returns the same date as now, but time part is fulfilled by 0. PHPRPC_Date constructor takes int and string type parameters, parse supports these two types as well. int type parameter is Unix timestamp, constructor and parse accept it identically. For string parameter, it is different, constructor accepts the same formats as strtotime, but parse accepts the following formats:

short format 2008-10-21
long format 2008-10-21 12:13:34
full format 2008-10-21 12:13:34.167

The full format is not supported by strtotime. parse takes PHPRPC_Date object as parameter, it will return the parameter directly.

PHPRPC_Date instance properties

year Year
month Month
day Day
hour Hour
minute Minute
second Second
millisecond Millisecond

PHPRPC_Date instance methods

addMilliseconds($milliseconds) add $milliseconds milliseconds, $milliseconds is integer, positive or negative number
addSeconds($seconds) add $seconds seconds, $seconds is integer, positive or negative number
addMinutes($minutes) add $minutes minutes, $minutes}}} is integer, positive or negative number
addHours($hours) add $hours hours, $hours is integer, positive or negative number
addDays($days) add $days days, $days is integer, positive or negative number
addMonths($months) add $months months, $months is integer, positive or negative number
addYears($years) add $years years, $years is integer, positive or negative number
after($when) If current object is later than $when, return true, otherwise, return false. $when should be accepted by parse
before($when) If current object is earlier than $when, return true, otherwise return false. $when should be accepted by parse
equals($when) If current object equals $when, return true, otherwise, return false. $when should be accepted by parse
set($year, $month, $day[, $hour, $minute, $second[, $millisecond]]) Set values
time() Return Unix timestamp of this object
toString() Return textual representation of current object, it is full format which is supported by parse
dayOfWeek() Return the day of the week, value, from 0-6, 0 is Sunday, 1 is Monday ... 6 is Saturday
dayOfYear() Return the day of the year

PHPRPC_Date static methods

dayOfWeek($year, $month, $day) Return the day of the week, value, from 0-6, 0 is Sunday, 1 is Monday ... 6 is Saturday
dayOfYear($year, $month, $day) Return the day of the year
isLeapYear($year) Return if it is leap year
daysInMonth($year, $month) Return how many days in this month
isValidDate($year, $month, $day) Is it a valid date?
isValidTime($hour, $minute, $second) Is it a valid time?
 
 
 
 
 

详解Linux中Load_average负载

        也许你在学习Linux操作系统,会遇到很多问题,这里为你讲解Linux系统Load average负载的知识,你可能对于 Linux 的负载均值(load averages)已有了充分的了解。负载均值在 uptime 或者 top 命令中可以看到,它们可能会显示成这个样子:

  load average: 0.09, 0.05, 0.01

  很多人会这样理解负载均值:三个数分别代表不同时间段的系统平均负载(一分钟、五 分钟、以及十五分钟),它们的数字当然是越小越好。数字越高,说明服务器的负载越 大,这也可能是服务器出现某种问题的信号。

  而事实不完全如此,是什么因素构成了负载均值的大小,以及如何区分它们目前的状况是 “好”还是“糟糕”?什么时候应该注意哪些不正常的数值?

  回答这些问题之前,首先需要了解下这些数值背后的些知识。我们先用最简单的例子说明, 一台只配备一块单核处理器的服务器。

  行车过桥

  一只单核的处理器可以形象得比喻成一条单车道。设想下,你现在需要收取这条道路的过桥 费 - 忙于处理那些将要过桥的车辆。你首先当然需要了解些信息,例如车辆的载重、以及还有多少车辆正在等待过桥。如果前面没有车辆在等待,那么你可以告诉后面的司机通过。 如果车辆众多,那么需要告知他们可能需要稍等一会。

  因此,需要些特定的代号表示目前的车流情况,例如:

  0.00 表示目前桥面上没有任何的车流。 实际上这种情况与 0.00 和 1.00 之间是相同的,总而言之很通畅,过往的车辆可以丝毫不用等待的通过。

  1.00 表示刚好是在这座桥的承受范围内。 这种情况不算糟糕,只是车流会有些堵,不过这种情况可能会造成交通越来越慢。

  超过 1.00,那么说明这座桥已经超出负荷,交通严重的拥堵。 那么情况有多糟糕? 例如 2.00 的情况说明车流已经超出了桥所能承受的一倍,那么将有多余过桥一倍的车辆正在焦急的等待。3.00 的话情况就更不妙了,说明这座桥基本上已经快承受不了,还有超出桥负载两倍多的车辆正在等待。

  上面的情况和处理器的负载情况非常相似。一辆汽车的过桥时间就好比是处理器处理某线程 的实际时间。Unix 系统定义的进程运行时长为所有处理器内核的处理时间加上线程 在队列中等待的时间。

  和收过桥费的管理员一样,你当然希望你的汽车(操作)不会被焦急的等待。所以,理想状态 下,都希望负载平均值小于 1.00 。当然不排除部分峰值会超过 1.00,但长此以往保持这 个状态,就说明会有问题,这时候你应该会很焦急。

  “所以你说的理想负荷为 1.00 ?”

  嗯,这种情况其实并不完全正确。负荷 1.00 说明系统已经没有剩余的资源了。在实际情况中 ,有经验的系统管理员都会将这条线划在 0.70:

  “需要进行调查法则”: 如果长期你的系统负载在 0.70 上下,那么你需要在事情变得更糟糕之前,花些时间了解其原因。

  “现在就要修复法则”:1.00 。 如果你的服务器系统负载长期徘徊于 1.00,那么就应该马上解决这个问题。否则,你将半夜接到你上司的电话,这可不是件令人愉快的事情。

  “凌晨三点半锻炼身体法则”:5.00。 如果你的服务器负载超过了 5.00 这个数字,那么你将失去你的睡眠,还得在会议中说明这情况发生的原因,总之千万不要让它发生。

  那么多个处理器呢?我的均值是 3.00,但是系统运行正常!

  哇喔,你有四个处理器的主机?那么它的负载均值在 3.00 是很正常的。

  在多处理器系统中,负载均值是基于内核的数量决定的。以 100% 负载计算,1.00 表示单个处理器,而 2.00 则说明有两个双处理器,那么 4.00 就说明主机具有四个处理器。

  回到我们上面有关车辆过桥的比喻。1.00 我说过是“一条单车道的道路”。那么在单车道 1.00 情况中,说明这桥梁已经被车塞满了。而在双处理器系统中,这意味着多出了一倍的 负载,也就是说还有 50% 的剩余系统资源 - 因为还有另外条车道可以通行。

  所以,单处理器已经在负载的情况下,双处理器的负载满额的情况是 2.00,它还有一倍的资源可以利用。

  多核与多处理器

  先脱离下主题,我们来讨论下多核心处理器与多处理器的区别。从性能的角度上理解,一台主 机拥有多核心的处理器与另台拥有同样数目的处理性能基本上可以认为是相差无几。当然实际 情况会复杂得多,不同数量的缓存、处理器的频率等因素都可能造成性能的差异。

  但即便这些因素造成的实际性能稍有不同,其实系统还是以处理器的核心数量计算负载均值 。这使我们有了两个新的法则:

  “有多少核心即为有多少负荷”法则: 在多核处理中,你的系统均值不应该高于处理器核心的总数量。

  “核心的核心”法则: 核心分布在分别几个单个物理处理中并不重要,其实两颗四核的处理器 等于 四个双核处理器 等于 八个单处理器。所以,它应该有八个处理器内核。

  审视我们自己

  让我们再来看看 uptime 的输出

  ~ $ uptime

  23:05 up 14 days, 6:08, 7 users, load averages: 0.65 0.42 0.36

  这是个双核处理器,从结果也说明有很多的空闲资源。实际情况是即便它的峰值会到 1.7,我也从来没有考虑过它的负载问题。

  那么,怎么会有三个数字的确让人困扰。我们知道,0.65、0.42、0.36 分别说明上一分钟、最后五分钟以及最后十五分钟的系统负载均值。那么这又带来了一个问题:

  我们以哪个数字为准?一分钟?五分钟?还是十五分钟?

  其实对于这些数字我们已经谈论了很多,我认为你应该着眼于五分钟或者十五分钟的平均数 值。坦白讲,如果前一分钟的负载情况是 1.00,那么仍可以说明认定服务器情况还是正常的。 但是如果十五分钟的数值仍然保持在 1.00,那么就值得注意了(根据我的经验,这时候你应该增加的处理器数量了)。

  那么我如何得知我的系统装备了多少核心的处理器?

  在Linux 下,可以使用

  cat /proc/cpuinfo

  获取你系统上的每个处理器的信息。如果你只想得到数字,那么就使用下面的命令:

  grep 'model name' /proc/cpuinfo | wc -l

  Popularity: 11% [?]

  以上就是Linux系统Load average负载的内容。