Доступ к заголовкам StreamListener из RequestContext или аналогичного - PullRequest
0 голосов
/ 05 июня 2019

У меня есть сервис, который вызывает дюжину других сервисов.Это читает тему Кафки, используя @StreamListener в классе контроллера.В целях прослеживаемости те же самые заголовки (исходный идентификатор запроса) из сообщения Kafka также необходимо перенаправить во все другие службы

Традиционно, с @PostMapping("/path") или GetMapping генерируется контекст запросаи можно получить доступ к заголовкам из любого места, используя RequestContextHolder.currentRequestAttributes(), и я бы просто передавал объект HttpHeaders в RequestEntity всякий раз, когда мне нужно сделать внешний вызов

Однако в 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);
  }
}

Мой текущий обходной путьэто изменить StreamListener на PostMapping и получить еще один PostMapping, который вызывает, чтобы можно было сгенерировать контекст запроса.Другой вариант состоял в том, чтобы использовать ThreadLocal, но он выглядит просто как janky

Я знаю аннотацию @Headers MessageHeaders для доступа к заголовкам потока, однако, это не легко сделать без передачи заголовков вниздля каждой службы и может повлиять на многие модульные тесты

В идеале мне нужен способ создания собственного контекста запроса (или какой бы то ни было правильной терминологии), чтобы иметь место для хранения объектов области запроса (* 1027)*) или другой потокобезопасный способ передачи заголовков запросов по стеку без добавления аргумента запроса к service.execute

1 Ответ

0 голосов
/ 03 июля 2019

Я нашел решение и оставляю его здесь для всех, кто пытается достичь чего-то подобного

Если ваша цель состоит в том, чтобы пересылать несколько заголовков сквозным образом через контроллеры REST и потоковые прослушиватели, вы можете рассмотреть возможность использования Spring Cloud Sleuth

Добавьте его в свой проект через конфигурацию maven или gradle:

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

В частности, в Spring Cloud Sleuth есть функция для пересылки заголовков или «багажа» путем установки свойства spring.sleuth.propagation-keys в вашем application.properties. Эти пары ключ-значение сохраняются в течение всей трассировки, включая любые нисходящие http или потоковые вызовы, которые также реализуют те же ключи распространения

Если к этим полям нужно обращаться на уровне кода, вы можете получить и установить их с помощью статических функций ExtraFieldPropagation:

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

Обратите внимание, что установщик ExtraFieldPropagation не может установить свойство, отсутствующее в определенном spring.sleuth.propagation-keys, поэтому произвольные ключи не будут приняты

Вы можете прочитать документацию для получения дополнительной информации

...