Нет ошибки запроса потока в параллельном потоке в контроллере - PullRequest
0 голосов
/ 20 сентября 2019

У меня есть конечная точка на контроллере, которая принимает список идентификаторов.Для каждого идентификатора я получаю детали из конечной точки REST, которые я хотел бы сделать асинхронно.Это моя реализация:

@PostMapping("/leaderboard")
@ApiOperation("Get clients leaderboard statistics")
public List<Dto> clientLeaderboard(@RequestBody List<String> accountIds) {

    IntStream.range(0, accountIds.size()).parallel().forEach(i -> {
        AccountDTO accountDTO = accountMaintenanceRestClient.getAccount(accountIds.get(i));
        // More logic
    }

}

Это дает мне ошибку:

Произошло необработанное исключение

java.lang.IllegalStateException: java.lang.IllegalStateException: Привязанный к потоку запрос не найден: Вы ссылаетесь на атрибуты запроса вне фактического веб-запроса или обрабатываете запрос вне первоначально получающего потока?Если вы действительно работаете с веб-запросом и по-прежнему получаете это сообщение, ваш код, вероятно, выполняется за пределами DispatcherServlet / DispatcherPortlet: в этом случае используйте RequestContextListener или RequestContextFilter для предоставления текущего запроса.

IЯ действительно уже сталкивался с этой ошибкой и смог исправить ее в ответе здесь .Здесь применимо такое же решение?Если да, то как я могу предоставить моего существующего исполнителя этому параллельному потоку?

Редактировать

Код AccountMaintenanceRestClient.Я использую OpenFeign как часть этого.

public interface AccountMaintenanceRestClient {
   @RequestLine("GET /account/{accountId}")
   @Profiled(tag = "AccountMaintenanceRestClient.{$0}.getAccountBalance", logFailuresSeparately = true)
   AccountDTO getAccount(@Param("accountId") String accountId);

   @RequestLine("GET /referencedata/statuscode")
   @Profiled(tag = "AccountMaintenanceRestClient.getStatusCodes", logFailuresSeparately = true)
   Map<String, String> getStatusCodes();

   @RequestLine("GET /referencedata/websites")
   @Profiled(tag = "AccountMaintenanceRestClient.getWebsitesIds", logFailuresSeparately = true)
   List<WebSiteDTO> getWebsitesIds();
}

Stacktrace:

java.lang.IllegalStateException: 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.
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:593)
    at java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:677)
    at java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:735)
    at java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:160)
    at java.util.stream.ForEachOps$ForEachOp$OfInt.evaluateParallel(ForEachOps.java:189)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
    at java.util.stream.IntPipeline.forEach(IntPipeline.java:404)
    at java.util.stream.IntPipeline$Head.forEach(IntPipeline.java:560)
    at com.foobar.wt.igip.gateway.web.controllers.surge.paris.SurgeParisController.clientLeaderboard(SurgeParisController.java:58)
    at com.foobar.wt.igip.gateway.web.controllers.surge.paris.SurgeParisController$$FastClassBySpringCGLIB$$a143909.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:667)
    at com.foobar.wt.igip.gateway.web.controllers.surge.paris.SurgeParisController$$EnhancerBySpringCGLIB$$67cb3a1e.clientLeaderboard(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:854)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:765)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:650)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:54)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:111)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at uk.co.igindex.commons.bluegreen.IGClusterDetailsFilter.doFilter(IGClusterDetailsFilter.java:46)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:208)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:347)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:263)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.boot.web.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:128)
    at org.springframework.boot.web.support.ErrorPageFilter.access$000(ErrorPageFilter.java:66)
    at org.springframework.boot.web.support.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.java:103)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.boot.web.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:121)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1757)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1716)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)
Caused by: 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.
    at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
    at org.springframework.web.context.support.WebApplicationContextUtils.currentRequestAttributes(WebApplicationContextUtils.java:309)
    at org.springframework.web.context.support.WebApplicationContextUtils.access$400(WebApplicationContextUtils.java:64)
    at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:325)
    at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:320)
    at org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke(AutowireUtils.java:307)
    at com.sun.proxy.$Proxy134.getHeader(Unknown Source)
    at com.foobar.wt.igip.gateway.support.FeignSSOTokenRequestInterceptor.apply(FeignSSOTokenRequestInterceptor.java:22)
    at feign.SynchronousMethodHandler.targetRequest(SynchronousMethodHandler.java:158)
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:88)
    at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76)
    at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)
    at com.sun.proxy.$Proxy156.getAccount(Unknown Source)
    at com.foobar.wt.igip.gateway.web.controllers.surge.paris.SurgeParisController.lambda$clientLeaderboard$2(SurgeParisController.java:59)
    at java.util.stream.ForEachOps$ForEachOp$OfInt.accept(ForEachOps.java:205)
    at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110)
    at java.util.Spliterator$OfInt.forEachRemaining(Spliterator.java:693)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:291)
    at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

