При использовании Spring, когда Кафка получает сообщение, не найдено ни одного привязанного к потоку запроса - PullRequest
0 голосов
/ 26 марта 2020

Я получаю эту ошибку от моего сервиса

jvm org.hibernate.internal.ExceptionMapperStandardImpl {"@trace_info":"[availability-psql,eba16d49e23479cc,675789f41e0dda5b,eba16d49e23479cc,false]", "@message": "HHH000346: Error during managed flush [Error creating bean with name 'scopedTarget.infoUser': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.]

Это из-за того, что у меня есть бин области видимости @ScopeRequest. Эта проблема появляется, когда получено новое сообщение от kafka, и я пытаюсь обновить свою базу данных данными весны. Если я удалю свой @Transactional, у меня не возникнет проблем с сохранением данных.

@KafkaListener(topics = "#{kafkaMastersConfig.topics}", containerFactory = "mastersContainerFactory")
  @Transactional
  @Authorized
  public void consumeWrapperMasterChangeEvent(@Payload String payload,
      @Header(KafkaHeaders.RECEIVED_TOPIC) String topic,  @Nullable @Header(AUTHORIZATION) String authorization) throws IOException {
    try {
    log.info("Received change event in masters: '{}'", payload);

    RequestAttributes context = RequestContextHolder.currentRequestAttributes();
    RequestContextHolder.setRequestAttributes(context);

    changeProcessorFactory.getEntityChangeProcessor(getEntityFromTopic(topic)).processChange(payload);

    } catch ( Exception e ) {
     log.error("Error proccesing message {} ", e.getMessage());
    } finally {
      RequestContextHolder.resetRequestAttributes();
    }


  }

И вот боб:

@RequestScope
@Component
@NoArgsConstructor
@Getter
@Setter
public class InfoUser {

  private DecodedJWT jwt;

  public String getCurrentUser() {
    if (jwt == null) {
      return null;
    }
    return jwt.getSubject();
  }

  public String getAuthorizationBearer() {
    if (jwt == null) {
      return null;
    }
    return jwt.getToken();
  }
}

И этот класс:

public class CustomRequestScopeAttr implements RequestAttributes {

  private Map<String, Object> requestAttributeMap = new HashMap<>();

  @Override
  public Object getAttribute(String name, int scope) {
    if (scope == RequestAttributes.SCOPE_REQUEST) {
      return this.requestAttributeMap.get(name);
    }
    return null;
  }

  @Override
  public void setAttribute(String name, Object value, int scope) {
    if (scope == RequestAttributes.SCOPE_REQUEST) {
      this.requestAttributeMap.put(name, value);
    }
  }

  @Override
  public void removeAttribute(String name, int scope) {
    if (scope == RequestAttributes.SCOPE_REQUEST) {
      this.requestAttributeMap.remove(name);
    }
  }

  @Override
  public String[] getAttributeNames(int scope) {
    if (scope == RequestAttributes.SCOPE_REQUEST) {
      return this.requestAttributeMap.keySet().toArray(new String[0]);
    }
    return new String[0];
  }

  @Override
  public void registerDestructionCallback(String name, Runnable callback, int scope) {
    // Not Supported
  }

  @Override
  public Object resolveReference(String key) {
    // Not supported
    return null;
  }

  @Override
  public String getSessionId() {
    return null;
  }

  @Override
  public Object getSessionMutex() {
    return null;
  }
}

И, кроме того, у меня есть класс аспектов для сохранения токена авторизации:

@Aspect
@Component
@RequiredArgsConstructor
public class AuthorizationAspect {

  private final AuthorizationDecoder authorizationDecoder;

  private final ApplicationContext applicationContext;

  @Around("@annotation(Authorized)")
  public Object setInfoUser(ProceedingJoinPoint joinPoint) throws Throwable {
    try {
      String[] parameterNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
      Object[] args = joinPoint.getArgs();
      Map<String, Object> arguments = new HashMap<>();
      for (int i = 0; i < args.length; i++) {
        if (null != args[i]) {
          arguments.put(parameterNames[i], args[i]);
        }
      }
      Object authorization = arguments.get("authorization");
      RequestContextHolder.setRequestAttributes(new CustomRequestScopeAttr());

      InfoUser infoUser = applicationContext.getBean(InfoUser.class);
      infoUser.setJwt(authorizationDecoder.decodeToken((String) authorization));

      return joinPoint.proceed();

    } finally {
      RequestContextHolder.resetRequestAttributes();
    }

  }

И последний класс пытается сохранить информацию:

@RequiredArgsConstructor
public class RoomChangeMaster implements ChangeMaster<Room> {

  private final TimetableRepository timetableRepository;

  private final AvailabilityRepository availabilityRepository;

  @Override
  public void processChange(Room entity, ActionEnum action) {

    if (action == ActionEnum.updated) {

      List<Timetable> timetables = (List<Timetable>) timetableRepository.findByRoomId(entity.getId());
      Room room = timetables.get(0).getRoom();
      room.setDescription(entity.getDescription());
      room.setCode(entity.getCode());
      timetables.forEach(timetable -> {
        timetable.setRoom(room);
        timetableRepository.save(timetable);
      });

      availabilityRepository
          .updateAvailabilityRoomByRoomId(room, entity.getId());

    } else {

      throw new IllegalStateException("Unexpected value: " + action);

    }
  }
}

Я потратил много времени на выяснение проблемы, но до сих пор не смог знать проблему. Любая идея будет оценена.

Спасибо

Ответы [ 2 ]

0 голосов
/ 27 марта 2020

Наконец, я решил это, изменив метод сохранения Hiberante с помощью saveAndFlu sh

0 голосов
/ 27 марта 2020

RequestContextHolder для Spring- MVC - он предназначен только для веб-запроса и заполняется информацией из HTTP-запроса.

/**
 * Holder class to expose the web request in the form of a thread-bound
 * {@link RequestAttributes} object. The request will be inherited
 * by any child threads spawned by the current thread if the
 * {@code inheritable} flag is set to {@code true}.
 *
...

Не существует эквивалента для контейнеров слушателей (любых введите), потому что нет «входящего запроса».

Похоже, ваш код гибернации тесно связан с сетью.

Если вы пытаетесь повторно использовать существующий код, вам нужно отделить его и использовать некоторые другие методы передачи информации между слоями (например, пользовательский эквивалент RequestContextHolder).

...