rabbitmq 使用(Rabbitmq介绍、安装、消息安全、持久化、闲置消费机制、发布订阅机制、发布分类订阅、topic模式、RPC模式、)

今日内容

1 消息队列Rabbitmq介绍

---------------------------------------------

# 消息队列
    也叫消息队列中间件
    celery中使用redis做过消息队列来用
    换Rabbitmq做消息队列,就只需要把broker的连接地址换成Rabbitmq的连接地址就行了
---------------------------------------------

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

---------------------------------------------

# MQ解决什么问题
    最主要就是: 应用解耦做分布式(微服务中,服务间数据的传递)
    流量削峰
    消息分发(发布订阅)
    异步
    IPC 进程间通信也可以通过消息队列

image
.
image
.
.
常见消息队列比较
image
.
.
.
.
.
.
.

2 rabbitmq安装


rabbitmq官网  https://www.rabbitmq.com/
----------------------------------------------

# windows安装地址 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
# 默认用户名是admin 密码是admin,做了两个端口映射,
# 一个端口是连接服务的端口5672
# 另一个是web管理界面的端口15672


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

windows安装erlang解释器
image
image
image
.
windows安装rabbitmq
image
image
.
.
用容器运行rabbitmq后,访问虚拟机的15672端口http://10.0.0.200:15672/ 就能登录了
重点就3个 channels信道 exchanges交换机 queues队列
image
.
.
看官网的7个案例,学习如何使用
image
image
image
.
.
.
.

windows 安装有点问题


安装了erlang解释器和rabbitmq的软件后,服务端起不来,登录浏览器,发现服务端页面没出来
应该是服务端没起来!!!!

有空再试试!!!

.
.
.
.
.
.
.
.

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

.
.
.
.
.
.
image

4 基本使用 发送基本的 hello world


# 生产者
import pika

# 第一步,连接服务端
# 无密码连接
# connection = pika.BlockingConnection(pika.ConnectionParameters(host='10.0.0.200',port=5672))

# 有密码连接
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.200', credentials=credentials))

# 第二步:连接channel信道
channel = connection.channel()

# 第三步:信道中创建一个队列,名字叫hello,也可以在图形化界面手动创建队列
channel.queue_declare(queue='hello')

# 第四步: 向hello队列中,发送Hello World
# routing_key是路由键或者路由规则用来指定消息要发送的队列
# 如果交换机名字为空,routing_key必须等于队列名字!,才能保证消息发能路由到队列里
# 因为交换机名字为空时,交换机与队列之间的Binding Key就是队列的名字
# body是发送的内容
channel.basic_publish(exchange='', routing_key='hello', body='Hello World !')
print("  Sent 'Hello World!'")
connection.close()

--------------------------------------------------

# 消费者
import pika


# 连接rabbitmq
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.200', 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()


# auto_ack=True  表示消费者从队列里拿出数据后,默认没有异常,就把数据删掉
# 如果回调函数运行过程中报错,队列里数据也没了,所以一般把自动确认设为False
# 在回到函数的最后,所有逻辑的处理完了,再确认队列里该数据可以删掉!!!

--------------------------------------------------

# 生产者每产生一个消息,都会放到队列里面
# 消费者会从队列里一个一个的取出消息来

--------------------------------------------------

image
.
可以设置页面刷新时间间隔,已经手动创建队列,等功能
image
.
.
实际上这种情况也是有一个隐藏的交换机在生产者与队列之间的
image
.
.
.

交换机的消息调度策略


生产者在将消息发送给Exchange的时候,一般会指定一个Routing Key,
来指定这个消息的路由规则

而这个Routing Key需要与Exchange Type及Binding Key联合使用才能最终生效

Exchange Type与Binding Key 一般都是固定配置好的,
我们的生产者就可以在发送消息给Exchange时,
通过指定Routing Key来决定消息流向哪个队列
----------------------------------------------------------
Direct:是rabbitmq默认的交换机模式,消息队列创建时就会被指定一个BindingKey,
当发送者发送消息的时候,需要指定对应的Routing Key,
当Routing key和消息队列的BingingKey一致的时候,
消息就会发送到该消息队列中(类似于精确查询)。
----------------------------------------------------------
topic:当发送者发送消息时,只有指定的Key和该模式相匹配的时候,
      消息才会被发送到该消息队列中。(类似于模糊查询,关键字为key)

