最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

golang beyla采集trace程序原理源碼解析

 更新時(shí)間:2024年02月16日 10:43:51   作者:a朋  
beyla支持通過(guò)ebpf,無(wú)侵入的、自動(dòng)采集應(yīng)用程序的trace信息,本文以golang的nethttp為例,講述beyla對(duì)trace的采集的實(shí)現(xiàn)原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助

一. 整體原理

trace采集時(shí),監(jiān)聽(tīng)了golang應(yīng)用程序的net/http中的函數(shù):

  • net/http.serverHandler.ServeHTTP;
  • net/http.(*Transport).roundTrip;

監(jiān)聽(tīng)ServeHTTP時(shí):

  • 若request中沒(méi)有trace信息,則生成traceparent,存入go_trace_map結(jié)構(gòu)(key=goroutine地址,value=trace信息);
  • 若request中有trace信息,則根據(jù)trace信息,重新生成span,存入go_trace_map結(jié)構(gòu);

監(jiān)聽(tīng)roundTrip的調(diào)用:

  • 首先,根據(jù)goroutine地址,讀go_trace_map結(jié)構(gòu),得到trace信息;
  • 然后,將當(dāng)前連接的trace信息,存入ongoing_http_client_requests結(jié)構(gòu)(key=goroutine地址,value=trace信息);

監(jiān)聽(tīng)roundTrip的調(diào)用返回:

  • 首先,根據(jù)goroutine地址,讀ongoing_http_client_requests結(jié)構(gòu),得到trace信息;
  • 然后,將當(dāng)前調(diào)用的trace信息,轉(zhuǎn)換為http_request_trace結(jié)構(gòu),保存到ringbuf中;

最終,ebpf用戶程序,讀取ringbuf中的trace信息,采集到trace信息。

二. 監(jiān)聽(tīng)uprobe/ServeHTTP

處理流程:

  • 首先,提取goroutine和request指針;
  • 然后,通過(guò)server_trace_parent()函數(shù),處理trace信息,存入go_trace_map結(jié)構(gòu);
  • 最后,將數(shù)據(jù)存入onging_http_server_requests結(jié)構(gòu);
// beyla/bpf/go_nethttp.c

SEC("uprobe/ServeHTTP")
int uprobe_ServeHTTP(struct pt_regs *ctx) {
    void *goroutine_addr = GOROUTINE_PTR(ctx);
    void *req = GO_PARAM4(ctx);
    http_func_invocation_t invocation = {
        .start_monotime_ns = bpf_ktime_get_ns(),
        .req_ptr = (u64)req,
        .tp = {0}
    };
    if (req) {
        // 處理trace信息,存入go_trace_map
        server_trace_parent(goroutine_addr, &invocation.tp, (void*)(req + req_header_ptr_pos));
    }
    // write event
    if (bpf_map_update_elem(&ongoing_http_server_requests, &goroutine_addr, &invocation, BPF_ANY)) {
        bpf_dbg_printk("can't update map element");
    }
    return 0;
}

重點(diǎn)看一下server_trace_parent()函數(shù):

  • 首先,從req_header讀取traceparent:

    • 若讀到了,則copy traceId,將parentId=上層的spanId;
    • 否則,則生成trace_id,將parentId=0;
  • 然后,使用urand,生成隨機(jī)的spanId;
  • 最后,將trace信息存入go_trace_map結(jié)構(gòu),key=goroutine地址,value=trace信息;
// bpf/go_common.h

static __always_inline void server_trace_parent(void *goroutine_addr, tp_info_t *tp, void *req_header) {
    // May get overriden when decoding existing traceparent, but otherwise we set sample ON
    tp->flags = 1;
    // Get traceparent from the Request.Header
    void *traceparent_ptr = extract_traceparent_from_req_headers(req_header);
    if (traceparent_ptr != NULL) {    // 讀到了traceparent
       ....
    } else {     // 未讀到traceparent
        bpf_dbg_printk("No traceparent in headers, generating");
        urand_bytes(tp->trace_id, TRACE_ID_SIZE_BYTES);       // 生成隨機(jī)的trace_id;
        *((u64 *)tp->parent_id) = 0;
    }

    urand_bytes(tp->span_id, SPAN_ID_SIZE_BYTES);
    bpf_map_update_elem(&go_trace_map, &goroutine_addr, tp, BPF_ANY);
}

