文档

用户体验监控关联Trace

更新时间:

ARMS用户体验监控支持以用户会话作为切入点,追踪用户与应用交互过程中的错误、缓慢、异常问题,通过与ARMS应用监控联动,实现端到端分析,帮助您打通问题分析链路。本文介绍如何通过用户体验监控实现端到端Trace打通。

前提条件

  • 确保您的前端应用(包含Web、小程序)已接入用户体验监控。具体操作,请参见接入Web & H5应用接入小程序

    说明

    移到端暂不支持关联前后端Trace。

  • 确保您的后端应用,已接入ARMS应用监控或可观测链路 OpenTelemetry 版。具体操作,请参见应用监控接入概述接入指南

  • 确保您的后端应用,有提供Web服务(HTTP),用于解析Header,关联Trace上下文。

支持的Trace协议类型

目前用户体验监控关联Trace功能已支持以下主流Trace透传协议:

  • w3c:包含opentelemetry客户端以及ARMS探针。

  • b3/b3multi:zipkin

  • jaeger

  • sw6/sw8:skywalking

开启RUM关联Trace

请在为前端应用在接入RUM探针时开启链路追踪配置项。

重要

开启RUM关联Trace会额外生成一部分Trace数据,这将对您的应用监控或可观测链路 OpenTelemetry 版的账单产生一些影响。

CDN同步

最简模式

常用于不需要跨域请求,后端默认采取OTel标准。allowedUrls类型为undefined,但会对同域请求加白。该方式只推荐浏览器中使用,小程序由于不存在同域请求的概念,所以必须配置allowedUrls。

<script>
  window.__rum = {
    pid: "<your pid>",
    endpoint: "<your endpoint>",
    //...此处省略其他配置项
    // 链路追踪配置开关,默认关闭
    tracing: true //等效于 { enable: true,  sample: 100, tracestate: true, allowedUrls:[], baggage: false }
  };
</script>
<script type="text/javascript" src="https://sdk.rum.aliyuncs.com/v2/browser-sdk.js " crossorigin></script>

完整模式

全量配置,用于对所有配置都进行控制,allowedUrls类型为Array<TraceOption>。

<script>
  window.__rum = {
    pid: "<your pid>",
    endpoint: "<your endpoint>",
    //...此处省略其他配置项
    // 链路追踪配置开关,默认关闭
    tracing: {
      enable: true, // 开启链路追踪,默认关闭
      sample: 60, // 采样率60%,默认100%
      tracestate: true, // 开启tracestate的透传,默认开启
      baggage: false, // 关闭baggage的透传,默认关闭
      allowedUrls:[
        {match: 'https://api.aliyun.com', propagatorTypes:['tracecontext', 'sw8']}, // 字符匹配 https://api.aliyun.com开头,使用w3c标准
        {match: /api\.alibaba\.com/i, propagatorTypes:['b3multi']}, // 正则匹配包含api.aliyun.com,使用b3multi多头标准
        {match: (url)=>url.includes('.api'), propagatorTypes:['jaeger']}, // 函数判断包含.api, 使用jaeger标准
      ]
    }
  };
</script>
<script type="text/javascript" src="https://sdk.rum.aliyuncs.com/v2/browser-sdk.js " crossorigin></script>

CDN异步

最简模式

常用于不需要跨域请求,后端默认采取OTel标准。allowedUrls类型为undefined,但会对同域请求加白。该方式只推荐浏览器中使用,小程序由于不存在同域请求的概念,所以必须配置allowedUrls。

<script>
  !(function(c,b,d,a){c[a]||(c[a]={});c[a].config=
    {
      pid: "<your pid>",
      endpoint: "<your endpoint>",
      //...此处省略其他配置项
      // 链路追踪配置开关,默认关闭
      tracing: true
    }
   with(b)with(body)with(insertBefore(createElement("script"),firstChild))setAttribute("crossorigin","",src=d)
  })(window,document,"https://sdk.rum.aliyuncs.com/v1/bl.js","__bl");
</script>
完整模式

全量配置,用于对所有配置都进行控制,allowedUrls类型为Array<TraceOption>。

