21 Haziran 2018 Perşembe

Servlet 3.0 DeferredResult Sınıfı İle Asynchronous Request Processing

Giriş
Açıklaması şöyle.
While DeferredResult is used to produce a single result, a ResponseBodyEmitter can be used to send multiple objects where each object is written with a compatible HttpMessageConverter
Açıklaması şöyle. Spring'e ait olmayan bir thread varsa kullanılır
A controller method can also return a DeferredResult to complete processing in a thread not known to Spring MVC. For example reacting to a JMS or an AMQP message, a Redis notification, and so on.
Kullanım
Eğer timeout istiyorsak şöyle yaparız
# Asynchronous request timeout
spring.mvc.async.request-timeout=2000
Örnek
Spring dokümantasyonundaki örnek şöyle. setResult() metodu mutlaka başka bir thread içinden çağrılmalı
@GetMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
  DeferredResult<String> deferredResult = new DeferredResult<>();
  // Save the deferredResult somewhere..
  return deferredResult;
}

// From some other thread...
deferredResult.setResult(data);
Örnek
Şöyle yaparız. Burada iş Fork Join Thread Pool içinde çalıştırılıyor.
@GetMapping("/async")
@ResponseBody
public DeferredResult<String> async() {
  DeferredResult<String> deferredResult = new DeferredResult<>();
  CompletableFuture.supplyAsync(() -> {
    // Perform asynchronous processing here
    return "Hello, async world!";
  }).whenCompleteAsync((result, throwable) -> {
    if (throwable != null) {
      deferredResult.setErrorResult(throwable);
    } else {
      deferredResult.setResult(result);
    }
  });
  return deferredResult;
}
Örnek
Şöyle yaparız. Burada iş Fork Join Thread Pool içinde çalıştırılıyor.
@Override
public DeferredResult pull(Long previousId, String username) {
  DeferredResult result = createPollingResult(previousId, username);

  CompletableFuture.runAsync(() -> {
    // this is where you encapsulate your db transaction
    List<MessageDTO> messages = messageService.findRecents(previousId, username);
      if (messages.isEmpty()) {
        pollingResults.putIfAbsent(username, result);
      } else {
        result.setResult(messages);
      }
  });
  return result;
}
constructor - milliseconds
Örnek
Şöyle yaparız
DeferredResult<ResponseEntity<?>> defResult = new DeferredResult<>(500L);
onCompletion metodu
Örnek
Şöyle yaparız
DeferredResult<?> defResult = ...;

defResult.onCompletion(new Runnable() {
  public void run() {
    ...
});

defResult.onTimeout(new Runnable() {
  public void run() {
    ...
});
setErrorResult metodu
Örnek
Şöyle yaparız
DeferredResult<ResponseEntity<?>> defResult = new DeferredResult<>(500L);

defResult.onTimeout(new Runnable() {
  public void run() {
    defResult.setErrorResult(ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT)
.body("Request timeout occurred.")));) });
setResult metodu
Örnek
Şöyle yaparız.
@GetMapping(value = "/math/square")
public DeferredResult<Integer> computeSquare(@RequestParam("x") int x) {
  
  DeferredResult<Integer> deferredResult = new DeferredResult<>();

  new Thread(() -> { // normally, you would probably use a thread pool
            // here's where the "heavy logic" takes place
    ...
    deferredResult.setResult(x * x);
  }).start();

  return deferredResult;
}
Örnek - ForkJoin
Şöyle yaparız
@RestController
public class AsyncDeferredController {
  private final TaskService taskService = ...;
     
  @Autowired
  public AsyncDeferredController(TaskService taskService) {
    this.taskService = taskService;
  }
     
  @RequestMapping(value = "/deferred", method = RequestMethod.GET, produces = "text/html")
  public DeferredResult<String> executeSlowTask() {
  
    DeferredResult<String> deferredResult = new DeferredResult<>();
    CompletableFuture.supplyAsync(taskService::execute)
      .whenCompleteAsync((result, throwable) -> deferredResult.setResult(result));
              
    return deferredResult;
  }
}
Örnek - ExecutorService
Şöyle yaparız
ExecutorService executorService = Executors.newFixedThreadPool(10);

@RequestMapping("/hello_v1")
public DeferredResult<String> hello_v1() {
    // Set timeout
    DeferredResult<String> deferredResult = new DeferredResult<>(7000L);
    // The callback method will be executed when the asynchronous thread processing ends
    deferredResult.onCompletion(() -> {
        log.info ("end of asynchronous thread processing");
    });
    // The method will time out if the callback execution time exceeds the setting
    deferredResult.onTimeout(() -> {
        log.info ("asynchronous thread timeout");
        // Set return result
        deferredResult.setErrorResult("timeout error");
    });
    deferredResult.onError(throwable -> {
        log.error (throwable);
        // Set return result
        deferredResult.setErrorResult("other error");
    });
    executorService.submit(() -> {
        try {
            TimeUnit.SECONDS.sleep(5);
            deferredResult.setResult("hello_v1");
            // Set return result
        } catch (Exception e) {
            e.printStackTrace();
            // If the asynchronous method has an internal exception
            deferredResult.setErrorResult("error");
        }
    });
    log.info ("servlet thread processing ended");
    return deferredResult;
}
Örnek - RxJava
Şöyle yaparız.
@RequestMapping(value = "/download/{templateName:.+}")
public DeferredResult<ResponseEntity> download(@PathVariable final String templateName,
                                          @RequestParam final Map<String, Object> args) {
  final DeferredResult<ResponseEntity> result = new DeferredResult<>();
  try {
    // it returns a RxJava Observable
    final ReportService reportService = getReportService(templateName);
    reportService.process(templateName, args).subscribe(new Consumer<byte[]>() {
      public void accept(byte[] bytes) throws Exception {
        HttpHeaders header = new HttpHeaders();
        header.setContentType(new MediaType("application", MEDIA_TYPE_SPREADSHEET));
        header.setContentDispositionFormData("attachment", templateName);

        InputStreamResource resource = new InputStreamResource(
          new ByteArrayInputStream(bytes));
        result.setResult(ResponseEntity.ok().headers(header).body(resource));
      }
    }, new Consumer<Throwable>() {
        public void accept(Throwable e) throws Exception {
          result.setResult(ResponseEntity.badRequest().build());
          // ...
        }
    });
  } catch (Exception e) {
    result.setResult(ResponseEntity.badRequest().build());
    // ...
  }
  return result;
}


Hiç yorum yok:

Yorum Gönder