rabbitmq

一、安装

linux(centos7)
    yum install erlang
    yum install rabbitmq-server* 

    可参考:https://www.cnblogs.com/web424/p/6761153.html
linux
问题1:
Failed to start LSB: Enable AMQP service provided by RabbitMQ broker.

# vi /etc/rabbitmq/rabbitmq-env.conf    
NODENAME=rabbit@localhost  
搞定!
(参考:http://blog.csdn.net/testcs_dn/article/details/52514199)
linux安装问题
    windows
    安装erlang,官网http://www.erlang.org/downloads
    安装RabbitMQ,官网http://www.rabbitmq.com/download.html


    可参考:https://www.cnblogs.com/ericli-ericli/p/5902270.html
windows
问题1:
C:\Program Files\RabbitMQ Server\rabbitmq_server-3.7.4\sbin>rabbitmqctl.bat status
Status of node rabbit@fat39 ...
Error: unable to perform an operation on node 'rabbit@fat39'. Please see diagnostics information and suggestions below.

Most common reasons for this are:

 * Target node is unreachable (e.g. due to hostname resolution, TCP connection or firewall issues)
 * CLI tool fails to authenticate with the server (e.g. due to CLI tool's Erlang cookie not matching that of the server)
 * Target node is not running

In addition to the diagnostics info below:

 * See the CLI, clustering and networking guides on http://rabbitmq.com/documentation.html to learn more
 * Consult server logs on node rabbit@fat39

DIAGNOSTICS
===========

attempted to contact: [rabbit@fat39]

rabbit@fat39:
  * connected to epmd (port 4369) on fat39
  * epmd reports node 'rabbit' uses port 25672 for inter-node and CLI tool traffic
  * TCP connection succeeded but Erlang distribution failed

  * Authentication failed (rejected by the remote node), please check the Erlang cookie


Current node details:
 * node name: rabbitmqcli84@fat39
 * effective user's home directory: C:\Users\fat39
 * Erlang cookie hash: E6ohUpM/NQ9szEKtdnLQnQ==





用搜索工具对电脑文件系统进行检索,找到C:\Windows\System32\config\systemprofile下有个.erlang.cookie文件,内容与C:\User\lujie\.erlang.cookie不一样,后来修改其中一个文件的内容,使两个文件内容一样。 再次执行命令rabbitmqctl status,成功
windows安装问题

 补充:linux测试有效参考:https://blog.csdn.net/lilin0800/article/details/80690752

 二、使用

0、使用前准备

linux
# 新增用户
sudo rabbitmqctl add_user admin admin123
# 授权
sudo rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"

windows
# 新增用户
C:\Program Files\RabbitMQ Server\rabbitmq_server-3.7.4\sbin\rabbitmqctl.bat add_user admin admin123
# 授权
C:\Program Files\RabbitMQ Server\rabbitmq_server-3.7.4\sbin\rabbitmqctl.bat set_permissions -p / admin ".*" ".*" ".*"
创建用户及授权
rabbitmqctl.bat add_user admin admin123  # 创建账号密码
rabbitmqctl.bat list_users  # 查看账号
rabbitmqctl.bat set_permissions admin .* .* .*  # 分发权限
rabbitmqctl.bat list_permissions  # 查看权限
rabbitmqctl.bat list_queues  # 查看队列、数量

./rabbitmqctl add_user admin admin     
./rabbitmqctl set_user_tags admin administrator  # 管理员

 

 

参数说明:

1、队列持久化durable
    说明:
        该queue保存在rabbitmq数据库中,若配置了True,则在rabbitmq重启后该queue还在;若False,则丢失。
        此配置与消息持久化delivery_mode配合使用
    配置:
        在producer端:
        channel.queue_declare(queue='myqueue',durable=True)



2、消息持久化delivery_mode
    说明:
        队列持久化的前提下配置消息持久化delivery_mode=2,则消息不会丢失;否则丢失。
        rabbitmq重启后的集中情况:
            队列不持久,消息不持久:均丢失;
            队列持久、消息持久:均不丢失;
            队列持久、消息不持久:队列不丢失、消息丢失;
            
    配置:
        在producer端:
        channel.basic_publish(exchange='',  # exchange与订阅有关
                              routing_key='myqueue',  # 发往的队列
                              body=msg,  # 消息内容
                              properties=pika.BasicProperties(
                                  delivery_mode=2,  # 消息持久化
                              ),
                              )

3、公平分发
    说明:
        默认消息队列里的数据是按照顺序被消费者拿走,例如:消费者1 去队列中获取 奇数 序列的任务,消费者2去队列中获取 偶数 序列的任务。
        channel.basic_qos(prefetch_count=1) 表示谁来谁取,不再按照奇偶数排列
    配置:
        在consumer端:
        channel.basic_qos(prefetch_count=1)  # 公平分发

4、消息确认basic_ack、no_ack
    说明:
        未确认:
            如果consumer从queue中提取消息的途中出错,导致消息没被处理,但消息已从producer取走,消息丢失。
        配置确认:
            consumer在处理callback函数的最后发出ack,告诉producer已处理完毕。如果处理消息途中出错,producer未收到ack,则producer重新把消息放回queue。
            
        no-ack = False,如果消费者遇到情况(its channel is closed, connection is closed, or TCP connection is lost)挂掉了,那么,RabbitMQ会重新将该任务添加到队列中。

    配置:
        在consumer端:
        def mycallback(ch, method, properties, body):
            print(" [x] Received %r" % body)
            time.sleep(6)
            print('msg handle done...', body)
            ch.basic_ack(delivery_tag=method.delivery_tag)  # 消息接收后,给rabbitmq返回确认消息

        channel.basic_consume(
            consumer_callback=mycallback, #取到消息后,调用callback 函数
            queue="myqueue",  # 目标队列
            # no_ack=True,  # 不返回确认消息
        )

 

1、简单使用

配置流程

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(
               'localhost'))
channel = connection.channel()

channel.queue_declare(queue='hello1',durable=True)  # durable队列持久化

channel.basic_publish(exchange='',
                      routing_key='hello1',
                      body='Hello World!',
                      properties=pika.BasicProperties(
                          delivery_mode=2,  # 消息持久化
                      ),
                      )
print(" [x] Sent 'Hello World!'")
connection.close()
producer
__author__ = 'Administrator'


import pika


# credentials = pika.PlainCredentials('admin', 'admin123')
#
# parameters = pika.ConnectionParameters(host='localhost',credentials=credentials)
# connection = pika.BlockingConnection(parameters)
#
# channel = connection.channel() #队列连接通道

connection = pika.BlockingConnection(pika.ConnectionParameters(
               'localhost'))
channel = connection.channel()

channel.basic_qos(prefetch_count=1)  # 公平分发

def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)
    #time.sleep(15)
    print('msg handle done...',body)
    ch.basic_ack(delivery_tag=method.delivery_tag)

