You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

遇到的报错

  1. 运行./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.
  1. 运行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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 00:29:51