呼叫我或异步REST
本文是使用Spring Boot + Java 8創建的工作正常的異步REST應用程序的非常簡單的示例。SpringBoot使得開發Web應用程序幾乎非常容易,但是為了簡化任務,我從Spring存儲庫中舉了一個例子,稱為rest- service ,將其分叉到我自己的存儲庫中 ,并出于我的目的對其進行了更改,以創建兩個應用程序:客戶端和服務器。
我們的服務器應用程序將是一個簡單的REST Web服務,它將查詢GitHub以獲取一些用戶數據并將其返回。 我們的客戶端應用程序還將是REST Web服務……將查詢第一個應用程序!
服務器代碼基本上由服務和控制器組成。 該服務使用帶有@Async批注的異步方法,如下所示。
@Service public class GitHubLookupService {private static final Logger logger = LoggerFactory.getLogger(GitHubLookupService.class);private final RestTemplate restTemplate;public GitHubLookupService(RestTemplateBuilder restTemplateBuilder) {this.restTemplate = restTemplateBuilder.build();}@AsyncCompletableFuture<User> findUser(String user) throws InterruptedException {logger.info("Looking up " + user);String url = String.format("https://api.github.com/users/%s", user);User results = restTemplate.getForObject(url, User.class);// Artificial delay of 1s for demonstration purposesThread.sleep(1000L);return CompletableFuture.completedFuture(results);}}服務器控制器:
@RestController public class GitHubController {private final GitHubLookupService lookupService;@Autowiredpublic GitHubController(GitHubLookupService lookupService) {this.lookupService = lookupService;}@RequestMapping("/user/{name}")public CompletableFuture<TimedResponse<User>> findUser(@PathVariable(value = "name") String name) throws InterruptedException, ExecutionException {long start = System.currentTimeMillis();ServerResponse response = new ServerResponse(Thread.currentThread().getName());return lookupService.findUser(name).thenApply(user -> {response.setData(user);response.setTimeMs(System.currentTimeMillis() - start);response.setCompletingThread(Thread.currentThread().getName());return response;});}}我們這里擁有的是來自Java 8的簡單CompletableFuture ,借助thenApply()我們將其轉換為所需的格式,該格式允許我們添加有關當前線程的一些數據,以確保執行確實是異步發生的,也就是說,完成工作的線程不是開始工作的線程。 我們可以確定這一點,運行應用程序并檢查調用結果:
marina@Marinas-MacBook-Pro:~$ http http://localhost:8080/user/mchernyavskaya HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 Date: Mon, 02 Oct 2017 18:07:54 GMT Transfer-Encoding: chunked{"completingThread": "SimpleAsyncTaskExecutor-1","data": {"avatar_url": "https://avatars2.githubusercontent.com/u/538843?v=4","company": "OLX","location": "Berlin, Germany","name": "Maryna Cherniavska","url": "https://api.github.com/users/mchernyavskaya"},"error": false,"startingThread": "http-nio-8080-exec-1","timeMs": 2002 }現在,我們需要創建一個將調用服務器應用程序的客戶端應用 程序 。 在Spring中有一個非常方便的用于使用REST的類,稱為RestTemplate 。 但是,RestTemplate是同步的,我們在服務器應用程序中進行的所有不錯的異步處理對客戶端應用程序完全沒有幫助。 這兩個應用程序是完全獨立的。 客戶端應用程序只知道它將處理一個相當長時間的調用。 由于客戶端應用程序知道這一點,并且由于它可能不想在服務器應用程序查詢的整個過程中都占用線程,因此我們也將使其異步。 AsyncRestTemplate即將解救!
我們的客戶端應用程序將更加簡單,并將主要由控制器代碼組成 。 要在一臺本地計算機上運行兩個應用程序,我們需要使用-Dserver.port = 8082參數更改服務器的端口。 因此,我們的服務器現在位于localhost:8080上,客戶端位于localhost:8082上。
客戶端控制器主要如下。
@RestController public class GitHubController {private static final String BASE_URL = "http://localhost:8080/";private final AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();@RequestMapping("/async/user/{name}")public ListenableFuture<ClientResponse> findUserAsync(@PathVariable(value = "name") String name)throws InterruptedException, ExecutionException {long start = System.currentTimeMillis();ClientResponse clientResponse = new ClientResponse(Thread.currentThread().getName());ListenableFuture<ResponseEntity<ServerResponse>> entity = asyncRestTemplate.getForEntity(BASE_URL + name, ServerResponse.class);entity.addCallback(new ListenableFutureCallback<ResponseEntity<ServerResponse>>() {@Overridepublic void onFailure(Throwable ex) {clientResponse.setError(true);clientResponse.setCompletingThread(Thread.currentThread().getName());clientResponse.setTimeMs(System.currentTimeMillis() - start);}@Overridepublic void onSuccess(ResponseEntity<ServerResponse> result) {clientResponse.setData(result.getBody());clientResponse.setCompletingThread(Thread.currentThread().getName());clientResponse.setTimeMs(System.currentTimeMillis() - start);}});} }我們正在獲取服務器響應,并將其包裝到有關時序和當前線程的更多數據中,以便更好地了解發生了什么。 AsyncRestTemplate提供了一個ListenableFuture ,但是我們用它完成了一個CompletableFuture ,因為它允許我們手動控制未來返回的時刻,并在此過程中轉換輸出。
當我們調用客戶服務時,它返回以下數據:
marina@Marinas-MacBook-Pro:~$ http http://localhost:8082/async/user/mchernyavskaya HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 Date: Mon, 02 Oct 2017 18:28:36 GMT Transfer-Encoding: chunked{"completingThread": "SimpleAsyncTaskExecutor-1","data": {"completingThread": "SimpleAsyncTaskExecutor-3","data": {"avatar_url": "https://avatars2.githubusercontent.com/u/538843?v=4","company": "OLX","location": "Berlin, Germany","name": "Maryna Cherniavska","url": "https://api.github.com/users/mchernyavskaya"},"error": false,"startingThread": "http-nio-8080-exec-7","timeMs": 1403},"error": false,"startingThread": "http-nio-8082-exec-3","timeMs": 1418 }您可以在此處閱讀有關Spring中異步方法的更多信息,但是這個簡單的示例應該可以幫助您了解事物的工作方式。 完整的代碼在存儲庫中 。 希望它有一定用處!
翻譯自: https://www.javacodegeeks.com/2017/10/call-asynchronous-rest.html
總結
以上是生活随笔為你收集整理的呼叫我或异步REST的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 介绍OpenHub框架
- 下一篇: 一楼怎么读 一楼用英语怎么读