skywalking oal

OAL概念

用戶自定義的描述分析過程的可擴(kuò)展, 輕量級(jí)的編譯型語言, 在運(yùn)行時(shí)編譯成 class 文件, 用于 skywalking 流計(jì)算

支持兩種

  • 硬編碼定義
  • OAL 定義, 用于指標(biāo)數(shù)據(jù), 針對(duì)特定服務(wù), 服務(wù)實(shí)例等進(jìn)行統(tǒng)計(jì)數(shù)據(jù)聚合計(jì)算

skywalking 流計(jì)算, 目前存在以下4中數(shù)據(jù)類型

  • Record: 明細(xì)數(shù)據(jù), Trace, 日志
  • Metrics: 指標(biāo)數(shù)據(jù), OAL 一般生成這個(gè)類型
  • TopN: 周期采樣數(shù)據(jù), SQL 周期性采集
  • ManagementData: 管理數(shù)據(jù), 目前只有 UITemplate用于配置管理后臺(tái)的頁面模板
  • NoneStream: 非流數(shù)據(jù)保存, 目前用于 ProfileTaskRecord

對(duì)應(yīng)的有以下處理器, 處理器的職責(zé)為將數(shù)據(jù)添加到處理隊(duì)列, 后續(xù)用 DAO 批量保存

  • RecordStreamProcessor
  • MetricsStreamProcessor
  • TopNStreamProcessor
  • ManagementStreamProcessor
  • NoneStreamStreamProcessor: 主要支撐頁面交互, 直接對(duì)db操作, 類似crud

OAL 主要用于指標(biāo)部分的自動(dòng)化生成, 對(duì)應(yīng) Metrics 和 MetricsStreamProcessor, 采用運(yùn)行時(shí)編譯成class, 因此 OAL 不會(huì)影響執(zhí)行效率

OAL 基本語法

