異步處理REST服務,提升服務器吞吐量html
AsyncController.java前端
@RestController @GetMapping("/async") public class AsyncController { private Logger logger = LoggerFactory.getLogger(getClass()); @RequestMapping("/order") public Callable<String> order() throws Exception { logger.info("主線程開始"); Callable<String> result = new Callable<String>() { @Override public String call() throws Exception { logger.info("副線程開始"); Thread.sleep(2000); // 模擬處理下單消耗的時間 logger.info("副線程結束"); return "success"; } }; logger.info("主線程結束"); return result; } }
應用1/線程1:接收下單請求,放到消息隊列java
應用1/線程2:監聽器,監聽消息隊列是否有下單處理結果,返回HTTP響應git
應用2:處理下單邏輯github
AsyncController.javaweb
@GetMapping("/order2") public DeferredResult<String> order2() throws Exception { logger.info("主線程開始"); // 主線程,至關於圖中應用1/線程1,接收HTTP請求 // 收到下單請求,生成一個隨機訂單號,放到消息隊列裏 String orderNumber = RandomStringUtils.randomNumeric(8); mockQueue.setPlaceOrder(orderNumber); // 用於接收處理結果 DeferredResult<String> result = new DeferredResult<>(); deferredResultHolder.getMap().put(orderNumber, result); logger.info("主線程結束"); return result; }
MockQueue.java,模擬隊列spring
@Component public class MockQueue { private String placeOrder; // 下單消息 private String completeOrder; // 訂單完成訂單完成 private Logger logger = LoggerFactory.getLogger(getClass()); public String getPlaceOrder() { return placeOrder; } public void setPlaceOrder(String placeOrder) { // 此線程是模擬應用2,處理下單邏輯 new Thread(() -> { logger.info("接到下單請求:" + placeOrder); try { Thread.sleep(1000); // 模擬處理下單過程 } catch (InterruptedException e) { e.printStackTrace(); } this.completeOrder = placeOrder; logger.info("下單請求處理完畢:" + placeOrder); }).start(); } public String getCompleteOrder() { return completeOrder; } public void setCompleteOrder(String completeOrder) { this.completeOrder = completeOrder; } }
DeferredResultHolder.java ,用於在線程1與線程2之間傳遞傳遞DeferredResult對象shell
@Component public class DeferredResultHolder { // 訂單號,訂單處理結果 private Map<String, DeferredResult<String>> map = new HashMap<>(); public Map<String, DeferredResult<String>> getMap() { return map; } public void setMap(Map<String, DeferredResult<String>> map) { this.map = map; } }
QueueListener.java,監聽器apache
@Component public class QueueListener implements ApplicationListener<ContextRefreshedEvent> { @Autowired private MockQueue mockQueue; @Autowired private DeferredResultHolder deferredResultHolder; private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { // 至關於圖中應用1/線程2,模擬監聽器 new Thread(() -> { while (true) { if (StringUtils.isNotBlank(mockQueue.getCompleteOrder())) { String orderNumber = mockQueue.getCompleteOrder(); logger.info("返回訂單處理結果:" + orderNumber); deferredResultHolder.getMap().get(orderNumber) .setResult("place order success"); mockQueue.setCompleteOrder(null); } else { try { Thread.sleep(100); } catch (InterruptedException e) { } } } }).start(); } }
啓動應用並訪問http://localhost:8080/async/order2服務器
用攔截器攔截異步處理的請求以有線程池的配置
// 用攔截器攔截異步處理的請求,有以下兩個方法註冊攔截器,分別對應異步處理的兩種方式 // 區別是有超時時間 // configurer.registerCallableInterceptors() // configurer.registerDeferredResultInterceptors() // Runnable使用的簡單的異步線程池來處理,線程不可重用
引入相關依賴,immoc-security-demo/pom.xml
<!-- 引入swagger --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency>
加註解,DemoApplication.java
@EnableSwagger2 // 啓用Swagger2
重啓應用,訪問連接http://localhost:8080/swagger-ui.html
方法的描述
@ApiOperation(value = "用戶查詢服務")
參數的描述
// 參數被封裝到對象裏 @ApiModelProperty("用戶名") // 參數直接寫在方法裏 @ApiParam("用戶ID")
與前端開發並行工做,開發階段,前端包括app和頁面開發時都須要測試數據,這時WireMock就派上用場了。這與你再寫個web應用提供測試數據有什麼不一樣呢。由於WireMock不用重啓,定義url和返回數據都很方便。
下載:http://wiremock.org/docs/running-standalone/
指定端口啓動:
java -jar wiremock-standalone-2.18.0.jar --port 9999 # --port 9999 指定端口,默認端口8080, --port 0 隨機端口
引入依賴
<!-- 引入WireMock--> <dependency> <groupId>com.github.tomakehurst</groupId> <artifactId>wiremock</artifactId> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency>
編寫代碼,MockServer.java
public class MockServer { public static void main(String[] args) throws IOException { configureFor("192.168.5.210", 9999); // configureFor(9999); removeAllMappings(); mock("/order/1", "01.txt"); mock("/order/2", "02.txt"); } private static void mock(String url, String fileName) throws IOException { ClassPathResource resource = new ClassPathResource("mock/response/" + fileName); String content = StringUtils.join(FileUtils.readLines(resource.getFile(), "UTF-8"), "\n"); stubFor(get(urlPathEqualTo(url)) .willReturn(aResponse().withBody(content).withStatus(200))); } }