vandarpay / orchestration-saga

编排 Saga Laravel 包

0.1.1 2022-07-30 09:36 UTC

This package is auto-updated.

Last update: 2024-08-29 06:02:03 UTC


README

此包用于实现 Saga 编排设计模式以设计微服务。使用此模式,您可以使用 RedisRabbitMQ 实现服务间的通信。

安装

composer require vandarpay/orchestration-saga

发布配置文件

php artisan vendor:publish --provider="vandarpay\OrchestrationSaga\OrchestrationSagaServiceProvider"

要求

  • PHP 8.1
  • RabbitMQ
  • Redis
  • PHP redis 扩展
  • PHP swoole 扩展

环境

请将此环境变量添加到 .env 文件中

RPC_RABBITMQ_HOST=localhost
RPC_RABBITMQ_PORT=5672
RPC_RABBITMQ_USER=guest
RPC_RABBITMQ_PASSWORD=guest
RPC_RABBITMQ_CONNECTION_TIMEOUT=30
RPC_RABBITMQ_READ_WRITE_TIMEOUT=30
RPC_RABBITMQ_KEEP_ALIVE=true
RPC_USE_EXCHANGE_LOG=true
RPC_REQUEST_TIMEOUT=30
RPC_DEBUG=false 
RPC_DEBUG_LOG_CHANNEL=rpc 
LOG_JOB=App/Jobs/RpcLogJob 
  • RPC_USE_EXCHANGE_LOG

如果启用此功能,则应用程序将在创建队列时使用 rabbitmq 交换 功能,并将所有请求和响应都复制到名为 log 的队列中。

  • RPC_REQUEST_TIMEOUT

此参数设置发送请求直到从另一个服务接收到响应的时间长度。

  • RPC_DEBUG

激活此参数后,您服务的所有通信步骤都将保存在日志文件中。仅在开发期间使用此功能。还可以使用 RPC_DEBUG_LOG_CHANNEL 参数指定日志存储通道。显然,在 RPC_DEBUG_LOG_CHANNEL 参数中引入的通道必须在 logging.php 文件中定义。

  • LOG_JOB

您可以使用此参数引入一个作业以存储请求发生的所有状态。此作业的输入类型为 RpcLogJobDto,并在该作业中可以接收到以下状态。

  • init:收到新请求
  • sent:请求已发送到目标服务器
  • received:已收到请求
  • expired:所需的请求已过期
  • before_transform:在将数据转换为发送到目标服务器的数据之前
  • Processing:请求正在处理
  • processed:请求已处理
  • reply_request:发送回复
  • answered:已发送回复
  • response_received:收到响应
  • server_exception:目标服务发生错误
  • client_exception:发生拒绝服务错误
  • completed:回复已完成

生成器命令

php artisan make:callback-event
php artisan make:rollback-event
php artisan make:rpc-service

RPC 命令

php artisan rpc:listen-client
php artisan rpc:publish-client
php artisan rpc:listen-server
php artisan rpc:publish-server

Supervisor 配置

要激活服务之间的连接,必须从上述每个命令中创建一个 进程。下面是使用 supervisor 完成此任务的配置。

[program:swoole-publish-client-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /{Project Path}/artisan rpc:publish-client
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=root
numprocs=1
redirect_stderr=true
stdout_logfile=/{Project Path}/storage/logs/publish-client.log
stopwaitsecs=3600
logfile_maxbytes=500000000


[program:swoole-listen-client-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /{Project Path}/artisan rpc:listen-client
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=root
numprocs=1
redirect_stderr=true
stdout_logfile=/{Project Path}/storage/logs/listen-client.log
stopwaitsecs=3600
logfile_maxbytes=500000000

[program:swoole-listen-server-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /{Project Path}/artisan rpc:listen-server
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=root
numprocs=1
redirect_stderr=true
stdout_logfile=/{Project Path}/storage/logs/listen-server.log
stopwaitsecs=3600
logfile_maxbytes=500000000

[program:swoole-publish-server-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /{Project Path}/artisan rpc:publish-server
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=root
numprocs=1
redirect_stderr=true
stdout_logfile=/{Project Path}/storage/logs/publish-server.log
stopwaitsecs=3600
logfile_maxbytes=500000000

队列连接是如何工作的?

我们使用 pub/sub Redis 结构将 PHP 连接到 rabbitmq,连接图如下。

rabbitmq connections with redis stack

在服务器端,我们使用 swoole 扩展中的进程管理器来减少 rabbitmq 连接,并且每个进程在生成响应后都会被杀死。

Rpc 服务

要创建 RPC 客户端,首先运行以下命令

php artisan make:rpc-service

最后,要向目标服务器发送信息,请在 service provider 中指定服务名称和连接类型,如下所示

$publisher = resolve(Publisher::class);
$publisher->setConnectMicroservice('microservice-a',RpcCallTypeEnum::Sync)
->setConnectMicroservice('microservice-a',RpcCallTypeEnum::Async);

以下是一个使用同步方法的调用服务的示例

$this
->setServiceName('test')
->setCallBackEvent(CallbackEvent::class)
->setRollBackEvent(RollbackEvent::class)
->callSync('actionA', ['input' => $input]);

在类内部,创建方法以调用目标服务的所需参数的方法。调用可以是两种类型:同步(callSync)和异步(callAsync),您还可以使用以下命令定义成功返回和错误返回的事件。

php artisan make:callback-event
php artisan make:rollback-event

请注意在事件提供者服务中建立您的事件和监听器之间的连接

服务版本控制

当然,在服务的开发过程中,有时需要升级旧版本,这部分功能在本软件包中得到全面支持。为此,在考虑了服务的必要文件夹后,也需要对服务转换器进行此操作。因为服务和转换器类的对象是自动创建的,所以您在通过rpc调用服务时,必须在您的rpc服务中设置所需的版本文件夹到$serviceVersion变量。

以下是如何进行此操作的示例

class MicroserviceAlphaRpcService extends Service
{
    protected string $microName = 'micro-a';
    protected string $serviceName = 'test';
    protected ?string $serviceVersion = 'v1';
    public function test(){
        return $this->callSync('actionA', ['input' => $input]);
    }
}

在这种情况下,文件夹结构将发生变化如下

├── Services
    ├── Test 
    |   ├── v1
    |   |   ├── TestException.php
    |   |   ├── TestRepository.php
    |   |   ├── TestTransformer.php
    |   |   └── TestService.php
    |   └── v2
    |       ├── TestException.php
    |       ├── TestRepository.php
    |       ├── TestTransformer.php
    |       └── TestService.php
    └── AlphaService