go_trace_map對(duì)象的定義:

struct {
    __uint(type, BPF_MAP_TYPE_LRU_HASH);
    __type(key, void *); // key: pointer to the goroutine
    __type(value, tp_info_t);  // value: traceparent info
    __uint(max_entries, MAX_CONCURRENT_SHARED_REQUESTS);
    __uint(pinning, LIBBPF_PIN_BY_NAME);
} go_trace_map SEC(".maps");

typedef struct tp_info {
    unsigned char trace_id[TRACE_ID_SIZE_BYTES];
    unsigned char span_id[SPAN_ID_SIZE_BYTES];
    unsigned char parent_id[SPAN_ID_SIZE_BYTES];
    u64 ts;
    u8  flags;
} tp_info_t;

三. 監(jiān)聽(tīng)uprobe/roundTrip

roundTrip函數(shù),在使用http client發(fā)起請(qǐng)求時(shí),被調(diào)用。

處理流程:

  • 首先,提取goroutine地址和request地址;
  • 然后,根據(jù)goroutine_addr和request,查找trace信息;
  • 最后,將trace信息寫(xiě)入ongoing_http_client_requests對(duì)象;
// beyla/bpf/go_nethttp.c
SEC("uprobe/roundTrip")
int uprobe_roundTrip(struct pt_regs *ctx) {
    roundTripStartHelper(ctx);
    return 0;
}
static __always_inline void roundTripStartHelper(struct pt_regs *ctx) {
    void *goroutine_addr = GOROUTINE_PTR(ctx);
    void *req = GO_PARAM2(ctx);
    http_func_invocation_t invocation = {
        .start_monotime_ns = bpf_ktime_get_ns(),
        .req_ptr = (u64)req,
        .tp = {0}
    };
    // 根據(jù)request和goroutine_addr,查找trace信息
    __attribute__((__unused__)) u8 existing_tp = client_trace_parent(goroutine_addr, &invocation.tp, (void*)(req + req_header_ptr_pos));
    // 將trace信息寫(xiě)入ongoing_http_client_requests
    if (bpf_map_update_elem(&ongoing_http_client_requests, &goroutine_addr, &invocation, BPF_ANY)) {
        bpf_dbg_printk("can't update http client map element");
    }
}

重點(diǎn)看一下查找trace信息的client_trace_parent()函數(shù):

  • 首先,嘗試從request的header中提取traceparent:

    • 若找到了,則copy traceId,設(shè)置當(dāng)前span.parentId=上游span的spanId;
  • 然后,再使用goroutine及其parent_goroutine,去go_trace_map中找:

    • 若找到了,則copy traceId,設(shè)置當(dāng)前span.parentId=上游span的spanId;
// beyla/go_common.h
static __always_inline u8 client_trace_parent(void *goroutine_addr, tp_info_t *tp_i, void *req_header) {
    u8 found_trace_id = 0;
    u8 trace_id_exists = 0;    
    // May get overriden when decoding existing traceparent or finding a server span, but otherwise we set sample ON
    tp_i->flags = 1;
    // 首先嘗試從request的header中提取traceparent
    if (req_header) {
        ...
    }
    // 然后再使用goroutine去go_trace_map中找
    if (!found_trace_id) {
        tp_info_t *tp = 0;
        u64 parent_id = find_parent_goroutine(goroutine_addr);
        if (parent_id) {// we found a parent request
            tp = (tp_info_t *)bpf_map_lookup_elem(&go_trace_map, &parent_id);
        }
        if (tp) {   // 找到了,copy traceId,當(dāng)前span.parentId=上流span.spanId
            *((u64 *)tp_i->trace_id) = *((u64 *)tp->trace_id);
            *((u64 *)(tp_i->trace_id + 8)) = *((u64 *)(tp->trace_id + 8));
            *((u64 *)tp_i->parent_id) = *((u64 *)tp->span_id);
            tp_i->flags = tp->flags;
        } 
        ...
        // 生成當(dāng)前span.spanId
        urand_bytes(tp_i->span_id, SPAN_ID_SIZE_BYTES);
    }
    return trace_id_exists;
}

