Storm Distributed RPC(DRPC)分布式远程过程调用

DRPC的主要作用就是利用Storm来完成那些计算密集型函数(CPU密集型函数)的实时并行计算。对于每一次函数调用,Storm topology将函数的参数当成是输入流,并且将函数运行的结果作为输出流。

总体概览

DRPC通过一个"DRPC server"来进行协调均衡。(Storm整合了DRPC server的一个实现)。DRPC server接受一个RPC请求,发送该请求给Storm topology,接受该Storm topology产生的结果,并把结果返回给客户端。对于客户端来说,一次DRPC调用就像是一次正常的RPC调用一样。例如:下面是一个客户端使用DRPC来获取以"http://twitter.com"为参数的"reach"函数的返回结果:

DRPCClient client = new DRPCClient("drpc-host", 3772);
String result = client.execute("reach", "http://twitter.com");

DRPC的工作流程图:


客户端将要执行的函数名以及相应的参数发送给DRPC server 。实现了这个函数的topology使用 DRPCSpout来接收从DRPC server传来的函数的远程调用流,从而来执行该函数。每一次函数的远程调用都被DRPC server附上了一个唯一的id。接下来topology计算结果,在最后topology中的bolt调用ReturnResults来连接DRPC server并将结果及相应的函数远程调用id返回给DRPC server。接下来DRPC server通过id来匹配相应的客户端,此时客户端还处于等待状态,匹配上后,疏通等待状态的客户端,并开始将结果发送给客户端。


LinearDRPCTopologyBuilder(线性DRPCTopologyBuilder)

Storm中有个LinearDRPCTopologyBuilder,实现了几乎所以DRPC步骤的自动化,这些步骤如下:

1.建立 spout

2.将结果返回到DRPC server

3.向bolts提供了在tuples集合上进行有限聚集的功能

看一个简单的例子。这个DRPC topology的实现,返回的结果是在其输入参数后加一个"!":

public static class ExclaimBolt extends BaseBasicBolt {
    public void execute(Tuple tuple, BasicOutputCollector collector) {
        String input = tuple.getString(1);
        collector.emit(new Values(tuple.getValue(0), input + "!"));
    }

    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields("id", "result"));
    }
}

public static void main(String[] args) throws Exception {
    LinearDRPCTopologyBuilder builder = new LinearDRPCTopologyBuilder("exclamation");
    builder.addBolt(new ExclaimBolt(), 3);
    // ...
}
如上所示,当我们创建LinearDRPCTopologyBuilder,我们将DRPC函数名告诉给topology(本例函数名为exclamation)。单个DRPC server可以负责处理多个函数,函数之间通过函数名来进行区分。第一个bolt的输入是一个2元组,第一个字段为request id,第二个字段为request对应的参数。

LinearDRPCTopologyBuilder最后一个bolt的输出流保护的2元组形式为[id,result]。最后,所有的中间元组必须以request id作为第一字段。

在本例中,最后一个也是第一个bolt "ExclaimBolt"简单的在元组的第二个字段的值后加了一个"!"。

LinearDRPCTopologyBuilder将会处理余下的操作:连接DRPC server以及发送结果。


Local mode DRPC(应该就是本地开放测试时使用,为了模拟环境嘛!)

DRPC可以以本地模式运行:

LocalDRPC drpc = new LocalDRPC();
LocalCluster cluster = new LocalCluster();

cluster.submitTopology("drpc-demo", conf, builder.createLocalTopology(drpc));

System.out.println("Results for 'hello':" + drpc.execute("exclamation", "hello"));

cluster.shutdown();
drpc.shutdown();

首先创建一个LocalDRPC对象。该对象将会在进程中模拟一个DRPC server。然后创建LocalCluster来以本地模式来运行该topology。LinearDRPCTopologyBuilder有单独的方法来创建本地的topologies以及远程的topologies。在本地模式中,LocalDRPC对象不会绑定到任何端口,所以,topology需要知道与其通信的对象(即将drpc作为参数传入:builder. createLocalTopology(drpc));

