分布式框架RQ和Ray的基本使用

前言
本篇文章介绍一下python的两种分布式框架,一种是rq,另一种是ray。使用分布式可以处理大量的并发且耗时的操作。

一、RQ框架

1.1 rq介绍
RedisQueue是一款轻量级的分布式异步任务队列调度框架,基于redis数据库作为broker,生产端将任务job存储到redis数据库中,消费端监听队列并取出任务执行。
1.2 安装
pip install rq
1.3 生产端
from rq import Queue
import redis

# 根据需要写逻辑
def count_read_files(file):
  	f = open(file)
    lines = f.read()
    f.close()
    return count(lines)

# 连接redis指定库
conn = redis.Redis(host='127.0.0.1', password='xxxxxx', port=6379, db=1)  
q = Queue("file", connection=conn)
file_list = ["a.txt", "b.txt", "c.txt"]

# 生产发送任务到指定的redis队列中去
for file in file_list:
  	q.enqueue_call(count_read_files, args(file,))
1.4 消费端
import redis
from multiprocessiong import Pool
from rq import Worker, Queue, Connection

# 连接redis指定的库
conn = redis.Redis(host='127.0.0.1', password='xxxxxx', port=6379, db=1)

def worker(listen):
  	with Connection(conn):
        worker = Worker(map(Queue, listen))
        worker.work()

def run(num, listen):
  	try:
      	p = Pool(num)
        for i in range(num):
          	p.apply_async(worker, args=(listen,))
      	p.close()
        p.join()
    except Exception as e:
      	print(e)
if __name__ == "__main__":
  	listen = ["file", "don", "image"]
    cpu_num = 4
    run(cpu_num, listen)
   
1.5 redis集群
# 当redis是集群是,可以这样获取

from rq.cli.helpers import get_redis_from_config

settings = {'SENTINEL': {
	                     'INSTANCES': [('redis1.cloud.bz', 6379), 
                                       ('redis2.cloud.bz', 6379),
				                       ('redis3.cloud.bz', 6379)],
	                     'SOCKET_TIMEOUT': None, 
                         'PASSWORD': 'xxxxxx', 
                         'DB': 1, 
                         'MASTER_NAME': 'sentinel-127.0.0.1-6379'
                        }
           }
conn = get_redis_from_config(settings)

二、Ray框架

2.1 介绍
Ray是UC Berkeley RISELab新推出的高性能分布式执行框架,它使用了和传统分布式计算系统不一样的架构和对分布式计算的抽象方式,具有比Spark更优异的计算性能
但是目前只要Linux下的编译版本,Windows暂时无法使用。
2.2 安装
pip install ray
2.3 示例
import ray

ray.init()

@ray.remote
def who():
  	return "xiao ming"
 
# 异步执行函数,返回对象ID
obj_id = who.remote()
name = ray.get(obj_id)
print(name)

"""
在Ray里,通过Python注解@ray.remote定义remote函数。使用此注解声明的函数都会自带一个默认的方法remote,通过此方法发起的函数调用都是以提交分布式任务的方式异步执行的,函数的返回值是一个对象id,使用ray.get内置操作可以同步获取该id对应的对象。
"""
import ray

ray.init()

@ray.remote
def num_one():
  	return "one"
  
@ray.remote
def num_two():
  	return "two"
  
@ray.remote
def add(one, two):
  	res = one + two
  	return res

one_id = num_one.remote()
two_id = num_two.remote()
add_id = add.remote(one_id, two_id)
print(ray.get(add_id))

"""
例子代码中,对函数num_one、num_two的调用是完全并行执行的,但是对函数add的调用依赖于num_one、num_two函数的返回结果。Ray可以保证函数add需要等待num_one、num_two函数的结果真正计算出来后才会执行。
这样可以解决在异步中,某一部分依赖另一部分的结果问题。
"""
2.4 Ray集群
ray.init(redis_address="xxx.xxx.x.x")
2.5 常用方法
ray.put()
"""
可以将Python对象存入本地ObjectStore,并且异步返回一个唯一的ObjectID。通过该ID,Ray可以访问集群中任一个节点上的对象
"""

ray.get()
"""
可以通过ObjectID获取ObjectStore内的对象并将之转换为Python对象。对于数组类型的对象,Ray使用共享内存机制减少数据的拷贝成本。而对于其它对象则需要将数据从ObjectStore拷贝到进程的堆内存中。

如果调用ray.get()操作时,对象尚未创建好,则get操作会阻塞,直到对象创建完成后返回。
"""
result_ids = [ray.put(i) for i in range(10)]
ray.get(result_ids)  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

ray.wait()
"""
支持批量的任务等待,基于此可以实现一次性获取多个ObjectID对应的数据
"""
# 启动5个remote函数调用任务
results = [f.remote(i) for i in range(5)]
# 阻塞等待4个任务完成,超时时间为2.5s
ready_ids, remaining_ids = ray.wait(results, num_returns=4, timeout=2500)

posted @ 2022-03-02 23:08  Lei、Sunny  阅读(941)  评论(0编辑  收藏  举报