DEV Community

Sergey Telpuk
Sergey Telpuk

Posted on

PHP gRPC-Server & NodeJS-Client

Hello friends, hope you are doing well!

I'm gonna describe how easy to run PHP gRPC-server and code NodeJS gRPC- client. At this article, I will highlight only the more important parts of setting up the workflow. Here, You can find a done repository. If you aren't acquainted with gRPC, look at the following link.

There will be considered a fast solution spiral/php-grpc to gRPC-server. Let's start off.

Firstly, look at docker-compose file:

version: '3.3'

services:

    grpc_php_server: # grpc server
        build:
            context: ./containers/php
            dockerfile: php.docker
        container_name: grpc_php_server
        working_dir: /app
        command: ./rr-grpc serve -v -d
        volumes:
            - ./src/php:/app

    grpc_php_protoc: # protoc generator 
        image:  grpc/php
        container_name: grpc_protoc
        working_dir: /app
        volumes:
            - ./src/php:/app

    client_nodejs: # grpc client
        image: node:latest
        container_name: client_nodejs
        command: node ./client.js
        working_dir: /app
        links:
          - grpc_php_server
        depends_on:
          - grpc_php_server
        volumes:
            - ./src/nodejs:/app

As you could see it's a very simple docker-compose file.
Note: if you want to play around, don't forget to grab all dependencies.

docker-compose run grpc_php_server composer install
docker-compose run client_nodejs npm install

The proto file was drawn up as very primitive way as I can 😏.
contrived.proto is for server and client:

syntax = "proto3";

package service;

service ContrivedService {
    rpc ContrivedMethod (ContrivedMessageRequest) returns (ContrivedMessageResponse) {
    }
}

message ContrivedMessageRequest {
    string body = 1;
}

message ContrivedMessageResponse {
    string body = 1;
}

That one was put at php/proto and nodejs/proto directories.

For generating needed interfaces and initiation skeleton for gRPC-server the following command was used:

docker-compose run grpc_php_protoc protoc --plugin=./protoc-gen-php-grpc --php_out=/app --php-grpc_out=/app ./proto/contrived.proto

The setting of grpc-server was hosted at root directory .rr.yaml:

grpc:
  listen: "tcp://:3000"
  proto: "./proto/contrived.proto"
  workers:
    command: "php worker.php"
    pool:
      numWorkers: 1
      maxJobs:    1

The worker.php can look like:

<?php
declare(strict_types=1);
/**
 * Sample GRPC PHP server.
 */
use Spiral\Goridge;
use Spiral\RoadRunner;
ini_set('display_errors', 'stderr');
require "vendor/autoload.php";
$server = new \Spiral\GRPC\Server();
$server->registerService(\Service\ContrivedServiceInterface::class, new \Service\ContrivedService());
$w = new RoadRunner\Worker(new Goridge\StreamRelay(STDIN, STDOUT));
$server->serve($w);

The ContrivedService.php can look like:

<?php
namespace Service;
use Service\ContrivedMessageRequest;
use Service\ContrivedMessageResponse;
use Service\ContrivedServiceInterface;
use Spiral\GRPC;
class ContrivedService implements ContrivedServiceInterface
{
    /**
     * @param GRPC\ContextInterface $ctx
     * @param ContrivedMessageRequest $in
     * @return ContrivedMessageResponse
     *
     * @throws GRPC\Exception\InvokeException
     */
    public function ContrivedMethod(GRPC\ContextInterface $ctx, ContrivedMessageRequest $in): ContrivedMessageResponse
    {
        $response = new ContrivedMessageResponse();
        $response->setBody("Hello");
        return $response;
    }
}

So, let's move up to js-client.

The client.js can look like:

const path = require('path');
const PROTO_PATH = path.resolve(__dirname, './proto/contrived.proto');
const GRPCClient = require('node-grpc-client');

const myClient = new GRPCClient(PROTO_PATH, 'service', 'ContrivedService', 'grpc_php_server:3000');

const dataToSend = {
    body: 'Nodejs client!'
};

myClient.runService('ContrivedMethod', dataToSend, (err, res) => {
    if (err) {
        console.error(err);
    }

    console.log('Service response\n', res);
});

For trying it out was used the following command:

 docker-compose up

The output result:

grpc_php_server    | DEBU[0000] [rpc]: started                               
grpc_php_server    | DEBU[0000] [grpc]: started                              
client_nodejs      | Service response
client_nodejs      |  { body: 'Hello' }

As you could see it's tremendous simple to start working with gRPC and start thinking of adapting that one to our workflow.

What kind of benefits of adopting GRPC:

  1. Easy to understand.
  2. Web infrastructure already built on top of HTTP.
  3. Great tooling for testing, inspection, and modification.
  4. Loose coupling between clients/server makes changes easy.
  5. High-quality HTTP implementations in every language.

Latest comments (1)

Collapse
 
wolfyj profile image
Anton Titov

Another benefit of GRPC is much higher performance compared to classic REST.