rabbit相关

一、消息队列

  1.1介绍

    就是基础数据结构中的“先进先出”的一种数据机构。

  1.2MQ解决问题

    MQ一直存在,解决微服务框架。两个服务(服务端和客户端)调用方法:restful(http协议),rpc(远程过程调用,一台机器调用另一台机器的函数或方法)  2.rpc:远程过程调用

	-gRPC:谷歌出的,跨语言
 3 不管用rpc或者restful来通信,涉及到同步,异步
 4 消息队列解决的问题
	-应用解耦
      -流量消峰
      -消息分发(发布订阅:观察者模式)
      -异步消息(celery就是对消息队列的封装)   
 5 rabbitmq,kafka
	-rabbitmq:吞吐量小,消息确认,订单,对消息可靠性有要求,就用它
      -kafka:吞吐量高,注重高吞吐量,不注重消息的可靠性,数据量特别大(一般用于大数据)
 6 电商、金融等对事务性要求很高的,可以考虑RabbitMQ
 7 日志考虑Kafka

二、Rabbitmq
  1、安装
1 原生安装
    -安装扩展epel源
    -yum -y install erlang
    -yum -y install rabbitmq-server
    -systemctl start rabbitmq-server

2 docker拉取
    -docker pull rabbitmq:management(自动开启了web管理界面)
    -docker run -di --name rabbitmq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 rabbitmq:management

  # 5672:是rabbitmq的默认端口 15672:web管理界面的端口

  2、创建用户再分配权限

4 创建用户
rabbitmqctl add_user aaa 123
5 分配权限 rabbitmqctl set_user_tags aaa administrator rabbitmqctl set_permissions -p "/" aaa ".*" ".*" ".*"
  3、基本使用
    生产者:
# 先安装包pika
# pip3 install pika

import pika
# 拿到连接对象
# connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166'))

