簡體   English   中英

Springboot:如何使Rest Service Controller不阻塞

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

我有一個Spring Boot應用程序,該應用程序針對給定的客戶端ID驗證文件,並返回驗證錯誤和警告的json響應,而在執行負載測試時,我們注意到大多數請求都被阻止了,所以我試圖通過利用Spring的使用來使我們的應用程序不被阻止非阻塞API

下面是我的春季版

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

下面是我的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
}

我已經如下修改了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)
}

}

下面是我的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;
    }
}

由於我的控制器是groovy類,因此我看到CompletableFuture lambda表達式存在一些編譯器錯誤,有人可以幫我使其適用於groovy控制器嗎?

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
}

使用上述控制器,不會出現以下錯誤

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)

看起來問題是,Hibernate會話未綁定到ExecutorService,並且validateLedes方法正在執行的新線程無法從數據庫中讀取,有人可以請我將Hibernate會話綁定到ExecutorService的線程池嗎?

您不能只是將lambda粘貼到Groovy中(直到Groovy 3)

您需要將它們轉換為Closures,例如:

() -> validate(clientId, file)

變為:

{ -> validate(clientId, file) }

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

將會:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM