Hướng dẫn giao tiếp giữa 2 ứng dụng laravel với Rabbit MQ

09/07/2023 - lượt xem
Chia sẻ
 
4.4/5 - (13 bình chọn)

Thông thường khi xây dựng ứng dụng với Laravel chúng ta thường làm dạng ứng dụng nguyên khối. Việc này gây ra nhiều hạn chế trong phát triển và mở rộng ứng dụng Laravel. Trong bài viết này hướng dẫn các bạn setup ứng dụng PHP Laravel dạng microservice và giao tiếp với nhau thông qua Rabbit MQ.

Bài viết này xuất phát từ nhu cầu thực tế khi CodeTuTam mong muốn tách nhỏ ứng dụng hiện tại của mình. Đương nhiên thì quá trình làm ban đầu không dễ dàng với những khái niệm còn khá nhiều bỡ ngỡ. Hi vọng bài viết này cũng sẽ giúp ích cho những bạn đang tìm kiếm giải pháp cho việc tối ưu hệ thống PHP Laravel của mình.

Ứng dụng nguyên khối là gì?

Như tên gọi của nó, ứng dụng nguyên khối là ứng dụng mà tất cả các chức năng đều nằm trong một dự án. Điều này có nghĩa là mỗi khi chúng ta thực hiện bất kỳ thay đổi nào (dù chỉ là một thay đổi nhỏ), chúng tôi sẽ triển khai lại toàn bộ ứng dụng của mình. Việc triển khai này khá bất tiện, cũng như tiềm ẩn nhiều rủi ro mang tính chất ảnh hưởng toàn bộ dự án.

Đặc biệt dự án quy mô lớn, việc duy trì, nâng cấp sẽ khó khăn hơn rất nhiều. Vì vậy, kiến trúc microservices ra đời để trợ giúp chúng ta có thể tách chức năng của nó thành một thành phần độc lập.

Vậy là tạm hiểu về ứng dụng nguyên khối và ứng dụng microservice rồi. Chúng ta sẽ tìm hiểu sơ lược qua thêm về RabbitMQ

RabbitMQ là gì

Rabbitmq là một message broker – nó hoạt động như một người trung gian cho các dịch vụ của chúng ta (microservice). Nhiệm vụ của Rabbitmq là nhận tin nhắn từ một ứng dụng (producer) và chuyển chúng cho ứng dụng khác (consume) để thực hiện công việc.

Rabbitmq có phiên bản cloud tại https://www.cloudamqp.com hoặc các bạn cũng có thể cài đặt phiên bản docker của rabbitmq.

Trong bài viết này sẽ chỉ cho bạn cách sử dụng RabbitMQ cho giao tiếp giữa các service sử dụng Laravel. Điều tuyệt vời khi sử dụng Rabbitmq là nó sẽ xếp hàng các tin nhắn được gửi tới. Trong trường hợp người nhận đang bận hoặc bị ngắt kết nối ngay bây giờ, thì tin nhắn sẽ được lưu trữ bên trong Bộ lưu trữ RabbitMQ tạm thời. Các tin nhắn này sẽ được đẩy lại bất cứ khi nào người tiêu dùng/người nhận sẵn sàng.

Cài đặt RabbitMQ qua docker

Để thuận tiện bạn có thể sử dụng sẵn file docker compose đã được tạo sẵn dưới đây

version: "3.2"
services:
  rabbitmq:
    image: rabbitmq:3-management
    hostname: 'rabbitmq'
    container_name: 'rabbitmq'
    ports:
      - 5672:5672
      - 15672:15672
    environment:
      - RABBITMQ_DEFAULT_USER=master_user_rabbit
      - RABBITMQ_DEFAULT_PASS=rabbit@13572468
    volumes:
      - ./data/:/var/lib/rabbitmq/
      - ./log/:/var/log/rabbitmq

Trong file docker-compose.yml này chúng ta sẽ cấu hình các port 5672 và 15672, trong đó port 15672 là port dùng để vào giao diện quản lý.

Ngoài ra cần cài đặt 2 biến môi trường là User và Password để đăng nhập vào màn hình quản lý của Rabbitmq.

Để tiến hành cài đặt, các bạn chạy lệnh

docker compose up

Chú ý rằng có thể thêm tham số -d để chạy ở chế độ ngầm

