Consul服务注册实现-Swoft框架

环境准备

Swoft框架

参见:Swoft入门及技术指南  https://blog.csdn.net/yan_dk/article/details/90228563

 Consul

参见:Consul入门及技术指南 https://blog.csdn.net/yan_dk/article/details/91488680

Swoft框架实现Consul服务注册

需求分析:我们使用应用框架Swoft,建立应用的业务服务如支付pay,发布给其他应用来调用,使用Consul组件,将服务注册到Consul数据中心,那么应用框架Swoft的服务要作为Consul客户端,向Consul中心注册服务,由Consul中心来统一暴露和分发服务,这样我们在Swoft中需要增加Consule-client服务组件,后续实现这样的流程。

Swoft框架因为设计思想的原因,目前在做减法,所以对于服务发现这块没有支持,所以我们需要为此框架提供服务发现的机制。

1.  建立服务提供者Provider

这是建立的目录结构,提供者ConsulProvider、负载均衡器(这个暂不重要)

namespace App\Components\Consul;

use Swlib\SaberGM;

class ConsulProvider{
    const REGISTER_PATH = '/v1/agent/service/register'; //服务注册路径
    const HEALTH_PATH = '/v1/health/service/'; //获取健康服务

    public function registerServer($config)    {
        //echo 'http://'.$config['address'].':'.$config['port'].self::REGISTER_PATH,json_encode($config['register']);
        //注册地址底层错误无法使用
        if (env('AUTOLOAD_REGISTER')) {
            //var_dump(json_encode($config['register']));
            $this->curl_request('http://' . $config['address'] . ':' . $config['port'] . self::REGISTER_PATH, "PUT", json_encode($config['register']));
            output()->writeln("<success>Rpc service Register success by consul tcp=" . $config['address'] . ":" . $config['port'] . "</success>");
        }
        echo "启动consul注册服务".PHP_EOL;
    }

    /**
     * 获取某个服务的列表
     */
    public function getServerList($serviceName, $config)   {
        $query=[
            'dc'=>$config['discovery']['dc']
        ];
        if(!empty($config['discovery']['tag'])){
            $query['tag']=$config['discovery']['tag'];
        }
        $queryStr=http_build_query($query);
        //排除不健康的服务,获取健康服务
        $url = 'http://' . $config['address'] . ':' . $config['port'] . self::HEALTH_PATH . $serviceName.'?'.$queryStr;
        //负载机制
        $serviceList = $this->curl_request($url, 'GET');

        $serviceList=json_decode($serviceList, true);
        $address=[];
        foreach ($serviceList as $k=>$v){
            //判断当前的服务是否是活跃的,并且是当前想要去查询服务
            foreach ($v['Checks'] as $c){
                if($c['ServiceName']==$serviceName && $c['Status']=="passing"){
                    $address[$k]['address']=$v['Service']['Address'].":".$v['Service']['Port'];
                    $address[$k]['weight']=$v['Service']['Weights']['Passing'];
                }
            }
        }
        return $address;
    }

    public function curl_request($url, $method = 'POST', $data = [])    {
        $method = strtoupper($method);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        if (!empty($data)) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        }
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $res = curl_exec($ch);
        curl_close($ch);
        return $res;
    }
}

2.  建立监听器Listener

 建立监听器RegisterServer,实现了事件监听接口EventHandlerInterface,框架将ConsulProvider装载到Bean容器内存。

use Swlib\SaberGM;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
use Swoft\Server\ServerEvent;
/**
 * Class RegisterServer
 * @package App\Listener
 * @Listener(ServerEvent::BEFORE_START)
 */
class RegisterServer implements EventHandlerInterface{
    public function handle(EventInterface $event): void   {
         $config = bean('config')->get('provider.consul');
         bean('consulProvider')->registerServer($config);
    }
}

3. 建立客户端Client

class Provider implements ProviderInterface{
    protected $serviceName;

    public function __construct($serviceName)    {
        $this->serviceName = $serviceName;
    }

    public function getList(): array    {
        $config = bean('config')->get('provider.consul');
        $address = bean('consulProvider')->getServerList($this->serviceName, $config);
        //负载均衡(加权随机)
        $address = RandLoadBalance::select(array_values($address))['address'];
        //根据服务名称consul当中获取动态地址
        return [$address];
    }
}
class Client extends \Swoft\Rpc\Client\Client {
    protected  $serviceName; //服务名称

    public function getProvider(): ?ProviderInterface   {

        //切换成curl发生在服务启动之前
        //$config = bean('config')->get('provider.consul');
        //bean('consulProvider')->registerServer($config);
        //不能区分当前调用的服务是哪个
         return $this->provider=new Provider($this->getServiceName());
    }
    /*
     * 获取服务名称
     */
    public  function  getServiceName(){
        return $this->serviceName;
    }
}

4. 配置bean.php

bean.php文件中增加如下配置


return [
......
'consulProvider'=>[
        'class'=>\App\Components\Consul\ConsulProvider::class
    ],
...
]

 4. 启动Swoft

查看启动信息,打印出 “启动Consul注册服务”

持续完善,待续...

发布了179 篇原创文章 · 获赞 16 · 访问量 22万+

猜你喜欢

转载自blog.csdn.net/yan_dk/article/details/91862498