zoukankan      html  css  js  c++  java
  • 使用Spring MVC开发RESTful API(续)

    使用多线程提高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"), "
    ");
            stubFor(get(urlPathEqualTo(url))
                    .willReturn(aResponse().withBody(content).withStatus(200)));
        }
    }
    

  • 相关阅读:
    【BZOJ1093】【ZJOI2007】最大半联通子图 [DP][Tarjan]
    【BZOJ1926】【SDOI2010】粟粟的书架 [主席树]
    【BZOJ4069】【APIO2015】巴厘岛的雕塑 [贪心][DP]
    【BZOJ2815】【ZJOI2012】灾难 [LCA]
    【BZOJ3453】XLkxc [拉格朗日插值法]
    【BZOJ2850】巧克力王国 [KD-tree]
    《类和对象》
    @RequestMapping映射请求
    010-Python-socket编程
    SSH-Struts第二弹:一个Form提交两个Action
  • 原文地址:https://www.cnblogs.com/okokabcd/p/9589025.html
Copyright © 2011-2022 走看看