使用PHP来简单的创建一个RPC服务

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 );      } }

目录结构如下:

上面我们自定义的协议,可以随意修改,只要是客户端和服务端两边能够统一并能解析。

客户端通过请求服务端,把要调用的类,方法和参数传递给服务端,服务端去通过实例化调用方法返回结果。