PHP微服务 hyperf+nacos使用

这里简单说下微服务,及架构方面东西
1:微服务对php +fpm 模式意义不是很大,原因就是php+fpm 天生支持模块拆分,热更新,如果只是性能上的考虑,那php+fpm 还是比较耗cpu的,这样用微服务就不如单体+多库的方案,即一个项目拆分多个数据库,不同业务调用对应的库即可

2:微服务可不可以直接调用http 接口?
答案是可以的,都是自己公司内部的项目,并且项目不是说特别大,完全可以直接通过接口,这里注意几个点:
a:保证这些接口通过内网调用,毕竟走外网时间是比较长的
b:Http接口默认为同步调用,当需要同时调用多个服务时,考虑接口时间问题
c:定义好规范,写好文档

3:微服务的服务治理方式
a:现在流行的都是统一服务治理中心(nacos/zookeeper)及 统一配置管理服务;
优点就是: 统一管理,动态增减,及时上下线
缺点是: 多个服务集中到了一起,容易形成瓶颈压力,多一层服务增加了系统的复杂性,在性能测试时需要考虑服务中心的压力
b:服务自治模式,即每个服务提供统一的对外域名,由服务内部自己负载均衡实现减压,这样可以省去服务治理这一层,也不需要服务一直ping 治理中心;
缺点就是新增服务得手动添加到负载均衡器里

这里主要记录使用统一治理中心(nacos) 跟hyerf 搭建微服务的过程及注意点

nacos 安装

官网链接:https://nacos.io/zh-cn/docs/quick-start.html
建议 直接下载nacos编译包解压后,开箱即用,这里注意配置,官方建议最低 2c 4g 太低可能导致启动失败

unzip nacos-server-$version.zip 或者 tar -xvf nacos-server-$version.tar.gz
  cd nacos/bin

启动建议先用单机模式

启动命令(standalone代表着单机模式运行,非集群模式):

sh startup.sh -m standalone

管理后台连接,默认账号 nacos 密码nacos
http://host:8848/nacos/
在这里插入图片描述

注意端口是否有放开,生产环境建议端口白名单方式,因为更新删除服务是不需要账号密码的,外网很容易被人攻击

PS:nacos 服务默认会健康检测,30s内没有心跳会默认删除,所以自己后台创建的 可能过一会就不见了,不要大惊小怪,还有通过api服务,创建要注意对应的命名空间,命名空间一定要在服务端先创建好,不然直接添加是不会在后端显示的,不熟悉的以为是bug

Hyperf 使用(下面流程都是参照官方文档,这里说明下注意点)

官网:https://hyperf.wiki/2.2

一:根据文档安装好 swoole,跟Hyperf框架

二:配置rpc 个服务 主要下面2个文档里
1:https://hyperf.wiki/2.2/#/zh-cn/json-rpc
2:https://hyperf.wiki/2.2/#/zh-cn/service-register

这里主要说下注意的
1:rpc 服务注解需要加上publishTo=“nacos” 才会同步到到nacos

<?php
/**
 * 测试微服务提供者
 * Created by PhpStorm.
 * User: 05
 * Date: 2022/8/10
 * Time: 10:06
 */

namespace App\JsonRpc;

use Hyperf\RpcServer\Annotation\RpcService;
/**
* 注意,如希望通过服务中心来管理服务,需在注解内增加 publishTo 属性
 * @RpcService(name="CaculatorService", protocol="jsonrpc-http", server="jsonrpc-http",publishTo="nacos")
 */
class CaculatorService extends DefaultService
{

    /**
     * @param int $a
     * @param int $b
     * @return int
     */
    public function add(array $pms)
    {

        return $pms['a']+$pms['b']+10;
    }


    public function get(array $pms)
    {
        return parent::get($pms); // TODO: Change the autogenerated stub
    }


}

2:配置config/autoload/services.php 的 namespace_id 需要提前在nacos 后台新增命名空间,获取对应的id,随便填写一个链接上nacos 也不会在nacos后台显示的

3:作为消费者端使用nacos的话 在config/autoload/services.php 也是要配置服务驱动相关配置drivers (服务提供端可以不配置consumers),不然获取不到服务
配置如下