<script>
  !(function(c,b,d,a){c[a]||(c[a]={});c[a].config=
    {
      pid: "<your pid>",
      endpoint: "<your endpoint>",
      //...此处省略其他配置项
      // 链路追踪配置开关,默认关闭
      tracing: {
        enable: true, // 开启链路追踪,默认关闭
        sample: 100, // 采样率,默认100%
        tracestate: true, // 开启tracestate透传,默认开启
        baggage: false, // 开启baggage透传,默认关闭
        allowedUrls:[
          {match: 'https://api.aliyun.com', propagatorTypes:['tracecontext', 'sw8']}, // 字符匹配 https://api.aliyun.com开头,使用w3c标准
          {match: /api\.alibaba\.com/i, propagatorTypes:['b3multi']}, // 正则匹配包含api.aliyun.com,使用b3multi多头标准
          {match: (url)=>url.includes('.api'), propagatorTypes:['jaeger']}, // 函数判断包含.api, 使用jaeger标准
        ]
      }
    }
    with(b)with(body)with(insertBefore(createElement("script"),firstChild))setAttribute("crossorigin","",src=d)
   })(window,document,"https://sdk.rum.aliyuncs.com/v1/bl.js","__bl");
</script>

NPM包

最简模式

常用于不需要跨域请求,后端默认采取OTel标准。allowedUrls类型为undefined,但会对同域请求加白。该方式只推荐浏览器中使用,小程序由于不存在同域请求的概念,所以必须配置allowedUrls。

import ArmsRum from '@arms/rum-browser';

ArmsRum.init({
  pid: 'your pid',
  endpoint: 'your endpoint',
  //...此处省略其他配置项
  // 链路追踪配置开关,默认关闭
  tracing: true, //等效于 { enable: true,  sample: 100, tracestate: true, allowedUrls:[], baggage: false }
});
完整模式

全量配置,用于对所有配置都进行控制,allowedUrls类型为Array<TraceOption>。

import ArmsRum from '@arms/rum-browser';

ArmsRum.init({
  pid: "your pid",
  endpoint: "your endpoint",
  //...此处省略其他配置项
  tracing: {
    enable: true, // 开启链路追踪,默认关闭
    sample: 100, // 采样率,默认100%
    tracestate: true, // 开启tracestate透传,默认开启
    baggage: false, // 开启baggage透传,默认关闭
    allowedUrls:[
      {match: 'https://api.aliyun.com', propagatorTypes:['tracecontext', 'sw8']}, // 字符匹配 https://api.aliyun.com开头,使用w3c标准
      {match: /api\.alibaba\.com/i, propagatorTypes:['b3multi']}, // 正则匹配包含api.aliyun.com,使用b3multi多头标准
      {match: (url)=>url.includes('.api'), propagatorTypes:['jaeger']}, // 函数判断包含.api, 使用jaeger标准
    ]
  }
});

Tracing配置项

字段

类型

默认值

备注

tracing.enable

Boolen

true

传入非Boolen类型时,会被重置为true。

tracing.sample

Number

100

取值范围[0,100],其他类型任何值都会被重置为100。

tracing.tracestate

Boolen

true

默认开启,仅w3c tracecontext模式下才会生效,其他模式不存在tracestate概念。

配置为false后,w3c模式下不会携带tracestate请求头。

tracing.baggage

Boolen

false

开启链路追踪时,RUM监控会在请求头中加入baggage,并携带相关信息,不区分tracing标准。

tracing.propagatorTypes

PropagatorType | PropagatorType[]

null

当前Trace采用的传递标准。

注意:

  • 如果allowedUrls配置了propagatorTypes,则当前配置会被覆盖。

  • 如果配置中包含sw8,则其他类型的配置会自动失效,只保留sw8标准。

tracing.allowedUrls

Array<MatchOption | TraceOption> | undefined

-

-

浏览器情况下tracing.allowedUrls会增加以下规则:

