hyperf | 带你一起看 hyperf 文档之 amqp

date: 2019-07-17 22:42:21
title: hyperf| 带你一起看 hyperf 文档之 amqp

hyperf 开源有一段时间了, 从开发者交流群就能感受到热度. 这段时间下来, 有一个明显的现象, 某A 提了一个技术问题, 某B 直接抛一个官方文档的对应链接. 这种现象实在太常见, 甚至衍生出了 欢迎进入 vip 交流群 这样的商机. 不得不说:

花 2 个小时认真看一遍文档, 比遇到问题就卡住然后到处问要高效得多.

重要的事情说三遍:

  • 认真看一遍文档
  • 认真看一遍文档
  • 认真看一遍文档

好了, 回到正题, 今天我们来玩 amqp.

项目准备

这些都可以在文档中找到, 所以直接上操作.

  • 使用开发组提供的 docker

hold 不住 docker, 不用也行, 但是要能基于开发组的 Dockerfile 配置好环境, 如果既不会用 docker, 也无法自己配置好开发环境, 请一定要努力哦.

使用 docker-compose 配置的全部环境:

version: '3'
services:
    ms:
        image: hyperf/hyperf
        volumes:
            - ../:/data
        ports:
            - "9501:9501"
        environment:
            APP_ENV: dev
        tty: true
    mysql:
        image: mysql:5.7.26
        volumes:
            - ./config/my.cnf:/etc/mysql/conf.d/my.cnf
            - ./config/sql:/docker-entrypoint-initdb.d
            - ./data/mysql:/var/lib/mysql
        ports:
            - "3306:3306"
        environment:
            TZ: Asia/Shanghai
            MYSQL_ROOT_PASSWORD: root
    redis:
        image: redis:alpine
        volumes:
            - ./config/redis.conf:/etc/redis/redis.conf
            - ./data/redis:/data
        ports:
            - "6379:6379"
    rabbitmq:
        image: rabbitmq:management-alpine
        hostname: myrabbitmq
        volumes:
            - ./data/rabbitmq:/var/lib/rabbitmq/mnesia
        ports:
            - "5672:5672" # mq
            - "15672:15672" # admin

这里多说一句, docker / Dockerfile / docker-compose 只是满足开发环境的使用的话, 真的很简单, 记住几个常用的 docker 命令, 清楚 Dockerfile 几个常用的指令(RUN CMD 等), docker-compose 只是 yaml 格式的配置文件而已.

推荐一个好习惯: 一个文档专门记 docker / Dockerfile / docker-compose 的常用内容, 使用过程中逐渐对这个文件进行增删查改(CRUD), 不用多久, 你就会发现自己用起 docker 来, 贼 6 !

另一个好习惯是 最佳实践, 你要从无到有用起来很难, 但是跟着最佳实践走, 就能又快又好 ! 当然, 再上一层楼, 你也能成为最佳实践.

  • 安装项目
composer create-project hyperf/hyperf-skeleton hyperf-demo

安装过程选择自己需要的组件, 不清楚就先不要选, 反正之后可以通过 composer require 安装. 其实我更想说的是:

安装的组件自己 hold 不住, 然后到处叫, 这样多没意思呀.

  • 配置 composer.json
"repositories": {
    "hyperf": {
        "type": "path",
        "url": "../hyperf/src/*"
    },
    "packagist": {
        "type": "composer",
        "url": "https://mirrors.aliyun.com/composer"
    }
}

添加了 path, 从我本地加载 hyperf 组件, 方便开发, 如果不参与 hyperf 组件开发, 可以忽略这一步.

  • 添加 hyperf/amqp
# 安装
composer require hyperf/amqp 

# 添加配置文件
php bin/hyperf.php vendor:publish hyperf/amqp
  • 修改配置, 启动并验证

我启动了 mysql / redis / rabbitmq, 配置相关组件的配置, 并启动框架进行验证

# config
vim .env
vim config/autoload/redis.php
vim config/autoload/database.php
vim config/autoload/amqp.php

# test
php bin/hyperf.php start

好了, 项目准备好了, 正式开始撸代码.

官方文档 amqp demo

文档有的, 还是直接贴:

# producer
php bin/hyperf.php gen:amqp-producer DemoProducer

# consumer
php bin/hyperf.php gen:amqp-consumer DemoConsumer

# 使用 command 盗用 DemoProducer 进行验证
php bin/hyperf.php gen:command TestCommand

producer 发个消息:

  • 设置 command 的名字: parent::__construct('t');
  • 使用 @Inject() 注解注入
  • 发消息, 一行搞定: $this->producer->produce(new DemoProducer('test'. date('Y-m-d H:i:s')));
<?php

declare(strict_types=1);

namespace App\Command;

use App\Amqp\Producer\DemoProducer;
use Hyperf\Amqp\Producer;
use Hyperf\Command\Command as HyperfCommand;
use Hyperf\Command\Annotation\Command;
use Hyperf\Di\Annotation\Inject;
use Psr\Container\ContainerInterface;

/**
 * @Command
 */
