• BeansTalkd 做消息队列服务


    无意间看到这个仓库讲php关于 BeanStalkd 的扩展,然后就去了解了一下beanstalkd,用它可以用来做队列服务。

    话不多说,安装一下试试。

    首先 sudo apt search beanstalk 搜索一下发现


    Sorting... Done
    Full Text Search... Done
    awscli/focal-updates,focal-updates 1.18.69-1ubuntu0.20.04.1 all
      Universal Command Line Environment for AWS

    beanstalkd/focal,now 1.11-1 amd64 [installed]
      simple, in-memory, workqueue service

    python-celery-common/focal,focal 4.2.1-5ubuntu1 all
      async task/job queue based on message passing (common files)

    python-celery-doc/focal,focal 4.2.1-5ubuntu1 all
      async task/job queue based on message passing (Documentation)

    python3-celery/focal,focal 4.2.1-5ubuntu1 all
      async task/job queue based on message passing (Python3 version)

    ruby-beaneater/focal,focal 1.0.0-1 all
      simple beanstalkd client for Ruby
     

    好了,找到这个beanstalkd了

    然后安装

     sudo apt-get install beanstalkd 

    Reading package lists... Done
    Building dependency tree       
    Reading state information... Done
    Suggested packages:
      doc-base
    The following NEW packages will be installed:
      beanstalkd
    0 upgraded, 1 newly installed, 0 to remove and 90 not upgraded.
    Need to get 43.9 kB of archives.
    After this operation, 125 kB of additional disk space will be used.
    Get:1 http://cn.archive.ubuntu.com/ubuntu focal/universe amd64 beanstalkd amd64 1.11-1 [43.9 kB]
    Get:1 http://cn.archive.ubuntu.com/ubuntu focal/universe amd64 beanstalkd amd64 1.11-1 [43.9 kB]
    Fetched 31.3 kB in 4s (7,047 B/s)     
    Selecting previously unselected package beanstalkd.
    (Reading database ... 256731 files and directories currently installed.)
    Preparing to unpack .../beanstalkd_1.11-1_amd64.deb ...
    Unpacking beanstalkd (1.11-1) ...
    Setting up beanstalkd (1.11-1) ...
    Created symlink /etc/systemd/system/multi-user.target.wants/beanstalkd.service → /lib/systemd/system/beanstalkd.service.
    beanstalkd.socket is a disabled or a static unit, not starting it.
    Processing triggers for man-db (2.9.1-1) ...
    Processing triggers for systemd (245.4-4ubuntu3.11) ...
     

    安装成功,这个东西会监听 11300 端口
    然后使用这个工具来看看


    监控工具:

    wget https://github.com/src-d/beanstool/releases/download/v0.2.0/beanstool_v0.2.0_linux_amd64.tar.gz

    获取文件后解压

    tar -xvzf beanstool_v0.2.0_linux_amd64.tar.gz

    然后拷贝到 /usr/local/bin/

    sudo cp beanstool_v0.2.0_linux_amd64/beanstool /usr/local/bin/

    这样就直接用 beanstool 了

    查看当前状态

    beanstool stats

    结果

    +---------+----------+----------+----------+----------+----------+----------+----------+
    | Name    | Buried   | Delayed  | Ready    | Reserved | Urgent   | Waiting  | Total    |
    +---------+----------+----------+----------+----------+----------+----------+----------+
    | default | 0        | 0        | 0        | 0        | 0        | 0        | 0        |
    +---------+----------+----------+----------+----------+----------+----------+----------+

    然后使用composer的vendor包

    composer require pda/pheanstalk
    安装完毕后创建一个input.php文件做生产者


    require __DIR__ . '/vendor/autoload.php';

    use Pheanstalk\Pheanstalk;

    $pheanstalk = Pheanstalk::create('127.0.0.1');

    // Queue a Job
    $pheanstalk
      ->useTube('testtube')
      ->put("job payload goes here\n");

    $pheanstalk
        ->useTube('testtube')
        ->put(
            json_encode(['test' => 'data']),  // encode data in payload
            Pheanstalk::DEFAULT_PRIORITY,     // default priority
            30, // delay by 30s
            60  // beanstalk will retry job after 60s
         );


    再创建一个output.php文件做消费者


    require __DIR__ . '/vendor/autoload.php';
    use Pheanstalk\Pheanstalk;

    $pheanstalk = Pheanstalk::create('127.0.0.1');

    // we want jobs from 'testtube' only.
    $pheanstalk->watch('testtube');

    // this hangs until a Job is produced.
    $job = $pheanstalk->reserve();

    try {
        $jobPayload = $job->getData();
        // do work.

        sleep(2);
        // If it's going to take a long time, periodically
        // tell beanstalk we're alive to stop it rescheduling the job.
        $pheanstalk->touch($job);
        sleep(2);

        // eventually we're done, delete job.
        $pheanstalk->delete($job);
    }
    catch(\Exception $e) {
        // handle exception.
        // and let some other worker retry.
        $pheanstalk->release($job); 
    }
     

    然后执行一下  php input.php

    查看 状态

     
    $ beanstool stats
    +----------+----------+----------+----------+----------+----------+----------+----------+
    | Name     | Buried   | Delayed  | Ready    | Reserved | Urgent   | Waiting  | Total    |
    +----------+----------+----------+----------+----------+----------+----------+----------+
    | default  | 0        | 0        | 0        | 0        | 0        | 0        | 0        |
    | testtube | 0        | 1        | 1        | 0        | 0        | 0        | 2        |
    +----------+----------+----------+----------+----------+----------+----------+----------+
     
    我们看到有一个在ready状态,一个在delayed状态,这是由于第二次的put采用了延时30s,然后过一段时间后再看

     
    $ beanstool stats
    +----------+----------+----------+----------+----------+----------+----------+----------+
    | Name     | Buried   | Delayed  | Ready    | Reserved | Urgent   | Waiting  | Total    |
    +----------+----------+----------+----------+----------+----------+----------+----------+
    | default  | 0        | 0        | 0        | 0        | 0        | 0        | 0        |
    | testtube | 0        | 0        | 2        | 0        | 0        | 0        | 2        |
    +----------+----------+----------+----------+----------+----------+----------+----------+
    复制代码
    已经有2个在ready状态了。

    此时我们用消费者执行一下 php output.php 

    与此同时迅速看状态

    复制代码
    $ beanstool stats
    +----------+----------+----------+----------+----------+----------+----------+----------+
    | Name     | Buried   | Delayed  | Ready    | Reserved | Urgent   | Waiting  | Total    |
    +----------+----------+----------+----------+----------+----------+----------+----------+
    | default  | 0        | 0        | 0        | 0        | 0        | 0        | 0        |
    | testtube | 0        | 0        | 1        | 1        | 0        | 0        | 2        |
    +----------+----------+----------+----------+----------+----------+----------+----------+
    再次执行
    $ beanstool stats
    +----------+----------+----------+----------+----------+----------+----------+----------+
    | Name     | Buried   | Delayed  | Ready    | Reserved | Urgent   | Waiting  | Total    |
    +----------+----------+----------+----------+----------+----------+----------+----------+
    | default  | 0        | 0        | 0        | 0        | 0        | 0        | 0        |
    | testtube | 0        | 0        | 1        | 0        | 0        | 0        | 2        |
    +----------+----------+----------+----------+----------+----------+----------+----------+
    复制代码
    因为我们有sleep(2),所以要尽量快点操作这个状态监控的命令,可以看到有一个拿出来放入了reserved,然后就消失了(实际上这是后面的代码delete导致的,因为已经消费完毕)

    再次执行 php output.php 

     
    $ beanstool stats:
    +----------+----------+----------+----------+----------+----------+----------+----------+
    | Name     | Buried   | Delayed  | Ready    | Reserved | Urgent   | Waiting  | Total    |
    +----------+----------+----------+----------+----------+----------+----------+----------+
    | default  | 0        | 0        | 0        | 0        | 0        | 0        | 0        |
    | testtube | 0        | 0        | 0        | 1        | 0        | 0        | 2        |
    +----------+----------+----------+----------+----------+----------+----------+----------+

    $ beanstool stats:
    +---------+----------+----------+----------+----------+----------+----------+----------+
    | Name    | Buried   | Delayed  | Ready    | Reserved | Urgent   | Waiting  | Total    |
    +---------+----------+----------+----------+----------+----------+----------+----------+
    | default | 0        | 0        | 0        | 0        | 0        | 0        | 0        |
    +---------+----------+----------+----------+----------+----------+----------+----------+
     
    同样也是迅速观测这个状态,发现消费1个,然后删除1个,现在队列空了,这说明确实是符合我们的期望的。

    然后回到文章开头提到的扩展,这个扩展就是帮我们实现了composer装的那个pheanstalk包。


    这个扩展如何安装呢?

    步骤如下:

    克隆项目

    git clone https://gitee.com/qzfzz/php-beanstalk.git
    进行编译 

    phpize

    然后

    ./configure

    之后

    make && make install

    sudo make install

    然后修改 php.ini

    sudo gedit /etc/php/7.4/cli/php.ini /etc/php/7.4/apache2/php.ini

    加上这句

    extension=beanstalk

    重启apache2

    sudo /etc/init.d/apache2 restart
    之后就可以使用这个扩展了。

    这个扩展它封装为函数了,可以看到他有个例子文件

    简单的找了几个例子

    复制代码

    $config = [
      'host' => '127.0.0.1',
      'port' => 11300
    ];
    $beanstalk_obj = beanstalk_connect($config['host'], $config['port']);
    $last_job_id = beanstalk_put($beanstalk_obj, "message detail");
    beanstalk_delete($beanstalk_obj, $last_job_id);
    $last_job_id = beanstalk_putInTube($beanstalk_obj, 'tubea', "message detail");
    复制代码
    可以看到使用 connect 连接, put 塞入新的job消息, putInTube 来塞入指定管道的tubea,delete来删除等等,具体可以看看源代码学习一下,我对比了一下这两种方式实现效率。

    第一种采用composer包(我还特意去掉了加载文件所需要的时间)

    复制代码

    use Pheanstalk\Pheanstalk;

    $start = microtime( true );
    $pheanstalk = Pheanstalk::create('127.0.0.1');

    $pheanstalk
      ->useTube('testtube')
      ->put(date("Y-m-d H:i:s") . "job payload goes here\n");

    $end = microtime(true);

    echo ($end - $start) * 1000 . " ms";
    复制代码
    执行需要2.59ms

    第二种直接用扩展函数


    $start = microtime(true);

    $config = [
      'host' => '127.0.0.1',
      'port' => 11300
    ];

    $beanstalk_object = beanstalk_connect($config['host'], $config['port']);
    $last_job_id = beanstalk_putInTube($beanstalk_object, 'testtube', date("Y-m-d H:i:s") . "job payload goes here\n");

    $end = microtime(true);

    echo ($end - $start) * 1000 . " ms";
    复制代码
    执行需要0.34ms

    不得不说,扩展就是扩展,就是快的多啊!

    我另外测试了一下投递极限

    循环投递10000次消息,大概在500ms左右

    复制代码
    $start = microtime( true );
    for ($i=0; $i < 10000; $i++) { 
      $pheanstalk
      ->useTube('testtube')
      ->put(date("Y-m-d H:i:s") . "job payload goes here\n");
    }
    $end = microtime( true );
    echo ($end - $start) * 1000 . " ms";


     
    也就意味着1秒钟只能投递20000条消息,这比rabbitmq的投递慢多了(参见这篇文章)

    但是它执行1次投递消息的时候却比这个rabbitmq的代码执行的快,原因是我测试了一下这个rabbitmq的连接上就耗费了9ms

    $connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');

    更别提还有这两步了

    $channel = $connection->channel();
    $channel->queue_declare('hello', false, false, false, false);
    这样来看,想迅速投递一条短消息,或者建立小信息量的job用beanstalk是很不错的,如果有大量消息集中投递,那使用 rabbitmq 是很不错的。

    另外这个beanstalk投递可以延时,非常适合有些时候需要在当前时间一段时间后执行某个任务,往后弄个类似于一次性的定时器的功能,这个东西值得尝试。

    而且如果使用while死循环将 output.php 脚本一直挂着跑,它就能一直消费消息了,这样就等于有个后端进程一直能帮我们做消费者处理堆积的任务了,特殊场景可以考虑一下这个方案。

    Windows 安装 Beanstalkd

    下载并安装 cygwin

    1. 添加镜像地址 - http://mirrors.aliyun.com
    2. 下载 `gcc`、`gcc-core`、`make`、`automake`, 在 Devel 分支下

    下载 beanstalkd-win 包

    1. 解压并进入beanstalkd-win目录
    2. 打开CMD窗口,运行 ./beanstalkd.exe -l 127.0.0.1 -p 11300
     

  • 相关阅读:
    CorelDRAW2023安装下载教程精简版矢量绘图软件
    2696. Minimum String Length Afte
    cesium for ue左下角图标隐藏
    详解Qt中的鼠标事件
    [Firefox/快捷键] 禁用Ctrl-W快捷键
    重定向Kubernetes pod中的tcpdump输出
    如何基于 Apache Doris 构建新一代日志分析平台
    mabatis2:简单启动mybatis
    Leetcode P620 DFS解法,思路简单易懂
    线上系统性能太差,我手写了字符串切割函数,性能提升10倍以上
  • 原文地址:https://blog.csdn.net/happyzhlb/article/details/134510121