使用Spring MVC開發RESTful API(續)
阿新 • • 發佈:2018-09-05
描述 多線程 用戶 接收http esp 訂單號 開始 邏輯 配置
使用多線程提高REST服務性能
異步處理REST服務,提高服務器吞吐量
使用Runnable異步處理Rest服務
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; } }
使用DeferredResult異步處理Rest服務
應用1/線程1:接收下單請求,放到消息隊列
應用1/線程2:監聽器,監聽消息隊列是否有下單處理結果,返回HTTP響應
應用2:處理下單邏輯
AsyncController.java
@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,模擬隊列
@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對象
@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,監聽器
@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使用的簡單的異步線程池來處理,線程不可重用
使用Swagger自動生成文檔
引入Swagger
引入相關依賴,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")
使用WireMock偽造REST服務
與前端開發並行工作,開發階段,前端包括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)));
}
}
使用Spring MVC開發RESTful API(續)