8.0.0 版本后在 /config/*.oal 中可以直接調(diào)整配置

基礎(chǔ)語法類似于 lambda 表達(dá)式

// 聲明 Metrics
METRICS_NAME = from(CAST SCOPE.(* | [FIELD][,FIELD ...]))
[.filter(CAST FIELD OP [INT | STRING])]
.FUNCTION([PARAM][, PARAM ...])

// 禁用 Metrics
disable(METRICS_NAME);
  • METRICS_NAME: 指標(biāo)名稱
  • from: 定義數(shù)據(jù)源
    • SCOPE: 定義為: All(全局訪問), Service(服務(wù)), ServiceInstance(服務(wù)實(shí)例), Endpoint, ServiceRelation, ServiceInstanceRelation, and EndpointRelation
    • (* | [FIELD][,FIELD ...]): 需要提取的字段
    • CAST: 類型轉(zhuǎn)換, 類似 lambda 的map, 例如: from((str->long)Service.tag["transmission.latency"]) 將 transmission.latency 字段轉(zhuǎn)換為 long 類型, filter, function 中都可以使用
  • filter(可選): 通過定義的字段來過濾, 可以是多個(gè), 也可以沒有, 多個(gè)時(shí)為 AND 關(guān)系
    • 多級(jí)過濾: service_2xx = from(Service.*).filter(responseCode >= 200).filter(responseCode < 400).cpm()
  • FUNCTION: 聚集函數(shù)定義, 聚合生成新的指標(biāo), 例如: 百分比, longAvg, percent, rate, count, histogram(熱力圖), apdex 等等

官方文檔

OAL 工作階段

詞法和語法分析

通過 Antlr 定義, 源碼語法定義在 oap-server/oal-grammar 中, 分為 OALLexer.g4 和 OALParser.g4

  • OALLexer: 定義詞法樹
  • OALParser: 定義語法樹

基本語法結(jié)構(gòu)如下

// 定義最上級(jí)語法,  Metrics 聲明 或是 禁用  Metrics
root
    : (aggregationStatement | disableStatement)*
    ;

// Metrics 聲明則包含  "變量 = metricStatement 注釋" 的機(jī)構(gòu)
aggregationStatement
    : variable (SPACE)? EQUAL (SPACE)? metricStatement DelimitedComment? LineComment? (SEMI|EOF)
    ;

// 聲明 disable語法, disable(METRICS_NAME);
disableStatement
    : DISABLE LR_BRACKET disableSource RR_BRACKET DelimitedComment? LineComment? (SEMI|EOF)
    ;

// 定義 from(...).filter(...)+.function(...) 的結(jié)構(gòu)
metricStatement
    : FROM LR_BRACKET source (sourceAttributeStmt+) RR_BRACKET (filterStatement+)? DOT aggregateFunction
    ;

動(dòng)態(tài)代碼生成

動(dòng)態(tài)代碼生成通過 Javassist 輔助生成運(yùn)行時(shí)代碼, 直接將生成好代碼注入 JVM, 代碼模板位于 oap-server/oal-rt 項(xiàng)目的 resouces 目錄中, 模板通過freemarker處理成真正的代碼

生成代碼的目標(biāo)為

  1. 生成帶 @Stream 注解的 XXXMetrics(抽象類) 的實(shí)現(xiàn)類, 此實(shí)現(xiàn)類和使用的 function 對(duì)應(yīng), 有 AvgFunction, AvgHistogramFunction 等等
  2. 生成 XXXMetrics 的 StorageBuilder , 用于 具體 Metrics 實(shí)現(xiàn)到 Map<String, Object> 相互轉(zhuǎn)換
  3. 生成 XXXDispather, 用于 metrics 的基本信息注冊(cè), 并調(diào)用 MetricsStreamProcessor#in 入隊(duì),稍后進(jìn)行批量處理

觸發(fā)的流程

  1. CoreModuleProvider#prepare 中注冊(cè) OALEngineLoaderService
  2. CoreModuleProvider#start 加載 oal/disable.oal
  3. MeshReceiverProvider#start 加載 oal/core.oal
  4. ...

加載代碼為 OALEngineLoaderService#load

// 加載引擎
OALEngine engine = loadOALEngine(define);
StreamAnnotationListener streamAnnotationListener = new StreamAnnotationListener(moduleManager);
engine.setStreamListener(streamAnnotationListener);
// 設(shè)置用于Metrics分配服務(wù) DispatcherManager
engine.setDispatcherListener(moduleManager.find(CoreModule.NAME)
                                          .provider()
                                          .getService(SourceReceiver.class)
                                          .getDispatcherDetectorListener());
// 設(shè)置dao存儲(chǔ)模塊                                          
engine.setStorageBuilderFactory(moduleManager.find(StorageModule.NAME)
                                             .provider()
                                             .getService(StorageBuilderFactory.class));
// 開始解析
engine.start(OALEngineLoaderService.class.getClassLoader());
engine.notifyAllListeners();

oalDefineSet.add(define);

OALEngine 目前實(shí)現(xiàn)類為 OALRuntime, OALRuntime 實(shí)現(xiàn)如下, 主要生成 Metrics 數(shù)據(jù), 以及 MetricsBuilder( Metrics 對(duì)象到 Map<String,Object> 的映射), 以下 XXX 對(duì)應(yīng) metrics name

  1. 初始化進(jìn)行 freemarker 的 Configuration 初始化, 加載 /code-templates 下模板
  2. 讀取配置文件 *.oal
  3. 通過語法分析ScriptParser 將 oal 中的配置解析成 OALScripts
  4. 將 OALScripts 處理成 XXXMetrics 對(duì)應(yīng)的 Class, 具體方法通過 OALRuntime#generateMetricsClass, 通過 javassit 生成 (OALRuntime#generateMetricsClass)
    • 構(gòu)建空的構(gòu)造函數(shù)
    • 添加 field
    • 通過 freemarker 生成 method, 定義好的 method 有 id, hashCode, equals, serialize, deserialize, getMeta, toHour, toDay, 每個(gè)方法在 /code-templates 下都有對(duì)應(yīng)模板
    • 添加 Stream 注解, @Stream(name = "${tableName}", scopeId = ${sourceScopeId}, builder = ${metricsName}Metrics.Builder.class, processor = MetricsStreamProcessor.class)
  5. 將 OALScripts 處理成對(duì)應(yīng)的 XXXMetricsBuilder, 實(shí)現(xiàn) entity2Storage 和 storage2Entity 方法, 用于 dao 層到 實(shí)體的轉(zhuǎn)換
  6. 將 OALScripts 處理成對(duì)應(yīng)的 ${scopeName}Dispatcher

生成器可以通過環(huán)境變量配置 SW_OAL_ENGINE_DEBUG 類設(shè)置是否生成對(duì)應(yīng)的.class文件

實(shí)例

oal 配置如下, from 定義了 Scope, function 定義了對(duì)應(yīng)的 Metrics 實(shí)現(xiàn)類

  • Scope 相同的將合并到同一個(gè) SourceDispather 中進(jìn)行分發(fā)
  • function 則通過不同的實(shí)現(xiàn)類進(jìn)行分發(fā)
all_percentile = from(All.latency).percentile(10);  // Multiple values including p50, p75, p90, p95, p99
all_heatmap = from(All.latency).histogram(100, 20);

對(duì)應(yīng)生成代碼, 上述兩條oal 都對(duì)應(yīng)到同一個(gè) Scope 模塊, 都對(duì)應(yīng)到 ALL 上

/**
* SourceDispatcher 分發(fā)器
*/
public class AllDispatcher implements SourceDispatcher<All> {
    private void doAllPercentile(All var1) {
        AllPercentileMetrics var2 = new AllPercentileMetrics();
        var2.setTimeBucket(var1.getTimeBucket());
        var2.combine(var1.getLatency(), 10);
        MetricsStreamProcessor.getInstance().in(var2);
    }