return [
    'enable' => [
        // 开启服务发现
        'discovery' => true,
        // 开启服务注册
        'register' => true,
    ],
    // 服务消费者相关配置
    //服务消费者
    'consumers' => [
        [
            // 对应消费者类的 $serviceName
            'name' => 'CaculatorService',
            // 这个消费者要从哪个服务中心获取节点信息,如不配置则不会从服务中心获取节点信息
            'registry' => [
                'protocol' => 'nacos',
                'address' => 'http://127.0.0.1:8848',
            ],
            // 如果没有指定上面的 registry 配置,即为直接对指定的节点进行消费,通过下面的 nodes 参数来配置服务提供者的节点信息
//            'nodes' => [
//                ['host' => '127.0.0.1', 'port' => 9513],
//            ],
        ],
        [
            // 对应消费者类的 $serviceName
            'name' => 'DefaultService',
            // 这个消费者要从哪个服务中心获取节点信息,如不配置则不会从服务中心获取节点信息
            'registry' => [
                'protocol' => 'nacos',
                'address' => 'http://127.0.0.1:8848',
            ],
            // 如果没有指定上面的 registry 配置,即为直接对指定的节点进行消费,通过下面的 nodes 参数来配置服务提供者的节点信息
//            'nodes' => [
//                ['host' => '127.0.0.1', 'port' => 9513],
//            ],
        ]
    ],
    // 服务提供者相关配置
    'providers' => [],
    // 服务驱动相关配置
    'drivers' => [
//        'consul' => [
//
//        ],
        'nacos' => [
            // nacos server url like https://nacos.hyperf.io, Priority is higher than host:port
            // 'url' => '',
            // The nacos host info
            'host' => '127.0.0.1',
            'port' => 8848,
            // The nacos account info
            'username' => 'nacos',//可以不用配置
            'password' => '',//似乎可以不用密码
            'guzzle' => [
                'config' => null,
            ],
            'group_name' => 'api',
            'namespace_id' => '950fdb39-edeb-4978-8ab6-3cf742732449',//这个要从nacos 后台获取
            'heartbeat' => 5,
            'ephemeral' => false, // 是否注册临时实例
        ],
    ],
];

链接成功后
在这里插入图片描述
使用&优化:
按照官方的使用方式是
服务提供者Service写好对应类 通过注解 RpcService 对外提供服务
服务消费者Consumer 需要写对应的类跟方法,之后才能调用
这里就会有个问题,如果一个服务被很多项目调用,那提供者每添加一个服务 其他的消费者都要对应添加一个服务类或者修改对应的方法,这个对消费者非常不友好,服务多了需要浪费大量的时间互相同步

简单优化
消费端提供默认消费者 DefaultConsumer ,生成唯一get 方法 通过传入方法名跟参数调用对应的服务,这样服务端新增一个服务时 消费者端就不在需要写对应的消费类,但这里要求服务接收的参数就只有一个数组,通过数组约定里面的参数(这里还是要生成对应的消费端类继承DefaultConsumer,将$serviceName 默认好,Hyperf 通过容器绑定了对象,初始化就会获取此属性,没法更改,如果自己new DefaultConsumer 会造成内存泄露,随着访问量上升cpu 跟内存很快就爆满…)
代码如下
DefaultConsumer

<?php
/**
 * Created by PhpStorm.
 * User: 05
 * Date: 2021/11/25
 * Time: 15:00
 */

namespace App\JsonRpc;


use Hyperf\Rpc\Protocol;
use Hyperf\Rpc\ProtocolManager;
use Hyperf\RpcClient\AbstractServiceClient;
use Hyperf\RpcClient\Client;
use Hyperf\Utils\ApplicationContext;
use Psr\Container\ContainerInterface;
use function Swoole\Coroutine\Http\get;

class DefaultConsumer extends AbstractServiceClient
{

    /**
     * 定义对应服务提供者的服务名称
     * @var string
     */
    protected $serviceName = 'DefaultService';


    /**
     * 定义对应服务提供者的服务协议
     * @var string
     */
    protected $protocol = 'jsonrpc-http';



    public function get(string $method, array $parms)
    {
        return $this->__request($method, ['pms'=>$parms]);
    }


}

CalculatorServiceConsumer

<?php
/**
 * Created by PhpStorm.
 * User: 05
 * Date: 2021/11/25
 * Time: 15:00
 */

namespace App\JsonRpc;



class CalculatorServiceConsumer extends DefaultConsumer
{

    /**
     * 只需要改这个属性,方法实现可以不要,直接通过参数传递
     * @var string
     */
    protected $serviceName = 'CaculatorService';


}

测试

消费者代码

<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */
namespace App\Controller;

use App\JsonRpc\DefaultConsumer;
use function EasyWeChat\Kernel\Support\get_server_ip;
use Hyperf\HttpServer\Annotation\AutoController;

/**
 * @AutoController();需要引入对应的文件
 * */
    class IndexController extends BaseController
{


       public function rpc_test(){
       //这里不要自己new 实例,不然内存泄露...
         $cus=   ApplicationContext::getContainer()->get(CalculatorServiceConsumer::class);
         $d= $cus->get("add",['a'=>1,'b'=>2]);
        return [
            '本机ip' => get_server_ip(),
            'message' => "微服务调用结果=$d",
        ];
    }
}

服务提供者代码

<?php
/**
 * 测试微服务提供者
 * Created by PhpStorm.
 * User: 05
 * Date: 2022/8/10
 * Time: 10:06
 */

namespace App\JsonRpc;

use Hyperf\RpcServer\Annotation\RpcService;
/**
 * @RpcService(name="CaculatorService", protocol="jsonrpc-http", server="jsonrpc-http",publishTo="nacos")
 */
class CaculatorService extends DefaultService
{

    /**
     * @param int $a
     * @param int $b
     * @return int
     */
    public function add(array $pms)
    {

        return "远端调用结果:".(($pms['a']+$pms['b'])*10);//这里是远端特意乘以10
    }




}

结果
在这里插入图片描述

注册中心下线服务后,再调用就不可用了
在这里插入图片描述
在这里插入图片描述

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