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.

Top comments (1)

Collapse
 
wolfyj profile image
Anton Titov

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

Classic DEV Post from 2020:

js visualized

🚀⚙️ JavaScript Visualized: the JavaScript Engine

As JavaScript devs, we usually don't have to deal with compilers ourselves. However, it's definitely good to know the basics of the JavaScript engine and see how it handles our human-friendly JS code, and turns it into something machines understand! 🥳

Happy coding!