Skip to content

request context

liubao edited this page Nov 23, 2022 · 5 revisions

请求上下文

请求上下文用于在调用链中传递公共信息,比如trace id、微服务名称、实例ID等。

使用如下典型的微服务架构:

    Spring Cloud Gateway --> Consumer --> Provider

在Spring Cloud Gateway添加一个上下文信息,可以在Consumer和Provider获取到上下文。

WebMVC 添加和读取上下文

可以通过如下代码获取和添加上下文

InvocationContext context = InvocationContextHolder.getOrCreateInvocationContext();
// 读取
context.getContext(InvocationContext.CONTEXT_TRACE_ID);
// 设置
context.putContext(InvocationContext.CONTEXT_TRACE_ID, InvocationContext.generateTraceId());

WebFlux 添加和读取上下文

WebFilter 中可以通过如下代码获取和添加上下文

InvocationContext context = exchange.getAttribute(InvocationContextHolder.ATTRIBUTE_KEY);
// 读取
context.getContext(InvocationContext.CONTEXT_TRACE_ID);
// 设置
context.putContext(InvocationContext.CONTEXT_TRACE_ID, InvocationContext.generateTraceId());

如果是 Spring Cloud Gateway,也可以在 GlobalFilter, GatewayFilter 中通过如下代码获取和添加上下文

InvocationContext context = exchange.getAttribute(InvocationContextHolder.ATTRIBUTE_KEY);
// 读取
context.getContext(InvocationContext.CONTEXT_TRACE_ID);
// 设置
context.putContext(InvocationContext.CONTEXT_TRACE_ID, InvocationContext.generateTraceId());

RestController 中可以通过注入 ServerWebExchange 获取请求上下文:

  @RequestMapping("/testWebFluxInvocationContext")
  public Mono<String> testWebFluxInvocationContext(ServerWebExchange exchange, @RequestParam("name") String name){
    InvocationContext context=exchange.getAttribute(InvocationContextHolder.ATTRIBUTE_KEY);
    ...
  }

使用 WebClient 访问下游服务的时候,需要从 ServerWebExchange 获取上下文并传递,然后就可以在 ExchangeFilterFunction 获取上下文。

// controller 设置上下文
@GetMapping(
  path = "/testWebClientRetry",
  produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<String> testWebClientRetry(ServerWebExchange exchange,
  @RequestParam(name = "invocationID") String invocationID) {
return webClientBuilder.build().get()
    .uri("http://webflux/testWebClientRetry?invocationID={1}", invocationID)
    .attribute(InvocationContextHolder.ATTRIBUTE_KEY, exchange.getAttribute(InvocationContextHolder.ATTRIBUTE_KEY))
    .retrieve()
    .bodyToMono(String.class);
}

// ExchangeFilterFunction获取上下文
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next){
    InvocationContext context=request.attribute(InvocationContextHolder.ATTRIBUTE_KEY).isPresent()?
    (InvocationContext)request.attribute(InvocationContextHolder.ATTRIBUTE_KEY).get():new InvocationContext();
    ...
}

将请求 header 或者 query 设置到上下文中

很多场景需要从系统外部传递一些信息,比如JS端生成trace-id, 需要将trace-id在整个请求过程中传递。 可以在网关或者微服务 应用配置哪些 header 或者 query 参数需要添加到上下文中,并指定上下文的键名称。

spring:
  cloud:
    servicecomb:
      context:
        headerContextMapper:
          header-context: x-header-context
          trace-id: x-trace-id
        queryContextMapper:
          query-context: x-query-context

上述方式将 query 参数 query-context 和 header 参数 header-context 设置到上下文中,上下文的 key 分别为 x-query-contextx-header-context

对于比较复杂的场景,还可以使用 contextMapper 流量治理能力来设置上下文。示例如下:

servicecomb:
  matchGroup:
    testWebMvcInvocationContext: |
      matches:
        - apiPath:
            exact: "/testWebMvcInvocationContext"
  contextMapper:
    testWebMvcInvocationContext: |
      target:
        x-c: foo
        x-header-context: $H{header-context}
        x-u: $U
        x-m: $M

contextMapper 流量治理的含义是如果流量特征匹配,那么将 target 定义的键值对设置到请求上下文中。contextMapper 流量治理包含几个特殊标签:

  • $U : 将 uri 设置到请求上下文中。 上述例子会设置 x-u = /testWebMvcInvocationContext
  • $M : 将 method 设置到请求上下文中。 上述例子会设置 x-m = GET
  • $H{header-name} : 将 header-name 对应的 HTTP header设置到请求上下文中。 上述例子会设置 x-header-context = header-context请求头的值
  • 其他 : 将 key -value 键值对设置到请求上下文中。 上述例子会设置 x-c = foo
Clone this wiki locally