第三十八章:rabbitMQ

一:Rabbitmq

1.介绍

# 官网
	https://www.rabbitmq.com/getstarted.html
# 消息队列
	-中间件(概念很大)---》叫消息队列中间件
    -使用 redis 当做消息队列来用
    	- blpop 阻塞式弹出,实现队列,先进先出
        
# MQ消息队列,MessageQueue 是什么
	消息队列就是基础数据结构中的“先进先出”的一种数据机构。想一下,生活中买东西,需要排队,先排的人先买消费,就是典型的“先进先出”
    
# MQ解决什么问题
	-应用解耦
    -流量削峰
    -消息分发(发布订阅)
    -异步消息
    -IPC 进程间通信也可以通过消息队列

2.rabbitmq 安装

# win:https://www.rabbitmq.com/install-windows-manual.html
	-erlang 解释器
    -rabbitmq 的软件

# centos
yum -y install erlang
yum -y install rabbitmq-server

# docker 安装
docker pull rabbitmq:management
docker run -di --name Myrabbitmq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 rabbitmq:management

# 访问虚拟机的 15672 端口,就可以看到图形化界面(官方提供的),手动点点点操作
http://47.100.163.117:15672/#/

3.基于 queue 实现生产者消费者

import Queue
import threading

message = Queue.Queue(10)

def producer(i):
    while True:
        message.put(i)

def consumer(i):
    while True:
        msg = message.get()

for i in range(12):
    t = threading.Thread(target=producer, args=(i,))
    t.start()

for i in range(10):
    t = threading.Thread(target=consumer, args=(i,))
    t.start()

4.基本使用

4.1 发送者

import pika

# 第一步:登录
# 无密码登录
# connection = pika.BlockingConnection(pika.ConnectionParameters(host='47.100.163.117', port=5672))
# 有密码登录
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('47.100.163.117', credentials=credentials))

# 第二步: 连接 channel
channel = connection.channel()
# 第三步: 创建一个队列,名字叫 hello
channel.queue_declare(queue='hello2')

# 第四步:向 hello 队列中,发送 Hello World
# body 中是发送的内容
channel.basic_publish(exchange='', routing_key='hello2', body='Hello World!')
print(" [x] Sent 'Hello World!'")
connection.close()

4.2 消费者

import pika


# 连接 rabbitmq
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('47.100.163.117', credentials=credentials))
# 假设队列不存在,创建
channel = connection.channel()
channel.queue_declare(queue='hello2')


# 回调函数
def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)


channel.basic_consume(queue='hello2', on_message_callback=callback, auto_ack=True)
print('[*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()  # 程序会夯在这里,等待从消息队列中取消息

5 消息安全(消费者)

# 通知中间件,逻辑处理完了,可以删除数据了
ch.basic_ack(delivery_tag=method.delivery_tag)
# 消费完,确认后,再删除消息
import pika

# 连接 rabbitmq
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('47.100.163.117', credentials=credentials))
# 假设队列不存在, 创建
channel = connection.channel()
channel.queue_declare(queue='hello2')


# 回调函数
def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)
    # 拿到消息队列中的数据,要开始消费,处理我的业务逻辑,在处理过程中,报惜了
    # raise Exception('异常了')
    # 通知中间件,逻辑处理完了,可以删除数据了
    ch.basic_ack(delivery_tag=method.delivery_tag)


# 消息安全机制:auto_ack=False
channel.basic_consume(queue='hello2', on_message_callback=callback, auto_ack=False)
print('[*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()  # 程序会夯在这里,等待从消息队列中取消息

6 持久化(生产者)

# 第三步: 创建一个队列,名字叫 hello
channel.queue_declare(queue='hello2', durable=True)     # durable=True 使 queue 做持久化

# 第四步:向 hello 队列中,发送 Hello World
# properties 消息做持久化
channel.basic_publish(exchange='', routing_key='hello2', body='Hello World!', properties=pika.BasicProperties(delivery_mode=2))
# queue 和 消息 都要持久化

import pika

# 第一步:登录
# 无密码登录
# connection = pika.BlockingConnection(pika.ConnectionParameters(host='47.100.163.117', port=5672))
# 有密码登录
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('47.100.163.117', credentials=credentials))