{
  match: (url) => (/^https?:\/\/*/.test(url) || startsWith(url, location.origin)),
  propagatorTypes: ['tracecontext']
}

MatchOption

type MatchOption = string | RegExp | ((value: string) => boolean);

allowedUrls与完整URL匹配。接受以下类型:

  • string:匹配任何以该值开头的URL,如https://api.aliyun.com可以匹配https://api.aliyun.com/v1/resource

  • RegExp:使用提供的RegExp和URL执行test。

  • function:以URL作为参数进行计算,返回true表示匹配。

PropagatorType

默认情况下,基于OpenTelemetry使用的是tracecontext。

type PropagatorType = 'tracecontext' | 'b3' | 'b3multi' | 'jaeger' | 'sw8';

以上几种Trace协议,透传的Header如下:

透传格式名称

格式

备注

tracecontext

traceparent : {version}-{trace-id}-{parent-id}-{trace-flags}

tracestate: rum={version}&{appType}&{pid}&{sessionId}

相关文档

b3

b3: {TraceId}-{SpanId}-{SamplingState}-{ParentSpanId}

相关文档

b3multi

X-B3-TraceId: {TraceId}

X-B3-SpanId: {SpanId}

X-B3-ParentSpanId: {ParentSpanId}

X-B3-Sampled: {SamplingState}

相关文档

jaeger

uber-trace-id : {trace-id}:{span-id}:{parent-span-id}:{flags}

相关文档

重要

以上各协议透传的Request Header,并不是标准的HTTP请求头,也不在CORS安全列表中(有时也称为:CORS-safelisted request-headers)。因此,当您的网站或应用存在跨域请求时(尤其是在小程序等场景),需要在服务端的Access-Control-Allow-Headers中明确指定。否则,跨域请求会因CORS策略而被浏览器拦截。

验证RUM关联Trace

Web & H5

  1. 访问您的网站或者Web H5页面。

  2. 打开浏览器控制台,切换到Network(网络)页签。

  3. 检查前端发起的请求(注意是API请求,类型为:xhr/fetch),查看请求头(Request Headers)中是否包含对应透传协议的Header。

小程序(支付宝/微信/钉钉)

  1. 使用小程序开发者模拟器运行;

  2. 打开开发者工具调试器,切换到网络(Network)Tab页;

  3. 检查小程序发起的Request请求头中是否包含对应的透传协议的Header。

配置后端应用Trace串联

为了打通完整Trace链路,还需要对后端应用进行配置,目前支持的后端应用类型以及接入方式如下:

Java应用

使用ARMS应用监控探针接入

ARMS应用监控探针默认集成了对OpenTelemetry协议的支持,因此无需额外配置,即可实现与RUM Trace关联,但需要确保以下两点:

  • 支持的ARMS应用监控探针版本:2.x、3.x、4.x,为了更好的使用体验,推荐升级到4.x版本。

  • 支持的Web容器和框架:支持Tomcat、Jetty、WebLogic、Undertow等主流Web容器,以及SpringBoot、SpringMVC等框架。支持的完整组件和框架请参见ARMS应用监控支持的Java组件和框架

ARMS应用监控探针接入操作请参见开始监控Java应用

使用OpenTelemetry接入

通过OpenTelemetry接入ARMS(可观测链路 OpenTelemetry 版),目前分为两种方式:自动埋点和手动埋点。

  • 自动埋点场景下,由于OpenTelemetry已经支持了绝大多数主流框架,因此,无需额外配置,即可实现与RUM Trace的串联。

    说明

    OpenTelemetry支持的Java框架,请参见通过OpenTelemetry上报Java应用数据

  • 手动埋点场景下,需要通过OpenTelemetry SDK提供的扩展机制,实现与RUM Trace的串联,即从前端请求头(traceparent、tracesate)中,解析出Trace上下文,以下是Springboot场景代码示例:

    1. 引入OpenTelemetry的依赖项。

      <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-api</artifactId>
      </dependency>
      <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-sdk-trace</artifactId>
      </dependency>
      <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-extension-annotations</artifactId>
        <version>1.18.0</version>
      </dependency>
      <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-exporter-otlp</artifactId>
      </dependency>
      <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-sdk</artifactId>
      </dependency>
      <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-semconv</artifactId>
        <version>1.30.1-alpha</version>
      </dependency>
      <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
        <version>1.34.1</version>
      </dependency>
      <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-extension-incubator</artifactId>
        <version>1.35.0-alpha</version>
      </dependency>
    2. 在OpenTelemetry初始化时,添加W3C Propagator。

      Resource resource = Resource.getDefault()
              .merge(Resource.create(Attributes.of(
                      ResourceAttributes.SERVICE_NAME, "otel-demo",
                      ResourceAttributes.HOST_NAME, "xxxx"
      )));
      
      SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
              .addSpanProcessor(BatchSpanProcessor.builder(OtlpHttpSpanExporter.builder()
                      .setEndpoint("Your Endpoint")
                      .addHeader("Authentication", "Your Token")
                      .build()).build())
              .setResource(resource)
              .build();
      
      openTelemetry = OpenTelemetrySdk.builder()
              .setTracerProvider(sdkTracerProvider)
              // 这里添加W3C Propagator
              .setPropagators(ContextPropagators.create(
                      TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), W3CBaggagePropagator.getInstance()))
               ).buildAndRegisterGlobal();
      // 这里需要使用扩展的Tracer
      tracer = ExtendedTracer.create(openTelemetry.getTracer("com.example.tracer", "1.0.0"));
    3. 在业务接口中,添加headers参数,并从请求头中解析Trace上下文,设置parent。

      // Controller中添加请求header参数,用于解析Trace上下文
      @RequestMapping("/test")
      public String test(@RequestHeader Map<String, String> headers) {
          Span span = OpenTelemetrySupport.getTracer()
                  .spanBuilder("/test")
                  // 从headers中解析parent span
                  .setParentFrom(OpenTelemetrySupport.getContextPropagators(), headers)
                  .setSpanKind(SpanKind.SERVER)
                  .startSpan();
          try (Scope scope = span.makeCurrent()) {
              // do something
          } catch (Throwable t) {
              span.setStatus(StatusCode.ERROR, "handle parent span error");
          } finally {
              span.end();
          }
          return "success";
      }

使用Jaeger接入

Jaeger目前针对Web场景,提供了:手动埋点和通过Spring Cloud组件埋点两种方式。完整接入方式请参见通过Jaeger上报Java应用数据

  • 通过SpringCloud组件埋点情况下,无需额外配置即可与RUM Trace打通。

  • 手动埋点场景下,需要自行从前端请求header中解析Trace上下文。代码配置示例如下:

    1. 引入依赖项。

      <dependency>
        <groupId>io.jaegertracing</groupId>
        <artifactId>jaeger-client</artifactId>
        <version>最新版本</version> <!-- 确保使用最新的Jaeger版本 -->
      </dependency>
    2. 初始化Tracer。

      请将<endpoint>替换成可观测链路 OpenTelemetry 版控制台集群配置 > 接入点信息页面相应客户端和地域的接入点。

      // 将manualDemo替换为您的应用名称。
      io.jaegertracing.Configuration config = new io.jaegertracing.Configuration("manualDemo");
      io.jaegertracing.Configuration.SenderConfiguration sender = new io.jaegertracing.Configuration.SenderConfiguration();
      // 将 <endpoint> 替换为控制台概览页面上相应客户端和地域的接入点。
      sender.withEndpoint("<endpoint>");
      config.withSampler(new io.jaegertracing.Configuration.SamplerConfiguration().withType("const").withParam(1));
      config.withReporter(new io.jaegertracing.Configuration.ReporterConfiguration().withSender(sender).withMaxQueueSize(10000));
      GlobalTracer.register(config.getTracer());
    3. 在业务接口中创建Span,可参考以下代码进行Trace关联。

      // Controller中添加请求header参数,由于解析Trace上下文
      @RequestMapping("/test")
      public String test(@RequestHeader Map<String, String> headers) {
          Tracer tracer = GlobalTracer.get();
          SpanContext parentCtx = tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(headers));
          Span span;
          if (parentCtx != null) {
              span = tracer.buildSpan("/test").asChildOf(parentCtx).start();
          } else {
              span = tracer.buildSpan("/test").start();
          }
          try (Scope ignored = tracer.activateSpan(span)) {
              tracer.activeSpan().setTag("methodName", "test");
              // do something
          } catch (Throwable t) {
              TracingHelper.onError(e, span);
              throw e
          } finally {
              span.finish();
          }
          return "success";
      }

使用Zipkin接入

完整接入方式请参见通过Zipkin上报Java应用数据

按照文档接入,然后在服务端代码中,从Request Header中解析Context,实现与RUM Trace串联。

// 从request header解析Context
extractor = tracing.propagation().extractor(Request::getHeader);

// convert that context to a span which you can name and add tags to
oneWayReceive = nextSpan(tracer, extractor.extract(request))
.name("process-request")
.kind(SERVER)
... add tags etc.

// start the server side and flush instead of finish
oneWayReceive.start().flush();

// you should not modify this span anymore as it is complete. However,
// you can create children to represent follow-up work.
next = tracer.newSpan(oneWayReceive.context()).name("step2").start();

使用SkyWalking接入

完整接入方式请参见Java Agent插件

SkyWalking目前仅提供了Java Agent接入方式,您只需要按照上述文档接入,即可实现RUM与后端Trace串联,需要注意,SkyWalking目前存在v2、v3两个版本,和RUM侧配置的协议匹配要求如下:

  • sw8(v3版本):对应SkyWalking 8.x版本探针。

  • sw6(v2版本):对应SkyWalking 6.x和7.x版本探针。

Go应用

通过OpenTelemetry接入

完整接入方式请参见通过OpenTelemetry上报Go应用数据

按照文档接入,然后在HTTP请求Handler中,通过request context生成Span,即可实现与RUM Trace串联。

// 初始化tracer
tracer := otel.Tracer(common.TraceInstrumentationName)
// 通过request context生成span
handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
    ctx := req.Context()
    span := trace.SpanFromContext(ctx)
    // do something
    w.Write([]byte("Hello World"))
})

通过Jaeger接入

完整接入方式请参见通过Jaeger上报Go应用数据

按照文档接入,然后从HTTP请求头中解析Span上下文,实现与RUM Trace串联,代码示例如下:

// 从HTTP对象解析出spanCtx。
spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
span := tracer.StartSpan("myspan", opentracing.ChildOf(spanCtx))
...
defer  span.Finish()

通过Zipkin接入

完整接入方式请参见通过Zipkin上报Go应用数据

按照文档接入,然后从HTTP请求头中解析Span上下文,实现与RUM Trace串联,代码示例如下:

// 初始化tracer
tracer, err := exampletracer.NewTracer("go-frontend", frontendPort)
// 通过request context生成span
router.Methods("GET").Path("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // retrieve span from context
	span := zipkin.SpanFromContext(r.Context())
    // add some tag
    span.Tag("some_key", "some_value")
    // do something...
	span.Annotate(time.Now(), "some_event")
})

使用SkyWalking接入

完整接入方式请参见通过SkyWalking上报Go应用数据

按照文档接入,推荐采用skywalking-go接入方式,该方式支持了主流的Web服务框架,如gin、go-restful、http、go-kratos v2、go-micro、go-resty等,无需修改代码即可实现与RUM Trace串联。

如果您希望手动从HTTP请求头中解析出Trace上下文,也可参考以下代码实现:

//Extract context from HTTP request header `sw8`
span, ctx, err := tracer.CreateEntrySpan(r.Context(), "/api/test", func(key string) (string, error) {
		return r.Header.Get(key), nil
})

Python应用

通过OpenTelemetry接入

完整接入方式请参见通过OpenTelemetry上报Python应用数据

按照文档接入,然后从HTTP请求头中解析Span上下文,实现与RUM Trace串联,代码示例如下:

// 初始化tracer
trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))

tracer = trace.get_tracer(__name__)

@app.route('/test')
def test():
    headers = dict(request.headers)

    // 从headers中解析trace context
    carrier ={'traceparent': headers['Traceparent'], 'tracestate': headers['Tracestate']}
    ctx = TraceContextTextMapPropagator().extract(carrier=carrier)

    with tracer.start_span("test", context=ctx):
        // do something
        return "success"

通过Jaeger接入

完整接入方式请参见通过Jaeger上报Python应用数据

按照文档接入,然后从HTTP请求头中解析Span上下文,实现与RUM Trace串联,代码示例如下:

import logging
from flask import Flask
from jaeger_client import Config
from opentracing.ext import tags
from opentracing.propagation import Format

## 初始化tracer
def init_tracer(service, scope_manager=None):
    logging.getLogger('').handlers = []
    logging.basicConfig(format='%(message)s', level=logging.DEBUG)

    config = Config(
        config={
            'sampler': {
                'type': 'const',
                'param': 1,
            },
            'logging': True,
            'reporter_batch_size': 1,
        },
        service_name=service,
        scope_manager=scope_manager
    )
    return config.initialize_tracer()

## trace decorator
def trace(tracer, span_name):
    def decorator(f):
        @functools.wraps(f)
        def wrapped(*args, **kwargs):
            ## 从headers中解析trace context 
            span_ctx = tracer.extract(Format.HTTP_HEADERS, request.headers)
            span_tags = {tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER}

            with tracer.start_active_span(span_name, child_of=span_ctx, tags=span_tags) as scope:
                rv = f(*args, **kwargs)

            return rv
        return wrapped
    return decorator

## api test example
@app.route('/test')
@trace(tracer, 'test')
def test():
    return "success"

使用SkyWalking接入

完整接入方式请参见通过SkyWalking上报Python应用数据

按照文档接入,然后从HTTP请求头中解析Span上下文,实现与RUM Trace串联,代码示例如下:

from skywalking import config, agent
from skywalking.trace.context import SpanContext, get_context
from skywalking.trace.carrier import CarrierItem

# 配置 SkyWalking,根据需要调整相关参数
config.init(agent_collector_backend_services='<endpoint>',
            agent_authentication='<auth-token>')

agent.start()

# 示例 HTTP 请求处理函数,需要传入 HTTP 请求的 headers
def handle_request(headers):
    # 从请求头 headers 中提取追踪信息
    carrier_items = []
    for item in SpanContext.make_carrier():
        carrier_header = headers.get(item.key.lower())
        if carrier_header:
            carrier_items.append(CarrierItem(item.key, carrier_header))

    carrier = SpanContext.make_carrier(carrier_items)

    # 从 Carrier 中提取追踪上下文
    context = get_context().extract(carrier)
    
    # 创建一个新 Span 来处理请求
    with get_context().new_entry_span(op='operation_name') as span:
        # 在这里处理请求,并在结束时自动提交 Span
        ...

# 模拟接收到的 HTTP 请求 header 包含 sw8
incoming_headers = {
    'sw8': '1-My40LjU=-MTY1MTcwNDI5OTk5OA==-xxxx-xx-x-x==',  # 示例值,实际根据请求填写
    # 其他 header...
}

# 调用函数处理请求
handle_request(incoming_headers)

查看端到端完整Trace数据

完成前后端Trace打通后,您可以在ARMS用户体验控制台查看前端请求对应的调用链。

image

单击查看调用链,可以看到请求的完整调用链路,以及应用拓扑。此时,可以结合RUM侧的请求明细数据,与后端Trace数据,分析错慢请求问题。

image.png

最上面的Span即代表RUM入口Span,根据端侧接入类型,分为以下几种:

  • Web & H5场景:应用名:rum-browser,Span名称前缀:browser.request:

  • 小程序场景:应用名:rum-miniapp,Span名称前缀:miniapp.request:

  • 移动端Android场景:应用名:rum-android,Span名称前缀:android.request:

  • 移动端iOS场景:应用名:rum-ios,Span名称前缀:ios.request:

image

同时,也可以通过拓扑图,直观的看到整个请求的上下游服务拓扑。

  • 本页导读 (1)
文档反馈