laravel微服務開發,整合rabbitmq

my38778570發表於2022-07-08
  • 安裝admin

    laravel new admin
  • 安裝laravel-ide-helper

    composer require --dev barryvdh/laravel-ide-helper
    php artisan ide-helper:generate
    #php artisan ide:models//要在容器裡面執行

    laravel微服務開發,整合rabbitmq

  • 建立Dockerfile

      FROM php:7.4
      RUN sed -i s/deb.debian.org/mirrors.aliyun.com/g /etc/apt/sources.list
    
      RUN apt-get update && apt-get install -y \
              libfreetype6-dev \
      #       libjpeng62-turbo-dev \
              libmcrypt-dev \
              libpng-dev \
              zlib1g-dev \
              libxml2-dev \
              libzip-dev \
              libonig-dev \
              graphviz \
    
        && docker-php-ext-configure gd \
        && docker-php-ext-install -j$(nproc) gd \
        && docker-php-ext-install pdo_mysql \
        && docker-php-ext-install mysqli \
        && docker-php-ext-install zip \
        && docker-php-ext-install sockets \
      #  && docker-php-ext-source delete \ #docker-php-source delete:在當前目錄下---刪除 /usr/src/php 目錄
        && curl -sS https://getcomposer.org/installer | php -- \
        --install-dir=/usr/local/bin --filename=composer
    
      WORKDIR /app
      COPY . .
      RUN composer install
    
      CMD php artisan serve --host=0.0.0.0
      EXPOSE 8000
  • 建立docker-compose.yaml

    version: '3.8'
    services:
    admin:
      build:
        context: .
        dockerfile: Dockerfile
      command: 'php artisan serve --host=0.0.0.0'
      volumes:
      - .:/app
      ports:
        - 8000:8000
      depends_on:
        - admin_db
    
    admin_queue:
      build:
        context: .
        dockerfile: Dockerfile
      command: 'php artisan queue:work'
      depends_on:
        - admin_db
    
    admin_db:
      image: mysql:5.7.22
      environment:
        MYSQL_DATABASE: root
        MYSQL_USER: root
        MYSQL_PASSWORD: root
        MYSQL_ROOT_PASSWORD: root
      volumes:
      - ./storage/dbdata:/var/lib/mysql
      ports:
      - 3307:3306
  • .env

      DB_CONNECTION=mysql
      DB_HOST=admin_db
      DB_PORT=3306
      DB_DATABASE=root
      DB_USERNAME=root
      DB_PASSWORD=root
    
      QUEUE_CONNECTION=rabbitmq
    
      RABBITMQ_HOST=shark.rmq.cloudamqp.com
      RABBITMQ_PORT=5672
      RABBITMQ_USER=xxx
      RABBITMQ_PASSWORD=xxxx-ruw1
      RABBITMQ_VHOST=xxxx
      RABBITMQ_QUEUE=admin_queue
    
- 檢驗啟動  `docker-compose up`
- `php artisan make:migration create_products_table`

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateProductsTable extends Migration
{
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('image');
            $table->unsignedBigInteger('likes')->default(0);
            $table->timestamps();
        });
    }
    public function down()
    {
        Schema::dropIfExists('products');
    }
}
- 進入容器

docker-compose exec admin sh

- php artisan make:migrate
- php artisan make:factory ProductFactory
- php artisan make:model Product
<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

class ProductFactory extends Factory
{
    public function definition()
    {
        return [
            'title' => $this->faker->text(30),
            'image' => $this->faker->imageUrl(),
            //
        ];
    }
}
- php artisan make:seeder ProductSeeder
- php artisan make:seeder UserSeeder
#DatabaseSeeder.php
    <?php
    namespace Database\Seeders;
    use Illuminate\Database\Seeder;
    class DatabaseSeeder extends Seeder
    {
        /**
         * Seed the application's database.
         *
         * @return void
         */
        public function run()
        {
            // \App\Models\User::factory(10)->create();
            $this->call([
                UserSeeder::class,
                ProductSeeder::class,
            ]);
        }
    }
