首页 > 其他分享 >全局调用链路traceId网关到业务层、feign调用统一问题记录

全局调用链路traceId网关到业务层、feign调用统一问题记录

时间:2023-08-28 15:01:30浏览次数:35  
标签:feign 调用 String request 网关 header traceId ID

              项目里面使用的traceId是基于skywalking进行打印的,但是实际使用的过程中发现网关处的traceId为空,而且feign调用其他服务时候的traceId 都不一样。 显示如下:

              网关traceId为空:

            

  

    基于此,想要把项目里面的以及feign调用的traceId统一成一样的,且在网关显示一样。

 

 首先是需要在日志设置打印traceId的格式:以logback-spring.xml为例

 

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
                <pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} %tid [%-5level] [%thread] [%logger{15}:%line] %X{traceId} --- %msg%n</pattern>
            </layout>
        </encoder>
    </appender>

  %X{traceId} 即为显示的traceId

 

 其次是在网关出需要优先把traceId进行打印显示:

  

            String traceId = StringUtils.isEmpty(exchange.getRequest().getHeaders().getFirst(TraceUtils.TRACE_ID))?TraceUtils.createTraceId():
                    exchange.getRequest().getHeaders().getFirst(TraceUtils.TRACE_ID);
            logger.info("当前request traceId:" + traceId);
public class TraceUtils {
    public static final String TRACE_ID = "traceId";

    public static synchronized String createTraceId() {
        String traceId = UUID.randomUUID().toString().replaceAll("-", "").toLowerCase();
        MDC.put(TRACE_ID, traceId);
        return traceId;
    }

    public static void destroyTraceId() {
        MDC.remove(TRACE_ID);
        MDC.clear();
    }


}

注意的是:如果在网关处有其他的业务日志,需要优先把traceId创建出来。否则使用的是默认的16位traceId.我自己在开发和测试环境测试发现,如果把traceId写在业务日志后面,显示的是16位的且和我本地不同的traceId.

 

feign时候需要通过request.getHeader("traceId")获取,且需要在网关处把生成的traceId放入header里面。大概的代码如下:

 

ServerHttpRequest request = exchange.getRequest().mutate()
                    .header(Constant.H_KEY_DEVICE_ID, deviceId)
                    .header(Constant.H_KEY_APP_ID, appId)
                    .header(Constant.H_KEY_BRAND, sourceApp)
                    .header(Constant.H_KEY_BRAND, sourceApp)
                    .header(Constant.H_KEY_IP, ip)
                    .header(Constant.H_URL, exchange.getRequest().getURI().toString())
                    .header(Constant.H_KEY_REQ_FROM,"outside")
                    .header(TraceUtils.TRACE_ID,traceId)
                    .build();
package com.gwm.marketing.feign.autoconfigure;

import com.gwm.marketing.common.constants.BaseCommon;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.Optional;

/**
 * FeignClient调用服务时添加广告主信息请求头
 *
 */
public class FeignAddHeadersRequestInterceptor implements RequestInterceptor {
    /**
     * 日志定义
     */
    private static final Logger LOG = LoggerFactory.getLogger(FeignAddHeadersRequestInterceptor.class);
    /**
     * 请求头名称
     */
    private static final String X_CLIENT_USER = "x-client-user";
    private static final String AUTHORIZATION = "Authorization";

    private static final String SOURCEAPP = "SourceApp";

    private static final String SOURCEAPPEGNORE= "sourceApp";
    private static final String TRACE_ID = "traceId";

    private static final String SOURCE_APP_VER = "SourceAppVer";

    private static final String SOURCE_APP_VER_IGNORE = "sourceappver";


    @Override
    public void apply(RequestTemplate template) {
        LOG.info("feign add header begin");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return;
        }
        HttpServletRequest request = attributes.getRequest();
        String header = request.getHeader(AUTHORIZATION);
        String traceId = request.getHeader(TRACE_ID);
        String sourceApp = request.getHeader(SOURCEAPP);
        String sourceAppVer = request.getHeader(SOURCE_APP_VER);
        if (header != null) {
            // 添加请求头
            template.header(AUTHORIZATION, header);
            LOG.info("feign add header done");
        }
        if(Objects.nonNull(sourceApp)){
            template.header(SOURCEAPP, sourceApp);
            LOG.info("feign souceApp done");
        }else {
            String sourceAppIgnore = request.getHeader(SOURCEAPPEGNORE);
            if(Objects.nonNull(sourceAppIgnore)){
                template.header(SOURCEAPPEGNORE,sourceAppIgnore);
                LOG.info("fein SOURCEAPPEGNORE done");
            }
        }
        if(Objects.isNull(traceId)){
            traceId = Optional.ofNullable(request.getAttribute(TRACE_ID)).map(t->t.toString()).orElse(null);
            LOG.info("current traceId:" + traceId);
        }
        if(Objects.nonNull(traceId)){
            //添加traceId
            template.header(TRACE_ID,traceId);
            MDC.put(TRACE_ID,traceId);
            LOG.info("request traceId:" + traceId);
        }
        if(Objects.nonNull(sourceAppVer)){
            template.header(BaseCommon.SOURCE_APP_VER,sourceAppVer);
        }else{
            String sourceAppVerIgnore = request.getHeader(SOURCE_APP_VER_IGNORE);
            if(Objects.nonNull(sourceAppVerIgnore)){
                template.header(BaseCommon.SOURCE_APP_VER,sourceAppVerIgnore);
                LOG.info("feign add sourceAppVerIgnore ");
            }
        }

    }
}

 

