Laravel 11.9集成Octane+RoadRunner gRPC插件故障求助
Laravel 11.9 + Octane + RoadRunner gRPC 集成问题与解决方法
环境与依赖
composer.json核心依赖:
... "require": { "php": "^8.2", "ext-grpc": "*", "google/protobuf": "^3.25", "grpc/grpc": "^1.57", "laravel/framework": "^11.9", "laravel/octane": "^2.3", "laravel/tinker": "^2.9", "spiral/goridge": "^4.2", "spiral/roadrunner": "^2024.1", "spiral/roadrunner-cli": "^2.6.0", "spiral/roadrunner-grpc": "^3.3", "spiral/roadrunner-http": "^3.3.0" }, ...
Proto定义与服务实现
voucher.proto 文件
package services; option php_namespace = "GrpcServices\\Voucher\\Messages"; option php_metadata_namespace = "GrpcServices\\Voucher"; service Voucher { rpc validateVoucher (VoucherMessageRequest) returns (VoucherMessageResponse) { } } message VoucherMessageRequest { repeated int32 VoucherId = 1; } message VoucherMessageResponse { int32 VoucherId = 1; int32 customerId = 2; }
Voucher服务实现(修正前)
namespace App\Grpc; use GrpcServices\Voucher\Messages\VoucherMessageRequest; use GrpcServices\Voucher\Messages\VoucherMessageResponse; use Spiral\Roadrunner\GRPC\ContextInterface; class VoucherService implements VoucherServiceInterface { public function validateVoucher(VoucherMessageRequest $request, ContextInterface $context): VoucherMessageResponse { $response = new VoucherMessageResponse(); return $response; } }
生成后的文件结构
app -Grpc | -GrpcServices | - Voucher | - Messages | - VoucherClient.php | - VoucherMessageRequest.php | - VoucherMessageResponse.php - Voucher.php
RoadRunner配置(.rr.yaml,修正前)
version: "3" rpc: listen: "tcp://127.0.0.1:6001" server: command: "php artisan octane:start --server=roadrunner --host=127.0.0.1 --port=8000" http: address: "0.0.0.0:8000" grpc: listen: "tcp://0.0.0.0:9090" proto: - "/var/www/html/server/protos/voucher.proto" logs: mode: development channels: grpc: level: debug type: stdout http: level: debug type: stdout server: level: debug type: stdout
遇到的报错
- 运行
./rr serve时的报错:
/var/www/html/server # ./rr serve -p 2024-06-06T07:59:27+0000 DEBUG rpc plugin was started {"address": "tcp://127.0.0.1:6001", "list of the plugins with RPC methods:": ["lock", "resetter", "app", "informer"]} handle_serve_command: Function call error: serve error from the plugin *grpc.Plugin stopping execution, error: grpc_plugin_serve: WorkerAllocate: static_pool_allocate_workers: goridge_frame_receive: validation failed on the message sent to STDOUT, see: https://roadrunner.dev/docs/known-issues-stdout-crc/current/en, invalid message: ERROR RoadRunner server is already running.
- 运行
php artisan octane:start后gRPC调用报错:
/var/www/html/client # php artisan app:test null // app/Console/Commands/test.php:36 {#566 +"metadata": [] +"code": 13 +"details": "sync_worker_receive_frame: Network: EOF" } // app/Console/Commands/test.php:36
正确集成步骤
1. 修正RoadRunner配置文件
核心问题是server.command使用了octane:start,导致RoadRunner启动后再次启动RoadRunner实例,引发冲突。修改为RoadRunner worker入口命令:
server: command: "php artisan roadrunner:worker"
2. 注册gRPC服务到RoadRunner
创建app/Providers/GrpcServiceProvider.php:
namespace App\Providers; use Illuminate\Support\ServiceProvider; use Spiral\RoadRunner\GRPC\Server; use App\Grpc\VoucherService; use GrpcServices\Voucher\VoucherInterface; class GrpcServiceProvider extends ServiceProvider { public function register() { $this->app->singleton(Server::class, function ($app) { $server = new Server(); // 注册生成的gRPC服务接口与实现类 $server->registerService(VoucherInterface::class, $app->make(VoucherService::class)); return $server; }); } }
然后在config/app.php的providers数组中添加:
App\Providers\GrpcServiceProvider::class,
3. 修正gRPC服务实现的接口
确保服务类实现的是protoc生成的VoucherInterface,而非自定义接口:
namespace App\Grpc; use GrpcServices\Voucher\Messages\VoucherMessageRequest; use GrpcServices\Voucher\Messages\VoucherMessageResponse; use Spiral\Roadrunner\GRPC\ContextInterface; use GrpcServices\Voucher\VoucherInterface; class VoucherService implements VoucherInterface { public function validateVoucher(VoucherMessageRequest $request, ContextInterface $context): VoucherMessageResponse { $response = new VoucherMessageResponse(); // 添加测试数据便于验证 $response->setVoucherId(1); $response->setCustomerId(1001); return $response; } }
4. 启动服务的正确方式
直接运行RoadRunner启动命令,无需再执行octane:start:
./rr serve
此时RoadRunner会同时管理HTTP服务(8000端口)和gRPC服务(9090端口)。
5. 验证gRPC调用
客户端测试代码示例:
namespace App\Console\Commands; use Illuminate\Console\Command; use GrpcServices\Voucher\Messages\VoucherMessageRequest; use GrpcServices\Voucher\VoucherClient; use Grpc\ChannelCredentials; class TestGrpc extends Command { protected $signature = 'app:test'; protected $description = 'Test gRPC service'; public function handle() { $client = new VoucherClient('localhost:9090', [ 'credentials' => ChannelCredentials::createInsecure(), ]); $request = new VoucherMessageRequest(); $request->addVoucherId(1); list($response, $status) = $client->validateVoucher($request)->wait(); dump($response); dump($status); } }
运行测试命令:
php artisan app:test
应返回包含设置数据的响应对象,状态码为0(成功)。
关键问题总结
./rr serve报错原因:配置中用octane:start启动worker,导致RoadRunner嵌套启动,端口冲突。octane:start启动后gRPC调用失败:Octane仅启动HTTP服务,未加载RoadRunner的gRPC插件,无法处理gRPC请求。- 核心解决思路:让RoadRunner通过配置统一管理所有服务(HTTP+gRPC),使用
roadrunner:worker作为worker入口,并在Laravel中注册gRPC服务实例。
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