channel.basic_consume(callback, #取到消息后,调用callback 函数
                      queue='hello1',)
                      #no_ack=True) #消息处理后,不向rabbit-server确认消息已消费完毕

print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming() #阻塞模式
consumer

 

2、订阅

之前的例子都基本都是1对1的消息发送和接收,即消息只能发送到指定的queue里,但有些时候你想让你的消息被所有的Queue收到,类似广播的效果,这时候就要用到exchange了,

Exchange在定义的时候是有类型的,以决定到底是哪些Queue符合条件,可以接收消息

fanout: 所有bind到此exchange的queue都可以接收消息
direct: 通过routingKey和exchange决定的那个唯一的queue可以接收消息
topic:所有符合routingKey(此时可以是一个表达式)的routingKey所bind的queue可以接收消息

   表达式符号说明:#代表一个或多个字符,*代表任何字符
      例:#.a会匹配a.a,aa.a,aaa.a等
          *.a会匹配a.a,b.a,c.a等
     注:使用RoutingKey为#,Exchange Type为topic的时候相当于使用fanout 

headers: 通过headers 来决定把消息发给哪些queue

 

(1)fanout模式

配置流程:

# -*- coding:utf-8 -*-
import sys
import pika
# 认证消息
credentials = pika.PlainCredentials('admin', 'admin123')
# 连接的参数,ip,端口,认证
parameters = pika.ConnectionParameters(host='localhost',credentials=credentials)
# 连接rabbitmq
connection = pika.BlockingConnection(parameters)
# 向rabbitmq声明通道,即创建通道
channel = connection.channel()
# 绑定通道和队列
channel.exchange_declare(exchange="myexchange",exchange_type="fanout")

msg = "...."
msg = ' '.join(sys.argv[1:]) or "info: Hello World!"
channel.basic_publish(
    exchange="myexchange",
    routing_key='',
    body=msg,
)

connection.close()
producer
# -*- coding:utf-8 -*-

import pika
# 认证消息
credentials = pika.PlainCredentials('admin', 'admin123')
# 连接的参数,ip,端口,认证
parameters = pika.ConnectionParameters(host='localhost',credentials=credentials)
# 连接rabbitmq
connection = pika.BlockingConnection(parameters)
# 向rabbitmq声明通道,即创建通道
channel = connection.channel()
# 绑定通道和频道
channel.exchange_declare(exchange="myexchange",exchange_type="fanout")  # 订阅频道为myexchange的消息,模式是广播fanout

# 自动生成一个唯一的队列
queue_obj = channel.queue_declare(exclusive=True) #不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
queue_name = queue_obj.method.queue
channel.queue_bind(queue=queue_name,exchange="myexchange")  # 队列和频道绑定在一块


print(' [*] Waiting for myexchange. To exit press CTRL+C')

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

channel.basic_consume(
    consumer_callback=mycallback,
    queue=queue_name,
    no_ack=True,
)

channel.start_consuming()
consumer

 

(2)direct模式

RabbitMQ还支持根据关键字发送,即:队列绑定关键字,发送者将数据根据关键字发送到消息exchange,exchange根据 关键字 判定应该将数据发送至指定队列。

配置流程:

__author__ = 'Administrator'
import pika
import sys

credentials = pika.PlainCredentials('admin', 'admin123')

parameters = pika.ConnectionParameters(host='localhost',credentials=credentials)
connection = pika.BlockingConnection(parameters)

channel = connection.channel() #队列连接通道

channel.exchange_declare(exchange='direct_log',exchange_type='direct')

log_level =  sys.argv[1] if len(sys.argv) > 1 else 'info'

message = ' '.join(sys.argv[1:]) or "info: Hello World!"

channel.basic_publish(exchange='direct_log',
                      routing_key=log_level,
                      body=message)
print(" [x] Sent %r" % message)
connection.close()
producer
__author__ = 'Administrator'
import pika,sys
credentials = pika.PlainCredentials('admin', 'admin123')

parameters = pika.ConnectionParameters(host='localhost',credentials=credentials)
connection = pika.BlockingConnection(parameters)

channel = connection.channel() #队列连接通道

queue_obj = channel.queue_declare(exclusive=True) #不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
queue_name = queue_obj.method.queue
print('queue name',queue_name,queue_obj)

log_levels = sys.argv[1:] # info warning errr

if not log_levels:
    sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
    sys.exit(1)


for level in log_levels:
    channel.queue_bind(exchange='direct_log',
                       queue=queue_name,
                       routing_key=level) #绑定队列到Exchange

print(' [*] Waiting for logs. To exit press CTRL+C')

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

channel.basic_consume(callback,queue=queue_name, no_ack=True)

channel.start_consuming()
consumer

consumer1:python consumer-4-direct.py info error

consuumer2: python consumer-4-direct.py info alert

producer:

  1、python  producer-4-direct.py info information

  2、python  producer-4-direct.py error errorinformation

  3、python  producer-4-direct.py alert alertinformat

c1:

c2:

p:

 

(3)topic模式

 

配置流程:

__author__ = 'Administrator'
import pika
import sys

credentials = pika.PlainCredentials('admin', 'admin123')

parameters = pika.ConnectionParameters(host='localhost',credentials=credentials)
connection = pika.BlockingConnection(parameters)

channel = connection.channel() #队列连接通道

channel.exchange_declare(exchange='topic_log',exchange_type='topic')

#log_level =  sys.argv[1] if len(sys.argv) > 1 else 'info'
log_level =  sys.argv[1] if len(sys.argv) > 1 else 'all.info'

message = ' '.join(sys.argv[1:]) or "all.info: Hello World!"

channel.basic_publish(exchange='topic_log',
                      routing_key=log_level,
                      body=message)
print(" [x] Sent %r" % message)
connection.close()
producer
__author__ = 'Administrator'
import pika,sys
credentials = pika.PlainCredentials('admin', 'admin123')

parameters = pika.ConnectionParameters(host='localhost',credentials=credentials)
connection = pika.BlockingConnection(parameters)

