简体   繁体   English

Springboot:如何使Rest Service Controller不阻塞

[英]Springboot: How to make Rest Service Controller non-blocking

I have a spring boot application which validates a file for a given client id and returns a json response of validation errors and warnings, while performing load test we noticed most the requests being blocked, so am trying to make our application non blocking by leveraging Spring's non blocking api 我有一个Spring Boot应用程序,该应用程序针对给定的客户端ID验证文件,并返回验证错误和警告的json响应,而在执行负载测试时,我们注意到大多数请求都被阻止了,所以我试图通过利用Spring的使用来使我们的应用程序不被阻止非阻塞API

Below is my spring version 下面是我的春季版

springBootVersion = '1.5.3.RELEASE'
springVersion = '4.3.8.RELEASE'

Below is my springboot ValidationController.groovy which is blocking requests 下面是我的springboot ValidationController.groovy ,它阻止了请求

@Controller
@ResponseBody
class ValidationController {

    @RequestMapping(value = "/validate", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    ResponseEntity<ValidationResult> validate(@RequestParam(value = "file", required = true) MultipartFile file,
                                    @RequestParam(value = "client_id", required = true) String clientId)
    {
        if (clientId.isEmpty()) {
                String msg = "client id is required"
                if (LOGGER.isErrorEnabled()) {
                    LOGGER.error(msg)
                }
                return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(msg);
            }

            String contentType = file.contentType.toLowerCase();
            if (LOGGER.isDebugEnabled()) LOGGER.debug("content type = $contentType");

            Client client = clientRepository.findByExternalId(clientId)

            if (client == null) {
                String msg = "client id is invalid"
                if (LOGGER.isErrorEnabled()) {
                    LOGGER.error(msg)
                }
                return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(msg);
            }

            if (file.isEmpty()) {
            String msg = "file is empty"
            if(LOGGER.isErrorEnabled()) {
                LOGGER.error(msg)
            }
            return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(msg);
        }

        ValidationResult result = validationService.validate(file, client);

        return ResponseEntity.ok(result)
    }
}

class ValidationResult {

    private List<Warning> warnings
    private List<Error> errors
    //getters setters for warnings and errors
}

class Warning {
    private String message
    private String type
    //getters setters for message and type
}

class Error {
    private String message
    private String type
    //getters setters for message and type
}

I have modified my ValidationController.groovy as below 我已经如下修改了ValidationController.groovy

@Controller
@ResponseBody
class ValidationController {

    @Autowired
    @Qualifier("postRequestExecutorService")
    private ExecutorService postRequestExecutor;


    @RequestMapping(value = "/validate", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public DeferredResult<ResponseEntity<ValidationResult>> validate(@RequestParam(value = "file", required = true) MultipartFile file,
                                                              @RequestParam(value = "client_id", required = true) String clientId)
    {
        DeferredResult<ResponseEntity<ValidationResult>> deferredResult = new DeferredResult<>();

       CompletableFuture.supplyAsync(() -> validate(clientId, file), postRequestExecutor)
                .whenComplete((result, throwable) ->
                {                    
                    deferredResult.setResult(result);
                }        );


    }

private ResponseEntity<ValidationResult> validateLedes(String clientId, MultipartFile file) {

    ValidationResult result;
    try{
        if (clientId.isEmpty()) {
            String msg = messageSource.getMessage("client.id.required", null, Locale.getDefault())
            LOGGER.error(msg)
            return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(msg);
        }

        String contentType = file.contentType.toLowerCase();
        if (LOGGER.isDebugEnabled()) LOGGER.debug("content type = $contentType");

        Client client = clientRepository.findByExternalId(clientId)

        if (client == null) {
            String msg = messageSource.getMessage("client.id.invalid", null, Locale.getDefault())
            LOGGER.error(msg)
            return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(msg);
        }

        if (file.isEmpty()) {
            String msg = messageSource.getMessage("ledes.file.empty", null, Locale.getDefault())
            LOGGER.error(msg)
            return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(msg);
        }

        result = validationService.validate(file, Ledesxmlebilling21.class, client);
    }
    catch (Exception ex){
        LOGGER.error("Exception in validateLedes = "+ex.message)
        LOGGER.error("StackTrace in validateLedes = "+ex.stackTrace)
    }

    return ResponseEntity.ok(result)
}

}

And below is my ExecutorServiceConfiguration 下面是我的ExecutorServiceConfiguration

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

@Configuration
public class RequestsExecuterServiceConfiguration {

    /**
     * Dedicated Thread Modeling to handle POST Requests
     */
    @Bean
    public ExecutorService postRequestExecutorService() {
        final ThreadFactory threadFactory = new ThreadFactoryBuilder()
                .setNameFormat("postRequestExecutor-%d")
                .setDaemon(true)
                .build();
        ExecutorService es = Executors.newFixedThreadPool(10,threadFactory);
        return es;
    }
}

Since my controller is a groovy class am seeing some compiler errors for the CompletableFuture lambda expression, can someone please help me make it work for groovy controller? 由于我的控制器是groovy类,因此我看到CompletableFuture lambda表达式存在一些编译器错误,有人可以帮我使其适用于groovy控制器吗?

UPDATE1 As per the Answer I've changed the labda expression to closure as below UPDATE1根据答案,我已将labda表达式更改为闭包,如下所示

@RequestMapping(value = "/validate", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public DeferredResult<ResponseEntity<ValidationResult>> validate(@RequestParam(value = "file", required = true) MultipartFile file,
                                                          @RequestParam(value = "client_id", required = true) String clientId)
{
    DeferredResult<ResponseEntity<ValidationResult>> deferredResult = new DeferredResult<ResponseEntity<ValidationResult>>();

    CompletableFuture.supplyAsync({ -> validateLedes(clientId, file) }, postRequestExecutor)
            .whenComplete({ futureResult, throwable -> deferredResult.setResult(futureResult);})

    deferredResult
}

With the above controller, am not getting below errors 使用上述控制器,不会出现以下错误

2018-04-11 15:07:45 - Exception in validateLedes = failed to lazily initialize a collection of role: com.validation.entity.Client.ruleConfigurations, could not initialize proxy - no Session
2018-04-11 15:07:45 - StackTrace in validateLedes = org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:587), org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:204), org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:148), org.hibernate.collection.internal.PersistentSet.size(PersistentSet.java:143)

Looks like the issue is, Hibernate session is not bound to ExecutorService and the new thread in which validateLedes method is executing it's unable to read from the database, can someone please me to binding Hibernate session to the the ExecutorService's thread pool? 看起来问题是,Hibernate会话未绑定到ExecutorService,并且validateLedes方法正在执行的新线程无法从数据库中读取,有人可以请我将Hibernate会话绑定到ExecutorService的线程池吗?

You can't just stick lambdas into Groovy (until Groovy 3) 您不能只是将lambda粘贴到Groovy中(直到Groovy 3)

You'll need to translate them to Closures, so for example: 您需要将它们转换为Closures,例如:

() -> validate(clientId, file)

becomes: 变为:

{ -> validate(clientId, file) }

And

(result, throwable) ->
{                    
    deferredResult.setResult(result);
} 

would be: 将会:

{ result, throwable -> deferredResult.setResult(result) } 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM