异步处理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))); } }