在建立了topology后,我们可以使用LocalDRPC的execute进行DRPC远程调用。

Remote mode DRPC

在一个真实的集群环境下使用DRPC也是很简单的,有下列3步:

1.建立DRPC server(s)

2.配置DRPC servers的位置

3.向Storm cluster提交DRPC topologies

可用storm脚本建立DRPC server:

bin/storm drpc
接下来,我们需要配置DRPC server(s)的位置,以便让Storm cluster知道DRPC server(s)的位置。这也是DRPCSpout知道从哪里读取函数的远程调用的原因。我们可以通过storm.yaml来进行配置或者之间在topology程序中进行配置。通过storm.yaml进行配置如下:

drpc.servers:
  - "drpc1.foo.com"
  - "drpc2.foo.com"
最后,我们可以通过StormSubmitter建立DRPC topologies:

StormSubmitter.submitTopology("exclamation-drpc", conf, builder.createRemoteTopology());
createRemoteTopology用来建立与Storm集群相适应的topologies。


一个更复杂的例子

通过这个例子我们将看到真正需要通过Storm集群来提供并行计算的DRPC函数。我们讲看到的这个例子是计算Twitter上URL的reach(影响力)

the reach of a URL就是上传该URL到Twitter上的人数。为了计算reach,我们需要:

1. 获取所有了推了(tweeted)该URL的人

2.获取发该URL推文的人的跟随人(即好友吧。。没用过推特)

3.对1、2步骤产生的人的集合进行去重。

4.计算集合中的人数。

reach计算将包含成千上万次的数据库访问以及几千万的跟随者记录。这绝对是个计算密集型的例子。而通过Storm来做这件事,一切将变得非常简单。在单机上,这个计算可能会花费长时间。而通过storm集群,可能只需要几秒钟。

以下是reach topology的实现:

LinearDRPCTopologyBuilder builder = new LinearDRPCTopologyBuilder("reach");
builder.addBolt(new GetTweeters(), 3);
builder.addBolt(new GetFollowers(), 12)
        .shuffleGrouping();
builder.addBolt(new PartialUniquer(), 6)
        .fieldsGrouping(new Fields("id", "follower"));
builder.addBolt(new CountAggregator(), 2)
        .fieldsGrouping(new Fields("id"));

topology执行了以下4步:

1.GetTweeters获取了发了URL推文的用户(tweeters)。将输入流[id,url]转换成输出流[id,tweeter]。每个url元组将会对应多个tweeter元组。

2.GetFollowers获取tweeters的followers(跟随者)。将输入流[id,tweeter]转换成输出流[id,follower]。很明显在这个例子中,follower元组会出现重复现象。因为某follower可能关注了发了推文的tweeters并且自己还发了推文。

3.PartialUniquer 按照[id,follower]元组将相同的follower聚集在一起,显然相同的follower必定被送到同一个PartialUniquer task中(可以看看storm的fieldGrouping),从而通过PartialUniquer达到了去重的目的。

4.最后,CountAggregator将进行计算。

以下为PartialUniquer bolt的实现:

public class PartialUniquer extends BaseBatchBolt {
    BatchOutputCollector _collector;
    Object _id;
    Set<String> _followers = new HashSet<String>();

    @Override
    public void prepare(Map conf, TopologyContext context, BatchOutputCollector collector, Object id) {
        _collector = collector;
        _id = id;
    }

    @Override
    public void execute(Tuple tuple) {
        _followers.add(tuple.getString(1));
    }

    @Override
    public void finishBatch() {
        _collector.emit(new Values(_id, _followers.size()));
    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields("id", "partial-count"));
    }
}
PartialUniquer通过继承BaseBatchBolt实现了IBatchBolt接口。一个批处理bolt提供了first class API(fist class啥意思)将批量的元组作为独立的单元来处理。每个 request id将会生存一个新的batch bolt实例。Storm负责在适当的时候清理这些实例。

但PartialUniquer在execute方法中接收到follower元组时,将会把它加入到一个HashSet中来达到去重的目的。