# 有用户名密码的情况,拿连接对象用这个
credentials = pika.PlainCredentials("admin","admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166',credentials=credentials))

# 拿到channel对象
channel = connection.channel()

# 声明一个队列
channel.queue_declare(queue='hello')  # 指定队列名字

# 生产者向队列中放一条消息
channel.basic_publish(exchange='',
                      routing_key='hello', #就是队列名字
                      body='aaa js nb')
print(" Sent 'Hello World!'")

# 关闭连接
connection.close()
    消费者:
import pika, sys, os

def main():
    #无验证密码情况用这个
    # connection = pika.BlockingConnection(pika.ConnectionParameters(host='101.133.225.166'))

    #有验证密码用这个
    credentials = pika.PlainCredentials("admin", "admin")
    connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166', credentials=credentials))
    channel = connection.channel()

    channel.queue_declare(queue='hello')

    #获取消息队列
    def callback(ch, method, properties, body):
        print(" [x] Received %r" % body)

    channel.basic_consume(queue='hello', on_message_callback=callback, auto_ack=True)

    channel.start_consuming()

if __name__ == '__main__':

    main()

  

  4、消息确认机制(消费者变化,主要是加这条代码确认:ch.basic_ack(delivery_tag=method.delivery_tag))

    生产者:# 先安装包pika

# pip3 install pika

import pika
# 拿到连接对象
# connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166'))

# 有用户名密码的情况,拿到连接对象
credentials = pika.PlainCredentials("admin","admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166',credentials=credentials))

# 拿到channel对象
channel = connection.channel()

# 声明一个队列
channel.queue_declare(queue='aaa',,durable=True) # 指定队列名字,并指明持久化

# 生产者向队列中放一条消息
channel.basic_publish(exchange='',
              routing_key
='aaa',
              body
='aaa jssss nb',
              properties=pika.BasicProperties(
              delivery_mode=2, # 此时声明消息持久化)
              )
print(" aaa jssss nb'")
# 关闭连接
connection.close()

    消费者:

import pika, sys, os

def main():
    # connection = pika.BlockingConnection(pika.ConnectionParameters(host='101.133.225.166'))

    有密码的情况
    credentials = pika.PlainCredentials("admin", "admin")
    connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166', credentials=credentials))

    channel = connection.channel()
  channel.queue_declare(queue='aaa',durable=True)          #此时声明队列持久化
def callback(ch, method, properties, body): print(" Received %r" % body) 
  
# 真正的消息处理完了,再发确认
  ch.basic_ack(delivery_tag=method.delivery_tag)
  ## 不会自动回复确认消息,
  ## auto_ack=True,队列收到确认,就会自动把消费过的消息删除,但一般不用这个,而是ch.basic_ack(delivery_tag=method.delivery_tag)
  channel.basic_consume(queue='aaa', on_message_callback=callback, auto_ack=False)
  channel.start_consuming()

if __name__ == '__main__': main()

 

  4、持久化(消费者代码队列变,生产者代码消息改变)

#在声明队列时,指定持久化,消费者和生产者都需要修改
channel.queue_declare(queue='aaa',durable=True)  

# 声明消息持久化,只在生产者指定
#在发布消息的时候,
properties=pika.BasicProperties(
delivery_mode=2,  # make message persistent
)

 5、闲置消费
  
谁闲置谁获取,没必要按照顺序一个一个来 消费者配置 channel.basic_qos(prefetch_count=1) ,生产者代码不变
import pika, sys, os

def main():
    # connection = pika.BlockingConnection(pika.ConnectionParameters(host='101.133.225.166'))
    #有用户名
    credentials = pika.PlainCredentials("admin", "admin")
    connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166', credentials=credentials))
    channel = connection.channel()

    channel.queue_declare(queue='lqz')

    def callback(ch, method, properties, body):
        import time
        time.sleep(50)
        print(" [x] Received %r" % body)
        # 真正的消息处理完了,再发确认
        ch.basic_ack(delivery_tag=method.delivery_tag)
    ## 不会自动回复确认消息,
    ## auto_ack=True,队列收到确认,就会自动把消费过的消息删除

    channel.basic_qos(prefetch_count=1)  #####此时就是配置谁闲置谁获取,没必要按照顺序一个一个来

    channel.basic_consume(queue='lqz', on_message_callback=callback, auto_ack=False)

    channel.start_consuming()

if __name__ == '__main__':

    main()

 

  6、消息订阅
    发布者(生产者):
import pika

credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166', credentials=credentials))
channel = connection.channel()

# 声明队列没有指定名字,指定了exchange
channel.exchange_declare(exchange='logs', exchange_type='fanout')

message = "info: Hello World!"
channel.basic_publish(exchange='logs', routing_key='', body=message)
print(" [x] Sent %r" % message)
connection.close()

      订阅者(消费者):启动多次,会创建出多个队列,都绑定到了同一个exchange上

import pika

credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166', credentials=credentials))
channel = connection.channel()

channel.exchange_declare(exchange='logs', exchange_type='fanout')

#这是result的queue会自动生成
result = channel.queue_declare(queue='', exclusive=True)
queue_name = result.method.queue
print(queue_name)    #每个消费者的名字是不一样的

#将发布者的exchange绑定
channel.queue_bind(exchange='logs', queue=queue_name)

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

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

channel.basic_consume(
    queue=queue_name, on_message_callback=callback, auto_ack=True)

channel.start_consuming()
  6.1发布订阅之routing关键字
    发布者:
import pika

credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166', credentials=credentials))
channel = connection.channel()

# 声明队列没有指定名字,指定了exchange
channel.exchange_declare(exchange='aaa123', exchange_type='direct')

message = "info: asdfasdfasdfsadfasdf World!"

#此时指定了关键字routing_key,代表只有订阅者订阅了bnb才能接收到这个消息
channel.basic_publish(exchange='lqz123', routing_key='bnb', body=message)
print(" [x] Sent %r" % message)
connection.close()

      订阅者1:

import pika

credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166', credentials=credentials))
channel = connection.channel()

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

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

#此时代表订阅者订阅了routing_key为nb的消息频道
channel.queue_bind(exchange='aaa123', queue=queue_name,routing_key='nb')

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

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

channel.basic_consume(
    queue=queue_name, on_message_callback=callback, auto_ack=True)

channel.start_consuming()

      订阅者2:

import pika

credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166', credentials=credentials))
channel = connection.channel()

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

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

#此时订阅者监听了两个频道,routing_key,此时两个频道都能收到消息
channel.queue_bind(exchange='aaa123', queue=queue_name,routing_key='nb')
channel.queue_bind(exchange='aaa123', queue=queue_name,routing_key='bnb')

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

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

channel.basic_consume(
    queue=queue_name, on_message_callback=callback, auto_ack=True)

channel.start_consuming()

    6.2模糊匹配:   # 表示后面可以跟任意字符任意长度都能匹配     *表示后面只能跟一个英文单词

      发布者:

import pika


credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166', credentials=credentials))
channel = connection.channel()

# 声明队列没有指定名字,指定了exchange
channel.exchange_declare(exchange='m3', exchange_type='topic')

message = "info: asdfasdfasdfsadfasdf World!"
channel.basic_publish(exchange='m3', routing_key='aaa.ddxxx', body=message)
print(" [x] Sent %r" % message)
connection.close()

      订阅者1:

import pika

credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166', credentials=credentials))
channel = connection.channel()

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

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

channel.queue_bind(exchange='m3', queue=queue_name,routing_key='aaa.*')  #指定模糊匹配,此时不能收到

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

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

channel.basic_consume(
    queue=queue_name, on_message_callback=callback, auto_ack=True)

channel.start_consuming()

      订阅者2:

import pika

credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166', credentials=credentials))
channel = connection.channel()

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

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

channel.queue_bind(exchange='m3', queue=queue_name,routing_key='aaa.#')   #指定模糊匹配,此时可以收到
print(' [*] Waiting for logs. To exit press CTRL+C')

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

channel.basic_consume(
    queue=queue_name, on_message_callback=callback, auto_ack=True)

channel.start_consuming()

 

 7、三种方法实现rpc
   1、通过rabbitmq实现(例子:服务端的函数,客户端来调用)(不推荐)
      服务端:
import pika

credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166', credentials=credentials))
channel = connection.channel()


channel.queue_declare(queue='rpc_queue')

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

def on_request(ch, method, props, body):
    n = int(body)

    print(" [.] fib(%s)" % n)
    response = fib(n)

    ch.basic_publish(exchange='',
                     routing_key=props.reply_to,
                     properties=pika.BasicProperties(correlation_id = \
                                                         props.correlation_id),
                     body=str(response))
    ch.basic_ack(delivery_tag=method.delivery_tag)

channel.basic_qos(prefetch_count=1)
channel.basic_consume(queue='rpc_queue', on_message_callback=on_request)

print(" [x] Awaiting RPC requests")
channel.start_consuming()
        客户端:
import pika
import uuid

class FibonacciRpcClient(object):

    def __init__(self):

        self.credentials = pika.PlainCredentials("admin", "admin")
        self.connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166', credentials=self.credentials))
        self.channel = self.connection.channel()

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

        self.channel.basic_consume(
            queue=self.callback_queue,
            on_message_callback=self.on_response,     #调用函数on_response
            auto_ack=True)

    def on_response(self, ch, method, props, body):
        if self.corr_id == props.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',
            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()
        return int(self.response)


fibonacci_rpc = FibonacciRpcClient()      #此时实例化一个对象,就会执行init方法

print(" [x] Requesting fib(30)")
response = fibonacci_rpc.call(10)  # 外界看上去,就像调用本地的call()函数一样
print(" [.] Got %r" % response)

    

    2、SimpleXMLRPCServer实现rpc(不推荐,比较慢,走XML协议)

      服务端:

from xmlrpc.server import SimpleXMLRPCServer
class RPCServer(object):

    def __init__(self):
        super(RPCServer, self).__init__()
        print(self)
        self.send_data = {'server:'+str(i): i for i in range(100)}
        self.recv_data = None

    def getObj(self):
        print('get data')
        return self.send_data

    def sendObj(self, data):
        print('send data')
        self.recv_data = data
        print(self.recv_data)
# SimpleXMLRPCServer
server = SimpleXMLRPCServer(('localhost',4242), allow_none=True)       #前面是监听地址和端口
server.register_introspection_functions()
server.register_instance(RPCServer())
server.serve_forever()

      客户端:

import time
from xmlrpc.client import ServerProxy

# SimpleXMLRPCServer
def xmlrpc_client():
    print('xmlrpc client')
    c = ServerProxy('http://localhost:4242')
    data = {'client:'+str(i): i for i in range(100)}
    start = time.clock()
    for i in range(50):
        a=c.getObj()
        print(a)
    for i in range(50):
        c.sendObj(data)
    print('xmlrpc total time %s' % (time.clock() - start))

if __name__ == '__main__':
    xmlrpc_client()

    

    3、ZeroRpc实现rpc(推荐,性能很高,走http协议)

      服务端:

import zerorpc

class RPCServer(object):

    def __init__(self):
        super(RPCServer, self).__init__()
        print(self)
        self.send_data = {'server:'+str(i): i for i in range(100)}
        self.recv_data = None

    def getObj(self):
        print('get data')
        return self.send_data

    def sendObj(self, data):
        print('send data')
        self.recv_data = data
        print(self.recv_data)
# zerorpc
s = zerorpc.Server(RPCServer())
s.bind('tcp://0.0.0.0:4243')
s.run()

      客户端:

import zerorpc
import time
# zerorpc
def zerorpc_client():
    print('zerorpc client')
    c = zerorpc.Client()
    c.connect('tcp://127.0.0.1:4243')
    data = {'client:'+str(i): i for i in range(100)}
    start = time.clock()
    for i in range(500):
        a=c.getObj()
        print(a)
    for i in range(500):
        c.sendObj(data)

    print('total time %s' % (time.clock() - start))


if __name__ == '__main__':
    zerorpc_client()

 

 



 
 

  

posted @ 2022-03-26 12:28  新入世界的小白  阅读(53)  评论(0)    收藏  举报