转载请注明出处:http://blog.youkuaiyun.com/lonelytrooper/article/details/9965867
获取数据
这里你将看到一些常用的设计spouts的方法,它们可以从多个源有效的收集数据。
直接连接
在直接连接的架构中,spout直接连接到一个消息发射器(见图4-1)。
图4-1 直接连接的spout
这种架构实现简单,特别是当消息发射器是一个众所周知的设备或设备组时。一个众所周知的设备是指一个在启动时就被知晓并且在整个topology生命周期中都保持一致的设备。一个未知的设备是一个topology已经运行后添加的设备。一个众所周知的设备组是一个组中所有设备在启动时就被知晓的设备组。
作为一个示例,创建一个spout来使用Twitter Streaming API读取Twitter流。Spout将直接连接到作为消息发射器的API。通过过滤流来获取所有公共的与track参数(就像Twitter开发页面上的文档)相匹配的tweets。完整的示例可以在Twitter示例的github页面上找到。
Spout从配置对象中获取连接参数(track,user,andpassword)并且创建一个到API的连接(在这个示例中,使用Apache的DefaultHttpClient)。它从连接中每次读取行,将该行从JSON格式解析为java对象,然后发射它。
public void nextTuple(){
//Create the client call
client = new DefaultHttpClient();
client.setCredentialsProvider(credentialProvider);
HttpGet get = new HttpGet(STREAMING_API_URL+track);
HttpResponse response;
try {
//Execute
response = client.execute(get);
StatusLine status = response.getStatusLine();
if(status.getStatusCode() == 200){
InputStream inputStream = response.getEntity().getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String in;
//Read line by line
while((in = reader.readLine())!=null){
try{
//Parse and emit
Object json = jsonParser.parse(in);
collector.emit(new Values(track,json));
}catch (ParseException e) {
LOG.error("Error parsing message from twitter",e);
}
}
}
} catch (IOException e) {
LOG.error("Error in communication with twitter api ["+get.getURI().toString()+"],
sleeping 10s");
try {
Thread.sleep(10000);
} catch (InterruptedException e1) {
}
}
}
这里你正在锁定nextTuple方法,所以你从未执行ack和fail方法。在实际的应用中,我们推荐你在一个单独的线程中加锁并且使用内部的队列来交换信息(你可以在34页的下一个示例”Enqueued Messages”中学到怎样做)。
这很好!
你使用一个单独的spout读取Twitter。如果你并行你的topology,你将有多个spouts来读取同一个流的不同部分,这没有意义。所以当你有多个流要读时你怎样并行你的处理呢?storm的一个有趣的特点是你可以从任何组件(spouts/bolts)访问TopologyContext。通过这个特性,你可以在你的spout实例之间划分流。
public void open(Map conf, TopologyContext context,
SpoutOutputCollector collector) {
//Get the spout size from the context
int spoutsSize =
context.getComponentTasks(context.getThisComponentId()).size();
//Get the id of this spout
int myIdx = context.getThisTaskIndex();
String[] tracks =((String) conf.get("track")).split(",");
StringBuffer tracksBuffer = new StringBuffer();
for(int i=0; i< tracks.length;i++){
//Check if this spout must read the track word
if( i % spoutsSize == myIdx){
tracksBuffer.append(",");
tracksBuffer.append(tracks[i]);
}
}
if(tracksBuffer.length() == 0) {
throw new RuntimeException("No track found for spout" +
" [spoutsSize:"+spoutsSize+",tracks:"+tracks.length+"] the amount" +
" of tracks must be more then the spoutparalellism");
this.track =tracksBuffer.substring(1).toString();
}
...
}
通过这种技术,你甚至可以在数据源间分布收集器。相同的技术可以被应用在其他的场景-例如,从web服务器收集日志文件。见图4-2。
图4-2 直接哈希连接
在前边的例子中,你连接spout到一个众所周知的设备。你可以使用相同的方法来连接到一个未知的设备并听过一个协同系统来维护设备列表。该协调器检测列表的变化并建立和销毁连接。例如,当从web服务器收集日志文件时,web服务器的列表可能随着时间变化。当一个web服务器被添加时,协调器检测到变化并为它建立一个新的spout。见图4-3。
推荐建立从spout到消息发射器的连接,而不是相反的方式。如果运行spout的集群挂掉了,storm会在别的机器上重新启动该spout,所以从spout来定位消息发射器比消息发射器保存spout所在的机器的路径简单。