Redis คิว

Redis คิวเป็นระบบจัดการข้อความที่ใช้ Redis เป็นฐานข้อมูล เพื่อรองรับการประมวลผลข้อความที่มีความช้า

การติดตั้ง

composer require webman/redis-queue

ไฟล์การกำหนดค่า

ไฟล์การกำหนดค่า Redis ถูกสร้างขึ้นที่ config/plugin/webman/redis-queue/redis.php มีเนื้อหาที่คล้ายกันกับตัวอย่างต่อไปนี้:

<?php
return [
    'default' => [
        'host' => 'redis://127.0.0.1:6379',
        'options' => [
            'auth' => '',         // รหัสผ่านที่เป็นตัวเลือก
            'db' => 0,            // ฐานข้อมูล
            'max_attempts'  => 5, // จำนวนครั้งในการลอง
            'retry_seconds' => 5, // ช่วงเวลาในการลองอีกครั้ง หน่วยวินาที
        ]
    ],
];

การลองทำการกระทำอีกครั้งเมื่อล้มเหลว

ถ้าการกระทำล้มเหลว (เกิดข้อผิดพลาด) ข้อความจะถูกนำเข้าไปยังคิวที่ล่าช้าและรอการกระทำอีกครั้ง จำนวนครั้งในการลองใช้ได้จากพารามิเตอร์ max_attempts และช่วงเวลาในการลองอีกครั้งจะได้จาก retry_seconds อย่างเช่น max_attempts เป็น 5, retry_seconds เป็น 10, ช่วงเวลาในการลองอีกครั้งครั้งแรกคือ 1*10 วินาที, ครั้งที่ 2 คือ 2*10 วินาที, ครั้งที่ 3 คือ 3*10 วินาที และต่อไป โดยกรณีที่เกินจำนวนครั้งที่กำหนดไว้ยังทำการ ข้อความจะถูกนำไปยังควีลล้มเหลวที่มีชื่อว่า {redis-queue}-failed

การส่งข้อความ (ซิงโครนัส)

โปรดทราบ
จำเป็นต้องใช้ webman/redis >= 1.2.0, ต้องใช้งานภาคขยาย redis

<?php
namespace app\controller;

use support\Request;
use Webman\RedisQueue\Redis;

class Index
{
    public function queue(Request $request)
    {
        // ชื่อคิว
        $queue = 'send-mail';
        // ข้อมูล, สามารถส่งแอเรย์ได้โดยตรง ไม่จำเป็นต้องดีซีเรียลไอ
        $data = ['to' => 'tom@gmail.com', 'content' => 'hello'];
        // ส่งข้อความ
        Redis::send($queue, $data);
        // ส่งข้อความที่ล่าช้า, ข้อความจะถูกดำเนินการหลังจาก 60 วินาที
        Redis::send($queue, $data, 60);

        return response('ทดสอบคิว Redis');
    }

}

การส่งสำเร็จ Redis::send() จะส่งค่า true มิฉะนั้นจะส่งค่า false หรือเกิดข้อผิดพลาด

เกร็ดความรู้
เวลาที่ข้อความที่ล่าช้าเป็นไปได้คำนึงถึงการผิดพลาดในการกระทำอีกครั้ง, เช่นการกระทำที่ช้ากว่าการผลิตโดยกำลังขยายคิวต่อไปซึ่งจะทำให้ข้อความเข้าสู่คิวในการทำอีกครั้ง วิธีการใช้งานหลักๆคือการเปิดผลิตภัณฑ์ขี้เสียหลายๆเรื่องให้ง่ายขึ้น


<?php
namespace app\controller;

use support\Request;
use Webman\RedisQueue\Client;

class Index
{
public function queue(Request $request)
{
// ชื่อคิว
$queue = 'send-mail';
// ข้อมูล, สามารถส่งแอเรย์ได้โดยตรง ไม่จำเป็นต้องดีซีเรียลไอ
$data = ['to' => 'tom@gmail.com', 'content' => 'hello'];
// ส่งข้อความ
Client::send($queue, $data);
// ส่งข้อความที่ล่าช้า, ข้อความจะถูกดำเนินการหลังจาก 60 วินาที
Client::send($queue, $data, 60);

    return response('ทดสอบคิว Redis');
}

}


`Client::send()` ไม่มีค่าการส่งคืน, มันเป็นการส่งโดยอัตโนมัติทำให้ข้อความไปถึง Redis อย่างแน่นอน

> **เกร็ดความรู้**
> หลักการทำงานของ `Client::send()` คือการสร้างคิวในหน่วยความจำภายในเครื่อง และการส่งต่อข้อความโดยอัตโนมัติไปยัง Redis (การส่งเร็วแทบจะมีประมาณ 10,000 ข้อความต่อวินาที) หากโปรเซสร์หยุดทำงาน และคิวที่อยู่ในหน่วยความจำมันไม่ได้ส่งสิ้นสุด, จะทำให้ข้อความหายไปได้ `Client::send()` การส่งโดยอัตโนมัติเหมาะกับการส่งข้อความที่สำคัญน้อย

> **เกร็ดความรู้**
> `Client::send()` คือฟังก์ชันการส่งแบบอัสซิงโครนัส มันสามารถใช้ได้เฉพาะในสภาพแวดล้อมการทำงานของเวบมัน สำหรับสคริปต์ชนิด命令行โปรดใช้อินเตอเฟซการส่งที่ซิงโครนัส `Redis::send()`
## การส่งข้อความในโปรเจคอื่น

บางครั้งเวลาคุณอาจต้องการส่งข้อความไปยังคิวในโปรเจคอื่นและไม่สามารถใช้ `webman\redis-queue`  คุณสามารถอ้างถึงฟังก์ชันต่อไปนี้เพื่อส่งข้อความไปยังคิว

```php
function redis_queue_send($redis, $queue, $data, $delay = 0) {
    $queue_waiting = '{redis-queue}-waiting';
    $queue_delay = '{redis-queue}-delayed';
    $now = time();
    $package_str = json_encode([
        'id'       => rand(),
        'time'     => $now,
        'delay'    => $delay,
        'attempts' => 0,
        'queue'    => $queue,
        'data'     => $data
    ]);
    if ($delay) {
        return $redis->zAdd($queue_delay, $now + $delay, $package_str);
    }
    return $redis->lPush($queue_waiting.$queue, $package_str);
}

ซึ่งพารามิเตอร์ $redis คืออินสแตนซ์ของ Redis เช่นการใช้งาน Redis คลาสการใช้ได้ดังตัวอย่างต่อไปนี้:

$redis = new Redis;
$redis->connect('127.0.0.1', 6379);
$queue = 'user-1';
$data= ['some', 'data'];
redis_queue_send($redis, $queue, $data);

การบริโภค

ไฟล์การกำหนดค่าสำหรับกระบวนการการบริโภคอยู่ที่ config/plugin/webman/redis-queue/process.php ไดเรกทอรี่ของผู้บริโภคอยู่ที่ app/queue/redis/

การดำเนินการคำสั่ง php webman redis-queue:consumer my-send-mail จะสร้างไฟล์ app/queue/redis/MyMailSend.php

โปรดทราบ
ถ้าคำสั่งไม่มีอาจาจะสร้างด้วยตนเอง

<?php

namespace app\queue\redis;

use Webman\RedisQueue\Consumer;

class MyMailSend implements Consumer
{
    // ชื่อคิวที่จะบริโภค
    public $queue = 'send-mail';

    // ชื่อการเชื่อมต่อ, สอดคตุผลใน plugin/webman/redis-queue/redis.php  ชื่อการเชื่อมต่อ`
    public $connection = 'default';

    // บริโภค
    public function consume($data)
    {
        // ไม่จำเป็นต้องดีโซรีไอ
        var_export($data); // ส่่งออก ['to' => 'tom@gmail.com', 'content' => 'hello']
    }
}

