如何復(fù)制Pinpoint中一條調(diào)用鏈的完整數(shù)據(jù)

如何復(fù)制Pinpoint中一條調(diào)用鏈的完整數(shù)據(jù)

如果你熟悉Pinpoint的話渗稍,你應(yīng)該知道一條調(diào)用鏈包含哪些數(shù)據(jù)
在這里我指的是com.navercorp.pinpoint.web.controller.BusinessTransactionController#transactionInfo方法中查詢會(huì)涉及到的HBase數(shù)據(jù)阴孟。
為什么會(huì)產(chǎn)生這個(gè)需求呢,HBase中的數(shù)據(jù)都是配置了TTL的循集,過一段時(shí)間會(huì)被清理,你可能就和要這條骨骼驚奇的調(diào)用鏈說拜拜了。
如果能夠離線或者保存另外的HBase里,可以更快的復(fù)現(xiàn)場景進(jìn)行調(diào)試和排查局雄。


image.png

先看一下到底會(huì)查哪些表吧。

  • TraceV2
  • ApiMetaData
  • StringMetaData
  • SqlMetaData_Ver2

Pinpoint在構(gòu)造調(diào)用鏈界面需要的信息的時(shí)候存炮,TraceV2是一行數(shù)據(jù)炬搭,用事務(wù)號(hào)就能查詢出來。
然后遍歷這行數(shù)據(jù)里的Span和SpanEvent穆桂,使用包含的apiId,stringId,sqlId去后三個(gè)表對應(yīng)查詢所關(guān)聯(lián)的數(shù)據(jù)宫盔。
后面三個(gè),一個(gè)復(fù)雜的調(diào)用鏈會(huì)查詢很多次充尉。怎么才能知道呢飘言?

下面只是記錄一下本地試驗(yàn)的方法,僅在測試環(huán)境中使用驼侠。

利用Spring AOP 將hbase查詢結(jié)果 插入到另外的hbase中

我想了下,如果在hbase查詢的時(shí)候進(jìn)行AOP攔截谆吴,并且把數(shù)據(jù)發(fā)送到另外一個(gè)hbase的話倒源,這樣不就能把一條調(diào)用鏈的數(shù)據(jù)給剝離出來了么?
我把將查詢出來的數(shù)據(jù)插入到別的hbase的過程叫做逆轉(zhuǎn)句狼。

我們要尋找一些合適的spring bean笋熬,因?yàn)閟pring的aop只能作用在spring創(chuàng)建的對象上。

首先我注意到 com.navercorp.pinpoint.common.hbase.RowMapper#mapRow

public interface RowMapper<T> {

    T mapRow(Result result, int rowNum) throws Exception;
}

第一個(gè)參數(shù)org.apache.hadoop.hbase.client.Result包含了查詢出來的一行數(shù)據(jù)腻菇,現(xiàn)在就差個(gè)表名了胳螟。

