【问题标题】:No thread-bound request found with Spring when Kafka receive a messageKafka 收到消息时,未使用 Spring 找到线程绑定请求
【发布时间】:2020-03-26 19:57:31
【问题描述】:

我的服务收到此错误

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 的 bean。当收到来自 kafka 的新消息并且我尝试使用 spring 数据更新我的数据库时,就会出现此问题。如果我删除我的@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();
    }

  }

最后一堂课正在尝试保存de info:

@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);

    }
  }
}

我花了很多时间找出问题所在,但到目前为止,我无法知道问题所在。任何想法将不胜感激。

谢谢

【问题讨论】:

    标签: spring hibernate spring-boot spring-kafka


    【解决方案1】:

    RequestContextHolder 用于 Spring-MVC - 它仅用于 Web 请求,并填充了来自 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 的自定义等效项)。

    【讨论】:

    • 谢谢,我正在研究其他技术,因为目前我不知道如何修复它
    【解决方案2】:

    最后,我已经解决了它通过 saveAndFlush 更改 hiberante 方法保存

    【讨论】:

      猜你喜欢
      • 2019-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-06
      • 1970-01-01
      • 1970-01-01
      • 2020-06-04
      • 2020-11-16
      相关资源
      最近更新 更多