欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringCloud通過MDC實(shí)現(xiàn)分布式鏈路追蹤

 更新時(shí)間:2024年11月03日 09:34:46   作者:YB_account  
在DDD領(lǐng)域驅(qū)動設(shè)計(jì)中,我們使用SpringCloud來去實(shí)現(xiàn),但排查錯誤的時(shí)候,通常會想到Skywalking,但是引入一個新的服務(wù),增加了系統(tǒng)消耗和管理學(xué)習(xí)成本,對于大型項(xiàng)目比較適合,但是小的項(xiàng)目顯得太過臃腫了,所以本文介紹了SpringCloud通過MDC實(shí)現(xiàn)分布式鏈路追蹤

引言

在DDD領(lǐng)域驅(qū)動設(shè)計(jì)中,我們使用SpringCloud來去實(shí)現(xiàn),但排查錯誤的時(shí)候,通常會想到Skywalking,但是引入一個新的服務(wù),增加了系統(tǒng)消耗和管理學(xué)習(xí)成本,對于大型項(xiàng)目比較適合,但是小的項(xiàng)目顯得太過臃腫了,我們此時(shí)就可以使用TraceId,將其存放到MDC中,返回的時(shí)候參數(shù)帶上它,訪問的時(shí)候日志打印出來,每次訪問生成的TraceId不同,這樣可以實(shí)現(xiàn)分布式鏈路追蹤的問題。

通用部分

封裝TraceIdUtil工具類

import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import cn.hutool.core.util.IdUtil;

public class TraceIdUtil {

    public static final String TRACE_ID_KEY = "TraceId";

    /**
     * 生成TraceId
     * @return
     */
    public static String generateTraceId(){
        String traceId = IdUtil.fastSimpleUUID().toUpperCase();
        MDC.put(TRACE_ID_KEY,traceId);
        return traceId;
    }

    /**
     * 生成TraceId
     * @return
     */
    public static String generateTraceId(String traceId){
        if(StringUtils.isBlank(traceId)){
            return generateTraceId();
        }
        MDC.put(TRACE_ID_KEY,traceId);
        return traceId;
    }

    /**
     * 獲取TraceId
     * @return
     */
    public static String getTraceId(){
        return MDC.get(TRACE_ID_KEY);
    }

    /**
     * 移除TraceId
     * @return
     */
    public static void removeTraceId(){
        MDC.remove(TRACE_ID_KEY);
    }
}

logback.xml日志文件的修改

<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [TRACEID:%X{TraceId}]  [%thread] %-5level %logger{36} -%msg%n</Pattern>

需注意:

biff 模塊

創(chuàng)建過濾器

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import com.karry.admin.bff.common.util.TraceIdUtil;
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@WebFilter
public class TraceFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("Init Trace filter   init.......");
        System.out.println("Init Trace filter  init.......");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        try {
            HttpServletRequest servletRequest = (HttpServletRequest) request;
            String gateWayTraceId = ((HttpServletRequest) request).getHeader(TraceIdUtil.TRACE_ID_KEY);
            String traceId = TraceIdUtil.generateTraceId(StringUtils.isEmpty(gateWayTraceId)
                    ? IdUtil.fastSimpleUUID().toUpperCase()
                    : gateWayTraceId
            );
            // 創(chuàng)建新的請求包裝器
            log.info("TraceIdUtil.getTraceId():"+TraceIdUtil.getTraceId());
            //將請求和應(yīng)答交給下一個處理器處理
            filterChain.doFilter(request,response);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //最后移除,不然有可能造成內(nèi)存泄漏
            TraceIdUtil.removeTraceId();
        }
    }

    @Override
    public void destroy() {
        log.info("Init Trace filter  destroy.......");
    }
}

配置過濾器生效

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import com.karry.admin.bff.common.filter.TraceFilter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Configuration
public class WebConfiguration {

    @Bean
    @ConditionalOnMissingBean({TraceFilter.class})
    @Order(Ordered.HIGHEST_PRECEDENCE + 100)
    public FilterRegistrationBean<TraceFilter> traceFilterBean(){
        FilterRegistrationBean<TraceFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new TraceFilter());
        bean.addUrlPatterns("/*");
        return bean;
    }
}