這里有個(gè)隱形的假設(shè)條件:

  • 一個(gè)goroutine及其child goroutine僅處理一個(gè)http請(qǐng)求;
  • nethttp的框架在設(shè)計(jì)時(shí),就由一個(gè)goroutine去處理一個(gè)http請(qǐng)求,是符合這個(gè)假設(shè)的;

四. 監(jiān)聽(tīng)uprobe/roundTrip_return

處理流程:

  • 首先,使用goroutine_addr,從ongoing_http_client_requests中找trace信息;
  • 然后,初始化http_request_trace:

    • 從request中找method/host/url/content_length,賦值給http_request_trace;
    • 將trace信息賦值到http_request_trace;
    • 從response中找status,賦值給http_request_trace;
  • 最后,將http_request_trace提交到ringbuf;
// beyla/bpf/go_nethttp.c
SEC("uprobe/roundTrip_return")
int uprobe_roundTripReturn(struct pt_regs *ctx) {
    void *goroutine_addr = GOROUTINE_PTR(ctx);
    // 使用goroutine_addr找ongoing_http_client_requests
    http_func_invocation_t *invocation =
        bpf_map_lookup_elem(&ongoing_http_client_requests, &goroutine_addr);
    bpf_map_delete_elem(&ongoing_http_client_requests, &goroutine_addr);
    http_request_trace *trace = bpf_ringbuf_reserve(&events, sizeof(http_request_trace), 0);
    // 初始化http_request_trace
    task_pid(&trace->pid);
    trace->type = EVENT_HTTP_CLIENT;
    trace->start_monotime_ns = invocation->start_monotime_ns;
    trace->go_start_monotime_ns = invocation->start_monotime_ns;
    trace->end_monotime_ns = bpf_ktime_get_ns();
    void *req_ptr = (void *)invocation->req_ptr;
    void *resp_ptr = (void *)GO_PARAM1(ctx);
    // 從request中找method,賦值給trace->method
    if (!read_go_str("method", req_ptr, method_ptr_pos, &trace->method, sizeof(trace->method))) {
        ...
    }
    // 從request中找host,賦值給trace->host
    if (!read_go_str("host", req_ptr, host_ptr_pos, &trace->host, sizeof(trace->host))) {
        ...
    }
    // 從request中找url,賦值給trace->path
    void *url_ptr = 0;
    bpf_probe_read(&url_ptr, sizeof(url_ptr), (void *)(req_ptr + url_ptr_pos));
    if (!url_ptr || !read_go_str("path", url_ptr, path_ptr_pos, &trace->path, sizeof(trace->path))) {
        ...
    }
    // 賦值trace信息
    trace->tp = invocation->tp;
    // 從request中找content_length,賦值給trace->content_length
    bpf_probe_read(&trace->content_length, sizeof(trace->content_length), (void *)(req_ptr + content_length_ptr_pos));
    // 從resp中找status,賦值給trace->status
    bpf_probe_read(&trace->status, sizeof(trace->status), (void *)(resp_ptr + status_code_ptr_pos));
    // 提交trace到ringbuf
    bpf_ringbuf_submit(trace, get_flags());
    return 0;
}

參考:

1.https://github.com/grafana/beyla/issues/521

2.https://github.com/grafana/beyla/blob/main/docs/sources/distributed-traces.md