.
.
.
.
.

5 消息安全机制


# 生产者 和上面的代码一致

---------------------------
# 消费者
import pika

# 连接rabbitmq
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.200', credentials=credentials))

# 连接信道
channel = connection.channel()

# 假设队列不存在,先创建队列,因为生产者与消费者不一定谁先运行
# 一旦该队列没有就创建,该队列已有就不创建
channel.queue_declare(queue='hello')


# 回调函数
def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)
    # 拿消息队列中的数据,处理我的业务逻辑过程中,
    # 如果报错了,队列里的数据也没有了,这样就不安全了,所以有了消息确认机制,
    # raise Exception('模拟出异常了')

    ch.basic_ack(delivery_tag=method.delivery_tag)
    # 告诉服务端我取的数据已经用完了,确认可以删掉队列里的该数据了

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

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

.
.
.
.
.
.
.

6 队列与消息持久化机制


# rabbitmq服务默认,队列里的数据是在内存里,没有持久化到硬盘里
# 当服务挂掉后,创建的队列没了,队列里的数据更没有了
# 所以queue和消息都要持久化!!!
----------------------------------------------------
# 生产者
import pika

# 第一步,连接服务端
# 无密码连接
# connection = pika.BlockingConnection(pika.ConnectionParameters(host='10.0.0.200',port=5672))

# 有密码连接
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.200', credentials=credentials))

# 第二步:连接channel信道
channel = connection.channel()

# 第三步:信道中创建一个队列,名字叫hello,也可以在图形化界面手动创建队列
channel.queue_declare(queue='hello', durable=True)
# durable=True申明该队列做持久化

# 第四步: 向hello队列中,发送Hello World
# body是发送的内容
channel.basic_publish(exchange='', routing_key='hello', body='Hello World',
                      properties=pika.BasicProperties(delivery_mode=2))

# properties=pika.BasicProperties(delivery_mode=2)  该参数是做消息的持久化的
print("  Sent 'Hello World!'")
connection.close()

-----------------------------------------------

# 消费者
import pika


# 连接rabbitmq
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.200', credentials=credentials))

# 连接信道
channel = connection.channel()

# 假设队列不存在,先创建队列,因为生产者与消费者不一定谁先运行
# 一旦该队列没有就创建,该队列已有就不创建
channel.queue_declare(queue='hello',durable=True)  # durable=True申明该队列做持久化


# 回调函数
def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)
    ch.basic_ack(delivery_tag=method.delivery_tag)
    # 告诉服务端我取的数据已经用完了,确认可以删掉队列里的该数据了


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

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

-------------------------------------

# 生产者与消费者里面队列都要申明做持久化 !!!
# 生成者里面,队列里面的消息也要申明做持久化 !!!

-------------------------------------

当申明持久化后,可以看到Features 下面出现标记了,说明该队列持久化了!!!
image
.
.
.
.
.
.
.

7 闲置消费机制


# 正常情况如果有2个消费者,
# 是按照顺序第1个消息给第1个消费者,第2个消息给第2个消费者,
# 第3个消息给第1个消费者、 第4个消息给第2个消费者

# 但假如 现在消费者1处理消息比较慢,那么假设队列里有8个消息
# 消费者1 刚把第一个消息处理完,第3 5 7个消息还没处理了
# 消费者2已经将 第2 4 6 8 个消息处理完了,消费者1会再慢慢的把第3 5 7个消息处理了

# mq服务端默认是: 不会让闲着的消费者,去主动消费第3 5 7闲置的消息

--------------------------------------------------

# 使用代码控制,让闲着的消费者,可以主动消费闲置的消息

# 就实现了 消费者1 刚把第一个消息处理完,
# 消费者2已经将 第2 3 4 5 6 7 8个消息全部处理了!!!
--------------------------

# 生产者代码和上面一样的

# 消费者 就加一行代码
import pika

# 连接rabbitmq
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.200', credentials=credentials))

channel = connection.channel()  # 连接信道

channel.queue_declare(queue='hello', durable=True)
# durable=True申明该队列做持久化


