0

我有一个调用十几个其他服务的服务。@StreamListener这使用控制器类中的 a从 Kafka 主题中读取。出于可追溯性的目的,来自 Kafka 消息的相同标头(原始请求 ID)也需要转发到所有其他服务

传统上,使用@PostMapping("/path")orGetMapping会生成一个请求上下文,并且可以从任何地方访问标头,并且只要我需要进行外部调用RequestContextHolder.currentRequestAttributes(),我只需将HttpHeaders对象传递给RequestEntity

但是在 a 中StreamListener,不会生成请求上下文并尝试RequestContextHolder在异常中访问结果

这是我尝试做的一个例子,它导致了一个异常:

public class Controller {
  @Autowired Service1 service1
  @Autowired Service2 service2

  @StreamListener("stream")
  public void processMessage(Model model) {
    service1.execute(model);
    service2.execute(model);
  }
}

public class Service {
  RestTemplate restTemplate;

  public void execute(Model model){
    // Do some stuff

    HttpHeaders httpHeaders = RequestContextHolder.currentRequestAttributes().someCodeToGetHttpHeaders();
    HttpEntity<Model> request = new HttpEntity(model, httpHeaders);
    restTemplate.exchange(url, HttpMethod.POST, request, String.class);
  }
}

我目前的解决方法是将 a 更改StreamListener为 aPostMapping并有另一个PostMapping调用它,以便可以生成请求上下文。另一种选择是使用 aThreadLocal但它看起来就像 janky

我知道@Headers MessageHeaders用于访问流标头的注释,但是,如果不将标头向下传递给每个服务,这将无法轻松访问,并且会影响许多单元测试

理想情况下,我需要一种方法来创建我自己的请求上下文(或任何适当的术语),以便有一个地方来存储请求范围的对象(the HttpHeader)或另一种线程安全的方式,以便在不添加请求参数的情况下将请求标头向下传递到堆栈至service.execute

4

1 回答 1

0

我找到了一个解决方案,并将其留给其他试图实现类似目标的人

如果您的目标是通过 REST 控制器和 Stream 侦听器端到端转发一堆标头,您可能需要考虑使用Spring Cloud Sleuth

通过您的 maven 或 gradle 配置将其添加到您的项目中:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

spring.sleuth.propagation-keys具体来说,在 Spring Cloud Sleuth 中,有一个功能可以通过在 application.properties 中设置属性来转发标头或“baggage” 。这些键值对在整个跟踪中保持不变,包括任何下游 http 或流调用,它们也实现了相同的传播键

如果需要在代码级别访问这些字段,您可以使用ExtraFieldPropagation静态函数获取和设置它们:

ExtraFieldPropagation.set("country-code", "FO"); // Set
String countryCode = ExtraFieldPropagation.get("country-code"); // Get

请注意,ExtraFieldPropagationsetter 无法设置定义中不存在的属性,spring.sleuth.propagation-keys因此不会接受任意键

您可以阅读文档以获取更多信息

于 2019-07-03T11:07:06.100 回答