    private void doAllHeatmap(All var1) {
        AllHeatmapMetrics var2 = new AllHeatmapMetrics();
        var2.setTimeBucket(var1.getTimeBucket());
        var2.combine(var1.getLatency(), 100, 20);
        MetricsStreamProcessor.getInstance().in(var2);
    }

    public void dispatch(ISource var1) {
        All var2 = (All)var1;
        this.doAllPercentile(var2);
        this.doAllHeatmap(var2);
    }

    public AllDispatcher() {
    }
}
/**
* 帶 function 的Metrics
*/
@Stream(
    name = "all_heatmap",
    scopeId = DefaultScopeDefine.ALL,
    builder = AllHeatmapMetricsBuilder.class,
    processor = MetricsStreamProcessor.class
)
public class AllHeatmapMetrics extends HistogramMetrics implements WithMetadata {
    public AllHeatmapMetrics() {
    }

    protected String id0() {
        StringBuilder var1 = new StringBuilder(String.valueOf(this.getTimeBucket()));
        return var1.toString();
    }

    public int hashCode() {
        byte var1 = 17;
        int var2 = 31 * var1 + (int)this.getTimeBucket();
        return var2;
    }

    public int remoteHashCode() {
        byte var1 = 17;
        return var1;
    }

    public boolean equals(Object var1) {
        if (this == var1) {
            return true;
        } else if (var1 == null) {
            return false;
        } else if (this.getClass() != var1.getClass()) {
            return false;
        } else {
            AllHeatmapMetrics var2 = (AllHeatmapMetrics)var1;
            return this.getTimeBucket() == var2.getTimeBucket();
        }
    }

    public Builder serialize() {
        Builder var1 = RemoteData.newBuilder();
        var1.addDataLongs(this.getTimeBucket());
        var1.addDataObjectStrings(this.getDataset().toStorageData());
        return var1;
    }

    public void deserialize(RemoteData var1) {
        this.setTimeBucket(var1.getDataLongs(0));
        this.setDataset(new DataTable(var1.getDataObjectStrings(0)));
    }

    public MetricsMetaInfo getMeta() {
        return new MetricsMetaInfo("all_heatmap", 0);
    }