FeignSSOTokenRequestInterceptor:

import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
public class FeignSSOTokenRequestInterceptor implements RequestInterceptor {
   private static final String SSO_HEADER_NAME = "X-SECURITY-TOKEN";
   private final HttpServletRequest request;

   @Autowired
   public FeignSSOTokenRequestInterceptor(HttpServletRequest request) {
      this.request = request;
   }

   @Override
   public void apply(RequestTemplate requestTemplate) {
      requestTemplate.header(SSO_HEADER_NAME, request.getHeader(SSO_HEADER_NAME));
   }
}

Ответы [ 2 ]

1 голос
/ 24 сентября 2019

Я хотел бы пролить свет на этот вопрос.

FeignSSOTokenRequestInterceptor - ваш пользовательский RequestInterceptor, и вы пытаетесь получить доступ к ServletRequest внутри метода apply, что видно из трассировки стека

RequestObjectFactory.getObject (WebApplicationContextUtils.java:320)

Этот RequestObjectFactory является статическим внутренним классом, который дает вам доступ к текущему запросу

private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
    private RequestObjectFactory() {
    }

    public ServletRequest getObject() {
        return WebApplicationContextUtils.currentRequestAttributes().getRequest();
    }

    public String toString() {
        return "Current HttpServletRequest";
    }
}

, текущий запрос получен изкласс RequestContextHolder, который получает атрибут из текущего потока, как показано ниже после серии вызовов метода

 //This is the current thread which holds the request attributes
    private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes");

     //This is the place where you get the actual exception from the spring RequestContextHolder class.
     public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
        RequestAttributes attributes = getRequestAttributes();
        if (attributes == null) {
            if (jsfPresent) {
                attributes = RequestContextHolder.FacesRequestAttributesFactory.getFacesRequestAttributes();
            }

            if (attributes == null) {
                throw new 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.");
            }
        }

        return attributes;
    }

     public static RequestAttributes getRequestAttributes() {
            RequestAttributes attributes = (RequestAttributes)requestAttributesHolder.get();
            if (attributes == null) {
                attributes = (RequestAttributes)inheritableRequestAttributesHolder.get();
            }

            return attributes;
        }

окончательный вердикт: , поэтому убедитесь, что вы не пытаетесь получить доступ к ServletRequest изRequestInterceptor, если вы используете параллельные потоки.поскольку параллельные потоки должны использоваться только с операциями без учета состояния, не мешающими и ассоциативными

, поскольку requestAttributesHolder.get () ищет текущий поток, не использует параллельные потоки в нем, как показано ниже. Что не рекомендуется, и в параллелизме есть много вещей, которые следует учитывать (см. link )

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

, если у вас нет кода, который обращается ктекущий запрос, и он вызывается из файлов класса OpenFeign , тогда единственный способ - использовать последовательные потоки вместо параллельных. Это просто

accountIds.stream().forEach(accountId -> {
    AccountDTO accountDTO = accountMaintenanceRestClient.getAccount(accountId);
    // More logic
}
0 голосов
/ 25 сентября 2019

Ваш FeignSSOTokenRequestInterceptor зависит от HttpServletRequest прокси-компонента.

Запрос выставляется через threadlocal, поэтому потоки в пуле потоков не знают о нем / не знают об этом, поэтому возникает исключение, когда он пытается прочитать запрос изКарта потоков.

Таким образом, в основном проблема сузилась до того, как установить запрос в контекст потока пула потоков.

Настройте пул потоков как ваш пост, на который есть ссылка, затем отправьте задачу,

@Autowired
ThreadPoolTaskExecutor poolExecutor;

CountdownLatch counter = new CountdownLatch(accountIds.size());
IntStream.range(0, accountIds.size()) // Note that .parallel is omitted
         .forEach(() -> poolExecutor.submit(() -> {
             try {
                your task
             } finally {
                counter.countDown();
             }
         })) 
counter.await(); // Wait for all tasks complete

Это предпочтительный способ, поскольку в параллельном потоке по умолчанию используется общий общий пул, вам не следует размещать задачу с интенсивным вводом-выводом, например API-доступом к ней)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...