微服务架构模式之 sidecar 模式(下):应用实例

这篇分享学院君主要通过示例代码的方式演示在 PHP 中基于 Micro Proxy 实现微服务的注册和引用,并使其能够与现有的其它语言实现的微服务(如 Go、Python、Java 等)可以相互通信。

整体思路

整体实现思路如下:

我将基于 PHP 的 Swoole 扩展来实现一个 HTTP 服务器提供对外的微服务接口,在 HTTP 服务器启动时,将该服务节点注册到 Go Micro 的注册中心(Consul),接下来,当用户通过 HTTP 发起远程服务请求时(通过 Micro Proxy),Micro Proxy 会将向注册中心查询对应的服务节点,然后将请求转发过来,基于 Swoole 实现的 HTTP 服务器会触发 onRequest 事件对应的回调函数的执行,根据请求参数将业务逻辑分发给对应的类去执行,最后再将处理结果返回给调用方。当 HTTP 服务器关闭时,会从注册中心删除对应的服务节点。

注:当然,这只是一个最基本的实现,主要用于演示 Micro Proxy 的应用场景,不要用于生产环境。

下面我们就来编写相应的 PHP 代码。

代理类

我在本地的 swoole 目录下创建了一个 microservice 子目录,用于存放本次开发编写的代码,首先创建一个 proxy.php 文件用于存放服务注册和模拟调用代码:

<?php

class Proxy {

    const REGISTRY_URL = 'http://localhost:8500/v1/agent/service/register';
    const DEREGISTRY_URL = 'http://localhost:8500/v1/agent/service/deregister';
    const PROXY_URL = 'http://localhost:8081';  // Micro Proxy 

    protected $headers = [
        'Content-Type' => 'application/json; charset=utf-8'
    ];

    /**
     * 注册服务到注册中心
     * @param $service
     */
    public function register($service)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, self::REGISTRY_URL);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true );
        curl_setopt($curl, CURLOPT_HTTPHEADER, $this->headers);
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT");
        curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($service));
        curl_exec($curl);
        curl_close($curl);
    }

    /**
     * 从注册中心删除服务
     * @param $service
     */
    public function deregister($service)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, self::DEREGISTRY_URL . '/' . $service['id']);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true );
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT");
        curl_exec($curl);
        curl_close($curl);
    }

    /**
     * 模拟对微服务发起 HTTP 请求
     * @param $path
     * @param $params
     * @return mixed
     */
    public function httpCall($path, $params)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, self::PROXY_URL);
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true );
        list($service, $endpoint) = explode('/', $path);
        $this->headers['Micro-Service'] = 'php.micro.srv.' . $service;
        $this->headers['Micro-Endpoint'] = $endpoint;
        curl_setopt($curl, CURLOPT_HTTPHEADER, $this->headers);
        curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($params));
        $result = curl_exec($curl);
        curl_close($curl);
        return $result;
    }
}

业务类

然后创建一个 greeter.php 文件用于存放问候服务类代码:

<?php
class greeter {
    public function hello($name = '学院君') {
        return json_encode(['msg' => '你好, ' . $name]);
    }
}

基于 HTTP 的 PHP 微服务实现

最后,我们基于 Swoole 来实现 HTTP 服务器代码:

<?php
include "proxy.php";
include "greeter.php";

// 表明程序启动后监听本地 9051 端口
$server = new swoole_http_server('192.168.31.218', 9508);

$proxy = new Proxy();

$service = [
    "id" => "php.micro.srv.greeter-" . uniqid(),
    "name" => "php.micro.srv.greeter",
    "address" => '192.168.31.218',
    "port" => 9508,
    "tags" => [
        "php-micro-service-demo"
    ]
];

// 启动事件回调函数
$server->on("start", function (\Swoole\Http\Server $server) use ($proxy, $service) {
    $proxy->register($service);
    echo "Swoole http server is started and micro services are registered\n";
});

// 请求事件回调函数
$server->on("request", function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) {
    if ($endpoint = $request->header['micro-endpoint']) {
        list($class, $method) = explode('/', $endpoint);
        $class = ucfirst($class);
        $obj = new $class;
        $params = json_decode($request->rawContent(), true);
        if (empty($params['name'])) {
            $data = $obj->$method();
        } else {
            $data = $obj->$method($params['name']);
        }
        $response->header("Content-Type", "application/json");
        $response->end($data);
    }
});

// 关闭事件回调函数
$server->on('shutdown', function ($server, $fd) use ($proxy, $service){
    $proxy->deregister($service);
    echo "Swoole http server is closed and micro services are deregistered\n";
});

// 启动 HTTP 服务器
$server->start();

我们将其保存为 server.php,可以看到,服务注册、调用和删除逻辑都在这里,我们重点看下服务调用代码,当 Micro Proxy 根据请求头中的 Micro-Service 值到注册中心查询到对应的服务节点(192.168.31.218:9508)后,会将请求转发到 Swoole HTTP 服务器来处理,并且将相应的 HTTP 请求信息都带过来,我们从请求头中拿到 Micro-Endpoint 字段值,并根据这个端点信息确定本地要调用的类和方法,然后将请求参数传进去,最后将处理结果以 JSON 格式返回。

这样一来,我们就可以将基于 PHP 实现的微服务纳入了 Go Micro 体系中,它们之间通过 Micro Proxy 通信,这使得 PHP 也就可以借助 Go Micro 的底层组件轻松实现微服务构建,而且这种实现对原本的 PHP 代码侵入很小,只需要在服务启动和关闭时从注册中心注册或删除服务节点即可。

微服务调用测试

首先,我们需要启动注册中心(已启动则忽略):

consul agent -dev

然后,要在 Go Micro 项目中启动 Micro Proxy:

micro proxy

最后,进入 swoole/microservice 目录启动基于 PHP 实现的微服务:

cd /path/to/swoole/microservice
php server.php

启动基于 PHP 实现的微服务

注册成功的话就可以在 Consul 控制面板中看到新注册的 php.micro.srv.greeter 服务节点了:

在 Consul 控制面板中看到新注册的服务节点

接下来,我们在 Postman 中模拟对这个远程服务接口的调用,基于 Micro Proxy 提供的代理地址:

在 Postman 中模拟对这个远程服务接口的调用

可以看到服务调用成功,对应的 CURL 代码如下:

curl -X POST \
  http://localhost:8081 \
  -H 'Content-Type: application/json' \
  -H 'Micro-Endpoint: greeter/hello' \
  -H 'Micro-Service: php.micro.srv.greeter' \
  -d '{"name": "学院君"}'

当然,你也可以修改请求参数对结果进行再次确认:

修改请求参数对结果进行再次确认

基于 PHP 实现的简单微服务到此就告一段落了,除了 PHP 之外,还可以基于 Micro Proxy 纳入其它编程语言实现的微服务到 Go Micro 体系,此外,感兴趣的同学,还可以参照此例实现基于 RPC 协议的 PHP 微服务。

上一篇: 微服务架构模式之 sidecar 模式(上):原理概述

下一篇: 通过命令行模式与 Go Micro 微服务进行交互