laravel/octane

Support Building a gRPC server in Octane + Roadrunner

dac-humi opened this issue ยท 7 comments

PHP doesn't have support for building gRPC servers, only gRPC clients.

Not true for PHP+Roadrunner.

Using Roadrunner version 2.10.7 Laravel can be configured to use Octane for HTTP alongside a separate PHP entrypoint for gRPC requests.

What follows is a proof of concept. I did it as part of a hackathon. It's not clean or platform agnostic (i'm on MacOS) but it can hopefully lead into a feature request where Laravel Octane support gRPC out of the box. ๐Ÿ™

Example .rr.yaml configuration.

version: '2.7'

rpc:
    listen: tcp://127.0.0.1:6001

grpc:
    listen: tcp://0.0.0.0:9001
    proto:
        - proto/service.proto
    tls:
        key: worker.key
        cert: worker.crt
    pool:
        command: 'php worker.php'
        num_workers: 2

Which, when all put together, will output for rr workers:

Screen Shot 2022-08-04 at 3 01 04 PM

Howto

A new gRPC services start with the protocol buffer language.

To work with .proto files you need a few tools:

  • composer require spiral/roadrunner-grpc
  • brew install protobuf
  • ./vendor/bin/rr download-protoc-binary -l ~/bin

Next, build a proto/service.proto file. This file structures protocol buffer data and is used to automatically generate boilerplate PHP to implement and extend. Example:

syntax = "proto3";
package service;

option php_namespace="Proto\\Service";
option php_metadata_namespace="Proto\\Service\\Meta";

service Notification {
  rpc Announcement (Message) returns (AnnouncementID) {
  }
}

message Message {
  string subject = 1;
  string message = 2;
}

message AnnouncementID {
  string id = 1;
}

With the tools and proto file, we can generate the PHP boilerplate:

protoc --php_out=proto/php/ --php-grpc_out=proto/php/ proto/service.proto

Then build a proto/php/Proto/NotificationService.php that uses the boilerplate:

<?php

namespace Proto;

use Proto\Service\AnnouncementID;
use Proto\Service\NotificationInterface;
use Proto\Service\Message;
use Spiral\RoadRunner\GRPC\ContextInterface;

class NotificationService implements NotificationInterface
{
    public function Announcement(ContextInterface $ctx, Message $in): AnnouncementID
    {
        $subject = $in->getSubject();
        $message = $in->getMessage();
        // ...
        $id = rand();
        $out = new AnnouncementID();
        $out->setId($id);
        return $out;
    }
}

And a worker.php that serves it:

#!/usr/bin/env php
<?php
use Proto\Service\NotificationInterface;
use Proto\NotificationService;
use Spiral\RoadRunner;

// Created a separate gRPC worker because Octane only handles PSR7, as seen in:
//   + vendor/laravel/octane/bin/roadrunner-worker
//   + vendor/laravel/octane/src/Commands/StartRoadRunnerCommand.php

// Bootstrap Laravel so we can muck about in it
require __DIR__ . '/bootstrap/autoload.php';
$app = require_once __DIR__ . '/bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$kernel->bootstrap();

// But, use Spiral to handle gRPC
$server = new RoadRunner\GRPC\Server(null, [
    'debug' => true,
]);
$server->registerService(NotificationInterface::class, new NotificationService());
$server->serve(RoadRunner\Worker::create());

Where composer.json contains...

        "psr-4": {
            "Proto\\": "proto/php/Proto"
        }

Certs are generated...

openssl req -newkey rsa:2048 -nodes -keyout worker.key -x509 -days 365 -out worker.crt

Etc.

Testing

To test use grpcui. It's sort of like Postman, but for gRPC APIs instead of REST.

brew install grpcui
grpcui -insecure -import-path ./proto/ -proto service.proto localhost:9001

This scans your proto files and opens a web browser with a list of available endpoint for you to try.

Better docs here

Thank you for your consideration.

I don't personally plan to invest any time into this as it is not something we would really be using or ever understand here at Laravel. Feel free to fork or create your own version of Octane that supports this if you wish!

image

@dac-humi You have no idea how much I needed this months ago on my microservice cluster. I did try to make a PoC of this back then by piecing out info out there, but I just wasn't successful and did not have the time to do so and settled with good ol' slow REST and message queues :(

A shame that the devs don't see the value on this. Although I would not be surprised if it gets quietely added in the future. I'll be certain to keep an eye on it and @ you.

Thanks. As mentioned in the linked ticket, it's easier to start with: https://github.com/spiral/app-grpc

To address the "or ever understand here at Laravel" if I were to rewrite the issue it I would have started with:

As developers, when we think of REST it's generally: HTTP requests, JSON responses.
There's another way: RPC requests, PROTOBUF responses.
Also known as gRPC. Devs want gRPC because it's faster, and the endpoints are self-documenting.

Good times.

I don't personally plan to invest any time into this as it is not something we would really be using or ever understand here at Laravel. Feel free to fork or create your own version of Octane that supports this if you wish!

It is good to know that there is a lack of curiosity.

Grpc would make Laravel more usable in microservices architecture.
I actually don't understand why Tylor is being negative about it .
@taylorotwell read the docs , Grpc is easier then rest

There's something missing in this write-up that might be of some help to others who can't rely on everything being in the path environment.

First of all, anyone using Windows with the native command line (was the case when I developed a console command to execute the process for me), use the usual C:\Path\To\Your\Files paradigm when using the protoc.exe executable (pre-built release in the assets section). Relative paths only seemed to work in bash (applicable for Git for Windows users).

When doing protoc.exe --help, you'll notice some verbiage about using plugins with the build process.

image

The roadrunner plugin example doesn't use this convention in their documentation.:

For me, it was necessary to follow this convention in order to get the plugin to fit into the build process without throwing errors. Otherwise, using only the documentation from roadrunner, I wasn't getting the proto(s) to build correctly.

image

I needed to do:

--plugin=protoc-gen-php-grpc=C:\Exact\Path\To\protoc-gen-php-grpc.exe in the options when executing the command.

Hope that helps anyone else that is kicking off their gRPC project in a similar fashion.