【问题标题】:Using HttpServletRequest with ParallelStream in Java 8在 Java 8 中使用 HttpServletRequest 和 ParallelStream
【发布时间】:2023-03-26 19:32:01
【问题描述】:

我有一个简单的应用程序,它将公开一个名为 'getAllDeviceData' 的 RESTFul GET 端点,它将简单地返回从数据库中的设备表中获取的所有设备的数据列表。

对于每个请求,我通过验证 HttpServletRequest.getUserPrincipal() 方法来验证用户。 为了加快这个过程,我使用了带有 lambda 表达式的 parallelStream。

在 ParallelStream 中,我调用了另一个名为 'getDeviceData' 的方法,我在其中进行身份验证并从数据库中获取数据。

问题是,当并行流进程调用getDeviceData 方法时,我得到一个NullPointer 异常并且未能完成并行流。

原因是,HttpServletRequest.getUserPrincipal() 在方法内部为空。但它实际上存在于'getAllDeviceData'(lambda 表达式所在的位置)中。

如果我只用'stream()' 替换'parallelStream()',这不会有任何问题,但在这种情况下并行性质不存在。

@Override
@ResponseBody
@RequestMapping(value = "getAllDeviceData", method = RequestMethod.GET, consumes = "*")
public List<List<Data>> getAllDeviceData(    
        @RequestParam(value = "recordLimit", required = false) final Integer recordLimit,         
        final HttpServletRequest request) {

    final List<Device> deviceList = deviceService.getAllDevices();   
    final List<List<Data>> dataList = deviceList.parallelStream().map(device -> getDeviceData(recordLimit, device.getDeviceId(), request)).collect(Collectors.toList());

    return alerts;
}

private List<Data> getDeviceData(@RequestParam(value = "recordLimit", required = false) Integer recordLimit, String deviceId, HttpServletRequest request) {
    if(request.getUserPrincipal() == null){
        logger.info("User Principle Null - 1");
    }else {
        logger.info("User Principle Not Null - 1");
    }
    authService.doAuthenticate(request);

    // if authrnticated proceed with following...
    List<Data> deviceData = deviceService.getGetDeviceData(deviceId);
    return deviceData;
}

但是,我观察到了一些事情。

查看上述应用程序的以下日志(省略了不必要的部分)。

在其中,主线程(例如:http-nio-7070-exec-2 等 - 这是此应用程序服务器的线程池的主线程)工作正常,因为它打印出“用户原则不是空 - 1”,但是在分解的线程中ForkJoinPool.commonPool-worker-2 等并行流的数量。HTTPServletRequest.getUserPrincipal() 正在变为 null。

2018-01-15 15:28:06,897 INFO [http-nio-7070-exec-2] User Principle Not Null - 1 
2018-01-15 15:28:06,897 INFO [ForkJoinPool.commonPool-worker-2] User Principle Null - 1 
2018-01-15 15:28:06,906 INFO [ForkJoinPool.commonPool-worker-3] User Principle Null - 1 

2018-01-15 15:28:06,955 INFO [ForkJoinPool.commonPool-worker-2] User Principle Null - 1 
2018-01-15 15:28:06,955 INFO [ForkJoinPool.commonPool-worker-1] User Principle Null - 1

2018-01-15 15:28:06,957 INFO [ForkJoinPool.commonPool-worker-2] User Principle Null - 1 
2018-01-15 15:28:06,959 INFO [ForkJoinPool.commonPool-worker-3] User Principle Null - 1
2018-01-15 15:28:07,064 INFO [ForkJoinPool.commonPool-worker-2] User Principle Null - 1 

2018-01-15 15:28:07,076 INFO [http-nio-7070-exec-2] User Principle Not Null -1

2018-01-15 15:28:07,078 INFO [ForkJoinPool.commonPool-worker-1] User Principle Null - 1

我对 lambda 表达式和并行流还很陌生。 请帮助我了解这里的问题。

Java 详细信息:

java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

【问题讨论】:

    标签: java spring-security parallel-processing java-8 java-stream


    【解决方案1】:

    根本原因是 Spring 将 SecurityContextHolderAwareRequestWrapper 实例注入到您的方法中。当调用equest.getUserPrincipal() 时,此包装器将调用以下行:

    private Authentication getAuthentication() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    

    SecurityContextHolder 有不同的策略。默认使用MODE_THREADLOCAL 策略。这就是为什么您在主线程中有用户原则但在 forkjoinpool 线程中没有用户原则的原因。

    -Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL VM 选项可以解决您的问题。 InheritableThreadLocal javadoc 和 InheritableThreadLocalSecurityContextHolderStrategy 源代码可能会带来额外的理解价值。

    【讨论】:

      猜你喜欢
      • 2013-11-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-24
      • 2014-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-28
      相关资源
      最近更新 更多