• RabbitMQ使用介绍3—Publish Subscribe


    在之前的课程中,默认是一个任务只交付给一个消费者进行处理,在本案例中,一个任务会交给多个消费者去处理(即publish/subscribe模式)

    在这一项中,我们创建一个工作队列,用于在多个工作者之间分配耗时的任务。

    为了说明这个模式,我们将构建一个简单的日志记录系统。它将由两个程序组成——第一个将发送日志消息,第二个将接收并打印它们

    在我们的日志系统中,接收程序的每一个运行副本都会得到消息。这样,我们就可以运行两个接收器,一个将日志引导到磁盘;另一个在屏幕上打印日志

    Exchanges

    上一个案例中,我们使用Queue完成了消息的发送和接收,现在我们来介绍一下Rabbit的消息模型,首先再解释一下几个概念

    1. 生产者(producer):发送消息的应用
    2. 队列:消息的缓冲存储区
    3. 消费者(consumer):接收消息的用户应用

    RabbitMQ的核心思想是,一条消息不会直接从生产者到队列,通常情况下,生产者甚至不知道消息有没有被传递到任何队列中

    生产者会首先将消息交付给交换器(exchanges)。交换器做的?是传给任意一个还是传递给多个或者是抛弃...根据策略不同有不同做法

    下图X为交换器(Exchanges)

    RabbitMQ提供的有四种策略:

    1. fanout:所有bind到此exchange的queue都可以接收消息
    2. direct:通过routingKey和exchcange决定的有条件的组播
    3. topic:所有符合routingKey(此时可以是一个表达式)的routing所bind的queue可以接收消息
    4. headers:通过headers来决定把消息发给哪些queue
    让我们讲解fanout,创建exchange的类型是fanout,叫logs:
    channel.exchange_declare(exchange='logs',
                             exchange_type='fanout')
    

    fanout exchange是非常简单的,从名字可看出它只是将接收到的所有消息广播到它知道的所有队列中。这正是我们所需要的

    列出服务器上的交换,可以运行有用的rabbitmqctl:

        sudo rabbitmqctl list_exchanges
    

    默认exchange

    在以前的部分我们知道是没有exchanges,但是仍然可以发送消息给队列,这可能是我们使用了默认exchange,我们使用空字符串""

    回想我们之前是如何发布消息:

    channel.basic_publish(exchange='',
                          routing_key='task_queue',
                          body=message,
                          properties=pika.BasicProperties(delivery_mode=2,#make message persistent
                                                          ))
    

    exchange参数是交换的名称,空字符串表示默认或无名称交换:消息将以routing_key指定的名称路由到队列(如果存在)

    现在我们可以发布到我们的exchange

    
    channel.basic_publish(exchange='logs',
                          routing_key='',#不能删除
                          body=message,
                          properties=pika.BasicProperties(delivery_mode=2,#make message persistent
                                                          ))
    

    临时队列

    首先,无论何时连接到Rabbit,我们都需要一个全新的空队列。 为此,我们可以创建一个具有随机名称的队列,或者甚至更好-让服务器为我们选择一个随机队列名称。 我们可以通过为queue_declare提供空的队列参数来做到这一点:

    result = channel.queue_declare(queue='')
    
    

    首先queue_name = result.method.queue 是获取queue名字,他可能是像这种随机值:amq.gen-JzTY20BRgKO-HjmUJj0wLg.

    第二只要消费者断开,队列应该就会被删除,可以用exclusive设置:

    result = channel.queue_declare(queue='',exclusive=True)
    

    Bindings

    我们已经创建fanout exchange和队列,现在我们需要告诉exchange发送消息给我们的队列,exchange和queue两者之间的关系叫做binding

    channel.queue_bind(exchange='logs',queue=queue_name)
    
    

    查看绑定

    rabbitmqctl list_bindings
    
    Puting it all together

    fanout_send.py

    # -*- coding: utf-8 -*-
    #send端
    import pika
    import sys
    connection = pika.BlockingConnection(pika.ConnectionParameters(
        'localhost'))#默认端口5672,可不写
    
    #创建通道,声明一个管道,在管道里发送消息
    channel = connection.channel()
    #在管道里声明queue
    channel.exchange_declare(exchange='logs',
                             exchange_type='fanout')
    message = ''.join(sys.argv[1:]) or "Hello World"
    #一条消息永远不能直接发送到队列,它总需要经过一个交换exchange
    channel.basic_publish(exchange='logs',
                          routing_key='',#不能删除
                          body=message,
                          properties=pika.BasicProperties(delivery_mode=2,#make message persistent
                                                          ))#设置routing_key(消息队列的名称)和body(发送的内容)
    print("[x] Sent %r" % message)
    connection.close()#关闭连接,队列关闭
    

    fanout_recive.py

    打开两个终端的消费者结果都是一样的,队列是不一样的

    # -*- coding: utf-8 -*-
    #receiving(消费者接收者)
    import pika
    import time
    #创建一个连接
    connection = pika.BlockingConnection(
        pika.ConnectionParameters('localhost'))#默认端口5672,可不写
    #创建通道,声明一个管道,在管道里发送消息
    channel = connection.channel()
    
    #把消息队列的名字为hello,把消费者和queue绑定起来,生产者和queue的也是hello
    #为什么又声明了一个hello队列
    #如果确定已经声明了,可以不声明。但是你不知道那个机器先运行,所以要声明两次
    channel.exchange_declare(exchange='logs',
                             exchange_type='fanout')
    #不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
    result = channel.queue_declare(queue='',exclusive=True)
    queue_name = result.method.queue
    print(queue_name)
    channel.queue_bind(exchange='logs',queue=queue_name)
    #回调函数get消息体
    def callback(ch,method,properties,body):#四个参数为标准格式
        #管道内存对象,内容相关信息
        #print("打印看下是什么:",ch,method,properties) #打印看下是什么
        print(" [x] Received %r" % body)
        time.sleep(body.count(b'.'))
        print("[x] Done")
        ch.basic_ack(delivery_tag=method.delivery_tag)#消息确认
    
    #消费消息
    channel.basic_consume(
        queue=queue_name,#你要从那个队列里收消息
        on_message_callback=callback,#如果收到消息,就调用callback函数来处理消息
        auto_ack=True #写的话,如果接收消息,机器宕机消息就丢了
        #一般不写,宕机则生产者检测到发给其他消费者
    )
    print(' [*] Waiting for messages. To exit press CTRL+C')
    channel.start_consuming() #创建死循环,监听消息队列,可使用CTRL+C结束监听
    

    link

    link

  • 相关阅读:
    JDK内置工具使用
    awk 数组
    c++面试题
    C++内存分配
    awk 数字比较
    awk脚本 排序
    awk 读取 shell 变量的方法
    NVelocity系列:Getting Start With NVelocity
    Castle.ActiveRecord分页示例
    NVelocity系列:NVelocity配置详解
  • 原文地址:https://www.cnblogs.com/venvive/p/11762452.html
Copyright © 2020-2023  润新知