# 第二步: 连接 channel
channel = connection.channel()
# 第三步: 创建一个队列,名字叫 hello
channel.queue_declare(queue='hello2', durable=True)     # durable=True 使 queue 做持久化

# 第四步:向 hello 队列中,发送 Hello World
# body 中是发送的内容
# properties 消息做持久化
channel.basic_publish(exchange='', routing_key='hello2', body='Hello World!', properties=pika.BasicProperties(delivery_mode=2))
print(" [x] Sent 'Hello World!'")
connection.close()

7 闲置消费(消费者)

# 在接收消息之前加
channel.basic_qos(prefetch_count=1)     # 就只有这一句话 谁闲置谁获取,没必要按照顺序一个一个来
# 正常情况如果有多个消费者,是按照顺序第一个消息给第一个消费者,第二个消息给第二个消费者
# 但是可能第一个消息的消费者处理消息很耗时,一直没结束,就可以让第二个消费者优先获得闲置的消息

import pika

# 连接 rabbitmq
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('47.100.163.117', credentials=credentials))
# 假设队列不存在,创建
channel = connection.channel()
channel.queue_declare(queue='hello2')


# 回调函数
def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)
    # 拿到消息队列中的数据,要开始消费,处理我的业务逻辑,在处理过程中,报惜了
    # raise Exception('异常了')
    # 通知中间件,逻辑处理完了,可以删除数据了
    ch.basic_ack(delivery_tag=method.delivery_tag)