以上就是golang beyla采集trace程序原理源碼解析的詳細(xì)內(nèi)容,更多關(guān)于golang beyla采集trace的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go語(yǔ)言中的range用法實(shí)例分析

    Go語(yǔ)言中的range用法實(shí)例分析

    這篇文章主要介紹了Go語(yǔ)言中的range用法,實(shí)例分析了range的功能與使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • 使用go在mangodb中進(jìn)行CRUD操作

    使用go在mangodb中進(jìn)行CRUD操作

    這篇文章主要介紹了使用go在mangodb中進(jìn)行CRUD操作,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-10-10
  • 一文讓你理解go語(yǔ)言的Context

    一文讓你理解go語(yǔ)言的Context

    在Go語(yǔ)言中,Context(上下文)是一個(gè)類(lèi)型,用于在程序中傳遞請(qǐng)求范圍的值、截止時(shí)間、取消信號(hào)和其他與請(qǐng)求相關(guān)的上下文信息,它在多個(gè)goroutine之間傳遞這些值,使得并發(fā)編程更加可靠和簡(jiǎn)單,本文詳細(xì)介紹go語(yǔ)言的Context,需要的朋友可以參考下
    2023-05-05
  • go語(yǔ)言調(diào)用其他包中的函數(shù)簡(jiǎn)單示例

    go語(yǔ)言調(diào)用其他包中的函數(shù)簡(jiǎn)單示例

    這篇文章主要給大家介紹了關(guān)于go語(yǔ)言調(diào)用其他包中的函數(shù)的相關(guān)資料,文中還介紹了Go語(yǔ)言同一個(gè)包中不同文件之間函數(shù)調(diào)用的相關(guān)問(wèn)題,需要的朋友可以參考下
    2023-01-01
  • 詳解Go中處理時(shí)間數(shù)據(jù)的方法

    詳解Go中處理時(shí)間數(shù)據(jù)的方法

    在許多場(chǎng)合,你將不得不編寫(xiě)必須處理時(shí)間的代碼。在Go中處理時(shí)間數(shù)據(jù)需要你從Go標(biāo)準(zhǔn)庫(kù)中導(dǎo)入?time?包。這個(gè)包有很多方法和類(lèi)型供你使用,但我選取了最常用的方法和類(lèi)型,并在這篇文章中進(jìn)行了描述,感興趣的可以了解一下
    2023-04-04
  • go語(yǔ)言中的Stringer的使用示例詳解

    go語(yǔ)言中的Stringer的使用示例詳解

    Go 語(yǔ)言中的 Stringer 是一個(gè)非常有用的接口,它在標(biāo)準(zhǔn)庫(kù)的 fmt 包中定義,Stringer 接口允許類(lèi)型定義它們的字符串表示方式,這在格式化輸出時(shí)特別有用,這篇文章主要介紹了go語(yǔ)言中的Stringer的使用,需要的朋友可以參考下
    2025-02-02
  • Go構(gòu)建高性能的事件管理器實(shí)例詳解

    Go構(gòu)建高性能的事件管理器實(shí)例詳解

    這篇文章主要為大家介紹了Go構(gòu)建高性能的事件管理器實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Go中獲取兩個(gè)切片交集的六種實(shí)現(xiàn)方式

    Go中獲取兩個(gè)切片交集的六種實(shí)現(xiàn)方式

    在Go開(kāi)發(fā)中,切片交集(Intersection) 是高頻需求:用戶標(biāo)簽匹配、權(quán)限校驗(yàn)、數(shù)據(jù)去重同步,本文系統(tǒng)梳理從基礎(chǔ)到高階的 6 種實(shí)現(xiàn)方式,含泛型、結(jié)構(gòu)體、去重、性能對(duì)比,助你寫(xiě)出工業(yè)級(jí)健壯代碼,需要的朋友可以參考下
    2025-12-12
  • golang動(dòng)態(tài)創(chuàng)建類(lèi)的示例代碼

    golang動(dòng)態(tài)創(chuàng)建類(lèi)的示例代碼

    這篇文章主要介紹了golang動(dòng)態(tài)創(chuàng)建類(lèi)的實(shí)例代碼,本文通過(guò)實(shí)例代碼給大家講解的非常詳細(xì),需要的朋友可以參考下
    2023-06-06
  • Go項(xiàng)目在GoLand中導(dǎo)入依賴標(biāo)紅問(wèn)題的解決方案

    Go項(xiàng)目在GoLand中導(dǎo)入依賴標(biāo)紅問(wèn)題的解決方案

    這篇文章主要介紹了Go項(xiàng)目在GoLand中導(dǎo)入依賴標(biāo)紅問(wèn)題的解決方案,文中通過(guò)代碼示例講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-06-06

最新評(píng)論

册亨县| 四子王旗| 桦甸市| 北安市| 长丰县| 枞阳县| 富裕县| 菏泽市| 翁牛特旗| 裕民县| 眉山市| 资源县| 眉山市| 福建省| 米脂县| 静乐县| 南康市| 奇台县| 孝感市| 塔河县| 明溪县| 珠海市| 镇宁| 巨鹿县| 丰都县| 龙口市| 正宁县| 鄯善县| 汾阳市| 恩平市| 丹阳市| 秭归县| 黔江区| 崇州市| 五莲县| 抚松县| 连江县| 涿州市| 南江县| 新河县| 九龙县|