• Rabbitmq


    一 消息队列介绍

    1.1 介绍

    消息队列就是基础数据结构中的“先进先出”的一种数据机构。想一下,生活中买东西,需要排队,先排的人先买消费,就是典型的“先进先出”

    image-20200907000210863

    1.2 MQ解决什么问题

    MQ是一直存在,不过随着微服务架构的流行,成了解决微服务之间问题的常用工具。

    应用解耦

    以电商应用为例,应用中有订单系统、库存系统、物流系统、支付系统。用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障,都会造成下单操作异常。

    当转变成基于消息队列的方式后,系统间调用的问题会减少很多,比如物流系统因为发生故障,需要几分钟来修复。在这几分钟的时间里,物流系统要处理的内存被缓存在消息队列中,用户的下单操作可以正常完成。当物流系统恢复后,继续处理订单信息即可,中单用户感受不到物流系统的故障。提升系统的可用性

    image-20200907000246573

    流量消峰

    举个栗子,如果订单系统最多能处理一万次订单,这个处理能力应付正常时段的下单时绰绰有余,正常时段我们下单一秒后就能返回结果。但是在高峰期,如果有两万次下单操作系统是处理不了的,只能限制订单超过一万后不允许用户下单。

    使用消息队列做缓冲,我们可以取消这个限制,把一秒内下的订单分散成一段时间来处理,这事有些用户可能在下单十几秒后才能收到下单成功的操作,但是比不能下单的体验要好。

    消息分发

    多个服务队数据感兴趣,只需要监听同一类消息即可处理。

    image-20200907000338060

    例如A产生数据,B对数据感兴趣。如果没有消息的队列A每次处理完需要调用一下B服务。过了一段时间C对数据也感性,A就需要改代码,调用B服务,调用C服务。只要有服务需要,A服务都要改动代码。很不方便。

    image-20200907000411649

    有了消息队列后,A只管发送一次消息,B对消息感兴趣,只需要监听消息。C感兴趣,C也去监听消息。A服务作为基础服务完全不需要有改动

    异步消息

    image-20200907000500012

    有些服务间调用是异步的,例如A调用B,B需要花费很长时间执行,但是A需要知道B什么时候可以执行完,以前一般有两种方式,A过一段时间去调用B的查询api查询。或者A提供一个callback api,B执行完之后调用api通知A服务。这两种方式都不是很优雅

    image-20200907000523720

    使用消息总线,可以很方便解决这个问题,A调用B服务后,只需要监听B处理完成的消息,当B处理完成后,会发送一条消息给MQ,MQ会将此消息转发给A服务。

    这样A服务既不用循环调用B的查询api,也不用提供callback api。同样B服务也不用做这些操作。A服务还能及时的得到异步处理成功的消息

    1.3 常见消息队列及比较

    1

    结论:

    Kafka在于分布式架构,RabbitMQ基于AMQP协议来实现,RocketMQ/思路来源于kafka,改成了主从结构,在事务性可靠性方面做了优化。广泛来说,电商、金融等对事务性要求很高的,可以考虑RabbitMQ和RocketMQ,对性能要求高的可考虑Kafka

    二 Rabbitmq安装

    官网:https://www.rabbitmq.com/getstarted.html

    2.1 服务端原生安装

    1
    2
    3
    4
    5
    # 安装配置epel源
    # 安装erlang
    yum -y install erlang
    # 安装RabbitMQ
    yum -y install rabbitmq-server

    2.2 服务端Docker安装

    1
    2
    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:managemen

    2.3 客户端安装

    1
    pip3 install pika

    2.4 设置用户和密码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    rabbitmqctl add_user lqz 123
    # 设置用户为administrator角色
    rabbitmqctl set_user_tags lqz administrator
    # 设置权限
    rabbitmqctl set_permissions -p "/" root ".*" ".*" ".*"

    # 然后重启rabbiMQ服务
    systemctl reatart rabbitmq-server

    # 然后可以使用刚才的用户远程连接rabbitmq server了。

    三 基于Queue实现生产者消费者模型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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()

    四 基本使用(生产者消费者模型)

    对于RabbitMQ来说,生产和消费不再针对内存里的一个Queue对象,而是某台服务器上的RabbitMQ Server实现的消息队列。

    生产者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import pika
    # 无密码
    # connection = pika.BlockingConnection(pika.ConnectionParameters('127.0.0.1'))

    # 有密码
    credentials = pika.PlainCredentials("admin","admin")
    connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166',credentials=credentials))
    channel = connection.channel()
    # 声明一个队列(创建一个队列)
    channel.queue_declare(queue='lqz')

    channel.basic_publish(exchange='',
    routing_key='lqz', # 消息队列名称
    body='hello world')
    connection.close()

    消费者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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='lqz')

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

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

    channel.start_consuming()

    五 消息安全之ack

    生产者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import pika
    # 无密码
    # connection = pika.BlockingConnection(pika.ConnectionParameters('127.0.0.1'))

    # 有密码
    credentials = pika.PlainCredentials("admin","admin")
    connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166',credentials=credentials))
    channel = connection.channel()
    # 声明一个队列(创建一个队列)
    channel.queue_declare(queue='lqz')

    channel.basic_publish(exchange='',
    routing_key='lqz', # 消息队列名称
    body='hello world')
    connection.close()

    消费者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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='lqz')

    def callback(ch, method, properties, body):
    print("消费者接受到了任务: %r" % body)
    # 通知服务端,消息取走了,如果auto_ack=False,不加下面,消息会一直存在
    # ch.basic_ack(delivery_tag=method.delivery_tag)

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

    channel.start_consuming()

    六 消息安全之durable持久化

    生产者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import pika
    # 无密码
    # connection = pika.BlockingConnection(pika.ConnectionParameters('127.0.0.1'))

    # 有密码
    credentials = pika.PlainCredentials("admin","admin")
    connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166',credentials=credentials))
    channel = connection.channel()
    # 声明一个队列(创建一个队列),durable=True支持持久化,队列必须是新的才可以
    channel.queue_declare(queue='lqz1',durable=True)

    channel.basic_publish(exchange='',
    routing_key='lqz1', # 消息队列名称
    body='111',
    properties=pika.BasicProperties(
    delivery_mode=2, # make message persistent,消息也持久化
    )
    )
    connection.close()

    消费者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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='lqz1')

    def callback(ch, method, properties, body):
    print("消费者接受到了任务: %r" % body)
    # 通知服务端,消息取走了,如果auto_ack=False,不加下面,消息会一直存在
    # ch.basic_ack(delivery_tag=method.delivery_tag)

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

    channel.start_consuming()

    七 闲置消费

    正常情况如果有多个消费者,是按照顺序第一个消息给第一个消费者,第二个消息给第二个消费者

    但是可能第一个消息的消费者处理消息很耗时,一直没结束,就可以让第二个消费者优先获得闲置的消息

    生产者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import pika
    # 无密码
    # connection = pika.BlockingConnection(pika.ConnectionParameters('127.0.0.1'))

    # 有密码
    credentials = pika.PlainCredentials("admin","admin")
    connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166',credentials=credentials))
    channel = connection.channel()
    # 声明一个队列(创建一个队列),durable=True支持持久化,队列必须是新的才可以
    channel.queue_declare(queue='lqz123',durable=True)

    channel.basic_publish(exchange='',
    routing_key='lqz123', # 消息队列名称
    body='111',
    properties=pika.BasicProperties(
    delivery_mode=2, # make message persistent,消息也持久化
    )
    )
    connection.close()

    消费者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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='lqz123')

    def callback(ch, method, properties, body):
    print("消费者接受到了任务: %r" % body)
    # 通知服务端,消息取走了,如果auto_ack=False,不加下面,消息会一直存在
    ch.basic_ack(delivery_tag=method.delivery_tag)

    channel.basic_qos(prefetch_count=1) #####就只有这一句话 谁闲置谁获取,没必要按照顺序一个一个来
    channel.basic_consume(queue='lqz123',on_message_callback=callback,auto_ack=False)

    channel.start_consuming()

    八 发布订阅

    发布者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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='m1',exchange_type='fanout')

    channel.basic_publish(exchange='m1',
    routing_key='',
    body='lqz nb')

    connection.close()

    订阅者(启动几次订阅者会生成几个队列)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import pika

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

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

    # 随机生成一个队列
    result = channel.queue_declare(queue='',exclusive=True)
    queue_name = result.method.queue
    print(queue_name)
    # 让exchange和queque进行绑定.
    channel.queue_bind(exchange='m1',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()

    九 发布订阅高级之Routing(按关键字匹配)

    发布者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    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='m2',exchange_type='direct')

    channel.basic_publish(exchange='m2',
    routing_key='bnb', # 多个关键字,指定routing_key
    body='lqz nb')

    connection.close()

    订阅者1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import pika

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

    # exchange='m1',exchange(秘书)的名称
    # exchange_type='direct' , 秘书工作方式将消息发送给不同的关键字
    channel.exchange_declare(exchange='m2',exchange_type='direct')

    # 随机生成一个队列
    result = channel.queue_declare(queue='',exclusive=True)
    queue_name = result.method.queue
    print(queue_name)
    # 让exchange和queque进行绑定.
    channel.queue_bind(exchange='m2',queue=queue_name,routing_key='nb')
    channel.queue_bind(exchange='m2',queue=queue_name,routing_key='bnb')


    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import pika

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

    # exchange='m1',exchange(秘书)的名称
    # exchange_type='direct' , 秘书工作方式将消息发送给不同的关键字
    channel.exchange_declare(exchange='m2',exchange_type='direct')

    # 随机生成一个队列
    result = channel.queue_declare(queue='',exclusive=True)
    queue_name = result.method.queue
    print(queue_name)
    # 让exchange和queque进行绑定.
    channel.queue_bind(exchange='m2',queue=queue_name,routing_key='nb')



    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()

    九 发布订阅高级之Topic(按关键字模糊匹配)

    发布者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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')

    channel.basic_publish(exchange='m3',
    # routing_key='lqz.handsome', #都能收到
    routing_key='lqz.handsome.xx', #只有lqz.#能收到
    body='lqz nb')

    connection.close()

    订阅者1

    *只能加一个单词

    #可以加任意单词字符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import pika

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

    # exchange='m1',exchange(秘书)的名称
    # exchange_type='direct' , 秘书工作方式将消息发送给不同的关键字
    channel.exchange_declare(exchange='m3',exchange_type='topic')

    # 随机生成一个队列
    result = channel.queue_declare(queue='',exclusive=True)
    queue_name = result.method.queue
    print(queue_name)
    # 让exchange和queque进行绑定.
    channel.queue_bind(exchange='m3',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()

    订阅者2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import pika

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

    # exchange='m1',exchange(秘书)的名称
    # exchange_type='topic' , 模糊匹配
    channel.exchange_declare(exchange='m3',exchange_type='topic')

    # 随机生成一个队列
    result = channel.queue_declare(queue='',exclusive=True)
    queue_name = result.method.queue
    print(queue_name)
    # 让exchange和queque进行绑定.
    channel.queue_bind(exchange='m3',queue=queue_name,routing_key='lqz.*')


    def callback(ch, method, properties, body):
    queue_name = result.method.queue # 发送的routing_key是什么
    print("消费者接受到了任务: %r" % body)

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

    channel.start_consuming()

    十 基于rabbitmq实现rpc

    服务端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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 on_request(ch, method, props, body):
    n = int(body)
    response = n + 100
    # props.reply_to 要放结果的队列.
    # props.correlation_id 任务
    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,)
    channel.start_consuming()

    客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    import pika
    import uuid

    class FibonacciRpcClient(object):
    def __init__(self):
    credentials = pika.PlainCredentials("admin", "admin")
    self.connection = pika.BlockingConnection(pika.ConnectionParameters('101.133.225.166', 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(50)
    print('返回结果:',response)
  • 相关阅读:
    让计算成为人类的能力,让数据变成世界的财富。今天更多人成了计算和数据的探索者和追梦者,阿里云也是其中之一。
    在巴山小站,我看到一个小男孩和小女孩共同拿着一部手机,在小站的一角蹭网,这是附近唯一能连上Wi-Fi的地方。那一幕对我内心的震撼,无法用言语形容,这就是互联网版的凿壁借光,这一缕光对这些还没有走出大山的孩子而言,是了解世界的窗户,也把他们的梦想和世界连在了一起。
    安存电子951335电话语录语音平台正式上线,你对自己想要录音的电话,可以拨打951335+对方的电话号码,录音内容就会保存在阿里云提供的云存储上,只要有互联网的地方就可以收听、下载及举证应用。
    阿里金融成立,从第一天开始,就使用阿里云的云计算平台作为运算淘宝和支付宝数据的计算后台。阿里小贷不需要抵押,纯信用贷款,目前已经能做到3分钟提交申请,1秒批准,0人工干预。数据成为新的信用。
    杭州成为第一个搭建数据大脑的城市,上接来自城市各路的数据,下接执行系统。这个城市大脑要帮助打通数据的静脉和筋脉,让过去沉淀淤积在一起的数据互通起来,让城市的“眼”摄像头和“手”(交通指挥)的动作协调起来。
    城市大脑的建设代表了杭州的城市管理理念,杭州要为中国和世界探索用云计算和大数据解决城市发展问题的方法。
    云栖小镇不是“镇”,就像中关村不是“村”。小镇是一个符号,就像起建于50年前的硅谷的“谷”,和100年前爱迪生所在的门洛公园。
    美国新泽西州,也有一个Menlo Park——门洛公园,尽管不太为人熟知,但爱迪生那里的一个发明却点亮了全世界。
    2013年1月,我们和华通合作在杭州转塘云计算园区建立了第一个数据中心,随后这个园区被我们称为云栖小镇。我们和园区管委会达成了一个共同目标,就是让云栖小镇成为创业创新的第一基地。
    客户教会了我们非常多的东西,甚至改变了我的世界观。是开发者、客户真正在教我们怎么做云计算。
  • 原文地址:https://www.cnblogs.com/bubu99/p/14742542.html
Copyright © 2020-2023  润新知