#ProductSeeder.php
    <?php

    namespace Database\Seeders;

    use App\Models\Product;
    use Illuminate\Database\Seeder;

    class ProductSeeder extends Seeder
    {
        /**
         * Run the database seeds.
         *
         * @return void
         */
        public function run()
        {
            Product::factory(20)->create();
        }
    }
#UserSeeder.php
    <?php

    namespace Database\Seeders;

    use App\Models\User;
    use Illuminate\Database\Seeder;

    class UserSeeder extends Seeder
    {
        /**
         * Run the database seeds.
         *
         * @return void
         */
        public function run()
        {
            User::factory(20)->create();
        }
    }
  • php artisan db:seed

  • php artisan make:controller ProductController

    <?php

    namespace App\Http\Controllers;

    use App\Jobs\ProductCreated;
    use App\Jobs\ProductDeleted;
    use App\Jobs\ProductUpdated;
    use App\Models\Product;
    use Illuminate\Http\Request;
    use Symfony\Component\HttpFoundation\Response;

    class ProductController extends Controller
    {
        public function index()
        {
            return Product::all();
        }

        public function show($id)
        {
            return Product::find($id);
        }

        public function store(Request $request)
        {
            $product = Product::create($request->only('title','image'));
            ProductCreated::dispatch($product->toArray())->onQueue('main_queue');
            return response($product, Response::HTTP_CREATED);
        }

        public function update($id, Request $request)
        {
            $product = Product::find($id);
            $product->update($request->only('title','image'));
            ProductUpdated::dispatch($product->toArray())->onQueue('main_queue');
            return response($product, Response::HTTP_ACCEPTED);
        }

        public function destroy($id)
        {
            Product::destroy($id);
            ProductDeleted::dispatch($id)->onQueue('main_queue');
            return response(null, Response::HTTP_NO_CONTENT);
        }
    }
  • api.php
      Route::apiResource('products', ProductController::class);
  • 建立HTTP Request
    laravel微服務開發,整合rabbitmq
      GET http://xxxxx:8000/api/products
      Accept: application/json
  • php artisan route:list
  • 安裝main

    laravel new main
  • php artisan ide-helper:generate

  • 建立Dockerfile

      FROM php:7.4
      RUN sed -i s/deb.debian.org/mirrors.aliyun.com/g /etc/apt/sources.list
    
      RUN apt-get update && apt-get install -y \
              libfreetype6-dev \
      #       libjpeng62-turbo-dev \
              libmcrypt-dev \
              libpng-dev \
              zlib1g-dev \
              libxml2-dev \
              libzip-dev \
              libonig-dev \
              graphviz \
    
        && docker-php-ext-configure gd \
        && docker-php-ext-install -j$(nproc) gd \
        && docker-php-ext-install pdo_mysql \
        && docker-php-ext-install mysqli \
        && docker-php-ext-install zip \
        && docker-php-ext-install sockets \
      #  && docker-php-ext-source delete \ #docker-php-source delete:在當前目錄下---刪除 /usr/src/php 目錄
        && curl -sS https://getcomposer.org/installer | php -- \
        --install-dir=/usr/local/bin --filename=composer
    
      WORKDIR /app
      COPY . .
      RUN composer install
  • 建立docker-compose.yaml

    version: '3.8'
    services:
    main:
      build:
        context: .
        dockerfile: Dockerfile
      command: 'php artisan serve --host=0.0.0.0'
      volumes:
      - .:/app
      ports:
        - 8001:8000
      depends_on:
        - main_db
    
    main_queue:
      build:
        context: .
        dockerfile: Dockerfile
      command: 'php artisan queue:work'
      depends_on:
        - main_db
    
    main_db:
      image: mysql:5.7.22
      environment:
        MYSQL_DATABASE: main
        MYSQL_USER: root
        MYSQL_PASSWORD: root
        MYSQL_ROOT_PASSWORD: root
      volumes:
      - ./storage/dbdata:/var/lib/mysql
      ports:
      - 3308:3306
  • .env

      DB_CONNECTION=mysql
      DB_HOST=main_db
      DB_PORT=3306
      DB_DATABASE=main
      DB_USERNAME=root
      DB_PASSWORD=root
    
      QUEUE_CONNECTION=rabbitmq
    
      RABBITMQ_HOST=shark.rmq.cloudamqp.com
      RABBITMQ_PORT=5672
      RABBITMQ_USER=uiuzjuuc
      RABBITMQ_PASSWORD=xxxxxxxxxx
      RABBITMQ_VHOST=xxxx
      RABBITMQ_QUEUE=main_queue
  • 檢驗啟動 docker-compose up

  • 進入容器

    docker-compose exec main sh
  • php artisan make:migration create_products_table

      <?php
    
      use Illuminate\Database\Migrations\Migration;
      use Illuminate\Database\Schema\Blueprint;
      use Illuminate\Support\Facades\Schema;
    
      class CreateProductsTable extends Migration
      {
          /**
           * Run the migrations.
           *
           * @return void
           */
          public function up()
          {
              Schema::create('products', function (Blueprint $table) {
                  $table->unsignedBigInteger('id')->primary();
                  $table->string('title');
                  $table->string('image');
                  $table->timestamps();
              });
          }
    
          /**
           * Reverse the migrations.
           *
           * @return void
           */
          public function down()
          {
              Schema::dropIfExists('products');
          }
      }
  • php artisan make:migrate

  • php artisan make:controller ProductController

      <?php
    
      namespace App\Http\Controllers;
    
      use App\Jobs\ProductLiked;
      use App\Models\Product;
      use App\Models\ProductUser;
      use Illuminate\Http\Request;
      use Symfony\Component\HttpFoundation\Response;
    
      class ProductController extends Controller
      {
          public function index()
          {
              return Product::all();
          }
    
          public function like($id, Request $request)
          {
      //        $response = \Http::get('http://docker.for.mac.localhost:8000/api/user');
              $response = \Http::get('http://47.113.xxx:8000/api/user');
              $user = $response->json();
              try {
                  $productUser = ProductUser::create([
                      'user_id' => $user['id'],
                      'product_id' => $id,
                  ]);
                  ProductLiked::dispatch($productUser->toArray())->onQueue('admin_queue');
                  return  response([
                      'message' => 'success',
                  ]);
              } catch (\Exception $exception) {
                  return  response([
                      'error' => 'you already liked this product!11111111',
                      'error1' => $exception->getMessage(),
                  ],Response::HTTP_BAD_REQUEST);
              }
    
              return $response->json();
          }
      }
  • php artisan make:model Product

  • api.php

    Route::get('products',[ProductController::class,'index']);
    Route::post('products/{id}/like',[ProductController::class,'like']);
  • rabbitmq

    # https://github.com/vyuldashev/laravel-queue-rabbitmq
    composer require vladimir-yuldashev/laravel-queue-rabbitmq
  • 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,
                  ],
              ],
              /*
               * Set to "horizon" if you wish to use Laravel Horizon.
               */
              'worker' => env('RABBITMQ_WORKER', 'default'),
          ],
  • php artisan command FireEvent

      <?php
    
      namespace App\Console\Commands;
    
      use App\Jobs\TestJob;
      use Illuminate\Console\Command;
    
      class FireEvent extends Command
      {
          protected $signature = 'fire';
    
          public function handle()
          {
              TestJob::dispatch();
          }
      }
  • php artisan make:job TestJob

