Revision: Sun, 29 Mar 2020 14:49:18 GMT

GRPC - Client SDK

You must generate the client SDK to access your endpoints. This article based on proto file described earlier.

Prerequisites

The PHP client library requires an installation of PHP grpc extension.

$ sudo pecl install grpc

Read more about the extension here.

Compile grpc-php-plugin

You must compile grpc-php-plugin in order to generate the PHP client code. Follow this guide.

$ git clone --recursive -b v1.27.x https://github.com/grpc/grpc
$ cd grpc
$ cd third_party/protobuf 
$ ./autogen.sh
$ ./configure CC=clang CXX=clang++
$ make
$ make install
$ cd ../..
$ make 
$ make grpc_php_plugin

Make sure to install clang.

The location of generated plugin is grpc/bin/opt/grpc_php_plugin.

Generate SDK

We can generate the PHP client SDK in a new project. Copy the proto/ directory into the designated folder.

$ protoc -I proto/ --plugin=protoc-gen-grpc=grpc/bins/opt/grpc_php_plugin --php_out=. --grpc_out=. proto/calculator.proto 

You can observe the generated client code in App/Calculator/CalculatorClient.php.

Composer

Make sure to create the composer.json and run composer update to make the generated code loadable:

{
  "require": {
    "grpc/grpc": "^1.27",
    "google/protobuf": "^3.11"
  },
  "autoload": {
    "psr-4": {
      "App\\": "App"
    }
  }
}

Certificate

To connect to the running server, make sure to copy app.crt to your client application.

Example

We can create the client now in client.php:

<?php

declare(strict_types=1);

require 'vendor/autoload.php';

$client = new \App\Calculator\CalculatorClient(
    'localhost:50051',
    [
        'credentials' => \Grpc\ChannelCredentials::createSsl(file_get_contents("app.crt")),
    ]
);

[$result, $mt] = $client->Sum(
    new \App\Calculator\Sum([
            'a' => 1,
            'b' => 2
    ])
)->wait();

print_r($result->getResult());

Run php client.php to test your client.

Passing Metadata

You can pass the metadata using the second argument of the Sum function, use $mt->metadata to read the response metadata.

To read and send metadata in Calculator:

<?php

declare(strict_types=1);

namespace App\Calculator;

use Spiral\GRPC;

class Calculator implements CalculatorInterface
{
    public function Sum(GRPC\ContextInterface $ctx, Sum $in): Result
    {
        dumprr($ctx->getValue('client-key'));
        $ctx->getValue(GRPC\ResponseHeaders::class)->set('server-key', 'serverValue');

        return new Result([
            'result' => $in->getB() + $in->getA()
        ]);
    }
}

Change client.php to read metadata from the call:

<?php

declare(strict_types=1);

require 'vendor/autoload.php';

$client = new \App\Calculator\CalculatorClient(
    'localhost:50051',
    [
        'credentials' => \Grpc\ChannelCredentials::createSsl(file_get_contents("app.crt")),
    ]
);

$call = $client->Sum(
    new \App\Calculator\Sum([
            'a' => 1,
            'b' => 2
    ]),
    [
        'client-key' => ['value']
    ]
);

print_r($call->wait()[0]->getResult());
print_r($call->getMetadata()['server-key']);

You can now observe the metadata passing from the client and server.

Golang Clients

GRPC allows you to create client SDK on any supported language. To generate client on Golang, install the GRPC toolkit first:

$ go get -u github.com/golang/protobuf/protoc-gen-go

Read more about how to create Golang GRPC clients and server here.

Init the the project in the same folder as PHP client to reuse .proto and .crt files. To generate client stub code:

$ go mod init client
$ go get google.golang.org/grpc
$ mkdir calculator
$ protoc -I proto/ proto/calculator.proto --go_out=plugins=grpc:calculator

Note the package name in calculator.proto.

You can now create main.go:

package main

import (
    app "client/calculator"
    "context"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
    "log"
)

func main() {
    creds, err := credentials.NewClientTLSFromFile("app.crt", "")
    if err != nil {
        panic(err)
    }

    conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds))
    if err != nil {
        panic(err)
    }

    client := app.NewCalculatorClient(conn)

    res, err := client.Sum(context.Background(), &app.Sum{A: 1,B: 2})
    if err != nil {
        panic(err)
    }

    log.Printf("Result: %v", res.Result)
}

To test your client:

$ go run main.go

Passing Metadata

To pass metadata between server and client:

package main

import (
    app "client/calculator"
    "context"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
    "google.golang.org/grpc/metadata"

    "log"
)

func main() {
    creds, err := credentials.NewClientTLSFromFile("app.crt", "")
    if err != nil {
        panic(err)
    }

    conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds))
    if err != nil {
        panic(err)
    }

    client := app.NewCalculatorClient(conn)

    // attach value to the server
    ctx := metadata.AppendToOutgoingContext(context.Background(), "client-key", "client-value")

    var header metadata.MD
    res, err := client.Sum(ctx, &app.Sum{A: 1, B: 2}, grpc.Header(&header))
    if err != nil {
        panic(err)
    }

    log.Println(header["server-key"])

    log.Printf("Result: %v", res.Result)
}

Read more about working with metadata in Golang here.

Edit this page