RPC全称为Remote Procedure Call,翻译过来为"远程过程调用"。主要应用于不同的系统之间的远程通信和相互调用。
比如有两个系统,一个是PHP写的,一个是JAVA写的,而PHP想要调用JAVA中的某个类的某个方法,这时候就需要用到RPC了。
怎么调?直接调是不可能,只能是PHP通过某种自定义协议请求JAVA的服务,JAVA解析该协议,在本地实例化类并调用方法,然后把结果返回给PHP。
这里我们用PHP的socket扩展来创建一个服务端和客户端,演示调用过程。
RpcServer.php代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | <?php class RpcServer { protected $serv = null; public function __construct( $host , $port , $path ) { //创建一个tcp socket服务 $this ->serv = stream_socket_server( "tcp://{$host}:{$port}" , $errno , $errstr ); if (! $this ->serv) { exit ( "{$errno} : {$errstr} \n" ); } //判断我们的RPC服务目录是否存在 $realPath = realpath (__DIR__ . $path ); if ( $realPath === false || ! file_exists ( $realPath )) { exit ( "{$path} error \n" ); } while (true) { $client = stream_socket_accept( $this ->serv); if ( $client ) { //这里为了简单,我们一次性读取 $buf = fread ( $client , 2048); //解析客户端发送过来的协议 $classRet = preg_match( '/Rpc-Class:\s(.*);\r\n/i' , $buf , $class ); $methodRet = preg_match( '/Rpc-Method:\s(.*);\r\n/i' , $buf , $method ); $paramsRet = preg_match( '/Rpc-Params:\s(.*);\r\n/i' , $buf , $params ); if ( $classRet && $methodRet ) { $class = ucfirst( $class [1]); $file = $realPath . '/' . $class . '.php' ; //判断文件是否存在,如果有,则引入文件 if ( file_exists ( $file )) { require_once $file ; //实例化类,并调用客户端指定的方法 $obj = new $class (); //如果有参数,则传入指定参数 if (! $paramsRet ) { $data = $obj -> $method [1](); } else { $data = $obj -> $method [1](json_decode( $params [1], true)); } //把运行后的结果返回给客户端 fwrite( $client , $data ); } } else { fwrite( $client , 'class or method error' ); } //关闭客户端 fclose( $client ); } } } public function __destruct() { fclose( $this ->serv); } } new RpcServer( '127.0.0.1' , 8888, './service' ); |
RpcClient.php代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | <?php class RpcClient { protected $urlInfo = array (); public function __construct( $url ) { //解析URL $this ->urlInfo = parse_url ( $url ); if (! $this ->urlInfo) { exit ( "{$url} error \n" ); } } public function __call( $method , $params ) { //创建一个客户端 $client = stream_socket_client( "tcp://{$this->urlInfo['host']}:{$this->urlInfo['port']}" , $errno , $errstr ); if (! $client ) { exit ( "{$errno} : {$errstr} \n" ); } //传递调用的类名 $class = basename ( $this ->urlInfo[ 'path' ]); $proto = "Rpc-Class: {$class};" . PHP_EOL; //传递调用的方法名 $proto .= "Rpc-Method: {$method};" . PHP_EOL; //传递方法的参数 $params = json_encode( $params ); $proto .= "Rpc-Params: {$params};" . PHP_EOL; //向服务端发送我们自定义的协议数据 fwrite( $client , $proto ); //读取服务端传来的数据 $data = fread ( $client , 2048); //关闭客户端 fclose( $client ); return $data ; } } $cli = new RpcClient( 'http://127.0.0.1:8888/test' ); echo $cli ->hehe(); echo $cli ->hehe2( array ( 'name' => 'test' , 'age' => 27)); |
然后分别运行上面两个脚本(注意,php要添加环境变量)
1 2 | > php RpcServer.php > php RpcClient.php |
结果如下:
Test.php代码如下:
1 2 3 4 5 6 7 8 9 | <?php class Test { public function hehe() { return 'hehe' ; } public function hehe2( $params ) { return json_encode( $params ); } } |
目录结构如下:
上面我们自定义的协议,可以随意修改,只要是客户端和服务端两边能够统一并能解析。
客户端通过请求服务端,把要调用的类,方法和参数传递给服务端,服务端去通过实例化调用方法返回结果。