<?php

namespace App\Providers;

use App\Jobs\ProductCreated;
use App\Jobs\ProductDeleted;
use App\Jobs\ProductUpdated;
use App\Jobs\TestJob;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;

class EventServiceProvider extends ServiceProvider
{

    public function boot()
    {
//        //7.4寫法
//        \App::bindMethod(TestJob::class . '@handle', fn($job) => $job->handle());
//        舊版本
//        \App::bindMethod(TestJob::class . '@handle', function ($job){
//            return $job->handle();
//        });

        \App::bindMethod(ProductCreated::class . '@handle', fn($job) => $job->handle());
        \App::bindMethod(ProductUpdated::class . '@handle', fn($job) => $job->handle());
        \App::bindMethod(ProductDeleted::class . '@handle', fn($job) => $job->handle());

    }
}
  • docker-compose exec main sh

  • php artisan queue:work

  • admin 執行php artisan make:job ProductCreated

      <?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 ProductCreated implements ShouldQueue
      {
          use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
          private $data;
          /**
           * Create a new job instance.
           *
           * @return void
           */
          public function __construct($data)
          {
              $this->data = $data;
          }
    
          /**
           * Execute the job.
           *
           * @return void
           */
          public function handle()
          {
              //
          }
      }
    # ProductUpdated.php
    <?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 ProductUpdated implements ShouldQueue
    {
      use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
      private $data;
      /**
       * Create a new job instance.
       *
       * @return void
       */
      public function __construct($data)
      {
          $this->data = $data;
      }
    
      /**
       * Execute the job.
       *
       * @return void
       */
      public function handle()
      {
          //
      }
    }
    #ProductLiked.php
    <?php
    namespace App\Jobs;
    use App\Models\Product;
    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 ProductLiked implements ShouldQueue
    {
      use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
      private $data;
      public function __construct($data)
      {
          $this->data = $data;
      }
      public function handle()
      {
          $product = Product::find($this->data['product_id']);
          $product->likes++;
          $product->save();
      }
    }
    #ProductDeleted.php
    <?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 ProductDeleted implements ShouldQueue
    {
      use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
      private $id;
      /**
       * Create a new job instance.
       *
       * @return void
       */
      public function __construct($id)
      {
          $this->id = $id;
      }
    
      /**
       * Execute the job.
       *
       * @return void
       */
      public function handle()
      {
          //
      }
    }
    <?php
    namespace App\Providers;
    use App\Jobs\ProductLiked;
    use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
    class EventServiceProvider extends ServiceProvider
    {
      public function boot()
      {
          \App::bindMethod(ProductLiked::class . '@handle', fn($job) => $job->handle());
      }
    }
  • main 執行php artisan make:job ProductCreated

      <?php
    
      namespace App\Jobs;
    
      use App\Models\Product;
      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 ProductCreated implements ShouldQueue
      {
          use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
          private $data;
    
          public function __construct($data)
          {
              $this->data = $data;
          }
    
          public function handle()
          {
              Product::create([
                  'id' => $this->data['id'],
                  'title' => $this->data['title'],
                  'image' => $this->data['image'],
                  'created_at' => $this->data['created_at'],
                  'updated_at' => $this->data['updated_at'],
              ]);
          }
      }
    #ProductDeleted.php
    <?php
    namespace App\Jobs;
    use App\Models\Product;
    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 ProductDeleted implements ShouldQueue
    {
      use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
      private $id;
      /**
       * Create a new job instance.
       *
       * @return void
       */
      public function __construct($id)
      {
          $this->id = $id;
      }
    
      /**
       * Execute the job.
       *
       * @return void
       */
      public function handle()
      {
          Product::destroy($this->id);
      }
    }
    #ProductLiked.php
    <?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 ProductLiked implements ShouldQueue
    {
      use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
      private $data;
    
      public function __construct($data)
      {
          $this->data = $data;
      }
      public function handle()
      {
      }
    }
    #ProductUpdated.php
    <?php
    namespace App\Jobs;
    use App\Models\Product;
    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 ProductUpdated implements ShouldQueue
    {
      use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
      private $data;
      /**
       * Create a new job instance.
       *
       * @return void
       */
      public function __construct($data)
      {
          $this->data = $data;
      }
      /**
       * Execute the job.
       *
       * @return void
       */
      public function handle()
      {
          //
          $product = Product::find($this->data['id']);
          $product->update([
              'title' => $this->data['data'],
              'image' => $this->data['image'],
              'created_at' => $this->data['created_at'],
              'updated_at' => $this->data['updated_at'],
          ]);
      }
    }
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章