โปรดทราบ
การบริโภคแล้วไม่มีการเกิดข้อผิดพลาด และขมริโภคการีรับว่าเป็นสำเร็จ, ไม่เช่นนั้นการบริโภคล้มเหลว จะถูกนำเข้าคิวที่ลองอีกครั้ง คิว Redis ไม่มีกลไก ack คุณสามารถเรียกคิวที่มันเป็นการ ack (ไม่มีการเกิดข้อผิดพลาดหรือข้อผิดพลาด) ถ้ากระบวนการการบริโภคต้องการทำเครื่องมือที่ไม่เสร็จสนิทคุณสามารถด่งตารายลุบออกความผิดพลาดเพื่อทำให้กระบวนการที่กำลังกระทำล้มเหลว ซึ่งแต่ทางทเ้อนทำอีกอย่าับถ้วนว่าม่ดีเหมือนกัน

เกร็ดความรู้
ผู้บริโภคสนับสนุนการทำงานในหลายเซิพเวอร์หลายกระบวน และข้อความที่แม่แน่นไม่ปรากฏซื้อสำหรับการบริโภคอีกครั้ง ข้อความที่ได้รับการบริโภคจะถูกยกเลิกอย่าบให้ยกเลิกด้วยตนเอง

เกรดความรู้
กระบวนการบริโภคสามารถบริโภคคิวหลาย ๆ และหลายคิว หมุนเวินให้เพิ่มท้ายคิวคือสิ่งที่ต้องการในการใช้งานคิวใหม่ไม่คงจำเป็นต้องแม้นการกำหนดค่าใน process.php แทนที่จะสมบรรทัดผู้บริโภคใหม่แค่ปรากฎใน app/queue/redis ในหน้าตาชัให้เพื่มขอให้เรียนเขาว่า $queue ลูกยของชนิด Consumer

เกรดความรู้
ผู้ใช้ของวินโดส จำเป็นต้องเรียกการบริโภค php windows.php เพื่อเรข้า webman หากไม่การี้เรเรเั้รดเขเข่เฒทาศัเข้ากระบวนการบริโภค

กำหนดกระบวนการการบริโภคที่แตกต่างสำหรับคิวที่แตกต่างกัน

โดยปกติแล้ว ผู้บริโภคทั้งหมดใช้กระบวนการบริโภคเดียวกัน แต่บางครั้งเราอาจต้องการแยกผู้บริโภคของคิวบางชุดออก เช่น การบริโภคธุรกิจที่ช้านำเข้ากรุ๊ปละเอียดเป็นหมู่อีกของที่ผ่านมาดำเนินการบริโภคในกรุ๊ปอีกแหล่ว ในกรณีนี้ เราสามารถแบ่งผู้บริโภคออกเป็นโฟลเดอร์สองโฟลเดอร์ เช่น app_path() . '/queue/redis/fast' และ app_path() . '/queue/redis/slow' (โปรดทราบว่าต้องทำการเปลี่ยนแปลง namespace ของคลาสบริโภคตามนั้น) ตัวอย่างเช่น:


return [
... ตัวอย่างการกำหนดค่าที่เหลือ...
'redis_consumer_fast'  => [
    'handler'     => Webman\RedisQueue\Process\Consumer::class,
    'count'       => 8,
    'constructor' => [
        // โฟลเดอร์ของคลาสผู้บริโภค
        'consumer_dir' => app_path() . '/queue/redis/fast'
    ]
],
'redis_consumer_slow'  => [
    'handler'     => Webman\RedisQueue\Process\Consumer::class,
    'count'       => 8,
    'constructor' => [
        // โฟลเดอร์ของคลาสผู้บริโภค
        'consumer_dir' => app_path() . '/queue/redis/slow'
    ]
]

];

