Rabbit MQ

    RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。
    RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。
    所有主要的编程语言均有与代理接口通讯的客户端库。
    1.安装
        1、下载erlang和rabbitmq-server的rpm
            http://www.rabbitmq.com/releases/erlang/erlang-19.0.4-1.el7.centos.x86_64.rpm
            http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.6/rabbitmq-server-3.6.6-1.el7.noarch.rpm
        2、安装erlang
            [root@rabbitmq ~]# cd /server/scripts/
            [root@rabbitmq scripts]# ll
            total 23508
            -rw-r--r--. 1 root root 18580960 Jan 28 10:04 erlang-19.0.4-1.el7.centos.x86_64.rpm
            -rw-r--r--. 1 root root 5487706 Jan 28 10:04 rabbitmq-server-3.6.6-1.el7.noarch.rpm
            [root@rabbitmq scripts]# rpm -ivh erlang-19.0.4-1.el7.centos.x86_64.rpm
            测试erlang是否安装成功:
                [root@rabbitmq scripts]# erl
                Erlang/OTP 19 [erts-8.0.3] [source] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
                Eshell V8.0.3 (abort with ^G)
                1> 5+6.
                11
                2> halt().  #退出
        3、安装socat (备注:安装RabbitMQ必须先安装socat依赖,否则会报错)
            [root@rabbitmq scripts]# yum install socat
        4、安装RabbitMQ
            [root@rabbitmq scripts]# rpm -ivh rabbitmq-server-3.6.6-1.el7.noarch.rpm
            启动和关闭:
            /sbin/service rabbitmq-server start #启动服务
            /sbin/service rabbitmq-server stop #关闭服务
            /sbin/service rabbitmq-server status #查看服务状态
        5、cd 到/sbin目录下:
            ./rabbitmq-plugins list
            ./rabbitmqctl status

    2.用户
        # 创建用户
        [root@localhost sbin]# rabbitmqctl add_user admin admin                # 创建 用户admin   密码admin
        Creating user "admin" ... 
        ...done. 
        [root@localhost sbin]# rabbitmqctl set_user_tags admin administraotr   # 为用户分配角色
        Setting tags for user "admin" to [administraotr] ... 
        [root@localhost sbin]# rabbitmqctl list_users                          # 查看用户列表     
        Listing users ... 
        admin   [administraotr] 
        guest   [administrator] 
        # 删除用户命令
        rabbitmqctl  delete_user  Username
        # 修改用户的密码命令
        rabbitmqctl  change_password  Username  Newpassword
    3.简单示例
        发送端:
        import pika

        auth = pika.PlainCredentials('admin', 'admin')
        connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.145.144',credentials=auth))
        channel = connection.channel()

        channel.queue_declare(queue="hello")

        channel.basic_publish(exchange='',
                              routing_key='hello',
                              body='Hello word!',)
        print(" [x] Sent 'Hello word!'")
        connection.close()
        接收端:
        import pika
        credentials = pika.PlainCredentials('admin',"admin")
        connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.145.144',credentials=credentials))
        channel = connection.channel()

        channel.queue_declare(queue='hello')

        def my_callback(ch,method,properties,body):
            print(" [x] Rev %r" % body)

        channel.basic_consume(queue='hello',
                              on_message_callback=my_callback,
        #                     auto_ack=True           # 自动应答,应答后队列中的消息消失
        )
        print(" [*]")
        channel.start_consuming()
        
        # 当有多个消费者使用同一个消息队列(queue)时,生产者生产的消息是公平的依次分发
    4.不同的消费者对消息的处理能力不同,假设A一秒处理十个消息,B十秒处理一个消息,按照消息的依次分发,消息会在B处堆积
            channel.basic_qos(prefetch_count=1),#预载数,B的消息未处理完,不会再给它分发消息
    5.消息持久化
            生产者生产完消息就宕机了,而且此时也没有消费者消费消息,此时如有需要,就可以使队列及队列中的消息持久化,
            在生产者恢复后依然存在
                channel.queue_declare(queue='hello2', durable=True)   # 队列持久化 在生产者和消费者中都要声明   
                                                                                                                        #durable:耐用的、持久的
                properties=pika.BasicProperties(
                    delivery_mode=2,                                                                    # 消息持久化
                  )
     6.消息订阅
         消息的发送顺序   发送端 -> exchange(交换机) -> queue(队列)-> 接收端
         交换机分配消息可以有不同的类型
             fanout: 所有绑定到此exchange的queue都可以接收消息
             direct: 通过routingKey和exchange决定的那个唯一的queue可以接收消息
             topic: 所有符合routingKey(此时可以是一个表达式)的routingKey所bind的queue可以接收消息
         fanout类型:  # 你会收到每个主播的消息
            发送端:
                import pika
                auth = pika.PlainCredentials('admin', 'admin')
                connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.145.144',credentials=auth))
                channel = connection.channel()

                # channel.queue_declare(queue="hello")
                channel.exchange_declare(exchange="logs",
                                         exchange_type="fanout",)

                channel.basic_publish(exchange='logs',
                                      # routing_key='hello',
                                      routing_key='',
                                      body='Hello word!',
                                      # properties=pika.BasicProperties(delivery_mode=True),
                                      )
                print(" [x] Sent 'Hello word!'")
                connection.close()
            接收端:
                import pika
                credentials = pika.PlainCredentials('admin',"admin")
                connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.145.144',credentials=credentials))
                channel = connection.channel()

                # channel.queue_declare(queue='hello')
                channel.exchange_declare(exchange="logs",
                                         exchange_type="fanout",
                                         )
                # 获取queue的名字
                result = channel.queue_declare(queue='',exclusive=True)
                queue_name = result.method.queue
                # 将队列和exchange绑定
                channel.queue_bind(
                    queue=queue_name,
                    exchange="logs",
                )

                def my_callback(ch,method,properties,body):
                    print(" [x] Rev %r" % body)

                channel.basic_consume(queue=queue_name,
                                      on_message_callback=my_callback,
                                      auto_ack=True
                )
                print(" [*]")
                channel.start_consuming()
         direct:               # 你可以指定收哪个主播的消息
            发送端:
                import pika
                import sys
                auth = pika.PlainCredentials('admin', 'admin')
                connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.145.144',credentials=auth))
                channel = connection.channel()

                # channel.queue_declare(queue="hello")
                channel.exchange_declare(exchange="direct_logs",
                                         exchange_type="direct",
                                         #exchange_type="fanout",
                                         )
                severity = sys.argv[1] if len(sys.argv[1]) > 1 else "info"
                message = 'direct_Hello word!'
                channel.basic_publish(exchange='direct_logs',
                                      # routing_key='hello',
                                      routing_key=severity,
                                      body=message,
                                      # properties=pika.BasicProperties(delivery_mode=2),
                                      )
                print(" [x] Sent 'Hello word!'")
                connection.close()

            接收端:
                import pika
                import sys
                credentials = pika.PlainCredentials('admin',"admin")
                channel = connection.channel()

                # channel.queue_declare(queue='hello')
                channel.exchange_declare(exchange="direct_logs",
                                         #exchange_type="fanout",
                                         exchange_type="direct",
                                         )
                # 获取queue的名字
                result = channel.queue_declare('',exclusive=True)
                queue_name = result.method.queue
                # 将队列和exchange绑定
                severities = sys.argv[1:]
                if not severities:
                    sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
                    sys.exit(1)
                for severity in severities:
                    channel.queue_bind(
                        queue=queue_name,
                        exchange="direct_logs",
                        routing_key = severity
                    )

                def my_callback(ch,method,properties,body):
                    print(" [x] Rev %r" % body)

                channel.basic_consume(queue=queue_name,
                                      on_message_callback=my_callback,
                                      auto_ack=True,
                )
                print(" [*]")
                channel.start_consuming()
         topic # 可以接收指定话题的消息   '#'可以接收所有
             发送端:    
                import pika
                import sys
                auth = pika.PlainCredentials('admin', 'admin')
                connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.145.144',credentials=auth))
                channel = connection.channel()

                # channel.queue_declare(queue="hello")
                channel.exchange_declare(exchange="topic_logs",
                                         exchange_type="topic",
                                         #exchange_type="fanout",
                                         )
                severity = sys.argv[1] if len(sys.argv) > 1 else "info"
                message = 'direct_Hello word!'
                channel.basic_publish(exchange='topic_logs',
                                      # routing_key='hello',
                                      routing_key=severity,
                                      body=message,
                                      # properties=pika.BasicProperties(delivery_mode=2),
                                      )
                print(" [x] Sent 'Hello word!'")
                connection.close()
             接收端:
                import pika
                import sys
                credentials = pika.PlainCredentials('admin',"admin")
                channel = connection.channel()

                # channel.queue_declare(queue='hello')
                channel.exchange_declare(exchange="topic_logs",
                                         #exchange_type="fanout",
                                         exchange_type="topic",
                                         )
                # 获取queue的名字
                result = channel.queue_declare('',exclusive=True)
                queue_name = result.method.queue
                # 将队列和exchange绑定
                severities = sys.argv[1:]
                if not severities:
                    sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
                    sys.exit(1)
                for severity in severities:
                    channel.queue_bind(
                        queue=queue_name,
                        exchange="topic_logs",
                        routing_key = severity
                    )

                def my_callback(ch,method,properties,body):
                    print(" [x] Rev %r" % body)

                channel.basic_consume(queue=queue_name,
                                      on_message_callback=my_callback,
                                      auto_ack=True,
                )
                print(" [*]")
                channel.start_consuming()
     7.RPC 实现(Remote procedure call 远程过程调用):双向交流
        客户端:
            import pika
            import sys
            import uuid

            class FibonacciRpcClient(object):
                def __init__(self):
                    self.auth = pika.PlainCredentials('admin', 'admin')
                    self.channel = self.connection.channel()
                    result = self.channel.queue_declare('',exclusive=True)
                    self.callback_queue = result.method.queue
                    self.channel.basic_consume(
                        queue=self.callback_queue,
                        on_message_callback=self.on_response,
                        auto_ack=True
                    )
                def on_response(self,ch,method,properties,body):
                    if self.corr_id == properties.correlation_id:
                        self.response = body
                def call(self,n):
                    self.response = None
                    self.corr_id = str(uuid.uuid4())
                    self.channel.basic_publish(
                        exchange='',
                        routing_key="rpc_queue",
                        body=str(n),
                        properties=pika.BasicProperties(
                            reply_to = self.callback_queue,
                            correlation_id=self.corr_id,
                        )
                    )
                    while not self.response:
                        self.connection.process_data_events()
                    return int(self.response)
            if __name__ == "__main__":
                fibonacci_rpc = FibonacciRpcClient()
                print(" [x] Requesting fib(7)")
                response = fibonacci_rpc.call(8)
                print(" [.] Got %r" % response)
        服务端:
            import pika
            import sys,time

            def fib(n):
            """计算指定位置的斐波那契数列"""
                if n==0:
                    return 0
                elif n == 1:
                    return 1
                else:
                    return fib(n-2) + fib(n-1)
                    
            def on_response(ch,method,properties,body):
                response = fib(int(body))
                ch.basic_publish(
                    exchange='',  # 把执行结果发回给客户端
                    routing_key = properties.reply_to,  # 客户端要求返回想用的queue
                    # 返回客户端发过来的correction_id 为了让客户端验证消息一致性
                    properties = pika.BasicProperties(correlation_id=properties.correlation_id),
                    body = str(response)
                )
                ch.basic_ack(delivery_tag = method.delivery_tag)
            if __name__ == "__main__":
                credentials = pika.PlainCredentials('admin',"admin")
                connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.145.144',credentials=credentials))
                channel = connection.channel()
                channel.queue_declare(queue='rpc_queue')

                channel.basic_consume(queue='rpc_queue',
                                      on_message_callback=on_response,
                                      auto_ack=True,
                )
                print(" [*]")
                channel.start_consuming()

 

posted @ 2020-06-13 17:56  pywhy  阅读(123)  评论(0编辑  收藏  举报