vandarpay / orchestration-saga
编排 Saga Laravel 包
Requires
- php: ^8.1
- ext-redis: *
- php-amqplib/php-amqplib: ^3.2
- vandarpay/service-repository: ^0.1
This package is auto-updated.
Last update: 2024-08-29 06:02:03 UTC
README
此包用于实现 Saga 编排设计模式以设计微服务。使用此模式,您可以使用 Redis
和 RabbitMQ
实现服务间的通信。
安装
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
如果启用此功能,则应用程序将在创建队列时使用 rabbitmq 交换 功能,并将所有请求和响应都复制到名为 log
的队列中。
此参数设置发送请求直到从另一个服务接收到响应的时间长度。
激活此参数后,您服务的所有通信步骤都将保存在日志文件中。仅在开发期间使用此功能。还可以使用 RPC_DEBUG_LOG_CHANNEL
参数指定日志存储通道。显然,在 RPC_DEBUG_LOG_CHANNEL
参数中引入的通道必须在 logging.php
文件中定义。
您可以使用此参数引入一个作业以存储请求发生的所有状态。此作业的输入类型为 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,连接图如下。
在服务器端,我们使用 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