[英]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.