# 回调函数
def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)
    ch.basic_ack(delivery_tag=method.delivery_tag)
    # 告诉服务端我取的数据已经用完了,确认可以删掉队列里的该数据了


# 只要有这句话  哪个消费者闲着  哪个消费就可以获取  没有必要按顺序一个一个来了
channel.basic_qos(prefetch_count=1)

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

channel.start_consuming()  # 程序会夯在这里,等待从消息队列中取消息

---------------------------------------------------

image
.
.
.
.
.
.
.
.
.
.

8 发布订阅机制 交换机类型要选择 fanout 模式


# 生产者发布了一个消息,通过交换机exchanges,放到每一个消费者自己的队列里
# 这样就实现生产者发布一个消息,所有消费者都能收到消息,的功能了

------------------------------------------
# 生产者
import pika

# 有密码连接
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.200', credentials=credentials))

channel = connection.channel()  # 第二步:连接channel信道


# 第三步: 申明exchange  交换机类型要选'fanout' 才能实现发布订阅效果
channel.exchange_declare(exchange='m1', exchange_type='fanout', durable=True) # durable=True申明 做持久化


# 第四步: 向交换机里发送消息hello  此时消息还没到对应消费者的队列里面去了
channel.basic_publish(exchange='m1', routing_key='', body='hello',
                      properties=pika.BasicProperties(delivery_mode=2))

connection.close()

--------------------------------------------
# 消费者
import pika

# 连接rabbitmq
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.200', credentials=credentials))
channel = connection.channel()  # 连接信道

# 1 也要申明交换机名字,并且要和生产者申明的名字一样
# 交换机类型要选'fanout',才能实现发布订阅效果
channel.exchange_declare(exchange='m1', exchange_type='fanout', durable=True)


# 消费者去申明创建自己的队列,然后该队列只要监听了交换机exchange,
# 交换机里的消息就会自动路由到队列里面去!!! queue='' 表示队列 自动随机生成
result = channel.queue_declare(queue='', exclusive=True, durable=True)

queue_name = result.method.queue
print('随机生成的队列的名字:', queue_name)

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


# 回调函数
def callback(ch, method, properties, body):
    print(" [√] Received %r" % body)
    ch.basic_ack(delivery_tag=method.delivery_tag)
    # 告诉服务端我取的数据已经用完了,确认可以删掉队列里的该数据了


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

channel.start_consuming()  # 程序会夯在这里,等待从消息队列中取消息

# 用了发布订阅模式,就不没有闲置消息的问题了
------------------------------------------------

image
.
.
.
.
.
.
.

9 发布订阅---分类订阅模式 交换机类型要选direct模式


# 消费者可以订阅生产者某个类型的消息,也就是生产者发的消息,只有符合对应的类型
# 才会被放到消费者的消息队列里,供消费者消费!!!

------------------------------------------

# 消息队列公司里大概率用来存日志,程序产生了很多日志,写到文件里太大了
# 每产生一条日志就把日志放到消息队列里面去,日志有不同的级别,多个订阅者都订阅了日志
# 但是有的订阅者只关注error级别,有的订阅者只关注warning级别,
# 那么这些订阅者收到的日志消息就不一样了,对于每种级别的日志消息就可以
# 制定不一样的通知订阅者的方式了!!!

------------------------------------------
------------------------------------------
# 生产者
import pika

# 有密码连接
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.200', credentials=credentials))


channel = connection.channel()  # 第二步:连接channel信道

# 第三步: 申明exchange  交换机类型要选'direct' 才能实现发布分类订阅效果
channel.exchange_declare(exchange='m2', exchange_type='direct', durable=True)


# 第四步: 向交换机里发送消息hello  此时消息还没到对应消费者的队列里面去了
# 生产者将消息发到交换机前,一般指定routing_key路由键,放在消息的头里
# 用于标记消息的路由规则,交换机根据路由键,来决定消息交给哪个队列
channel.basic_publish(exchange='m2', routing_key='lqz', body='999',
                      properties=pika.BasicProperties(delivery_mode=2))

# routing_key='lqz' 就发给到消费者1的消息队列里了
# routing_key='teng' 就发给了消费者2的消息队列里了
# 如果消息的路由键routing_key,不符合所有的消费者队列
# 那么发布的该消息所有消费者都不会收到

