我有一个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的线程池?
答案 0 :(得分:1)
你不能只将lambdas粘贴到Groovy(直到Groovy 3)
您需要将它们翻译为Closures,例如:
() -> validate(clientId, file)
变为:
{ -> validate(clientId, file) }
和
(result, throwable) ->
{
deferredResult.setResult(result);
}
将是:
{ result, throwable -> deferredResult.setResult(result) }