通过实现RequestInterceptor接口的apply方法,把feign调用的traceId给打通。大概思路是这样。这样打通后,一个完整的请求的traceId即可打通。

网关:

 

业务层:feign调用显示也和网关的都一样的了

 

标签:feign,调用,String,request,网关,header,traceId,ID
From: https://www.cnblogs.com/thinkingandworkinghard/p/17662283.html

相关文章

  • 物通博联工业智能网关实现环保HJ212协议对接到水污染监控平台
    环保HJ212协议是一种用于环保数据上报的行业标准协议。它定义了数据格式、数据传输方式等规范,以便在全国范围内实现统一的环境监测和管理。HJ212协议上报的过程包括数据采集、数据封装、网络传输、服务器接收等步骤。 对此,物通博联推出的工业智能网关可以实现HJ212对接到水污染监......
  • 如何调用api接口获取到商品数据
    要调用API接口获取商品数据,需要进行以下步骤:确定API接口首先需要确定要使用的API接口,可以通过搜索引擎或者相关文档来查找适合的API接口。以淘宝开放平台为例,可以使用淘宝的商品信息查询API接口来获取商品数据。注册API账号并获取API密钥要使用API接口,需要先在API平台上注册一个账......
  • DWR的注释(annotations)使用及反向调用(Reverse Ajax)
    先说说注释语法,省掉dwr.xml。(自从用了java5之后,现在越看一堆堆的配置文件越烦,越来越喜欢注释方式来的直接简单了)  首先下载最新的稳定版本的dwr.jar文件放到你的工程中。(还有需要其它的吗?不需要了,dwr就是这么简单)然后在web.xml中添加如下一段<!--DWRServlet--><servle......
  • 多线程以rtsp流调用多路海康摄像头的思考
    如题,我使用了多线程,以rtsp流调用多路海康摄像头。使用了opencv作为拉流库,但是结果不如人意。当摄像头数增加时,cpu占用率变化不大,但是却出现了卡顿。当增大到5个时,甚至发生崩溃。我使用了千兆光纤网,显然不是带宽问题。那会不会是imshwo显示久了不更新呢,显然不是。接受速度快于显......
  • 粘贴板工具Ditto(存储粘贴板历史,可以使用快捷键调用,减少重复操作)相关配置
    官方地址:https://ditto-cp.sourceforge.io/一般下载便携版即zip压缩包的版本即可配置:1.更改界面语言右键-options-language-下拉选择Chinesesimple2.快捷键设置-点击"键盘快捷键"选项卡 激活使用默认的ctrl+`即可重点:最后十个项目的快捷键第一个可以不设,因为使......
  • S调用函数时什么时候加小括号()?什么时候不加小括号()?
    JS调用函数时什么时候加小括号()?什么时候不加小括号()? 2019.03.1809:11:32字数83阅读1,937加括号后代表立即执行这个方法,不加括号代表这个方法不是立即执行,需要等待某个时机,如下:     不带括号的调用function,函数体对象为参数带括号的调用function(),立即执......
  • 【题解】 P7077 [CSP-S2020] 函数调用(拓扑排序)
    题意题目给定了一个长度为\(n\)序列\(a\)与\(m\)个操作,操作一共有3种:1.给定\(x,y\),使\(a_x\)增加\(y\)。2.给定\(x\),使\(a\)中所有数全部乘上\(x\)。3.给出k个数\(c_1,c_2,...,c_k\),表示这个操作的任务是按照先后顺序执行编号为\(c_1,c_2,...,c_k\)的\(k\)的操作。最后,题目相......
  • Struts2 中拦截器和Action的调用关系
    所谓的拦截器就是指实现了Interceptor接口的类,该接口中定义了三个方法:init(),destroy(),intercept()。init()用于在服务器启动的时候,初始化这个拦截器。destroy()用于清理分配给拦截器的资源,intercept()是用来起拦截作用的,这个方法还有一个ActionInvocation类型的参数invocation,并且......
  • PROFIBUS主站转MODBUS TCP网关
    1.产品功能YC-DPM-TCP网关在Profibus总线侧实现主站功能,在以太网侧实现ModbusTcp服务器功能。可将ProfibusDP从站接入到ModbusTcp网络;通过增加DP/PA耦合器,也可将ProfibusPA从站接入ModbusTcp网络。YC-DPM-TCP网关最多支持125个Profibus从站的通信,只支持单主站系统,不支持多主站......
  • Java RMI实现RPC(远程过程调用)
    RMI(RemoteMethodInvocation,远程方法调用)是一个JavaRPC的API,用于一台主机传递参数并远程调用另一台主机上的方法,下面给出一个简单实例。环境:win10宿主机作为rmiclient,ubuntu虚拟机(IP为192.168.129.49)作为rmiserver。现在client希望向server传递参数,调用server端的服务并获取......