Hướng dẫn dùng buffering means trong PHP

It's not considered bad [or good]. Some people like it, some don't Personally I think there are reasons not to use it. I think of it as a last resort. Sometimes you may have situation where output buffering may be your only choice to solve a particular problem, so save this option for just such situations.

I don't think there is any performance gain or speeding up of page load by using it, but it also depends on what server you using and if you using php as mod_php or as cgi or fastcgi.

Chắc hẳn khi mới đọc tiêu đề, nhiều bạn thắc mắc tại sao lại biến ứng dụng đơn giản trở nên phức tạp để làm gì không biết, nhưng thực ra việc phức tạp này sẽ mang lại cho chúng ta rất nhiều lợi ích, với những ứng dụng nhỏ thì gRPC chưa thực sự cần thiết, tuy nhiên áp vào các ứng dụng lớn, cần được mở rộng trong tương lai, việc chuyển đổi từ REST sang gRPC đem lại hiệu quả rất lớn.

RPC

RPC, đó là chữ viết tắt của Remote Procedure Calls [tạm dịch là các cuộc gọi thủ tục từ xa], là một khái niệm nhằm cố gắng khái quát một lời gọi thủ tục thông thường trong trường hợp mà caller và receiver không cùng nằm trong một process - và được phân tán trên các máy riêng biệt. Việc này có ý nghĩa rất quan trọng vì trong các hệ thống phân tán [distributed system], application code ở nhiều server hơn là một server. Ví dụ thường thấy nhất chính là kiến trúc Microservice.

gRPC

Tối ưu cho việc "giao tiếp" giữa các server là lý do gRPC ra đời.

gRPC là một RPC framework gíup bạn kết nối giữa các service trong hệ thống, nó hỗ trợ load balancing, tracing, health checking và authentication, hỗ trợ từ ứng dụng mobile, trình duyệt cho tới back-end service, do Google phát triển.

Để giải bài toán trên, gRPC đã sử dụng binary để truyền đi thay vì phải encode chúng thành các ngôn ngữ trung gian JSON/XML. Việc này rõ ràng đã làm tăng tốc giao tiếp các servers lên rất nhiều, giảm overhead cho CPUs.

Thứ giúp gRPC giao tiếp binary ngon vậy chính là http/2, đây vốn là giao thức có rất nhiều cải tiến so với http/1.1

Sử dụng gRPC trong PHP

Không giống như Java, Go, hay Ruby,... PHP hiện tại chưa được chính Google hỗ trợ xây dựng SDK để dựng thành 1 gRPC server. Vì thế chúng ta muốn sử gRPC cho server thì cần thông qua 1 framework gọi là RoadRunner.

RoadRunner là 1 framework ở tầng infrastructure cho các ứng dụng PHP, nó được viết bằng Golang. Công việc của RoadRunner là chạy PHP dưới dạng các worker

Golang sẽ giúp RoadRunner chạy PHP app trên goroutine và hỗ trợ cân bằng tải trên các worker.

RoadRunner sẽ giữ các PHP worker luôn alive giữa các request, tránh việc tái khởi đọng lại app và tăng tốc cho các ứng dụng lớn. PHP worker được đặt trong resident memory, và luôn sẵn sàng cho request tiếp theo. RoadRunner còn sử dụng Goridge RPC sẽ giúp đẩy nhanh tốc độ load của ứng dụng lên server.

Cài đặt gRPC

Trước khi bắt đầu implement gRPC cho ứng dụng Laravel thì chúng ta setup môi trường development cho đủ các công cụ cần thiết

  • gRPC PHP extension
  • Google Protobuf
  • Google Protobuf compiler cho PHP server
  • Roadrunner

gRPC PHP extension

Việc cài thêm extension khá đơn giản, bạn chỉ cần sử dụng PECL và chạy lệnh

$ sudo pecl install grpc

Tuy nhiên nếu máy bạn có cài nhiều version PHP thì sẽ phức tạp hơn chút, mình đã gặp issue khi cài grpc là extension được build xong, khi sử dụng trong

$ sudo pecl install protobuf
8 thì PHP không tìm thấy extension. Mình khắc phục bằng cách gỡ bỏ extension cũ và cài bằng lệnh này

$ sudo pecl php_suffix=7.4 install grpc 

thật [magic] PECL sẽ compile extension cho đúng phiên bản PHP mà bạn đang chọn là PHP 7.4

Cuối cùng bạn tìm file

$ sudo pecl install protobuf
8 và thêm dòng này vào
$ go get github.com/spiral/php-grpc/cmd/protoc-gen-php-grpc
0

