[강운덕] [LUCYSUS-1744] filter 기반 map 생성 로직 리팩토링. string 을 key로 삼는 부분 개선.

git-svn-id: http://svn.bds.nhncorp.com/pe/hippo-web/trunk@2877 84d0f5b1-2673-498c-a247-62c4ff18d310
This commit is contained in:
Woonduk Kang
2013-11-12 10:04:36 +00:00
parent e031104de0
commit 3c900c8597
10 changed files with 360 additions and 119 deletions
@@ -130,7 +130,6 @@ public class ApplicationMapServiceImpl implements ApplicationMapService {
* @param calleeApplicationName
* @param from
* @param to
* @param foundApplications
* @return
*/
private Set<TransactionFlowStatistics> selectCaller(String calleeApplicationName, short calleeServiceType, long from, long to, Set<String> calleeFoundApplications, Set<String> callerFoundApplications) {
@@ -180,6 +179,7 @@ public class ApplicationMapServiceImpl implements ApplicationMapService {
Application app = hostApplicationMapDao.findApplicationName(stat.getTo(), from, to);
if (app != null) {
logger.debug("Application info replaced. {} => {}", stat, app);
stat.setTo(app.getApplicationName());
stat.setToServiceType(app.getServiceType());
return true;
@@ -0,0 +1,73 @@
package com.nhn.pinpoint.web.service;
/**
* @author emeroad
*/
public class ComplexNodeId implements NodeId {
private Node src;
private Node dest;
public ComplexNodeId(Node src, Node dest) {
if (src == null) {
throw new NullPointerException("src must not be null");
}
if (dest == null) {
throw new NullPointerException("dest must not be null");
}
this.src = src;
this.dest = dest;
}
public Node getSrc() {
return src;
}
public void setSrc(Node src) {
if (src == null) {
throw new NullPointerException("src must not be null");
}
this.src = src;
}
public Node getDest() {
return dest;
}
public void setDest(Node dest) {
if (dest == null) {
throw new NullPointerException("dest must not be null");
}
this.dest = dest;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ComplexNodeId that = (ComplexNodeId) o;
if (!dest.equals(that.dest)) return false;
if (!src.equals(that.src)) return false;
return true;
}
@Override
public int hashCode() {
int result = src.hashCode();
result = 31 * result + dest.hashCode();
return result;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("ComplexNodeId{");
sb.append("src=").append(src);
sb.append(", dest=").append(dest);
sb.append('}');
return sb.toString();
}
}
@@ -9,6 +9,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -85,22 +86,15 @@ public class FilteredApplicationMapServiceImpl implements FilteredApplicationMap
StopWatch watch = new StopWatch();
watch.start();
List<List<SpanBo>> transactionList = this.traceDao.selectAllSpans(traceIdSet);
List<SpanBo> transaction = new ArrayList<SpanBo>();
for (List<SpanBo> t : transactionList) {
if (filter.include(t)) {
for (SpanBo span : t) {
transaction.add(span);
}
}
}
List<List<SpanBo>> originalList = this.traceDao.selectAllSpans(traceIdSet);
List<SpanBo> filteredTransactionList = filterList(originalList, filter);
LinkStatistics statistics = new LinkStatistics(from, to);
// TODO fromToFilter처럼. node의 타입에 따른 처리 필요함.
// scan transaction list
for (SpanBo span : transaction) {
for (SpanBo span : filteredTransactionList) {
if (srcApplicationName.equals(span.getApplicationId()) && srcServiceType == span.getServiceType().getCode()) {
List<SpanEventBo> spanEventBoList = span.getSpanEventBoList();
if (spanEventBoList == null) {
@@ -127,7 +121,27 @@ public class FilteredApplicationMapServiceImpl implements FilteredApplicationMap
return statistics;
}
@Override
private List<SpanBo> filterList(List<List<SpanBo>> transactionList, Filter filter) {
final List<SpanBo> filteredResult = new ArrayList<SpanBo>();
for (List<SpanBo> transaction : transactionList) {
if (filter.include(transaction)) {
filteredResult.addAll(transaction);
}
}
return filteredResult;
}
private List<List<SpanBo>> filterList2(List<List<SpanBo>> transactionList, Filter filter) {
final List<List<SpanBo>> filteredResult = new ArrayList<List<SpanBo>>();
for (List<SpanBo> transaction : transactionList) {
if (filter.include(transaction)) {
filteredResult.add(transaction);
}
}
return filteredResult;
}
@Override
public ApplicationMap selectApplicationMap(TransactionId transactionId) {
if (transactionId == null) {
throw new NullPointerException("transactionId must not be null");
@@ -155,126 +169,58 @@ public class FilteredApplicationMapServiceImpl implements FilteredApplicationMap
// 개별 객체를 각각 보고 재귀 내용을 삭제함.
// 향후 tree base로 충돌구간을 점검하여 없앨 경우 여기서 filter를 치면 안됨.
Collection<TransactionId> filterdList = recursiveCallFilter(transactionIdList);
final Collection<TransactionId> recursiveFilterList = recursiveCallFilter(transactionIdList);
// FIXME 나중에 List<Span>을 순회하면서 실행할 process chain을 두는것도 괜찮을듯.
List<List<SpanBo>> transactionList = this.traceDao.selectAllSpans(filterdList);
final List<List<SpanBo>> originalList = this.traceDao.selectAllSpans(recursiveFilterList);
final List<List<SpanBo>> filterList = filterList2(originalList, filter);
Set<TransactionFlowStatistics> statisticsData = new HashSet<TransactionFlowStatistics>();
Map<String, TransactionFlowStatistics> statisticsMap = new HashMap<String, TransactionFlowStatistics>();
Map<Long, SpanBo> transactionSpanMap = new HashMap<Long, SpanBo>();
Set<TransactionFlowStatistics> statisticsData = new HashSet<TransactionFlowStatistics>();
Map<NodeId, TransactionFlowStatistics> statisticsMap = new HashMap<NodeId, TransactionFlowStatistics>();
TimeSeriesStore tr = new TimeSeriesStoreImpl2(from, to);
final TimeSeriesStore timeSeriesStore = new TimeSeriesStoreImpl2(from, to);
/**
* 통계정보로 변환한다.
*/
for (List<SpanBo> transaction : transactionList) {
if (!filter.include(transaction)) {
continue;
}
transactionSpanMap.clear();
for (List<SpanBo> transaction : filterList) {
final Map<Long, SpanBo> transactionSpanMap = new HashMap<Long, SpanBo>(transactionIdList.size());
for (SpanBo span : transaction) {
transactionSpanMap.put(span.getSpanId(), span);
}
final SpanBo old = transactionSpanMap.put(span.getSpanId(), span);
logger.warn("duplicated span found:{}", old);
}
for (SpanBo span : transaction) {
String src;
String dest;
ServiceType srcServiceType;
ServiceType destServiceType;
SpanBo parentSpan = transactionSpanMap.get(span.getParentSpanId());
if (span.isRoot() || parentSpan == null) {
src = span.getApplicationId();
srcServiceType = ServiceType.CLIENT;
} else {
src = parentSpan.getApplicationId();
srcServiceType = parentSpan.getServiceType();
}
dest = span.getApplicationId();
destServiceType = span.getServiceType();
if (!destServiceType.isRecordStatistics() || destServiceType.isRpcClient()) {
final Node srcNode = createNode(span, transactionSpanMap);
final Node destNode = new Node(span.getApplicationId(), span.getServiceType());
// TODO API의 의미가 link라고 하기는 좀 애매함. 변경필요.
if (destNode.isLink()) {
continue;
}
String statId = TransactionFlowStatisticsUtils.makeId(src, srcServiceType, dest, destServiceType);
TransactionFlowStatistics stat = (statisticsMap.containsKey(statId) ? statisticsMap.get(statId) : new TransactionFlowStatistics(src, srcServiceType, dest, destServiceType));
final ComplexNodeId statId = new ComplexNodeId(srcNode, destNode);
TransactionFlowStatistics stat;
if (statisticsMap.containsKey(statId)) {
stat = statisticsMap.get(statId);
}
else {
stat = new TransactionFlowStatistics(srcNode.getName(), srcNode.getServiceType(), destNode.getName(), destNode.getServiceType());
}
int slot;
if (span.hasException()) {
slot = Histogram.ERROR_SLOT.getSlotTime();
} else {
slot = destServiceType.getHistogram().findHistogramSlot(span.getElapsed()).getSlotTime();
}
final int slot = getHistogramSlot(span, destNode.getServiceType());
stat.addSample(dest, destServiceType.getCode(), (short) slot, 1);
stat.addSample(destNode.getName(), destNode.getServiceType().getCode(), (short) slot, 1);
statisticsData.add(stat);
statisticsMap.put(statId, stat);
// link timeseries statistics추가.
tr.add(statId, span.getCollectorAcceptTime(), slot, 1L, span.hasException());
timeSeriesStore.add(statId, span.getCollectorAcceptTime(), slot, 1L, span.hasException());
// application timeseries statistics
tr.add(span.getApplicationId(), span.getCollectorAcceptTime(), slot, 1L, span.hasException());
timeSeriesStore.add(new SimpleNodeId(span.getApplicationId()), span.getCollectorAcceptTime(), slot, 1L, span.hasException());
/**
* span event의 statistics추가.
*/
List<SpanEventBo> spanEventBoList = span.getSpanEventBoList();
if (spanEventBoList == null || spanEventBoList.isEmpty()) {
continue;
}
src = span.getApplicationId();
srcServiceType = span.getServiceType();
for (SpanEventBo spanEvent : spanEventBoList) {
dest = spanEvent.getDestinationId();
destServiceType = spanEvent.getServiceType();
if (!destServiceType.isRecordStatistics() /*|| destServiceType.isRpcClient()*/) {
continue;
}
// rpc client이면서 acceptor가 없으면 unknown으로 변환시킨다.
// 내가 아는 next spanid를 spanid로 가진 span이 있으면 acceptor가 존재하는 셈.
if (destServiceType.isRpcClient()) {
if (transactionSpanMap.containsKey(spanEvent.getNextSpanId())) {
continue;
} else {
destServiceType = ServiceType.UNKNOWN_CLOUD;
}
}
String statId2 = TransactionFlowStatisticsUtils.makeId(src, srcServiceType, dest, destServiceType);
TransactionFlowStatistics stat2 = (statisticsMap.containsKey(statId2) ? statisticsMap.get(statId2) : new TransactionFlowStatistics(src, srcServiceType, dest, destServiceType));
int slot2;
if (spanEvent.hasException()) {
slot2 = Histogram.ERROR_SLOT.getSlotTime();
} else {
slot2 = destServiceType.getHistogram().findHistogramSlot(spanEvent.getEndElapsed()).getSlotTime();
}
// FIXME
// stat2.addSample((dest == null) ? spanEvent.getEndPoint() : dest, destServiceType.getCode(), (short) slot2, 1);
stat2.addSample(spanEvent.getEndPoint(), destServiceType.getCode(), (short) slot2, 1);
// agent 정보추가. destination의 agent정보 알 수 없음.
statisticsData.add(stat2);
statisticsMap.put(statId2, stat2);
// link timeseries statistics추가.
tr.add(statId2, span.getStartTime() + spanEvent.getStartElapsed(), slot2, 1L, spanEvent.hasException());
// application timeseries statistics
tr.add(spanEvent.getDestinationId(), span.getCollectorAcceptTime(), slot2, 1L, spanEvent.hasException());
}
}
addNodeFromSpanEvent(statisticsData, statisticsMap, timeSeriesStore, transactionSpanMap, span);
}
}
// mark agent info
@@ -283,7 +229,7 @@ public class FilteredApplicationMapServiceImpl implements FilteredApplicationMap
}
ApplicationMap map = new ApplicationMap(statisticsData).build();
map.setTimeSeriesStore(tr);
map.setTimeSeriesStore(timeSeriesStore);
watch.stop();
logger.debug("Select filtered application map elapsed. {}ms", watch.getTotalTimeMillis());
@@ -291,7 +237,93 @@ public class FilteredApplicationMapServiceImpl implements FilteredApplicationMap
return map;
}
private Collection<TransactionId> recursiveCallFilter(List<TransactionId> transactionIdList) {
private void addNodeFromSpanEvent(Set<TransactionFlowStatistics> statisticsData, Map<NodeId, TransactionFlowStatistics> statisticsMap, TimeSeriesStore timeSeriesStore, Map<Long, SpanBo> transactionSpanMap, SpanBo span) {
/**
* span event의 statistics추가.
*/
final List<SpanEventBo> spanEventBoList = span.getSpanEventBoList();
if (CollectionUtils.isEmpty(spanEventBoList)) {
return;
}
final Node srcNode = new Node(span.getApplicationId(), span.getServiceType());
for (SpanEventBo spanEvent : spanEventBoList) {
final String dest = spanEvent.getDestinationId();
ServiceType destServiceType = spanEvent.getServiceType();
if (!destServiceType.isRecordStatistics() /*|| destServiceType.isRpcClient()*/) {
continue;
}
// rpc client이면서 acceptor가 없으면 unknown으로 변환시킨다.
// 내가 아는 next spanid를 spanid로 가진 span이 있으면 acceptor가 존재하는 셈.
if (destServiceType.isRpcClient()) {
if (transactionSpanMap.containsKey(spanEvent.getNextSpanId())) {
continue;
} else {
destServiceType = ServiceType.UNKNOWN_CLOUD;
}
}
final NodeId spanEventStatId = new ComplexNodeId(srcNode, new Node(dest, destServiceType));
TransactionFlowStatistics stat2;
if (statisticsMap.containsKey(spanEventStatId)) {
stat2 = statisticsMap.get(spanEventStatId);
} else {
stat2 = new TransactionFlowStatistics(srcNode.getName(), srcNode.getServiceType(), dest, destServiceType);
}
final int slot2 = getHistogramSlot(spanEvent, destServiceType);
// FIXME
// stat2.addSample((dest == null) ? spanEvent.getEndPoint() : dest, destServiceType.getCode(), (short) slot2, 1);
stat2.addSample(spanEvent.getEndPoint(), destServiceType.getCode(), (short) slot2, 1);
// agent 정보추가. destination의 agent정보 알 수 없음.
statisticsData.add(stat2);
statisticsMap.put(spanEventStatId, stat2);
// link timeseries statistics추가.
timeSeriesStore.add(spanEventStatId, span.getStartTime() + spanEvent.getStartElapsed(), slot2, 1L, spanEvent.hasException());
// application timeseries statistics
timeSeriesStore.add(new SimpleNodeId(spanEvent.getDestinationId()), span.getCollectorAcceptTime(), slot2, 1L, spanEvent.hasException());
}
}
private Node createNode(SpanBo span, Map<Long, SpanBo> transactionSpanMap) {
SpanBo parentSpan = transactionSpanMap.get(span.getParentSpanId());
if (span.isRoot() || parentSpan == null) {
String src = span.getApplicationId();
ServiceType srcServiceType = ServiceType.CLIENT;
return new Node(src, srcServiceType);
} else {
String src = parentSpan.getApplicationId();
ServiceType serviceType = parentSpan.getServiceType();
return new Node(src, serviceType);
}
}
private int getHistogramSlot(SpanEventBo spanEvent, ServiceType serviceType) {
return getHistogramSlot(spanEvent.hasException(), spanEvent.getEndElapsed(), serviceType);
}
private int getHistogramSlot(SpanBo span, ServiceType serviceType) {
return getHistogramSlot(span.hasException(), span.getElapsed(), serviceType);
}
private int getHistogramSlot(boolean hasException, int elapsedTime, ServiceType serviceType) {
if (hasException) {
return Histogram.ERROR_SLOT.getSlotTime();
} else {
return serviceType.getHistogram().findHistogramSlot(elapsedTime).getSlotTime();
}
}
private Collection<TransactionId> recursiveCallFilter(List<TransactionId> transactionIdList) {
if (transactionIdList == null) {
throw new NullPointerException("transactionIdList must not be null");
}
@@ -335,4 +367,5 @@ public class FilteredApplicationMapServiceImpl implements FilteredApplicationMap
}
return agentSet;
}
}
@@ -0,0 +1,79 @@
package com.nhn.pinpoint.web.service;
import com.nhn.pinpoint.common.ServiceType;
/**
* @author emeroad
*/
public class Node {
private String name;
private ServiceType serviceType;
public Node(String name, ServiceType serviceType) {
if (name == null) {
throw new NullPointerException("name must not be null");
}
if (serviceType == null) {
throw new NullPointerException("serviceType must not be null");
}
this.name = name;
this.serviceType = serviceType;
}
public String getName() {
return name;
}
public void setName(String name) {
if (name == null) {
throw new NullPointerException("name must not be null");
}
this.name = name;
}
public ServiceType getServiceType() {
return serviceType;
}
public void setServiceType(ServiceType serviceType) {
if (serviceType == null) {
throw new NullPointerException("serviceType must not be null");
}
this.serviceType = serviceType;
}
public boolean isLink() {
// record해야 되거나. rpc콜은 링크이다.
return !serviceType.isRecordStatistics() || serviceType.isRpcClient();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Node node = (Node) o;
if (!name.equals(node.name)) return false;
if (serviceType != node.serviceType) return false;
return true;
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + serviceType.hashCode();
return result;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Node{");
sb.append("name='").append(name).append('\'');
sb.append(", serviceType=").append(serviceType);
sb.append('}');
return sb.toString();
}
}
@@ -0,0 +1,7 @@
package com.nhn.pinpoint.web.service;
/**
* @author emeroad
*/
public interface NodeId {
}
@@ -0,0 +1,44 @@
package com.nhn.pinpoint.web.service;
/**
* @author emeroad
*/
public class SimpleNodeId implements NodeId{
private String key;
public SimpleNodeId(String key) {
if (key == null) {
throw new NullPointerException("key must not be null");
}
this.key = key;
}
public String getKey() {
return key;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SimpleNodeId that = (SimpleNodeId) o;
if (!key.equals(that.key)) return false;
return true;
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("SimpleNodeId{");
sb.append("key='").append(key).append('\'');
sb.append('}');
return sb.toString();
}
}
@@ -1,5 +1,7 @@
package com.nhn.pinpoint.web.util;
import com.nhn.pinpoint.web.service.NodeId;
/**
*
* @author netspider
@@ -1,5 +1,6 @@
package com.nhn.pinpoint.web.vo;
import com.nhn.pinpoint.web.service.NodeId;
import com.nhn.pinpoint.web.util.JsonSerializable;
/**
@@ -8,5 +9,5 @@ import com.nhn.pinpoint.web.util.JsonSerializable;
*
*/
public interface TimeSeriesStore extends JsonSerializable {
void add(String key, long timestamp, int responseTimeslot, long callCount, boolean isFailed);
void add(NodeId key, long timestamp, int responseTimeslot, long callCount, boolean isFailed);
}
@@ -7,6 +7,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.nhn.pinpoint.web.service.NodeId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -45,7 +46,7 @@ public class TimeSeriesStoreImpl1 implements TimeSeriesStore {
/**
* key = id value = value list
*/
private final Map<String, List<Long>> values = new HashMap<String, List<Long>>();
private final Map<NodeId, List<Long>> values = new HashMap<NodeId, List<Long>>();
private final long from;
private final long to;
@@ -61,7 +62,7 @@ public class TimeSeriesStoreImpl1 implements TimeSeriesStore {
}
@Override
public void add(String key, long timestamp, int responseTimeslot, long callCount, boolean isFailed) {
public void add(NodeId key, long timestamp, int responseTimeslot, long callCount, boolean isFailed) {
}
};
@@ -93,7 +94,7 @@ public class TimeSeriesStoreImpl1 implements TimeSeriesStore {
return list;
}
public void add(String key, long timestamp, int responseTimeslot, long callCount, boolean isFailed) {
public void add(NodeId key, long timestamp, int responseTimeslot, long callCount, boolean isFailed) {
logger.debug("add sample key={}, timestamp={} responseTimeSlot={}, count={}", key, timestamp, responseTimeslot, callCount, isFailed);
List<Long> list = values.get(key);
@@ -117,9 +118,9 @@ public class TimeSeriesStoreImpl1 implements TimeSeriesStore {
}
StringBuilder sb = new StringBuilder();
sb.append("{\"values\":{");
Iterator<Entry<String, List<Long>>> entryIterator = values.entrySet().iterator();
Iterator<Entry<NodeId, List<Long>>> entryIterator = values.entrySet().iterator();
while (entryIterator.hasNext()) {
Entry<String, List<Long>> entry = entryIterator.next();
Entry<NodeId, List<Long>> entry = entryIterator.next();
sb.append("\"").append(entry.getKey()).append("\":[");
Iterator<Long> valueIterator = entry.getValue().iterator();
while (valueIterator.hasNext()) {
@@ -3,6 +3,7 @@ package com.nhn.pinpoint.web.vo;
import java.util.HashMap;
import java.util.Map;
import com.nhn.pinpoint.web.service.NodeId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -19,7 +20,7 @@ public class TimeSeriesStoreImpl2 implements TimeSeriesStore {
private final long from;
private final long to;
private final Map<String, LinkStatistics> data = new HashMap<String, LinkStatistics>();
private final Map<NodeId, LinkStatistics> data = new HashMap<NodeId, LinkStatistics>();
private final boolean enabled;
public TimeSeriesStoreImpl2(long from, long to) {
@@ -33,7 +34,7 @@ public class TimeSeriesStoreImpl2 implements TimeSeriesStore {
}
@Override
public void add(String key, long timestamp, int responseTimeslot, long callCount, boolean isFailed) {
public void add(NodeId key, long timestamp, int responseTimeslot, long callCount, boolean isFailed) {
if (!enabled) {
logger.debug("store is not enabled.");
return;