print("  Sent 'Hello World!'")
connection.close()
------------------------------------

# 消费者
import pika

# 连接rabbitmq
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.200', credentials=credentials))
channel = connection.channel()  # 连接信道

# 1 申明交换机名字要和生产者申明的名字一样,交换机类型要选'direct',才能实现发布分类订阅效果
channel.exchange_declare(exchange='m2', exchange_type='direct', durable=True)


# 消费者去申明创建自己的队列    queue='' 表示队列 自动随机生成
result = channel.queue_declare(queue='', exclusive=True, durable=True)
queue_name = result.method.queue
print('随机生成的队列的名字:', queue_name)


# 2 让交换机和队列进行绑定,并用生成 Binding Key 绑定关键字
# 只要生产者发布的 消息的路由关键字='lqz' 就可以通过交换机路由到该队列中!!!
channel.queue_bind(exchange='m2', queue=queue_name, routing_key='lqz')


# 回调函数
def callback(ch, method, properties, body):
    print(" [√] Received %r" % body)
    ch.basic_ack(delivery_tag=method.delivery_tag)
    # 告诉服务端我取的数据已经用完了,确认可以删掉队列里的该数据了


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

channel.start_consuming()  # 程序会夯在这里,等待从消息队列中取消息


image
.
.
.
.
.
.
.

10 topic模式 (按关键字匹配)

# 生产者
import pika

# 有密码连接
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.200', credentials=credentials))


channel = connection.channel()  # 第二步:连接channel信道

# 第三步: 申明exchange
channel.exchange_declare(exchange='m3', exchange_type='topic', durable=True)

# 第四步: 向交换机里发送消息
channel.basic_publish(exchange='m3', routing_key='hh.tt.yy', body='999',
                      properties=pika.BasicProperties(delivery_mode=2))

print("  Sent 'Hello World!'")
connection.close()

----------------------------------------------
# 消费者
import pika

# 连接rabbitmq
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.200', credentials=credentials))
channel = connection.channel()  # 连接信道

# 1 也要申明交换机名字,并且要和生产者申明的名字一样,交换机类型要选'direct',才能实现发布选择订阅效果
channel.exchange_declare(exchange='m3', exchange_type='topic', durable=True)  # durable=True申明该队列做持久化

# 消费者去申明创建自己的队列,  queue='' 表示队列 自动随机生成
result = channel.queue_declare(queue='', exclusive=True, durable=True)  # durable=True申明该队列做持久化

queue_name = result.method.queue
print('随机生成的队列的名字:', queue_name)

# 2 让队列和交换机进行绑定  并指明消息带什么样的路由键 能路由到该队列里来
channel.queue_bind(exchange='m3', queue=queue_name, routing_key='hh.#')
# channel.queue_bind(exchange='m3', queue=queue_name, routing_key='hh.*')
# * 代表一部分   # 代表一部分或多部分    点号有分割的意思
# 所以消息的routing_key='hh.tt.yy'  那么只能是routing_key='hh.#'  才能收到!!

# 回调函数
def callback(ch, method, properties, body):
    print(" [√] Received %r" % body)
    ch.basic_ack(delivery_tag=method.delivery_tag)  # 告诉服务端我取的数据已经用完了,确认可以删掉队列里的该数据了


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

channel.start_consuming()  # 程序会夯在这里,等待从消息队列中取消息

--------------------------------------------------

image
.
.
.
.
.

rabbitmq实现rpc


# 可以实现跨语言  客户端将要运行的函数名与参数放到队列里,服务端拿到函数名与参数
# 通过反射运行自己的函数,再把函数返回值,再通过对列返给客户端

# 原理:
    客户端想要调用远程服务端的一个方法,首先创建一个队列,然后把调用的函数名
    函数的参数等这些数据放到队列里,

    然后服务端从队列里面取出数据后,执行对应的函数,并给函数传参数,
    然后把函数运行的结果,放到另一个队列里面,
    客户端再从另一个队列里面拿函数的运行结果

------------------------------------------------------
# 服务端代码
import pika

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


# 服务端声明一个队列rpc_queue接收客户端的消息
channel.queue_declare(queue='rpc_queue')


