package com.example.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration // enable asynchronous processing with Java configuration @EnableAsync public class AsyncConfig { @Bean("githubUserLookupExecutor") public ThreadPoolTaskExecutor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(20); executor.setMaxPoolSize(1000); executor.setWaitForTasksToCompleteOnShutdown(true); executor.setThreadNamePrefix("Async-"); return executor; } }
Create a GitHub Lookup Service:
package com.example.service; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.net.InetSocketAddress; import java.net.Proxy; import java.util.concurrent.CompletableFuture; /** * GitHub Lookup Service */ @Slf4j @Service public class GitHubLookupService { @Value("${http.proxy.host}") private String host; @Value("${http.proxy.port}") private Integer port; /** * The method needs to be public so that it can be proxied. * And self-invocation doesn’t work because it bypasses the proxy and * calls the underlying method directly * * @param name user name * @return */ @Async("githubUserLookupExecutor") public CompletableFuture<User> findUser(String name) { log.debug("looking up {}", name); String url = String.format("https://api.github.com/users/%s", name); User user = new RestTemplate(setupProxy()).getForObject(url, User.class); return CompletableFuture.completedFuture(user); } private SimpleClientHttpRequestFactory setupProxy() { SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port)); requestFactory.setProxy(proxy); return requestFactory; } /** * Representation of a GitHub User (JSON to Object) */ @Data @JsonIgnoreProperties(ignoreUnknown = true) public static class User { private String login; private String name; private String blog; private String location; } }
Test code:
package com.example.test; import com.example.service.GitHubLookupService; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import java.util.concurrent.CompletableFuture; @SpringBootApplication @ComponentScan(basePackages = "com.example") @Slf4j public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Bean CommandLineRunner run(GitHubLookupService service) { return args -> { // Kick off multiple asynchronous lookups CompletableFuture<GitHubLookupService.User> junyuo = service.findUser("junyuo"); CompletableFuture<GitHubLookupService.User> springProjects = service.findUser("Spring-Projects"); CompletableFuture<GitHubLookupService.User> tensorflow = service.findUser("tensorflow"); CompletableFuture<GitHubLookupService.User> dotnet = service.findUser("dotnet"); CompletableFuture<GitHubLookupService.User> google = service.findUser("google"); // Wait until they are all done CompletableFuture.allOf(junyuo, springProjects, tensorflow, dotnet, google).join(); log.debug("user = {}", junyuo.get()); log.debug("user = {}", dotnet.get()); log.debug("user = {}", springProjects.get()); log.debug("user = {}", google.get()); log.debug("user = {}", tensorflow.get()); }; } }
Console log:
2018-12-06 11:01:52.358 INFO 11440 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2018-12-06 11:01:52.361 INFO 11440 --- [ main] com.example.test.TestApplication : Started TestApplication in 2.522 seconds (JVM running for 3.465) 2018-12-06 11:01:52.370 DEBUG 11440 --- [ Async-3] com.example.service.GitHubLookupService : looking up tensorflow 2018-12-06 11:01:52.370 DEBUG 11440 --- [ Async-4] com.example.service.GitHubLookupService : looking up dotnet 2018-12-06 11:01:52.370 DEBUG 11440 --- [ Async-5] com.example.service.GitHubLookupService : looking up google 2018-12-06 11:01:52.370 DEBUG 11440 --- [ Async-2] com.example.service.GitHubLookupService : looking up Spring-Projects 2018-12-06 11:01:52.370 DEBUG 11440 --- [ Async-1] com.example.service.GitHubLookupService : looking up junyuo 2018-12-06 11:01:53.759 DEBUG 11440 --- [ main] com.example.test.TestApplication : user = GitHubLookupService.User(login=junyuo, name=Albert Kuo, blog=http://albert-kuo.blogspot.com/, location=Taipei) 2018-12-06 11:01:53.759 DEBUG 11440 --- [ main] com.example.test.TestApplication : user = GitHubLookupService.User(login=dotnet, name=.NET Foundation, blog=http://www.dotnetfoundation.org, location=null) 2018-12-06 11:01:53.759 DEBUG 11440 --- [ main] com.example.test.TestApplication : user = GitHubLookupService.User(login=spring-projects, name=Spring, blog=http://spring.io/projects, location=null) 2018-12-06 11:01:53.759 DEBUG 11440 --- [ main] com.example.test.TestApplication : user = GitHubLookupService.User(login=google, name=Google, blog=https://opensource.google.com/, location=null) 2018-12-06 11:01:53.759 DEBUG 11440 --- [ main] com.example.test.TestApplication : user = GitHubLookupService.User(login=tensorflow, name=null, blog=http://www.tensorflow.org, location=null)