    public Metrics toHour() {
        AllHeatmapMetrics var1 = new AllHeatmapMetrics();
        DataTable var2 = new DataTable();
        var2.copyFrom(this.getDataset());
        var1.setDataset(var2);
        var1.setTimeBucket(this.toTimeBucketInHour());
        return var1;
    }

    public Metrics toDay() {
        AllHeatmapMetrics var1 = new AllHeatmapMetrics();
        DataTable var2 = new DataTable();
        var2.copyFrom(this.getDataset());
        var1.setDataset(var2);
        var1.setTimeBucket(this.toTimeBucketInDay());
        return var1;
    }
}

/**
* StorageBuilder 實(shí)現(xiàn)
*/
public class AllHeatmapMetricsBuilder implements StorageHashMapBuilder {
    public AllHeatmapMetricsBuilder() {
    }

    public Map entity2Storage(StorageData var1) {
        AllHeatmapMetrics var2 = (AllHeatmapMetrics)var1;
        HashMap var3 = new HashMap();
        var3.put((Object)"dataset", var2.getDataset());
        var3.put((Object)"time_bucket", new Long(var2.getTimeBucket()));
        return var3;
    }

    public StorageData storage2Entity(Map var1) {
        AllHeatmapMetrics var2 = new AllHeatmapMetrics();
        var2.setDataset(new DataTable((String)var1.get("dataset")));
        var2.setTimeBucket(((Number)var1.get("time_bucket")).longValue());
        return var2;
    }
}


@Stream(
    name = "all_percentile",
    scopeId = DefaultScopeDefine.ALL,
    builder = AllPercentileMetricsBuilder.class,
    processor = MetricsStreamProcessor.class
)
public class AllPercentileMetrics extends PercentileMetrics implements WithMetadata {
    public AllPercentileMetrics() {
    }
    ...
}

grpc 的模塊依賴

Trace 模塊依賴如下, AnalyzerModule 會(huì)依賴 AnalyzerModule, AnalyzerModule 加載時(shí)會(huì)對(duì) core.oal 進(jìn)行加載, 因此在 grpc 接口調(diào)用時(shí) oal 已生成完成

@Override
public String[] requiredModules() {
    return new String[] {
        TelemetryModule.NAME,
        CoreModule.NAME,
        AnalyzerModule.NAME,
        SharingServerModule.NAME,
        ConfigurationModule.NAME
    };
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市撵渡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖寸爆,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寞钥,死亡現(xiàn)場(chǎng)離奇詭異蹋肮,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瘾腰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來覆履,“玉大人蹋盆,你說我怎么就攤上這事费薄。” “怎么了栖雾?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵楞抡,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我析藕,道長(zhǎng)召廷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任账胧,我火速辦了婚禮竞慢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘治泥。我一直安慰自己筹煮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布车摄。 她就那樣靜靜地躺著寺谤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吮播。 梳的紋絲不亂的頭發(fā)上变屁,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音意狠,去河邊找鬼粟关。 笑死,一個(gè)胖子當(dāng)著我的面吹牛环戈,可吹牛的內(nèi)容都是我干的闷板。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼院塞,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼遮晚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拦止,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤县遣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后汹族,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體萧求,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年顶瞒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了夸政。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡榴徐,死狀恐怖守问,靈堂內(nèi)的尸體忽然破棺而出匀归,到底是詐尸還是另有隱情,我是刑警寧澤酪碘,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布朋譬,位于F島的核電站,受9級(jí)特大地震影響兴垦,放射性物質(zhì)發(fā)生泄漏徙赢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一探越、第九天 我趴在偏房一處隱蔽的房頂上張望狡赐。 院中可真熱鬧,春花似錦钦幔、人聲如沸枕屉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搀擂。三九已至,卻和暖如春卷玉,著一層夾襖步出監(jiān)牢的瞬間哨颂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工相种, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留威恼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓寝并,卻偏偏與公主長(zhǎng)得像箫措,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子衬潦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

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