/* * visitante: Web analytic using Hadoop Map Reduce * Author: Pranab Ghosh * * Licensed under the Apache License, Version 2.0 (the "License"); you * may not use this file except in compliance with the License. You may * obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, softwarSessionSummarizere * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ package org.visitante.realtime; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.chombo.storm.GenericBolt; import org.chombo.storm.MessageHolder; import org.chombo.storm.MessageQueue; import org.chombo.util.ConfigUtility; import org.chombo.util.Utility; import org.hoidla.window.BiasedReservoirWindow; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import backtype.storm.Config; import backtype.storm.task.TopologyContext; import backtype.storm.tuple.Tuple; /** * Tracks visit length or page count. Generates bounce rate or visit depth * distribution * @author pranab * */ public class VisitDepthBolt extends GenericBolt { private int tickFrequencyInSeconds; private int windowSize; private int minWindowSizeForStat; private BiasedReservoirWindow<Integer> window; private Map<String, BiasedReservoirWindow<Integer>> windows = new HashMap<String, BiasedReservoirWindow<Integer>>(); private String resultType; private MessageQueue msgQueue; private String vistDepthStatQueue; private static final Logger LOG = LoggerFactory.getLogger(VisitDepthBolt.class); public VisitDepthBolt(int tickFrequencyInSeconds) { super(); this.tickFrequencyInSeconds = tickFrequencyInSeconds; } @Override public Map<String, Object> getComponentConfiguration() { Config conf = new Config(); conf.put(Config.TOPOLOGY_TICK_TUPLE_FREQ_SECS, tickFrequencyInSeconds); return conf; } @Override public void intialize(Map stormConf, TopologyContext context) { debugOn = ConfigUtility.getBoolean(stormConf,"debug.on", false); if (debugOn) { } windowSize = ConfigUtility.getInt(stormConf, "window.size"); minWindowSizeForStat = ConfigUtility.getInt(stormConf, "min.window.size.for.stat", (windowSize * 3) / 4); resultType = ConfigUtility.getString(stormConf, "result.type"); vistDepthStatQueue = ConfigUtility.getString(stormConf, "visit.depth.stat.queue"); msgQueue = MessageQueue.createMessageQueue(stormConf, vistDepthStatQueue); } @Override public boolean process(Tuple input) { boolean status = true; outputMessages.clear(); if (isTickTuple(input)) { for (String pageId : windows.keySet()) { window = windows.get(pageId); LOG.debug("got tick tuple page ID :" + pageId + " window size:" + window.size()); if (window.size() >= minWindowSizeForStat) { LOG.debug("going to do stat"); if (resultType.equals("bounceRate")) { //bounce rate int bounceCount = 0; Iterator<Integer> it = window.getIterator(); while(it.hasNext()) { if (it.next() == 1) { ++bounceCount; } } int bounceRate = (bounceCount * 100) / window.size(); LOG.debug("bounce rate:" + bounceRate); msgQueue.send(pageId + ":" + bounceRate); } else if (resultType.equals("depthDistr")){ //depth distribution TreeMap<Integer, Integer> depthDistr = new TreeMap<Integer, Integer>(); Iterator<Integer> it = window.getIterator(); while(it.hasNext()) { Integer depth = it.next(); Integer count = depthDistr.get(depth); if (null == count) { depthDistr.put(depth, 1); } else { depthDistr.put(depth, count+1); } } //serialize and write to queue String distr = Utility.serializeMap(depthDistr, ",", ":"); msgQueue.send(pageId + ":" + distr); } else if (resultType.equals("avDepth")) { int sum = 0; Iterator<Integer> it = window.getIterator(); while(it.hasNext()) { Integer depth = it.next(); sum += depth; } int avDepth = Math.round((float)sum / window.size()); LOG.debug("average depth:" + avDepth); msgQueue.send(pageId + ":" + avDepth); } } } } else { String pageId = input.getStringByField(VisitTopology.PAGE_ID); int pageCount = input.getIntegerByField(VisitTopology.PAGE_COUNT); LOG.debug("got session pageId:" + pageId + " pageCount:" + pageCount); window = windows.get(pageId); if (null == window) { window = new BiasedReservoirWindow<Integer>(windowSize); LOG.debug("created new window"); windows.put(pageId, window); } window.add(pageCount); LOG.debug("added to window new size:" + window.size()); } return status; } @Override public List<MessageHolder> getOutput() { return null; } }