channel = connection.channel() #队列连接通道

queue_obj = channel.queue_declare(exclusive=True) #不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
queue_name = queue_obj.method.queue


log_levels = sys.argv[1:] # info warning errr

if not log_levels:
    sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
    sys.exit(1)


for level in log_levels:
    channel.queue_bind(exchange='topic_log',
                       queue=queue_name,
                       routing_key=level) #绑定队列到Exchange

print(' [*] Waiting for logs. To exit press CTRL+C')

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

channel.basic_consume(callback,queue=queue_name, no_ack=True)

channel.start_consuming()
consumer
接收所有logs
"#"
python receive_logs_topic.py "#"

接收以xx开头、xx中间、xx结尾
以"*"为占位符
python receive_logs_topic.py "kern.*"
python receive_logs_topic.py "kern.*" "abc.*.critical"
python receive_logs_topic.py "kern.*" "*.critical"
参数说明

 

c1:侦听 *.abc.*  info.*  err.*

c2:侦听 abc.*

p:

 

 

3、RPC模式

clinet和server需要对话时,使用rpc模式。

 

__author__ = 'Administrator'

# 1.声明一个队列,作为reply_to返回消息结果的队列
# 2.  发消息到队列,消息里带一个唯一标识符uid,reply_to
# 3.  监听reply_to 的队列,直到有结果
import queue

import pika
import uuid

class CMDRpcClient(object):
    def __init__(self):
        credentials = pika.PlainCredentials('admin', 'admin123')
        parameters = pika.ConnectionParameters(host='localhost', credentials=credentials)
        self.connection = pika.BlockingConnection(parameters)
        self.channel = self.connection.channel()

        queue_obj = self.channel.queue_declare(exclusive=True)
        self.callback_queue = queue_obj.method.queue #命令的执行结果的queue

        #声明要监听callback_queue
        self.channel.basic_consume(self.on_response, no_ack=True,
                                   queue=self.callback_queue)

    def on_response(self, ch, method, props, body):
        """
        收到服务器端命令结果后执行这个函数
        :param ch:
        :param method:
        :param props:
        :param body:
        :return:
        """
        if self.corr_id == props.correlation_id:
            self.response = body.decode("gbk") #把执行结果赋值给Response

    def call(self, n):
        self.response = None
        self.corr_id = str(uuid.uuid4()) #唯一标识符号
        self.channel.basic_publish(exchange='',
                                   routing_key='rpc_queue2',
                                   properties=pika.BasicProperties(
                                         reply_to = self.callback_queue,
                                         correlation_id = self.corr_id,
                                         ),
                                   body=str(n))


        while self.response is None:
            self.connection.process_data_events()  #检测监听的队列里有没有新消息,如果有,收,如果没有,返回None
            #检测有没有要发送的新指令
        return self.response

cmd_rpc = CMDRpcClient()

print(" [x] Requesting fib(30)")
response = cmd_rpc.call('ipconfig')

print(response)
client
__author__ = 'Administrator'

#1 。 定义fib函数
#2. 声明接收指令的队列名rpc_queue
#3. 开始监听队列,收到消息后 调用fib函数
#4 把fib执行结果,发送回客户端指定的reply_to 队列
import subprocess
import pika
import time
credentials = pika.PlainCredentials('admin', 'admin123')

parameters = pika.ConnectionParameters(host='localhost',credentials=credentials)
connection = pika.BlockingConnection(parameters)

channel = connection.channel() #队列连接通道

channel.queue_declare(queue='rpc_queue2')

def fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)


def run_cmd(cmd):
    cmd_obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    result = cmd_obj.stdout.read() + cmd_obj.stderr.read()

    return result


def on_request(ch, method, props, body):
    cmd = body.decode("utf-8")

    print(" [.] run (%s)" % cmd)
    response = run_cmd(cmd)

    ch.basic_publish(exchange='',
                     routing_key=props.reply_to, #队列
                     properties=pika.BasicProperties(correlation_id = \
                                                         props.correlation_id),
                     body=response)

    ch.basic_ack(delivery_tag = method.delivery_tag)

channel.basic_consume(on_request, queue='rpc_queue2')

print(" [x] Awaiting RPC requests")
channel.start_consuming()
server

 

posted @ 2018-03-16 22:26  fat39  阅读(376)  评论(0编辑  收藏  举报