Batch bolts提供 finishBatch方法,该方法将在所以的元组都被处理完之后执行。在这里,PartialUniquer 发送了一个元组包含了唯一的count。(始终注意一个request id对应一个Batch bolt实例)。

在底层,CoordinatedBolt被用来检测给定的bolt是否接收了所有的来自给定request id的元组。CoordinatedBolt利用direct streams来进行协调。

余下来的留给大家自己探索了。我们应该知道,在该计算中,每一步都是并行的,并且定义DRPC topology特别的简单。


Non-linear DRPC topologies

LinearDRPCTopologyBuilder只能用来处理线性的DRPC topologies,例如想reach那样的一步接着下一步的。如果遇到bolts间有分支和合并的,我们就需要使用到CoordinatedBolt。为了更了解非线性的DRPC topologies,我们得聊聊更加一般性的抽象DRPC topologies结构。(什么鬼)


LinearDRPCTopologyBuilder如何工作

~DRPCSpout 发送[args,return-info]。return-info由DRPC server的主机及端口号以及DRPC server产生的id组成。

~构造一个 topology包括:

·DRPCSpout

·PrepareRequest(生成一个request id和一个return info的流以及一个参数流)

·CoordinatedBolt wrappers(包装?) 以及直接分组

·JoinResult(将结果与return info合并)

·ReturnResult(连接DRPC server并返回结果)

~LinearDRPCTopologyBuilder 是个由Storm的一些基本元素构成的一个好的高层抽象的例子。


提升篇

~KeyedFairBolt 同时处理多个request

~如何直接使用CoordinateBolt

敬请期待!!!


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当使用`torch.distributed.rpc`库时,你可以在分布式环境中使用远程过程调用RPC)来实现进程间的通信。RPC允许你在不同的Python进程之间调用函数,并且可以用于构建分布式训练、数据并行、模型并行等应用。 以下是使用`torch.distributed.rpc`的基本步骤: 1. 导入必要的库: ```python import torch import torch.distributed.rpc as rpc import torch.multiprocessing as mp ``` 2. 定义一个远程函数,该函数将在远程节点上执行。这个函数必须是全局可见的,并且可以通过`@torch.jit.export`装饰器导出。 ```python @torch.jit.export def remote_function(): # 远程节点上执行的代码 pass ``` 3. 在每个节点上启动一个进程,并指定每个进程的角色(`MASTER`或`WORKER`)。 ```python def run_master(rank, world_size): # 在MASTER节点上执行的代码 def run_worker(rank, world_size): # 在WORKER节点上执行的代码 if __name__ == "__main__": world_size = 2 # 设置总共的节点数 # 启动一个进程作为MASTER节点 mp.spawn(run_master, args=(world_size,), nprocs=1) # 启动其他进程作为WORKER节点 mp.spawn(run_worker, args=(world_size,), nprocs=world_size-1) ``` 4. 在MASTER节点上,使用`rpc.init_rpc`初始化RPC环境,并注册远程函数。 ```python def run_master(rank, world_size): # 初始化RPC环境 rpc.init_rpc(name="master", rank=rank, world_size=world_size) # 注册远程函数 rpc.rpc_async(worker_name, remote_function) # 等待远程函数执行完毕 rpc.shutdown() ``` 5. 在WORKER节点上,使用`rpc.init_rpc`初始化RPC环境,并注册远程函数。 ```python def run_worker(rank, world_size): # 初始化RPC环境 rpc.init_rpc(name="worker{}".format(rank), rank=rank, world_size=world_size) # 注册远程函数 rpc.rpc_async(master_name, remote_function) # 等待远程函数执行完毕 rpc.shutdown() ``` 6. 运行代码,启动所有的进程。 通过以上步骤,你可以在不同的节点上调用远程函数,实现分布式任务的协同工作。需要注意的是,你需要确保在所有节点上运行相同的代码,并且每个节点都能够连接到其他节点。 这只是`torch.distributed.rpc`的基本使用方法,还有很多其他功能和选项可以用来处理更复杂的分布式场景。你可以查阅官方文档以获取更详细的信息和示例代码:https://pytorch.org/docs/stable/rpc.html

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值