Menunda Tugas di Laravel Menggunakan Queues
Laravel 2022-06-09
Pada artikel ini, kita akan mengeksplorasi Queue API di web framework Laravel. Ini memungkinkan Anda untuk menunda tugas sumber daya-intensif selama eksekusi skrip untuk meningkatkan pengalaman pengguna akhir secara keseluruhan. Setelah memperkenalkan terminologi dasar, saya akan menunjukkannya dengan menerapkan contoh dunia nyata.
Waktu buka halaman adalah aspek penting dari situs web yang sukses, dan orang tidak boleh mengabaikan pentingnya hal itu karena hal itu mempengaruhi SEO situs dan keseluruhan pengalaman pengguna akhir juga. Lebih sering daripada tidak, Anda akhirnya perlu men-debug halaman web dengan waktu muat halaman yang panjang. Tentu saja, ada beberapa pendekatan berbeda yang dapat Anda gunakan untuk memperbaiki masalah ini.
Setelah diselidiki, Anda sering menyadari bahwa ada beberapa blok kode yang menyebabkan penundaan eksekusi halaman. Hal berikutnya yang dapat Anda coba adalah mengidentifikasi blok yang dapat ditangguhkan untuk diproses dan tidak berdampak nyata pada hasil akhir halaman saat ini. Itu benar-benar harus meningkatkan kecepatan halaman web secara keseluruhan karena kita telah menghilangkan blok kode yang menyebabkan penundaan.
Hari ini, kita akan mengeksplorasi konsep serupa dalam konteks web framework Laravel. Sebenarnya, Laravel sudah menyediakan API bawaan yang berguna untuk memungkinkan kita menunda pemrosesan tugas - Queue API. Tanpa menyia-nyiakan sebagian besar waktu Anda, saya akan melanjutkan dan mendiskusikan elemen dasar dari Queue API.
Driver, Koneksi, Queue, dan Pekerjaan
Tujuan dasar Queue API adalah menjalankan pekerjaan yang ditambahkan dalam queue. Selanjutnya, queue bisa termasuk dalam koneksi tertentu, dan koneksi itu mungkin termasuk driver queue tertentu yang dikonfigurasi dengan koneksi itu sendiri. Mari kita mencoba memahami apa yang baru saja saya katakan.
Driver Queue
Dengan cara yang sama Anda akan menggunakan driver yang berbeda untuk koneksi database Anda, Anda juga bisa memilih dari berbagai driver queue yang berbeda. Queue API mendukung berbagai adapter seperti database, beanstalkd, sqs, dan redis.
Driver queue hanyalah tempat yang digunakan untuk menyimpan informasi terkait queue. Jadi jika Anda menggunakan driver queue database, misalnya, pekerjaan baru akan ditambahkan di tabel pekerjaan di database. Di sisi lain, jika Anda telah mengonfigurasi redis sebagai driver queue default, pekerjaan akan ditambahkan ke redis server.
Queue API juga menyediakan dua driver queue khusus untuk tujuan pengujian—sinkronisasi dan null. Sinkronisasi driver queue digunakan untuk menjalankan tugas queue seketika, sementara driver queue null digunakan untuk melewatkan pekerjaan sehingga tidak akan dieksekusi sama sekali.
Koneksi
Saat Anda mengkonfigurasi API Antrian untuk pertama kalinya, Anda perlu menentukan koneksi default yang harus digunakan untuk pemrosesan queue default. Paling tidak, koneksi diharapkan bisa memberikan informasi berikut:
- driver queue yang akan digunakan
- nilai konfigurasi khusus driver queue
- nama queue default dimana pekerjaan akan ditambahkan
Queues
Bila Anda menambahkan pekerjaan ke dalam queue, itu akan ditambahkan ke dalam queue default. Sebenarnya, itu pasti bagus dalam kebanyakan kasus, kecuali Anda memiliki pekerjaan yang perlu diberi prioritas lebih tinggi daripada pekerjaan lain. Dalam hal ini, Anda bisa membuat queue yang diberi nama tinggi dan menempatkan pekerjaan dengan prioritas lebih tinggi dalam queue tertentu.
Saat Anda menjalankan queue wotker yang memproses pekerjaan queue, Anda dapat secara opsional melewatkan parameter --queue
, yang memungkinkan Anda mencantumkan nama queue sesuai urutan pemrosesannya. Misalnya, jika Anda menentukan --queue=high,default
, awalnya akan memproses pekerjaan dalam queue tinggi, dan setelah selesai, ia menjemput pekerjaan di queue default.
Pekerjaan
Pekerjaan di Queue API adalah tugas yang ditangguhkan dari arus eksekusi utama. Misalnya, jika Anda ingin membuat thumbnail saat pengguna mengupload gambar dari front-end, Anda bisa membuat pekerjaan baru yang menangani pemrosesan gambar kecil. Dengan cara ini, Anda bisa menunda tugas pemrosesan gambar kecil dari aliran eksekusi utama.
Itu adalah pengantar dasar terminologi Queue API. Dari bagian selanjutnya dan seterusnya, kita akan membahas cara membuat queue job khusus dan menjalankannya dengan menggunakan Laravel queue worker.
Buat Tugas Queue Pertama Anda
Sekarang, Anda harus merasa yakin dengan pekerjaan antrian. Dari bagian ini dan seterusnya, kita akan menerapkan contoh dunia nyata yang menunjukkan konsep pekerjaan queue di Laravel.
Lebih sering daripada tidak, Anda berakhir dalam situasi di mana Anda perlu membuat versi gambar kecil yang berbeda dari gambar yang diunggah oleh pengguna. Dalam kebanyakan kasus, pengembang mencoba memprosesnya secara real time sehingga berbagai versi gambar dibuat saat pengguna mengupload gambar.
Tampaknya menjadi pendekatan yang masuk akal jika Anda ingin membuat beberapa versi dan tidak memakan banyak waktu di tempat pertama. Di sisi lain, jika Anda berurusan dengan aplikasi yang memerlukan pemrosesan yang berat dan dengan demikian menghabiskan lebih banyak sumber daya, pemrosesan real-time bisa berakhir dengan pengalaman pengguna yang buruk.
Pilihan yang jelas muncul di benak Anda adalah menunda pemrosesan generasi gambar kecil selambat mungkin. Pendekatan paling sederhana yang dapat Anda terapkan dalam skenario spesifik ini adalah menetapkan cron job yang memicu pemrosesan secara berkala, dan Anda seharusnya baik-baik saja.
Pendekatan yang jauh lebih baik, di sisi lain, adalah menunda dan mendorong tugas ke queue, dan membiarkan pekerja queue memprosesnya saat mendapat kesempatan untuk melakukannya. Di lingkungan produksi, pekerja queue adalah script daemon yang selalu berjalan dan memproses tugas dalam queue. Manfaat nyata dari pendekatan ini adalah pengalaman pengguna akhir yang jauh lebih baik, dan Anda tidak perlu menunggu cron dijalankan karena pekerjaan akan diproses sesegera mungkin.
Saya kira itu teori yang cukup untuk memulai implementasi yang sesungguhnya.
Dalam kasus kita, kita akan menggunakan database
driver queue, dan mengharuskan kita membuat tabel jobs
di database. tabel jobs
memegang semua pekerjaan yang perlu diproses dalam menjalankan queue worker berikutnya.
Sebelum kita melanjutkan dan membuat tabel jobs
, mari kita ubah defaulr konfigurasi queue dari sync
ke database
di file config/queue.php
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
... ... /* |-------------------------------------------------------------------------- | Default Queue Driver |-------------------------------------------------------------------------- | | Laravel's queue API supports an assortment of back-ends via a single | API, giving you convenient access to each back-end using the same | syntax for each one. Here you may set the default queue driver. | | Supported: "sync", "database", "beanstalkd", "sqs", "redis", "null" | */ 'default' => env( 'QUEUE_DRIVER' , 'database' ), ... ... |
Sebenarnya, Laravel sudah menyediakan perintah artisan yang membantu kita membuat tabel jobs
. Jalankan perintah berikut di root aplikasi Laravel Anda, dan ini seharusnya membuat migrasi database yang diperlukan membuat tabel jobs
.
1
|
$php artisan queue:table |
File migrasi yang dihasilkan di database/migrations/YYYY_MM_DD_HHMMSS_create_jobs_table.php
akan terlihat seperti ini:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateJobsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create( 'jobs' , function (Blueprint $table ) { $table ->bigIncrements( 'id' ); $table ->string( 'queue' ); $table ->longText( 'payload' ); $table ->unsignedTinyInteger( 'attempts' ); $table ->unsignedInteger( 'reserved_at' )->nullable(); $table ->unsignedInteger( 'available_at' ); $table ->unsignedInteger( 'created_at' ); $table ->index([ 'queue' , 'reserved_at' ]); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists( 'jobs' ); } } |
Selanjutnya, mari jalankan perintah migrate
sehingga benar-benar membuat tabel jobs
dalam sebuah database.
1
|
php artisan migrate |
Itu saja sejauh job
migrasi diperhatikan.
Selanjutnya, mari buat model Image
yang akan digunakan untuk mengelola gambar yang diunggah oleh pengguna akhir. Model gambar juga memerlukan tabel database yang terkait, jadi kita akan menggunakan opsi --migrate
saat membuat model Image
.
1
|
php artisan make:model Image --migration |
Perintah di atas harus membuat class model Image
dan sebuah database yang terkait juga.
Class model Image
akan terlihat seperti ini:
01
02
03
04
05
06
07
08
09
10
|
<?php // app/Image.php namespace App; use Illuminate\Database\Eloquent\Model; class Image extends Model { // } |
Dan file migrasi database seharusnya dibuat di database/migrations/YYYY_MM_DD_HHMMSS_create_images_table.php
. Kami juga ingin menyimpan lokasi asli dari gambar yang diupload oleh pengguna akhir. Mari kita merevisi kode file migrasi database Image
agar terlihat seperti berikut.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<?php // database/migrations/YYYY_MM_DD_HHMMSS_create_images_table.php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateImagesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create( 'images' , function (Blueprint $table ) { $table ->increments( 'id' ); $table ->timestamps(); $table ->string( 'org_path' ); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists( 'images' ); } } |
Seperti yang Anda lihat, kita telah menambahkan kolom $table->string('org_path')
untuk menyimpan lokasi gambar asli. Selanjutnya, anda hanya perlu menjalankan perintah migrate
untuk benar-benar membuat tabel tersebut dalam database.
1
|
$php artisan migrate |
Dan itulah sejauh ini sebagai model Image
yang bersangkutan.
Selanjutnya, mari kita membuat pekerjaan queue sebenarnya yang bertanggung jawab untuk pengolahan gambar kecil. Untuk pengolahan gambar kecil, kita akan menggunakan library pengolahan gambar yang sangat populer—Intervention Image.
Untuk memasang library Intervention Image, lanjutkan dan jalankan perintah berikut pada root aplikasi anda.
1
|
$php composer.phar require intervention/image |
Sekarang, saatnya untuk membuat class Job
, dan kita akan menggunakan perintah artisan untuk melakukan itu.
1
|
$php artisan make:job ProcessImageThumbnails |
Seharusnya itu membuat template class Job
di app/Jobs/ProcessImageThumbnails.php
. Mari kita ganti isi dari file yang sama dengan berikut.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
<?php // app/Jobs/ProcessImageThumbnails.php namespace App\Jobs; use App\Image as ImageModel; use Illuminate\Bus\Queueable; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Support\Facades\DB; class ProcessImageThumbnails implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $image ; /** * Create a new job instance. * * @return void */ public function __construct(ImageModel $image ) { $this ->image = $image ; } /** * Execute the job. * * @return void */ public function handle() { // access the model in the queue for processing $image = $this ->image; $full_image_path = public_path( $image ->org_path); $resized_image_path = public_path( 'thumbs' . DIRECTORY_SEPARATOR . $image ->org_path); // create image thumbs from the original image $img = \Image::make( $full_image_path )->resize(300, 200); $img ->save( $resized_image_path ); } } |
Ketika queue worker mulai memproses setiap pekerjaan, Ini mencari metode handle
. Jadi metode handle
ini adalah yang memegang logika utama dari pekerjaan Anda.
Dalam kasus kita, kita perlu membuat gambar kecil dari gambar yang diupload oleh pengguna. Kode metode handle
ini cukup sederhana—kita mengambil gambar dari model ImageModel
dan membuat gambar kecil menggunakan library Intervention Image. Tentu saja, kita harus melalui model Image
yang sesuai ketika kita mengirimkan tugas kita, dan kita akan melihatnya dalam sekejap.
Untuk menguji pekerjaan yang baru kita buat, kita akan menciptakan form upload sederhana yang memungkinkan pengguna untuk meng-upload gambar. Tentu saja, kita tidak akan segera membuat gambar kecil; kita akan menunda tugas itu sehingga dapat diproses oleh queue worker.
Mari kita buat file controller di app/Http/Controllers/ImageController.php
, seperti yang ditunjukkan di bawah ini.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
<?php namespace App\Http\Controllers; use App\Image; use App\Jobs\ProcessImageThumbnails; use Illuminate\Http\Request; use Illuminate\Support\Facades\Redirect; use App\Http\Controllers\Controller; use Validator; class ImageController extends Controller { /** * Show Upload Form * * @param Request $request * @return Response */ public function index(Request $request ) { return view( 'upload_form' ); } /** * Upload Image * * @param Request $request * @return Response */ public function upload(Request $request ) { // upload image $this ->validate( $request , [ 'demo_image' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048' , ]); $image = $request ->file( 'demo_image' ); $input [ 'demo_image' ] = time(). '.' . $image ->getClientOriginalExtension(); $destinationPath = public_path( '/images' ); $image ->move( $destinationPath , $input [ 'demo_image' ]); // make db entry of that image $image = new Image; $image ->org_path = 'images' . DIRECTORY_SEPARATOR . $input [ 'demo_image' ]; $image ->save(); // defer the processing of the image thumbnails ProcessImageThumbnails::dispatch( $image ); return Redirect::to( 'image/index' )->with( 'message' , 'Image uploaded successfully!' ); } } |
Mari kita membuat file view terkait di resources/views/upload_form.blade.php
.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
<!DOCTYPE html> <html lang= "{{ config('app.locale') }}"> <head> <meta charset= "utf-8"> <meta http-equiv= "X-UA-Compatible" content= "IE=edge" > <meta name= "viewport" content= "width=device-width, initial-scale=1" > <meta name= "csrf-token" content= "{{ csrf_token() }}" /> <title>Laravel</title> <!-- Fonts --> <link href= "https://fonts.googleapis.com/css?family=Raleway:100,600" rel= "stylesheet" type= "text/css" > <!-- Styles --> <style> html, body { background-color: #fff; color: #636b6f; font-family: 'Raleway' , sans-serif; font-weight: 100; height: 100vh; margin: 0; } .full-height { height: 100vh; } <
Search Artikel
3 Artikel Terbaru
|