遠程過程調用

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

    }

}

目錄結構如下:

上面我們自定義的協議,可以隨意修改,只要是客戶端和服務端兩邊能夠統一併能解析。

客戶端通過請求服務端,把要調用的類,方法和參數傳遞給服務端,服務端去通過實例化調用方法返回結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章