Google Protobuf

Protocol buffer còn được biết như protobuf là language-neutral, platform-neutral của google phiên bản nội bộ được công bố vào năm 2001 và phiên bản công khai đầu tiên được giới thiệu vào năm 2008 [ Repository ], về cơ bản nó được sủ dụng để Serialized object, có vẻ nó khá giống XML hoặc JSON. Nó lưu trữ dữ liệu có cấu trúc có thể được Serialize hoặc De-Serialized tự động bưởi nhiều ngôn ngữ khác nhau. Nó được thiết kế để trở thành language/platform neutral và có thể mở rộng.

Việc cài protobuf cũng tương tự gRPC , bạn chạy command sau và rồi thêm dòng

$ go get github.com/spiral/php-grpc/cmd/protoc-gen-php-grpc
1 vào
$ sudo pecl install protobuf
8

$ sudo pecl install protobuf

Google Protobuf compiler cho PHP server

Bởi vì gRPC chưa trực tiếp hỗ trợ các server viết bằng PHP nên với PHP chúng ta sử dụng 1 plugin để compile các file .proto cho PHP server

$ go get github.com/spiral/php-grpc/cmd/protoc-gen-php-grpc

để protoc có thể tìm thấy plugin mà bạn vừa kéo về thì bạn thêm vào file

$ go get github.com/spiral/php-grpc/cmd/protoc-gen-php-grpc
3 [nếu đang dùng zsh] hoặc
$ go get github.com/spiral/php-grpc/cmd/protoc-gen-php-grpc
4 2 dòng này

export GO_PATH=~/go
export PATH=$PATH:/$GO_PATH/bin

đây là 1 pre-build binary để gen proto file, để sử dụng nó ta chỉ cần thêm plugin đó trong command compile ví dụ:

$ protoc --php_out=target-dir/ --php-grpc_out=target-dir/ sample.proto

Roadrunner

RoadRunner sẽ hỗ trợ bạn serve ứng dụng lên, tương tự nhưng

$ go get github.com/spiral/php-grpc/cmd/protoc-gen-php-grpc
5 trong Laravel ý, bạn chỉ cần tải file
$ go get github.com/spiral/php-grpc/cmd/protoc-gen-php-grpc
6 về và để vào thư mục root của app

RoadRunner sẽ hỗ trợ bạn serve ứng dụng lên, tương tự nhưng

$ go get github.com/spiral/php-grpc/cmd/protoc-gen-php-grpc
5 trong Laravel ý, bạn chỉ cần tải file
$ go get github.com/spiral/php-grpc/cmd/protoc-gen-php-grpc
6 về và để vào thư mục root của app

  • //github.com/spiral/php-grpc/releases

Implement gRPC server

Trong bài viết này mình sẽ implement gRPC cho phía server bằng PHP, và sử dụng luôn framework Laravel chọn xịn xò =]]

Init project

Đầu tiền chúng ta cần init project

$ composer create-project laravel/laravel grpc-php-server

sau đó chúng ta cần cài thêm các package cần thiết như

$ go get github.com/spiral/php-grpc/cmd/protoc-gen-php-grpc
9,
export GO_PATH=~/go
export PATH=$PATH:/$GO_PATH/bin
0,... thành phẩm chúng ta sẽ có 1 file composer.json như này

{
    "name": "laravel/laravel",
    "type": "project",
    "description": "The Laravel Framework.",
    "keywords": ["framework", "laravel"],
    "license": "MIT",
    "require": {
        "php": "^7.4|^8.0",
        "ext-grpc": "^1.37",
        "fideloper/proxy": "^4.4",
        "fruitcake/laravel-cors": "^2.0",
        "google/common-protos": "^1.3",
        "google/protobuf": "^3.16",
        "grpc/grpc": "^1.36",
        "guzzlehttp/guzzle": "^7.0.1",
        "laravel/framework": "^8.12",
        "laravel/tinker": "^2.5",
        "nyholm/psr7": "^1.4",
        "spiral/php-grpc": "^v1.5.0",
        "spiral/roadrunner": "^1.9",
        "spiral/roadrunner-laravel": "^3.7",
        "ext-json": "*"
    },
    "require-dev": {
        "facade/ignition": "^2.5",
        "fakerphp/faker": "^1.9.1",
        "laravel/sail": "^1.0.1",
        "mockery/mockery": "^1.4.2",
        "nunomaduro/collision": "^5.0",
        "phpunit/phpunit": "^9.3.3",
        "spiral/dumper": "^1.1.7"
    },
    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Database\\Factories\\": "database/factories/",
            "Database\\Seeders\\": "database/seeders/",
            "": "protos/generated/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    },
    "scripts": {
        "post-autoload-dump": [
            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
            "@php artisan package:discover --ansi"
        ],
        "post-root-package-install": [
            "@php -r \"file_exists['.env'] || copy['.env.example', '.env'];\""
        ],
        "post-create-project-cmd": [
            "@php artisan key:generate --ansi"
        ]
    },
    "extra": {
        "laravel": {
            "dont-discover": []
        }
    },
    "config": {
        "optimize-autoloader": true,
        "preferred-install": "dist",
        "sort-packages": true,
        "platform": {
            "php": "7.4.18"
        }
    },
    "minimum-stability": "dev",
    "prefer-stable": true
}

