• 微服务 »
  • 零侵入微服务日志追踪(五):网关

零侵入微服务日志追踪(五):网关

微服务横行的年代,网关已经成为微服务的标配。作为服务的入口,网关在请求进去实际业务集群前,可以做很多公共的事情,使得业务微服务更加的专注于业务,比如统一的鉴权、限流、熔断保护、降级。

但其实网关还可以做一件很重要的事:给每个响应增加TraceId。把TraceId返回给调用方,在前后端联调、开放接口出错等情况下,客户只需要把TraceId告诉接口负责人,负责人便可以利用APM系统、日志系统进行精准定位,极大的加快问题的定位和分析速度。

为例保证无侵入,网关可以把TraceId写入额外的HTTP Header,放回给调用方,这样就不需要解析业务微服务的响应体。

我们以Zuul为基础实现api网关,借助前面提到的Pinpoint无侵入的生成唯一TrxId。TrxId格式$agentId^$startTimestamp^$seq,$agentId为应用名-主机IP,$seq表示这个请求是这个实例处理的第$seq个请求,$startTimestamp是实例启动时间。TrxId包含了较多的敏感信息,不易直接暴露给客户端。因此可以做个映射,TrxId <-> TraceId,输出不含敏感信息的TraceId。

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.vanke.gateway.common.constants.Constants;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

@Component
public class TraceIdPreFilter extends ZuulFilter {

    protected static final Logger LOG = LoggerFactory.getLogger(TraceIdPreFilter.class);

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 2;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        // 更新MDC内的ptxId,不能删除
        // 由于线程复用,MDC可能是上个请求的trx信息,强制调用一次log,可以更新MDC的内容为本次请求对应的trx
        LOG.info("生产内部TraceId");
        String traceId = UUID.randomUUID().toString().replace("-", "");
        RequestContext context = RequestContext.getCurrentContext();
        context.set(Constants.REQ_CTX_TRACE_ID, traceId);

        String ptxId = MDC.get("PtxId");
        if (StringUtils.isBlank(ptxId)) {
            return null;
        }
        LOG.info("设置X-Trace-Id: {}, 关联pinpoint trxId: {}", traceId, ptxId);
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletResponse resp = ctx.getResponse();
        resp.setHeader("X-Trace-Id", traceId);


        return null;
    }
}

输出示例如下:

通过ELK可以找到对应的Pinpoint TrxId:

利用Pinpoint TrxId就可以进行更进入的追踪了。

This entry was posted in 微服务