class TestCommand extends HyperfCommand
{
    /**
     * @var ContainerInterface
     */
    protected $container;

    /**
     * @Inject()
     * @var Producer
     */
    protected $producer;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;

        parent::__construct('t');
    }

    public function configure()
    {
        $this->setDescription('Hyperf Demo Command');
    }

    public function handle()
    {
        $this->producer->produce(new DemoProducer('test'. date('Y-m-d H:i:s')));
    }
}

愉快的玩耍起来:

# produce
php bin/hyperf.php t

# consume
php bin/hyperf.php start # 会使用 swoole process 启动 DemoConsumer

# 也可以访问 rabbitmq admin 控制台
http://localhost:15672

撸一撸 rabbitmq 官网 tutorial

跟着 rabbitmq 官网 tutorial, 见识一下 hyperf 中的 amqp 有多简单

// consumer
/**
 * @Consumer()
 */
class DemoConsumer extends ConsumerMessage
{
    protected $exchange = 'hello';
    protected $type = Type::FANOUT;
    protected $queue = 'hello';

    public function consume($data): string
    {
        var_dump($data);
        return Result::ACK;
    }
}

// producer
/**
 * @Producer()
 */
class DemoProducer extends ProducerMessage
{
    protected $exchange = 'hello';
    protected $type = Type::FANOUT;
    protected $routingKey = 'hello';
    public function __construct($data)
    {
        $this->payload = $data;
    }
}

设置一下 nums 参数, 就可以多进程.

// Consumer
/**
 * @Consumer(nums=2)
 */
class DemoConsumer extends ConsumerMessage
{
    protected $exchange = 'task';
    protected $type = Type::FANOUT;
    protected $queue = 'task';

    public function consume($data): string
    {
        var_dump($data);
        return Result::ACK;
    }
}

// producer
/**
 * @Producer()
 */
class DemoProducer extends ProducerMessage
{
    protected $exchange = 'task';
    protected $type = Type::FANOUT;
    protected $routingKey = 'task';
    public function __construct($data)
    {
        $this->payload = $data;
    }
}

和上面的 hello world 一致

终于看到 routing_key 的作用了

// consumer
/**
 * @Consumer()
 */
class DemoConsumer extends ConsumerMessage
{
    protected $exchange = 'routing';
    protected $type = Type::DIRECT;
    // 这个 consumer 只消费 error 级别的日志
    protected $queue = 'routing.error';
    protected $routingKey = 'error';

    public function consume($data): string
    {
        var_dump($data);
        return Result::ACK;
    }
}

/**
 * @Consumer()
 */
class Demo2Consumer extends ConsumerMessage
{
    protected $exchange = 'routing';
    protected $type = Type::DIRECT;
    // 这个 consumer 消费所有级别的日志
    protected $queue = 'routing.all';
    protected $routingKey = [
        'info',
        'warning',
        'error',
    ];

    public function consume($data): string
    {
        var_dump($data);
        return Result::ACK;
    }
}

// producer
/**
 * @Producer()
 */
class DemoProducer extends ProducerMessage
{
    protected $exchange = 'routing';
    protected $type = Type::DIRECT;
    public function __construct($data, $routingKey)
    {
        $this->routingKey = $routingKey;
        $this->payload = $data;
    }
}

// produce
$this->producer->produce(new DemoProducer('info'. date('Y-m-d H:i:s'), 'info'));
$this->producer->produce(new DemoProducer('warning'. date('Y-m-d H:i:s'), 'warning'));
$this->producer->produce(new DemoProducer('error'. date('Y-m-d H:i:s'), 'error'));
var_dump('done');

和的, 和上面的 routing 差不多

// consume
/**
 * @Consumer()
 */
class DemoConsumer extends ConsumerMessage
{
    protected $exchange = 'topics';
    protected $type = Type::TOPIC;
    protected $queue = 'topics.t1';
    // protected $routingKey = '#'; // all
    // protected $routingKey = 'kern.*';
    // protected $routingKey = '*.critical';
    // protected $routingKey = 'kern.critical';
    protected $routingKey = [
        'kern.*',
        '*.critical',
    ];

    public function consume($data): string
    {
        var_dump($data);
        return Result::ACK;
    }
}

// produce
/**
 * @Producer()
 */
class DemoProducer extends ProducerMessage
{
    protected $exchange = 'topics';
    protected $type = Type::TOPIC;
    public function __construct($data, $routingKey)
    {
        $this->routingKey = $routingKey;
        $this->payload = $data;
    }
}

可以看到, 想要用 amqp, 自动生成好代码后, 改一改属性就成, so easy~

再聊 amqp

amqp 难不难用? 至少基础的使用还是很好掌握的, 下面有一张图可供参考

amqp 基础使用

producer consumer connection vhost channel exchange queue routing_key bind publish consume msg 几个概念了解了, 基础使用就能很顺手. 而在 hyperf 中, 得益于对一些常用使用的方式的封装, 自动生成代码 + 改改类属性 就能把 amqp 用起来 !

写在最后

无他, 唯手熟尔.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355