Mastering Client-Server Communication with gRPC: A Comprehensive Guide
In the ever-evolving landscape of software development, efficient communication between client and server is paramount. Enter gRPC, a high-performance open-source framework that facilitates seamless and robust communication across various programming languages. This article series delves into the intricacies of setting up a client-server architecture using gRPC, with a focus on PHP and Go.
Part 1: PHP gRPC Client Configuration
In this initial installment, we guide you through configuring a gRPC client in PHP. From setting up the project structure and creating proto files to dockerizing your environment and writing PHP gRPC client code, each step is carefully explained. By the end of this article, you’ll have a fully functional PHP gRPC client ready to communicate with the server.
Explore the essentials:
- Initial folder structures
- Proto file creation
- Composer file creation
- Composer autoloading configuration
- Composer installation script
- Dockerfile setup for PHP
- PHP gRPC client code
- Docker Compose configuration
Ready to embark on the journey of mastering client-server communication? Let’s begin with configuring the PHP gRPC client environment. Once you’ve mastered this, proceed to the next article to Configure gRPC Server Environment in Go.
1. Initial Folder Structures
Ensure your project has the following directory structure:
|-php-grpc
|- docker
| |- php
| | |- Dockerfile
| | |- install-composer.sh
| |- go
| | |- Dockerfile
| |- docker-compose.yml
|- grpc-client-php
| |- composer.json
| |- src
| | |- user.php
|- grpc-server-go
| |- main.go
|- protos
|- userService.proto
2. Create Proto File
Create the protos/serService.proto
file with the following content:
// The user service definition.
service User {
rpc FindById (FindByIdRequest) returns (FindByIdReply) {}
}
// The request message containing the user's id.
message FindByIdRequest {
string id= 1;
}
// The response message containing the user
message FindByIdReply {
string id = 1;
string name = 2;
}
After compiling userService.proto
file on docker image building process, two folders will be created ./grpc-client-php/src/GPBMetadata
and ./grpc-client-php/src/Grpc
with the following structure
app
|- grpc-client-php
|- GPBMetadata
| |- UserService.php
|- Grpc
| |- FindByIdReply.php
| |- FindByIdRequest.php
| |- UserClient.php
3. Composer file
{
"name": "php/grpc",
"description": "grpc example",
"type": "project",
"require": {
"grpc/grpc": "^1.57",
"google/protobuf": "^4.0@RC"
},
"license": "Free"
}
4. Composer Autoloading Configuration
In your composer.json
file located at ./grpc-client-php/composer.json
, add the following lines:
{
...,
"autoload": {
"psr-4": {
"Grpc\\": "src/Grpc",
"GPBMetadata\\": "src/GPBMetadata"
}
}
}
5. Composer Installation Script
Composer will be installed during creating docker image. The sh script below downloads and installscomposer
Create the ./docker/php/install-composer.sh
file with the following content:
#!/bin/sh
echo 'It is being installed a COMPOSER'
EXPECTED_CHECKSUM="$(php -r 'copy("https://composer.github.io/installer.sig", "php://stdout");')"
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")"
if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]
then
>&2 echo 'ERROR: Invalid installer checksum'
rm composer-setup.php
exit 1
fi
php composer-setup.php --quiet
RESULT=$?
rm composer-setup.php
mv composer.phar /usr/local/bin/composer
exit $RESULT
6. Dockerfile for PHP
create the./docker/php/Dockerfile
file with following data
FROM php:8.3-fpm-alpine
COPY docker/php/install-composer.sh /tmp
RUN chmod +x /tmp/install-composer.sh && sh /tmp/install-composer.sh
COPY ./grpc-client-php /app/grpc-client-php
COPY ./protos /app/protos
RUN apk add --update linux-headers \
&& apk add --no-cache autoconf gcc make g++ zlib-dev git cmake \
&& pecl channel-update pecl.php.net \
# Install the grpc extension
&& pecl install grpc \
&& docker-php-ext-enable grpc
# INstall gRPC PHP plugin
RUN cd /app && git clone https://github.com/grpc/grpc \
&& cd /app/grpc && git submodule update --init \
&& mkdir -p cmake/build && cd cmake/build && cmake ../.. && make protoc grpc_php_plugin
RUN cd /app/grpc-client-php \
#Install required packages
&& composer install \
# install protobuf compiler
&& apk add protobuf \
# Compile userService.proto file
&& cd /app && protoc --proto_path=protos \
--php_out=./grpc-client-php/src \
--grpc_out=./grpc-client-php/src \
--plugin=protoc-gen-grpc=./grpc/cmake/build/grpc_php_plugin \
./protos/userService.proto
WORKDIR /app
CMD ["php-fpm"]
7. PHP gRPC Client Code
Create the file grpc-client-php/src/user.php
with the following content:
<?php
use Grpc\UserClient;
use Grpc\ChannelCredentials;
use Grpc\FindByIdRequest;
require dirname(__FILE__) . '/../vendor/autoload.php';
/**
* parses GRPC errors
*/
function getErrorMessage(\stdClass $status, string $id): string
{
if ($status->code !== \Grpc\STATUS_NOT_FOUND)
return sprintf("User %s was not found", $id);
return "ERROR: " . $status->code . ", " . $status->details;
}
function run(string $hostname, string $id): void
{
// creates grpc client
$userClient = new UserClient($hostname, [
'credentials' => ChannelCredentials::createInsecure(),
]);
// creates grpc request
$request = new FindByIdRequest();
$request->setId($id);
// calls the FindById remote procedure
list($response, $status) = $userClient->FindById($request)->wait();
// in case of an error, show it and exit
if ($status->code !== \Grpc\STATUS_OK) {
echo getErrorMessage($status, $id) . PHP_EOL;
exit(1);
}
// shows received user name
echo $response->getName() . PHP_EOL;
}
// desire user id
$id = !empty($argv[1]) ? $argv[1] : 'some-unique-id';
// grpc server address
$hostname = !empty($argv[2]) ? $argv[2] : 'grpc-server:9090';
run($hostname, $id);
8. Docker Compose
Create the docker/docker-compose.yml
file:
version: '3.6'
services:
grpc-client:
build:
context: ../
dockerfile: docker/php/Dockerfile
container_name: grpc-client
volumes:
- ../grpc-client-php:/project
networks:
- dev_grpc_network
networks:
dev_grpc_network:
external: true
9. Build and Run
Create a Docker network:
docker network create dev_grpc_network
Build and run the gRPC client container:
cd ./docker
docker compose up --build grpc-client -d
10. Links
- Protocol Buffers Installation
- PHP gRPC Plugin Installation
- gRPC PHP Classes and Constants
- gRPC Error Codes
- Source code for this tutorial
Continue to the next article: Configure gRPC Server (Go) Environment