Sau khi cài đặt thành công các bạn có thể truy cập vào địa chỉ http://localhost:15672/ và đăng nhập với tài khoản đã tạo bên trên.

Màn hình quản lý của Rabbitmq sau khi cài đặt thành công
Màn hình quản lý của Rabbitmq sau khi cài đặt thành công

Cài đặt và cấu hình service Laravel với rabbitmq

Cài đặt Laravel

Chúng ta sẽ cần cài đặt 2 service để giả lập cho producer và consume. Để cài đặt Laravel chúng ta sử dụng lệnh như sau

// Tạo dự án 1
composer create-project laravel/laravel rabbitmq_laravel1

// tạo dự án 2
composer create-project laravel/laravel rabbitmq_laravel2

Sau khi khởi tạo dự án thành công, chúng ta cần cài thêm package để hỗ trợ kết nối với Rabbitmq

Github dự án cũng như các tài liệu hướng dẫn có thể xem thêm tại đây: https://github.com/vyuldashev/laravel-queue-rabbitmq

composer require vladimir-yuldashev/laravel-queue-rabbitmq

Sau khi cài đặt thành công chúng ta tiến hành cấu hình dự án, chú ý rằng việc cấu hình này là giống nhau trên cả 2 dự án (Service). Do vậy các bạn có thể tiến hành làm trọn vẹn 1 service trước clone ra service thứ 2.

Điều chỉnh file .env

QUEUE_CONNECTION=rabbitmq


RABBITMQ_HOST=
RABBITMQ_PORT=5672
RABBITMQ_USER=master_user_rabbit
RABBITMQ_PASSWORD=rabbit@13572468
RABBITMQ_VHOST=laravel_consume

Chú ý điền đúng tài khoản và mật khẩu đã tạo, chỗ vhost bạn có thể điền tên bất kì nhưng hãy ghi nhớ nó vì chúng ta sẽ cần cấu hình trong màn hình quản lý của rabbitmq.

Điều chỉnh file config/queue.php

'rabbitmq' => [
            'driver' => 'rabbitmq',
            'queue' => env('RABBITMQ_QUEUE', 'default'),
            'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class,

            'hosts' => [
                [
                    'host' => env('RABBITMQ_HOST', '127.0.0.1'),
                    'port' => env('RABBITMQ_PORT', 5672),
                    'user' => env('RABBITMQ_USER', 'guest'),
                    'password' => env('RABBITMQ_PASSWORD', 'guest'),
                    'vhost' => env('RABBITMQ_VHOST', '/'),
                ],
            ],

            'options' => [
                'ssl_options' => [
                    'cafile' => env('RABBITMQ_SSL_CAFILE', null),
                    'local_cert' => env('RABBITMQ_SSL_LOCALCERT', null),
                    'local_key' => env('RABBITMQ_SSL_LOCALKEY', null),
                    'verify_peer' => env('RABBITMQ_SSL_VERIFY_PEER', true),
                    'passphrase' => env('RABBITMQ_SSL_PASSPHRASE', null),
                ],
                'queue' => [
                    'job' => VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob::class,
                    // 'job' => App\Jobs\CustomHandleJob::class,
                ],
            ],

            /*
             * Set to "horizon" if you wish to use Laravel Horizon.
             */
            'worker' => env('RABBITMQ_WORKER', 'default'),

        ],

Chú ý tên queue hiện tại đang chưa khai báo trong env, do vậy sẽ là tên mặc định: default

Như vậy bạn đã cấu hình thành công 2 dự án laravel rồi, chúng ta sẽ cấu hình thêm 1 chút cho Rabbitmq

Cấu hình cho Rabbitmq

Đăng nhập vào màn hình quản lý của Rabbitmq tại http://localhost:15762 với tài khoản mật khẩu trước đó.

Thêm virtual host laravel_consume như sau

Thêm virtual host trong rabbitmq
Thêm virtual host trong rabbitmq

Sau khi thêm virtual host thành công, hãy thêm queue cho virtual host này. Như codetutam đã ghi bên trên, bạn đặt tên queue này là default.

Thêm Queue cho Virtual Host Rabbitmq
Thêm Queue cho Virtual Host Rabbitmq

Chạy thử nghiệm truyền message giữa 2 service Laravel bằng Rabbitmq

Tại ứng dụng Laravel 1 và Laravel 2 chúng ta tạo ra 1 PingJob với nội dung lần lượt như sau

