閱讀324 返回首頁    go 汽車大全


服務治理深入淺出(1)- 遠程方法調用的實現

需求

在了解了前麵我們關於服務治理出現的必要性之後。我們知道服務治理是建立在眾多“服務”基礎之上的,那麼,第一步,打通這些服務是基礎,也就是我們常說的 RPC 遠程調用。要像調用本地方法一樣調用遠程服務器上的方法。

現在簡單粗暴口語化的方式來介紹一個需求:

A 服務器上部署的項目中,有一個UserService裏麵有一個getUserInfo的方法。
B 服務器上想"直接"調用該方法,怎麼辦?

分析

我們以 PHP 為例來進行分析。
我們希望在 B 服務器上實現類似於 A 服務器上直接調用方式

$userService = new UserService();
$userService->getUserInfo($uid);

我們經常會使用 SDK 來調用第三方提供的 api 服務,我們的做法肯定類似於

$client  = new \SDK\Client();
$request = new \SDK\UserService\Request\GetStudentInfoRequest();
$request->setUid($uid);
$request->setMethod("GET");
$response = $client->doAction($request);

sdk 裏麵的 GetStudentInfoRequest 通過http映射 A 服務器上的UserService::getUserInfo

sdk 的改造

我們隻需要在原來的基礎上稍作修改即可,下麵的代碼僅做簡單的演示

服務端

該服務部署在localhost:8081

class UserService
{
    public static function getUserInfo($uid)
    {
        // 假設以下內容從數據庫取出
        return [
            'id'       => $uid,
            'username' => 'mengkang',
        ];
    }
}

$service = $_GET['service'];
$action = $_GET['action'];
$argv = file_get_contents("php://input");

if (!$service || !$action) {
    die();
}

if ($argv) {
    $argv = json_decode($argv, true);
}


$res = call_user_func_array([$service, $action], $argv);

echo json_encode($res);

客戶端

class Client
{
    private $url;
    private $service;

    private $rpcConfig = [
        "UserService" => "https://127.0.0.1:8081",
    ];

    /**
     * Client constructor.
     * @param $service
     */
    public function __construct($service)
    {
        if (array_key_exists($service, $this->rpcConfig)) {
            $this->url = $this->rpcConfig[$service];
            $this->service = $service;
        }
    }

    public function __call($action, $arguments)
    {


        $content = json_encode($arguments);
        $options['http'] = [
            'timeout' => 5,
            'method'  => 'POST',
            'header'  => 'Content-type:application/x-www-form-urlencoded',
            'content' => $content,
        ];

        $context = stream_context_create($options);

        $get = [
            'service' => $this->service,
            'action'  => $action,
        ];

        $url = $this->url . "?" . http_build_query($get);

        $res = file_get_contents($url, false, $context);

        return json_decode($res, true);
    }

}

$userService = new Client('UserService');
var_export($userService->getUserInfo(103));

這樣是不是就非常方便的在客戶端實現了像在本地一樣調用遠程的方法呢?這也是鳥哥 @Laruence yar 的操作原理。下麵對比下 Yar 的 demo:

Yar 演示

yar https://github.com/laruence/yar
yar 的 java 客戶端 https://github.com/zhoumengkang/yar-java-client

客戶端代碼,假設該服務設在局域網10.211.55.4上

class RpcClient {
    // RPC 服務地址映射表
    public static $rpcConfig = array(
        "RewardScoreService"    => "https://10.211.55.4/yar/server/RewardScoreService.class.php",
    );

    public static function init($server){
        if (array_key_exists($server, self::$rpcConfig)) {
            $uri = self::$rpcConfig[$server];
            return new Yar_Client($uri);
        }
    }
}

$RewardScoreService = RpcClient::init("RewardScoreService");
var_dump($RewardScoreService->support(1, 2));

服務器端代碼

class RewardScoreService {
    /**
     * $uid 給 $feedId 點讚
     * @param $feedId  interge
     * @param $uid  interge
     * @return void
     */
    public function support($uid,$feedId){
        return "uid = ".$uid.", feedId = ".$feedId;
    }
}

$yar_server = new Yar_server(new RewardScoreService());
$yar_server->handle();

yar 背後的故事就是我前麵那段 sdk 改造的代碼演示。想必看到這裏,rpc 框架不再那麼神秘了吧。
當然這隻是實現了 rpc 的一小部分,簡單的遠程調用。畢竟 php 是世界上最好的語言
java 上麵執行遠程調用也類似。

java 遠程調用

如果換成 java 可稍微麻煩點,java 實現起來之後會讓你覺得更加的本地化,所以 java 也是最強大的語言
由於 java 是靜態編譯的,不存在類似於 php 裏的__call方法的方式來實現遠程調用,一般通過動態代理來實現

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Created by zhoumengkang on 5/12/15.
 */

/**
 * 點讚的積分服務接口
 */
interface RewardScoreService{
    String support(int uid,int feedId);
}

public class SupportService {

    public static void main(String[] args) {
        add(1,2);
    }

    /**
     * uid 給 feedId 點讚
     * @param uid
     * @param feedId
     * @return
     */
    public static String add(int uid, int feedId){
        YarClient yarClient = new YarClient();
        RewardScoreService rewardScoreService = (RewardScoreService) yarClient.proxy(RewardScoreService.class);
        return rewardScoreService.support(uid, feedId);
    }

}

class YarClient {

    public final Object proxy(Class type) {
        YarClientInvocationHandler handler = new YarClientInvocationHandler();
        return Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, handler);
    }
}

final class YarClientInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("這裏的動態調用實現了 php 的 __call 方法");

        System.out.println("method : " + method.getName());
        for (int i = 0; i < args.length; i++) {
            System.out.println("args["+ i +"] : " + args[i]);
        }

        return null;
    }
}

了解更多

看完本篇,是不是頓時覺得 rpc 框架不再那麼神秘,有一點點感覺了呢?

老鐵周末的直播:揭開她的神秘麵紗 - 零基礎構建自己的服務治理框架 趕快上車
https://segmentfault.com/l/1500000011300619

最後更新:2017-11-09 15:06:13

  上一篇:go  阿裏90後工程師,如何用AI程序寫出雙11打call歌?
  下一篇:go  能免費能發新聞軟文外鏈的網站部分匯總