getting start with storm 翻译 第六章 part-3

来源:互联网 发布:什么微单最好 一款知 编辑:程序博客网 时间:2024/06/06 13:57

转载请注明出处:http://blog.csdn.net/lonelytrooper/article/details/9979019

ProductCategoriesCounterBolt

ProductCategoriesCounterBolt类负责记录所有的产品-分类关系。它接收由UsersHistoryBolt发射的产品-分类对并更新计数器。

每个产品-分类对的事件信息被存放在Redis服务器。出于性能的原因,使用一个本地的用于读的缓存和一个写缓存。事件信息被通过一个后台进程发送到Redis。

对于输入,该bolt也会发送一个包含了更新的计数器的元组来供topology中下一个bolt消费,NewsNotifierBolt,它用于广播消息到最终用户来用于实时更新。

public class ProductCategoriesCounterBoltextendsBaseRichBolt{

...

@Override

public voidexecute(Tuple input) {

String product =input.getString(0);

String categ =input.getString(1);

int total=count(product,categ);

collector.emit(newValues(product,categ,total));

}

...

private intcount(String product,String categ) {

int count=getProductCategoryCount(categ,product);

count ++;

storeProductCategoryCount(categ,product,count);

return count;

}

...

}

该bolt中的持久化被隐藏在getProductCategoryCount和storeProductCategoryCount方法中。我们进一步看下:

package storm.analytics;

...

public class ProductCategoriesCounterBoltextendsBaseRichBolt{

// ITEM:CATEGORY -> COUNT

HashMap<String,Integer>counter=newHashMap<String,Integer>();

// ITEM:CATEGORY -> COUNT

HashMap<String,Integer>pendingToSave=newHashMap<String,Integer>();

...

public intgetProductCategoryCount(Stringcateg,Stringproduct) {

Integer count =counter.get(buildLocalKey(categ,product));

if(count==null) {

String sCount =jedis.hget(buildRedisKey(product),categ);

if(sCount==null||"nil".equals(sCount)){

count =0;

} else{

count =Integer.valueOf(sCount);

}

}

return count;

}

...

private voidstoreProductCategoryCount(Stringcateg,Stringproduct,intcount) {

String key =buildLocalKey(categ,product);

counter.put(key,count);

synchronized(pendingToSave) {

pendingToSave.put(key,count);

}

}

...

}

getProductCategoryCount首先查询内存缓存计数器。如果查不到相应信息,它将从Redis服务器中获取它。

storeProductCategoryCount更新计数器缓存和pendingToSave缓冲区。该缓冲区被下边的后台进程持久化:

package storm.analytics;

public class ProductCategoriesCounterBoltextendsBaseRichBolt{

...

private voidstartDownloaderThread() {

TimerTask t =newTimerTask(){

@Override

public voidrun(){

HashMap<String,Integer>pendings;

synchronized(pendingToSave) {

pendings =pendingToSave;

pendingToSave =newHashMap<String,Integer>();

}

for (String key:pendings.keySet()) {

String[]keys=key.split(":");

String product =keys[0];

String categ =keys[1];

Integer count =pendings.get(key);

jedis.hset(buildRedisKey(product),categ,count.toString());

}

}

};

timer =newTimer("Item categories downloader");

timer.scheduleAtFixedRate(t,downloadTime,downloadTime);

}

...

}

下载线程锁定pendingToSave,当它发送旧的buffer到Redis的同时建立新的缓冲区来供其他线程使用。该代码块每隔downloadTime毫秒运行一次并且可以通过down-load topology配置参数来配置。download-time越长,执行Redis写的次数越少,因为连续的添加被一次写入。

再次牢记,正如在前边的bolt中一样,当分配资源到该bolt时,应用正确的域分组是非常重要的,在该例中根据产品分组。那是因为它按产品存储该信息的内存拷贝,并且如果一些缓存和buffer的拷贝存在,将出现不一致。

NewsNotifierBolt

NewsNotifierBolt负责通知web应用统计数据的变化,目的是为了使用户可以实时的看到变化。通知使用Apache HttpClient构建HTTP POST,发送到topology中配置的web服务器的URL地址。POST体被编码成JSON。

当测试的时候,该bolt被从topology中删除。

package storm.analytics;

...

public class NewsNotifierBoltextendsBaseRichBolt{

...

@Override

public voidexecute(Tuple input) {

String product =input.getString(0);

String categ =input.getString(1);

int visits=input.getInteger(2);

String content ="{ \"product\": \""+product+"\",\"categ\":\""+categ+"\",

\"visits\":"+visits+"}";

HttpPost post =newHttpPost(webserver);

try {

post.setEntity(newStringEntity(content));

HttpResponse response =client.execute(post);

org.apache.http.util.EntityUtils.consume(response.getEntity());

} catch(Exception e) {

e.printStackTrace();

reconnect();

}

}

...

}

原创粉丝点击