Building a gRPC Service with Hyperf 3
Developing gRPC services within a Hyperf 3 application typically involves setting up a suitable environment, defining protocol buffers, generating code, and implementing both server and client components. For optimal setup and to avoid potential dependency conflicts, especially concerning GCC versions on Linux, utilizing Docker is highly recommended.
Docker Environment Setup
Starting with a Dockerized environment streamlines the development process. Below is a Dockerfile that esatblishes a PHP 8.1 FPM base image, installs essential tools, and includes critical PHP extensions like Swoole, Redis, and gRPC.
FROM php:8.1-fpm-alpine
# Install core dependencies and tools
RUN apk add --no-cache \
git \
openssl-dev \
zlib-dev \
libzip-dev \
autoconf \
automake \
libtool \
protobuf-dev \
protobuf-compiler \
cmake \
vim \
net-tools \
zip \
unzip \
php8-zip \
&& rm -rf /var/cache/apk/*
# Install PHP extensions: Swoole, Redis, gRPC, PCNTL, and Composer
RUN pecl install swoole \
&& docker-php-ext-enable swoole \
&& echo "swoole.use_shortname='Off'" >> /usr/local/etc/php/conf.d/docker-php-ext-swoole.ini \
&& pecl install redis \
&& docker-php-ext-enable redis \
&& docker-php-ext-install pcntl \
&& pecl install grpc \
&& docker-php-ext-enable grpc \
&& echo 'grpc.enable_fork_support=1' >> /usr/local/etc/php/conf.d/docker-php-ext-grpc.ini \
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Set the working directory for the application
WORKDIR /app
# Expose the default Hyperf HTTP port
EXPOSE 9501
To build and run the Docker container:
# Build the Docker image named 'hyperf-grpc-env'
docker build -t hyperf-grpc-env .
# Run the container, mapping port 9501 and mounting the current directory
docker run -it -p 9501:9501 -v $(pwd):/app --name hyperf-grpc-app -d hyperf-grpc-env
# Access the shell inside the running container
docker exec -it hyperf-grpc-app sh
Hyperf Project Initialization
Once inside the container, create a new Hyperf project:
composer create-project hyperf/hyperf-skeleton hyperf-grpc-demo
cd hyperf-grpc-demo
Define Protocol Buffer (Proto) File
Create a new file, greeting.proto, in the project root to define your gRPC service and message structures:
syntax = "proto3";
package hyperf_grpc;
service GreeterService {
rpc SayHello (HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
string name = 1;
int32 age = 2;
}
message HelloResponse {
string message = 1;
HelloRequest user_info = 2;
}
Generate PHP gRPC Code
Use the protoc compiler with the PHP plugin to generate the necessary PHP classes from your .proto file. Create a directory for the generated code, then run the command:
mkdir app/Grpc
protoc --php_out=app/Grpc/ --grpc_out=app/Grpc/ --plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin greeting.proto
This command generates files like GPBMetadata/Greeting.php, App/Grpc/HelloRequest.php, App/Grpc/HelloResponse.php, and App/Grpc/GreeterServiceInterface.php (and related client/server stubs) within the app/Grpc directory.
Configure Composer Autoload
Ensure your composer.json correctly loads the generated gRPC files by adding an entry under autoload.psr-4:
{
"autoload": {
"psr-4": {
"App\\": "app/",
"GPBMetadata\\": "app/Grpc/GPBMetadata/"
}
}
}
After modifying composer.json, run composer dump-autoload to update the autoloader.
gRPC Server Implementation
First, install the Hyperf gRPC server component:
composer require hyperf/grpc-server
Next, define the gRPC server settings in config/autoload/grpc_server.php:
<?php
use Hyperf\Server\Event;
use Hyperf\Server\Server;
return [
'servers' => [
[
'name' => 'grpc',
'type' => Server::SERVER_GRPC,
'host' => '0.0.0.0',
'port' => 9503, // A dedicated port for gRPC
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [
Event::ON_RECEIVE => [Hyperf\GrpcServer\Server::class, 'onReceive'],
],
'options' => [
// gRPC specific options
],
],
],
];
Implement the gRPC service by creating app/Controller/GreeterServiceController.php:
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Grpc\HelloRequest;
use App\Grpc\HelloResponse;
use App\Grpc\GreeterServiceInterface;
use Hyperf\GrpcServer\Annotation\GrpcService;
#[GrpcService(name: 'GreeterService', service: GreeterServiceInterface::class)]
class GreeterServiceController implements GreeterServiceInterface
{
public function SayHello(HelloRequest $request): HelloResponse
{
$response = new HelloResponse();
$response->setMessage('Hello, ' . $request->getName() . '! You are ' . $request->getAge() . ' years old.');
$response->setUserInfo($request);
return $response;
}
}
gRPC Client Ipmlementation
Install the Hyperf gRPC client component:
composer require hyperf/grpc-client
Create a client wrapper, app/Client/GreeterServiceClient.php:
<?php
declare(strict_types=1);
namespace App\Client;
use App\Grpc\GreeterServiceClient as GrpcClient;
use App\Grpc\HelloRequest;
use App\Grpc\HelloResponse;
use Hyperf\GrpcClient\GrpcClient as BaseGrpcClient;
class GreeterServiceClient
{
private GrpcClient $client;
public function __construct(string $host = '127.0.0.1:9503')
{
$this->client = new GrpcClient($host, ['credentials' => BaseGrpcClient::getDefaultCredentials()]);
}
public function sendGreeting(string $username, int $userAge): HelloResponse
{
$request = new HelloRequest();
$request->setName($username);
$request->setAge($userAge);
/** @var HelloResponse $response */
[$response, $status] = $this->client->SayHello($request);
if ($status->code !== 0) {
throw new \RuntimeException("gRPC call failed: {$status->details} ({$status->code})");
}
return $response;
}
}
Create an HTTP endpoint to trigger the gRPC client call in app/Controller/ClientTestController.php:
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Client\GreeterServiceClient;
use Hyperf\HttpServer\Annotation\AutoController;
use Psr\Http\Message\ResponseInterface;
#[AutoController]
class ClientTestController extends AbstractController
{
public function greet(): ResponseInterface
{
$client = new GreeterServiceClient();
$response = $client->sendGreeting('Alice', 30);
return $this->response->json([
'received_message' => $response->getMessage(),
'user_details' => [
'name' => $response->getUserInfo()->getName(),
'age' => $response->getUserInfo()->getAge(),
],
]);
}
}
Register the HTTP route in config/routes.php:
<?php
use Hyperf\HttpServer\Router\Router;
use App\Controller\ClientTestController;
Router::get('/grpc/greet', [ClientTestController::class, 'greet']);
Testing the gRPC Service
Start the Hyperf server:
php ./bin/hyperf.php start
Open your web browser or use a tool like Postman/cURL to access the client test endpoint:
http://127.0.0.1:9501/grpc/greet
This will trigger the HTTP controller, which in turn calls the gRPC client, communicating with the gRPC server running on port 9503.