figen接口發(fā)送的head修改

此處修改了發(fā)送的請求的header,在其他模塊就可以獲取從biff層生成的traceId了。

import org.springframework.context.annotation.Configuration;
import com.karry.admin.bff.common.util.TraceIdUtil;
import feign.RequestInterceptor;
import feign.RequestTemplate;

@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template){
        String traceId = TraceIdUtil.getTraceId();
        //當(dāng)前線程調(diào)用中有traceId,則將該traceId進(jìn)行透傳
        if (traceId != null) {
            template.header(TraceIdUtil.TRACE_ID_KEY,TraceIdUtil.getTraceId());
        }
    }
}

統(tǒng)一返回處理

此種情況時(shí)針對BaseResult,,這種統(tǒng)一返回的對象無法直接修改的情況下使用的,如果可以直接修改:

    /**
     * 鏈路追蹤TraceId
     */
    public String traceId = TraceIdUtil.getTraceId();

不可以直接修改就用響應(yīng)攔截器進(jìn)行處理:

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import com.karry.app.common.utils.TraceIdUtil;
import com.karry.order.sdk.utils.BeanCopyUtils;
import com.souche.platform.common.model.base.BaseResult;
import lombok.SneakyThrows;


@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    /**
     * 開關(guān),如果是true才會調(diào)用beforeBodyWrite
     */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }


    @SneakyThrows//異常拋出,相當(dāng)于方法上throw一個異常
    @Override
    public Object beforeBodyWrite(Object object, MethodParameter methodParameter, MediaType mediaType, Class aClass,
            ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        BaseResult result = BeanCopyUtils.copy(object, BaseResult.class);
        result.setTraceId(TraceIdUtil.getTraceId());
        return result;
    }

}

非biff模塊

創(chuàng)建過濾器

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import com.karry.app.common.utils.TraceIdUtil;
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@WebFilter
public class TraceFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("Init Trace filter   init.......");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        try {
            HttpServletRequest servletRequest = (HttpServletRequest) request;
            String gateWayTraceId = ((HttpServletRequest) request).getHeader(TraceIdUtil.TRACE_ID_KEY);
            String traceId = TraceIdUtil.generateTraceId(StringUtils.isEmpty(gateWayTraceId)
                    ? IdUtil.fastSimpleUUID().toUpperCase()
                    : gateWayTraceId
            );
            //將請求和應(yīng)答交給下一個處理器處理
            filterChain.doFilter(request,response);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //最后移除,不然有可能造成內(nèi)存泄漏
            TraceIdUtil.removeTraceId();
        }
    }

    @Override
    public void destroy() {
        log.info("Init Trace filter  destroy.......");
    }
}

配置過濾器生效

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import com.karry.admin.bff.common.filter.TraceFilter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Configuration
public class WebConfiguration {

    @Bean
    @ConditionalOnMissingBean({TraceFilter.class})
    @Order(Ordered.HIGHEST_PRECEDENCE + 100)
    public FilterRegistrationBean<TraceFilter> traceFilterBean(){
        FilterRegistrationBean<TraceFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new TraceFilter());
        bean.addUrlPatterns("/*");
        return bean;
    }
}

線程池

上面對于單線程的情況可以進(jìn)行解決,traceId和Threadlocal很像,是鍵值對模式,會有內(nèi)存溢出問題,還是線程私有的。 所以在多線程的情況下就不能獲取主線程的traceId了。我們就需要設(shè)置線程工廠包裝 Runnable 來解決這個問題。