def on_request(ch, method, props, body):
    n = int(body)
    print(" [√] 结果为: %s" % n)
    response = n * 2

    # 服务端发消息的交换机名字为空,那么交换机与队列的binging key 就是队列名
    # 申明routing_key等于队列名 就等于binging key 了
    # 持久化的
    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_consume(queue='rpc_queue', on_message_callback=on_request, auto_ack=False)
print(" [x] Awaiting RPC requests")
channel.start_consuming()
# 服务端程序会夯在这里,等待从消息队列中取消息,一旦取出来就会触发on_request函数执行,
# n处理后的结果再通过交换机发布放到队列里去,让客户端去拿

--------------------------------------------------

# 客户端代码
import pika
import uuid


class FibonacciRpcClient(object):

    def on_response(self, ch, method, props, body):
        if self.corr_id == props.correlation_id:
            self.response = body
            # ch与method都是确认删除对列消息用的

    def __init__(self):
        credentials = pika.PlainCredentials("admin", "admin")
        self.connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.200', 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 send(self, n):
        self.response = None
        self.corr_id = str(uuid.uuid4())

        # 客户端 给 服务端 发送一个任务:  任务id = corr_id
        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.send(55)
# 整体效果就是,类加括号的时候生成对象的时候,已经与mq的服务端连接了,
# 并随机生成了一个用于接收结果的消息队列

# 并且监控该队列,一旦有值,取出该值,并调用回调函数on_response,
# 并把取出的值赋值给response属性

# 然后对象点send方法将消息通过交换机发到队列中去,一旦服务端收到消息处理后,
# 又放到客户端要收的队列里去

# 客户端这边取出队列里的数据,并触发回调函数on_response,并把取出的值赋给response
# 并将response的值返回出去

print('返回结果:  ', response.decode('utf8'))  # 打印response的值

------------------------------------------------

客户端发送消息 >>>加个routing_key在头部 >>>无名交换机 >>>队列(服创建) >>>服务端
客户端 <<< 队列(客创建) <<< 无名交换机 <<< 加个routing_key在头部 <<< 服务端回消息
.
.
.
.
.
.
.
.
.
.
.

rpc 远程过程调用 介绍


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

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

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

--------------------------------------------------
实际场景的选择

# Spring Cloud : Spring全家桶,用起来很舒服,只有你想不到没有它做不到,比较看好。
# Dubbox: 相对于Dubbo支持了REST,估计是很多公司选择Dubbox的一个重要原因之一,但如果使用Dubbo的RPC调用方式,服务间仍然会存在API强依赖,各有利弊,懂的取舍吧。

# rpcx/gRPC: 在服务没有出现严重性能的问题下,或技术栈没有变更的情况下,可能一直不会引入,即使引入也只是小部分模块优化使用。

# protobuf格式-----grpc自己定制的远程过程调用,之间的通信格式

--------------------------------------------------

.
.
.
.
.

2 python实现rpc


python中有两个模块,但用的都不太多,  一般都推荐用谷歌的grpc支持跨语言

# SimpleXMLRPCServer 自带的模块
# ZeroRPC  第三方模块
------------------------------------------------------
# SimpleXMLRPCServer 速度慢 通信使用xml格式 进行通信

# 服务端
from xmlrpc.server import SimpleXMLRPCServer
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')  # 指定服务端的地址与端口

    data = 'lqz nb'
    start = time.time()
    for i in range(500):
        res=c.getObj()
        print(res)
    for i in range(500):
        res2 = c.sendObj(data)
        print(res2)

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

if __name__ == '__main__':
    xmlrpc_client()

.
.
.
.

第三方 zeroRpc模块


# 底层使用zeromq与messagepack  速度快 响应短 并发高

# 客户端
import zerorpc
import time

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

---------------------

# 服务端

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)

s = zerorpc.Server(RPCServer())
s.bind('tcp://0.0.0.0:4243')
s.run()

-------------------------------------------------

.
.
.
.
.

3 通过rabbitmq实现rpc调用


# 见 rabbitmq RPC模式

.
.
.
.
.
.
.
.
.
.
.

posted @ 2023-05-05 17:22  tengyifan  阅读(50)  评论(0编辑  收藏  举报