Bạn có thể sử dụng lệnh sau để tạo Job

 php artisan make:job PingJob

File PingJob ở Laravel 1:

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class PingJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * Create a new job instance.
     */
    public function __construct()
    {
        //
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        //
    }
}

File PingJob.php ở Laravel 2

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class PingJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * Create a new job instance.
     */
    public function __construct()
    {
        //
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        echo 'Ping Event Received' . PHP_EOL;
    }
}

Tiếp đến ở Laravel 1, chúng ta tạo thêm 1 command với nội dung như sau

Tham khảo lệnh tạo command

php artisan make:command PingJobCommand

File PingJobCommand.php với nội dung như sau:

<?php

namespace App\Console\Commands;

use App\Jobs\PingJob;
use Illuminate\Console\Command;

class PingJobCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'ping:job';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Execute the console command.
     */
    public function handle()
    {
        PingJob::dispatch();
    }
}

Chạy thử lệnh ping:job

php artisan ping:job

Đương nhiên khi này bên Service Laravel 2 sẽ không có thông tin gì, vì chúng ta cần phải đăng ký lắng nghe sự kiện nữa.

php artisan rabbitmq:consume

Đối với ứng dụng thực tế thì bạn có thể đẩy vào trong supervisord để thực hiện lệnh này.

Ngay sau khi chạy thành công, bạn sẽ nhận được 1 thông tin như sau:

Ping Job giữa 2 service Laravel sử dụng RabbitMQ
Ping Job giữa 2 service Laravel sử dụng RabbitMQ

Tiếp đến, chúng ta thử nghiệm với việc truyền sự kiện có tham số kèm theo

Tương tự như ví dụ trên chúng ta sẽ tạo ra Job UserCreated

php artisan make:job UserCreated

File UserCreated.php nội dung như sau

Laravel 1:

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class UserCreated implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    private $data;
    /**
     * Create a new job instance.
     */
    public function __construct($data)
    {
        $this->data = $data;
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        //
    }
}

Laravel 2:

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class UserCreated implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    private $data;
    /**
     * Create a new job instance.
     */
    public function __construct($data)
    {
        $this->data = $data;
    }

    /**
     * Execute the job.
     */
    public function handle()
    {
        echo 'Event: UserCreated' . PHP_EOL;
        echo json_encode($this->data) . PHP_EOL;
        // TODO: Event User Created
    }
}

Tạo file command như sau

<?php

namespace App\Console\Commands;

use App\Jobs\UserCreated;
use Illuminate\Console\Command;

class UserJobCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'ping:user';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Execute the console command.
     */
    public function handle()
    {
        UserCreated::dispatch(["id"=>1,"name"=>"Hello","created_at"=>new \DateTime()]);
    }
}

Khi chạy command php artisan ping:user ở bên laravel 1 thì bên laravel 2 sẽ nhận được như sau

User Created Job với Rabbitmq laravel
User Created Job với Rabbitmq laravel

Một số chú ý khác

Như các bạn đã thấy, chúng ta luôn phải tạo 2 file đồng thời ở cả 2 service. Việc này tạo ra sự bất tiện cho triển khai, để khắc phục điều này hãy xem kĩ hơn tại github vyuldashev/laravel-queue-rabbitmq. Trong Github đã nêu rõ cách xử lý vấn đề này “Use your own RabbitMQJob class”.

Ngoài ra bạn có thể custom các queue khác nhau cho việc giao tiếp nếu trong hệ thống của bạn có nhiều hơn 2 service.

Khi đó lệnh gọi của bạn có thể ở dạng như sau

PingJob::dispatch()->onQueue('custom');

Chú ý rằng, việc thêm các custom queue cần khai báo với Rabbitmq nhé.

 

Kết luận

Trong bài viết này đã hướng dẫn cơ bản các bạn sử dụng Rabbitmq để truyền message giữa các service bằng PHP Laravel. Hi vọng với việc này sẽ giúp ích cho các bạn trong quá trình học tập và làm việc của mình. Nếu bạn thấy bài viết hay và hữu ích hãy like và share bài viết để nhiều người biết đến CodeTuTam hơn bạn nhé!

    Liên hệ với chúng tôi

    Để lại thông tin để nhận được các bài viết khác

    4.4/5 - (13 bình chọn)

    Xem thêm nhiều bài tin mới nhất về Dev Ops

    Xem thêm