Trong file

export GO_PATH=~/go
export PATH=$PATH:/$GO_PATH/bin
1 này mình có bổ sung thêm

"autoload": {
        "psr-4": {
            "App\\": "app/",
            "Database\\Factories\\": "database/factories/",
            "Database\\Seeders\\": "database/seeders/",
            "": "protos/generated/"
        }
    },

mục đích là để load thêm các file được compile từ Protobuf vào project.

Tạo auth.proto

Mục đích của file này là giúp define ra các service và cấu trúc request/response. File này sẽ được viết bằng Protobuf hay Protocols Buffer, là một ngôn ngữ dùng để mô tả các cấu trúc dữ liệu, chúng ta dùng protoc để biên dịch chúng thành mã nguồn của các ngôn ngữ lập trình khác nhau có chức năng serialize và deserialize các cấu trúc dữ liệu này thành dạng binary stream.

syntax = "proto3";

package protobuf.identity;

option php_metadata_namespace = "Protobuf\\Identity\\Metadata";

service AuthService {
    rpc SignIn [SignInRequest] returns [Response] {}
    rpc SignUp [SignUpRequest] returns [Response] {}
}

message SignInRequest {
    string email = 1;
    string password = 2;
}

message SignUpRequest {
    string name = 1;
    string email = 2;
    string password = 3;
    string password_confirmation = 4;
}

message Response {
    int64 id = 1;
    string token = 2;
}

Trong file này đơn giản chỉ định nghĩa service AuthService sẽ có 2 method rpc là SignIn và SignUp, chúng nhận vào tham số message được define theo cấu trúc ở bên dưới.

Sau khi đã tạo file

export GO_PATH=~/go
export PATH=$PATH:/$GO_PATH/bin
2 chúng ta cần phải compile chúng ra thành các file để sử dụng trong project, bằng command:

$ sudo pecl php_suffix=7.4 install grpc 
0

như vậy là chúng ta sẽ có file source code nằm trong thư mục

export GO_PATH=~/go
export PATH=$PATH:/$GO_PATH/bin
3 như sau:

đây cũng là thư mục mà mình bổ sung vào trong file

export GO_PATH=~/go
export PATH=$PATH:/$GO_PATH/bin
1 ở trên đấy

Implement logic cho AuthServiceInterface

Sau khi sử dụng protoc để có được file AuthServiceInterface, chúng ta đơn giản chỉ cần tiến hành implement logic cho các func trong service thôi

$ sudo pecl php_suffix=7.4 install grpc 
1

đơn giản vậy thôi là chúng ta đã xong phần logic cho các method rpc của app rồi

Implement PHP worker

Phần quan trọng nhất đây rồi.

Chúng ta sử dụng RoadRunner để serve các PHP worker lên và lắng nghe các rpc nên PHP worker là 1 thành phần quan trọng trong ứng dụng của bạn

$ sudo pecl php_suffix=7.4 install grpc 
2

Khi đã có PHP worker rồi, chúng ta sẽ sử dụng

$ go get github.com/spiral/php-grpc/cmd/protoc-gen-php-grpc
6 đã tải về ở trên để serve app lên thôi.
$ go get github.com/spiral/php-grpc/cmd/protoc-gen-php-grpc
6 có yêu cầu file config như sau:

$ sudo pecl php_suffix=7.4 install grpc 
3

Bây giờ chúng ta chạy command:

export GO_PATH=~/go
export PATH=$PATH:/$GO_PATH/bin
7 là xong

server đã sẵn sàng lắng nghe các yêu cầu từ client rồi

Cấu trúc thư mục hoàn chỉnh

Tạm kết

Như vậy là chúng ta đã implement được phía server, do bài viết đã dài nên mình tạm kết ở đây, trong bài viết tới mình sẽ tiếp tục implement phía client, làm cách nào để client có thể giao tiếp với server thông qua gRPC.

Chủ Đề