import org.slf4j.MDC;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Configuration
public class MyThreadPoolConfig {
    @Bean
    public ThreadPoolExecutor threadPoolExecutor() {
        // 自定義 ThreadFactory
        ThreadFactory threadFactory = new ThreadFactory() {
            private final ThreadFactory defaultFactory = Executors.defaultThreadFactory();
            private final String namePrefix = "Async---";

            @Override
            public Thread newThread(Runnable r) {
                // 獲取主線程的 MDC 上下文
                Map<String, String> contextMap = MDC.getCopyOfContextMap();

                // 包裝 Runnable 以設(shè)置 MDC 上下文
                Runnable wrappedRunnable = () -> {
                    try {
                        // 設(shè)置 MDC 上下文
                        MDC.setContextMap(contextMap);
                        // 執(zhí)行任務(wù)
                        r.run();
                    } finally {
                        // 清除 MDC 上下文
                        MDC.clear();
                    }
                };

                Thread thread = defaultFactory.newThread(wrappedRunnable);
                thread.setName(namePrefix + thread.getName());
                return thread;
            }
        };

        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                5,
                10,
                30L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(500),
                threadFactory,
                new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}

以上就是SpringCloud通過MDC實(shí)現(xiàn)分布式鏈路追蹤的詳細(xì)內(nèi)容,更多關(guān)于SpringCloud MDC鏈路追蹤的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • mybatis中的多重if?條件判斷

    mybatis中的多重if?條件判斷

    這篇文章主要介紹了mybatis中的多重if?條件判斷,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • springboot 實(shí)現(xiàn)Http接口加簽、驗(yàn)簽操作方法

    springboot 實(shí)現(xiàn)Http接口加簽、驗(yàn)簽操作方法

    這篇文章主要介紹了springboot 實(shí)現(xiàn)Http接口加簽、驗(yàn)簽操作,服務(wù)之間接口調(diào)用,通過簽名作為安全認(rèn)證來保證API的安全性,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-09-09
  • ssm?mybatis如何配置多個mapper目錄

    ssm?mybatis如何配置多個mapper目錄

    這篇文章主要介紹了ssm?mybatis如何配置多個mapper目錄,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教。
    2022-01-01
  • JDK的一個Bug監(jiān)聽文件變更的初步實(shí)現(xiàn)思路

    JDK的一個Bug監(jiān)聽文件變更的初步實(shí)現(xiàn)思路

    這篇文章主要介紹了JDK的一個Bug監(jiān)聽文件變更要小心了,本篇文章就帶大家簡單實(shí)現(xiàn)一個對應(yīng)的功能,并分析一下對應(yīng)的Bug和優(yōu)缺點(diǎn),需要的朋友可以參考下
    2022-05-05
  • Java集合之Set接口及其實(shí)現(xiàn)類精解

    Java集合之Set接口及其實(shí)現(xiàn)類精解

    set接口是繼承自Collection的子接口,特點(diǎn)是元素不重復(fù),存儲無序。在set接口的實(shí)現(xiàn)類中添加重復(fù)元素是不會成功的,判斷兩個元素是否重復(fù)根據(jù)元素類重寫的
    2021-09-09
  • Java對List進(jìn)行排序的兩種實(shí)現(xiàn)方法

    Java對List進(jìn)行排序的兩種實(shí)現(xiàn)方法

    這篇文章主要給大家介紹了關(guān)于Java對List進(jìn)行排序的兩種實(shí)現(xiàn)方法,第一種是實(shí)體類自己實(shí)現(xiàn)比較,第二種是借助比較器進(jìn)行排序,下面開一起看看詳細(xì)的介紹吧,有需要的朋友們可以參考借鑒。
    2016-12-12
  • SpringBoot啟動自動終止也不報(bào)錯的原因及解決

    SpringBoot啟動自動終止也不報(bào)錯的原因及解決

    這篇文章主要介紹了SpringBoot啟動自動終止也不報(bào)錯的原因及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • JAVA Map架構(gòu)和API介紹

    JAVA Map架構(gòu)和API介紹

    JAVA Map架構(gòu)和API介紹:Map、Map.Entry、AbstractMap、SortedMap、 NavigableMap、Dictionary。
    2013-11-11
  • Java用BigDecimal類解決Double類型精度丟失的問題

    Java用BigDecimal類解決Double類型精度丟失的問題

    這篇文章主要介紹了Java用BigDecimal類解決Double類型精度丟失的問題,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-12-12
  • Java8的Stream()與ParallelStream()的區(qū)別說明

    Java8的Stream()與ParallelStream()的區(qū)別說明

    這篇文章主要介紹了Java8的Stream()與ParallelStream()的區(qū)別說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07

最新評論