โดยการจัดกลุ่มโฟลเดอร์และการกำหนดค่าที่เกี่ยวข้อง เราสามารถกำหนดกระบวนการการบริโภคที่แตกต่างสำหรับผู้บริโภคที่แตกต่างกันได้โดยง่ายดาย
## การกำหนดค่า Redis หลายรายการ
#### การกำหนดค่า
`config/plugin/webman/redis-queue/redis.php`
```php
<?php
return [
    'default' => [
        'host' => 'redis://192.168.0.1:6379',
        'options' => [
            'auth' => null,       // รหัสผ่าน เป็นตัวหลักข้อความ ตัวเลือกที่สามารถใช้งานได้
            'db' => 0,            // ฐานข้อมูล
            'max_attempts'  => 5, // หลังจากการบริโภคล้มเหลว จำนวนครั้งในการลองบริโภคใหม่
            'retry_seconds' => 5, // ระยะเวลาระหว่างการลองบริโภคใหม่ หน่วยเป็นวินาที
        ]
    ],
    'other' => [
        'host' => 'redis://192.168.0.2:6379',
        'options' => [
            'auth' => null,       // รหัสผ่าน เป็นตัวหลักข้อความ ตัวเลือกที่สามารถใช้งานได้
            'db' => 0,             // ฐานข้อมูล
            'max_attempts'  => 5, // หลังจากการบริโภคล้มเหลว จำนวนครั้งในการลองบริโภคใหม่
            'retry_seconds' => 5, // ระยะเวลาระหว่างการลองบริโภคใหม่ หน่วยเป็นวินาที
        ]
    ],
];

โปรดทราบว่าการกำหนดค่าได้เพิ่มขึ้นอีก Redis หนึ่งรายการที่เรียกว่า other เป็น key

การบริโภคข้อความจาก Redis หลายรายการ

// บริโภคข้อความจากคิวที่เป็น key ตั้งเริ่มต้น
Client::connection('default')->send($queue, $data);
Redis::connection('default')->send($queue, $data);
// คือเหมือนกับ
Client::send($queue, $data);
Redis::send($queue, $data);

// บริโภคข้อความจากคิวที่เป็น key อื่น
Client::connection('other')->send($queue, $data);
Redis::connection('other')->send($queue, $data);

การบริโภคข้อความจาก Redis หลายรายการ

การกำหนดค่าการบริโภคข้อความจาก key other ในกลุ่มแม้จะมีการบริโภค

namespace app\queue\redis;

use Webman\RedisQueue\Consumer;

class SendMail implements Consumer
{
    // ชื่อคิวที่จะบริโภค
    public $queue = 'send-mail';

    // === ตั้งค่าเป็น other แทนการบริโภคจากคิวที่ key other ===
    public $connection = 'other';

    // การบริโภค
    public function consume($data)
    {
        // ไม่จำเป็นต้องยึดค่ากลับ
        var_export($data);
    }
}

ปัญหาที่พบบ่อย

ทำไมมันขึ้นข้อผิดพลาด "Workerman\Redis\Exception: Workerman Redis Wait Timeout (600 seconds)"

ข้อผิดพลาดนี้จะเกิดขึ้นเมื่อใช้งานฟังก์ชัน Client::send() แบบซิงโครนัส หรือเมื่อมีการส่งข้อความที่ใช้เวลานานในการส่งไปยังเซิร์ฟเวอร์ Redis หากข้อความที่ส่งไปยัง Redis มีความเร็วน้อยกว่าการส่งข้อความ หรือหน่วยความจำมีปัญหาในการเตรียมส่งข้อความ การสร้างการส่งข้อความนั้นจะทำให้ข้อผิดพลาดเกิดขึ้น หากมีความผิดพลาดในการสื่อสารเกิน 600 วินาที ข้อผิดพลาดนี้ก็จะเกิดขึ้น

วิธีการแก้ไข: ควรใช้ฟังก์ชันส่งข้อความแบบซิงโครนัส Redis::send() แทน