# 闲置消费
channel.basic_qos(prefetch_count=1)     # 就只有这一句话 谁闲置谁获取,没必要按照顺序一个一个来
# 消息安全机制:auto_ack=False
channel.basic_consume(queue='hello2', on_message_callback=callback, auto_ack=False)
print('[*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()  # 程序会夯在这里,等待从消息队列中取消息

二:其他模式

1.发布订阅 fanout 模式

1.1 发布者

# 声明 exchange='y1'
channel.exchange_declare(exchange='y1', exchange_type='fanout')
import pika

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

# 声明 exchange='y1'
channel.exchange_declare(exchange='y1', exchange_type='fanout')
channel.basic_publish(exchange='y1',
                      routing_key='',
                      body='ysg 1231231')
connection.close()

1.2 订阅者

# 声明 exchange='m1'  交换机
channel.exchange_declare(exchange='y1', exchange_type='fanout')

# 队列 queue 绑定交换机
# 让 exchange 和 queque 进行绑定.
channel.queue_bind(exchange='y1', queue=queue_name)
import pika

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

# exchange='m1', exchange(秘书)的名称
# exchange_type='fanout' , 秘书工作方式将消息发送给所有的队列
# 声明 exchange='m1'  交换机
channel.exchange_declare(exchange='y1', exchange_type='fanout')

# 随机生成一个队列
result = channel.queue_declare(queue='', exclusive=True)
queue_name = result.method.queue
print('随机生成的queue的名字', queue_name)

# 队列 queue 绑定交换机
# 让 exchange 和 queque 进行绑定.
channel.queue_bind(exchange='y1', queue=queue_name)


def callback(ch, method, properties, body):
    print("消费者接受到了任务: %r" % body)


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

2.Routing 按关键字匹配

2.1 发布者

channel.basic_publish(exchange='y1',
                      # routing_key='error',
                      routing_key='info',
                      body='ysg info')
'''
# -*- coding:utf-8 -*-
# @Project: rabbitMQ
# @Date: 2023/5/14 - 9:49
# @Author: ysg
# @Describe: 

send.py
'''
import pika

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

# 声明 exchange='y1'
channel.exchange_declare(exchange='y1', exchange_type='direct')
channel.basic_publish(exchange='y1',
                      # routing_key='error',
                      routing_key='info',
                      body='ysg info')
connection.close()

2.2 订阅者

# 让 exchange 和 queque 进行绑定.
channel.queue_bind(exchange='y1', queue=queue_name, routing_key='error')
import pika

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

# exchange='m1', exchange(秘书)的名称
# exchange_type='fanout' , 秘书工作方式将消息发送给所有的队列
# 声明 exchange='m1'  交换机
channel.exchange_declare(exchange='y1', exchange_type='direct')

# 随机生成一个队列
result = channel.queue_declare(queue='', exclusive=True)
queue_name = result.method.queue
print('随机生成的queue的名字', queue_name)
# 队列 queue 绑定交换机
# 让 exchange 和 queque 进行绑定.
channel.queue_bind(exchange='y1', queue=queue_name, routing_key='error')


def callback(ch, method, properties, body):
    print("消费者接受到了任务: %r" % body)


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

3.topic 模式

3.1 发布者

### 声明exchange
channel.exchange_declare(exchange='m1',exchange_type='topic')
import pika
credentials = pika.PlainCredentials("admin","admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.101',credentials=credentials))
channel = connection.channel()


### 声明exchange
channel.exchange_declare(exchange='m1',exchange_type='topic')

channel.basic_publish(exchange='m1',
                      routing_key='lqz.handsome.ssda',
                      body='aaa')

connection.close()

3.2 订阅者

# 1 声明 exchange='m1'  交换机
channel.exchange_declare(exchange='m1',exchange_type='topic')

# 2 队列 queue 绑定交换机
# 让 exchange 和 queque 进行绑定.
channel.queue_bind(exchange='m1',queue=queue_name,routing_key='lqz.#')  #代表一个或多个部分

import pika

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

# exchange='m1',exchange(秘书)的名称
# exchange_type='fanout' , 秘书工作方式将消息发送给所有的队列
# 1 声明 exchange='m1'  交换机
channel.exchange_declare(exchange='m1',exchange_type='topic')

# 随机生成一个队列
result = channel.queue_declare(queue='',exclusive=True)
queue_name = result.method.queue
print('随机生成的queue的名字',queue_name)
# 2 队列 queue 绑定交换机
# 让 exchange 和 queque 进行绑定.
channel.queue_bind(exchange='m1',queue=queue_name,routing_key='lqz.#')  #代表一个或多个部分


def callback(ch, method, properties, body):
    print("消费者接受到了任务: %r" % body)

channel.basic_consume(queue=queue_name,on_message_callback=callback,auto_ack=True)
channel.start_consuming()
# 1 声明 exchange='m1' 交换机
channel.exchange_declare(exchange='m1', exchange_type='topic')

# 2 队列 queue 绑定交换机
# 让 exchange 和 queque 进行绑定.
channel.queue_bind(exchange='m1',queue=queue_name,routing_key='lqz.*')  # 代表一个部分
import pika

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

# exchange='m1',exchange(秘书)的名称
# exchange_type='fanout' , 秘书工作方式将消息发送给所有的队列
# 1 声明 exchange='m1' 交换机
channel.exchange_declare(exchange='m1', exchange_type='topic')

# 随机生成一个队列
result = channel.queue_declare(queue='',exclusive=True)
queue_name = result.method.queue
print('随机生成的queue的名字',queue_name)
# 2 队列 queue 绑定交换机
# 让 exchange 和 queque 进行绑定.
channel.queue_bind(exchange='m1', queue=queue_name, routing_key='lqz.*')  # 代表一个部分


def callback(ch, method, properties, body):
    print("消费者接受到了任务: %r" % body)

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

三:RPC

1.rpc 介绍

# RPC(Remote Procedure Call) 是指远程过程调用,也就是说两台服务器 A,B 一个应用部署在 A 服务器上,想要调用 B 服务器上应用提供的函数或方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据

# 为什么要用 RPC
就是无法在一个进程内,甚至一个计算机内通过本地调用的方式完成的需求,比如比如不同的系统间的通讯,甚至不同的组织间的通讯。由于计算能力需要横向扩展,需要在多台机器组成的集群上部署应用



# 常见 RPC 框架
	-dubbo java
    -gRpc  跨语言 

# Spring Cloud : Spring 全家桶,用起来很舒服,只有你想不到,没有它做不到。可惜因为发布的比较晚,国内还没出现比较成功的案例,大部分都是试水,不过毕竟有 Spring 作背书,还是比较看好。
# Dubbox: 相对于 Dubbo 支持了REST,估计是很多公司选择 Dubbox 的一个重要原因之一,但如果使用 Dubbo 的RPC调用方式,服务间仍然会存在API强依赖,各有利弊,懂的取舍吧。
# Thrift:如果你比较高冷,完全可以基于 Thrift 自己搞一套抽象的自定义框架吧。
# Montan:可能因为出来的比较晚,目前除了新浪微博 16 年初发布的,
# Hessian:如果是初创公司或系统数量还没有超过 5 个,推荐选择这个,毕竟在开发速度、运维成本、上手难度等都是比较轻量、简单的,即使在以后迁移至SOA,也是无缝迁移。
# rpcx/gRPC: 在服务没有出现严重性能的问题下,或技术栈没有变更的情况下,可能一直不会引入,即使引入也只是小部分模块优化使用。

# protohuf grpc 自己定制的远程过程调用的通信格式

2.python 实现 rpc

# SimpleXMLRPCServer 自带的
# ZeroRPC

2.1 内置的

# 服务端
from xmlrpc.server import SimpleXMLRPCServer

# 通信使用xml格式
class RPCServer(object):

    def __init__(self):
        super(RPCServer, self).__init__()
        print(self)
        self.send_data = 'lqz nb'
        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)
        return '收到了'+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')

    # res=c.getObj()
    # print(res)
    # res=c.sendObj('xxx')
    # print(res)

    data = 'lqz nb'
    start = time.time()
    for i in range(500):
        a=c.getObj()
        print(a)
    for i in range(500):
        c.sendObj(data)
    print('xmlrpc total time %s' % (time.time() - start))

if __name__ == '__main__':
    xmlrpc_client()

2.2 zeroRpc

# 服务端
import zerorpc

class RPCServer(object):

    def __init__(self):
        super(RPCServer, self).__init__()
        print(self)
        self.send_data = 'lqz nb'
        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 = 'lqz nb'

    start = time.time()
    for i in range(500):
        a=c.getObj()
        print(a)
    for i in range(500):
        c.sendObj(data)

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


if __name__ == '__main__':
    zerorpc_client()

3.rabbitmq 实现 rpc

# 跨语言

# 服务端
import pika
credentials = pika.PlainCredentials("admin","admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.101',credentials=credentials))
channel = connection.channel()

# 声明一个队列rpc_queue
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):
        credentials = pika.PlainCredentials("admin", "admin")
        self.connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.101', credentials=credentials))
        self.channel = self.connection.channel()

        # 随机生成一个消息队列(用于接收结果)
        result = self.channel.queue_declare(queue='', exclusive=True)
        self.callback_queue = result.method.queue

        # 监听消息队列中是否有值返回,如果有值则执行 on_response 函数(一旦有结果,则执行on_response)
        self.channel.basic_consume(queue=self.callback_queue, on_message_callback=self.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())

        # 客户端 给 服务端 发送一个任务:  任务id = corr_id / 任务内容 = '30' / 用于接收结果的队列名称
        self.channel.basic_publish(exchange='',
                                   routing_key='rpc_queue',  # 服务端接收任务的队列名称
                                   properties=pika.BasicProperties(
                                       reply_to=self.callback_queue,  # 用于接收结果的队列
                                       correlation_id=self.corr_id,  # 任务ID
                                   ),
                                   body=str(n))
        while self.response is None:
            self.connection.process_data_events()

        return self.response


fibonacci_rpc = FibonacciRpcClient()
response = fibonacci_rpc.call(9)
print('返回结果:', response)
posted @ 2023-05-14 10:46  亦双弓  阅读(11)  评论(0)    收藏  举报