仔細(xì)看pinpoint的代碼昔馋,每次執(zhí)行hbase操作前都會(huì)調(diào)用com.navercorp.pinpoint.common.hbase.TableDescriptor#getTableName獲取表名。
這樣設(shè)置一個(gè)線程上下文(https://github.com/apache/shiro/blob/master/core/src/main/java/org/apache/shiro/util/ThreadContext.java)糖耸,就能將表名和多行數(shù)據(jù)完整聯(lián)系在一起了秘遏。

最后線程上下文里面還需要設(shè)置一個(gè)是否逆轉(zhuǎn)查詢數(shù)據(jù)的標(biāo)志。
對于查詢單條調(diào)用鏈來說就是com.navercorp.pinpoint.web.service.SpanService#selectSpan方法進(jìn)入的時(shí)候開啟標(biāo)志嘉竟。

實(shí)現(xiàn)

首先仿照shiro弄個(gè)線程上下文邦危。

package com.navercorp.pinpoint.web.dao;

import java.util.HashMap;
import java.util.Map;

/**
 * @author tankilo
 * https://github.com/apache/shiro/blob/master/core/src/main/java/org/apache/shiro/util/ThreadContext.java
 */
public final class ThreadContext {
    private ThreadContext() {

    }

    public static final String REVERSE = "REVERSE";
    public static final String TABLE_NAME = "TABLE_NAME";
    private static ThreadLocal<Map<String, Object>> resources = new InheritableThreadLocal<Map<String, Object>>() {
        @Override
        protected Map<String, Object> initialValue() {
            return new HashMap<>(8);
        }
    };

    public static void put(String key, Object value) {
        if (key == null) {
            throw new IllegalArgumentException("key cannot be null");
        }

        if (value == null) {
            remove(key);
            return;
        }
        ensureResourcesInitialized();
        resources.get().put(key, value);
    }

    private static void ensureResourcesInitialized() {
        if (resources.get() == null) {
            resources.set(new HashMap<>(8));
        }
    }

    public static void remove(String key) {
        Map<String, Object> map = resources.get();
        if (map != null) {
            map.remove(key);
        }
    }

    public static Object get(String key) {
        Map<String, Object> map = resources.get();
        if (map != null) {
            return map.get(key);
        } else {
            return null;
        }
    }

    private static Object getValue(Object key) {
        Map<String, Object> perThreadResources = resources.get();
        return perThreadResources != null ? perThreadResources.get(key) : null;
    }

    private static Boolean getBoolean(String key, Boolean defaultValue) {
        Object value = getValue(key);
        if (null != value) {
            return Boolean.valueOf(value.toString());
        }
        return defaultValue;
    }

    private static String getString(String key) {
        Object value = getValue(key);
        if (null != value) {
            return value.toString();
        } else {
            return null;
        }
    }

    public static void setReverse(boolean reverse) {
        put(REVERSE, reverse);

    }

    public static boolean isReverse() {
        return getBoolean(REVERSE, false);
    }

    public static void setTableName(String tableName) {
        put(TABLE_NAME, tableName);
    }

    public static String getTableName() {
        return getString(TABLE_NAME);
    }

    public static void remove() {
        resources.remove();
    }
}

切面

@Aspect
public class HbaseTemplateReverseAspect {

    @Autowired
    @Qualifier("hbaseTemplateReverse")
    private HbaseOperations2 template2;

    @Autowired
    private HbaseTableNameProvider tableNameProvider;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Pointcut("execution(public * com.navercorp.pinpoint.common.hbase.RowMapper.mapRow(..))")
    public void pointCut() {
    }

    @After("pointCut() && args(result,rowNum)")
    public void doBefore(Result result, int rowNum) {
        if (ThreadContext.isReverse()) {
            String tableNameStr = ThreadContext.getTableName();
            TableName tableName = tableNameProvider.getTableName(tableNameStr);
            Put put = new Put(result.getRow());
            Cell[] rawCells = result.rawCells();
            for (Cell cell : rawCells) {
                put.addColumn(CellUtil.cloneFamily(cell), CellUtil.cloneQualifier(cell), cell.getTimestamp(), CellUtil.cloneValue(cell));
            }
            template2.asyncPut(tableName, put);
        }
    }

    @Before("execution(public * com.navercorp.pinpoint.web.dao.hbase.HbaseTraceDaoV2.selectSpan(..))")
    public void selectSpan() {
        ThreadContext.setTableName(HbaseTable.TRACE_V2.getName());
    }

    @AfterReturning(returning = "tableName", pointcut = "execution(public * com.navercorp.pinpoint.common.hbase.TableDescriptor.getTableName())")
    public void getTableName(TableName tableName) {
        ThreadContext.setTableName(tableName.getNameAsString());
    }

    @Around("execution(public * com.navercorp.pinpoint.web.service.SpanService.selectSpan(..))")
    public Object dealContext(ProceedingJoinPoint jp) throws Throwable {
        Object result;
        try {
            ThreadContext.setReverse(true);
            result = jp.proceed();
        } finally {
            ThreadContext.remove();
        }
        return result;
    }
}

遺憾

里面有些曲折,看上面代碼也知道舍扰。
com.navercorp.pinpoint.web.dao.hbase.HbaseTraceDaoV2#spanMapperV2 不是spring bean倦蚪,而是手動(dòng)構(gòu)造的。
這里我就沒有繼續(xù)弄下去了边苹,因?yàn)楦杏Xspring aop的局限性很大,計(jì)劃用java instrumentation api陵且,和pinpoint的agent一樣重新弄下。

代碼提交備份在我的github上 https://github.com/tankilo/pinpoint/tree/spring-aop-hbase-reverse

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末个束,一起剝皮案震驚了整個(gè)濱河市滩报,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌播急,老刑警劉巖脓钾,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件硝岗,死亡現(xiàn)場離奇詭異短条,居然都是意外死亡痹筛,警方通過查閱死者的電腦和手機(jī)锹安,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門议忽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赴魁,“玉大人拐邪,你說我怎么就攤上這事丹弱±檬澹” “怎么了谨胞?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蒜鸡。 經(jīng)常有香客問我胯努,道長,這世上最難降的妖魔是什么逢防? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任叶沛,我火速辦了婚禮,結(jié)果婚禮上忘朝,老公的妹妹穿的比我還像新娘灰署。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布溉箕。 她就那樣靜靜地躺著晦墙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪肴茄。 梳的紋絲不亂的頭發(fā)上晌畅,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音独郎,去河邊找鬼踩麦。 笑死,一個(gè)胖子當(dāng)著我的面吹牛氓癌,可吹牛的內(nèi)容都是我干的谓谦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼贪婉,長吁一口氣:“原來是場噩夢啊……” “哼反粥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起疲迂,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對情侶失蹤才顿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后尤蒿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體郑气,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年腰池,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了尾组。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡示弓,死狀恐怖讳侨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奏属,我是刑警寧澤跨跨,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站囱皿,受9級(jí)特大地震影響勇婴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜铆帽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一咆耿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧爹橱,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至组砚,卻和暖如春吻商,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背糟红。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工艾帐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盆偿。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓柒爸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親事扭。 傳聞我的和親對象是個(gè)殘疾皇子捎稚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容