使用多线程提高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(续)的更多相关文章

  1. 使用Spring MVC开发RESTful API

    第3章 使用Spring MVC开发RESTful API Restful简介 第一印象 左侧是传统写法,右侧是RESTful写法 用url描述资源,而不是行为 用http方法描述行为,使用http状 ...

  2. Spring Boot开发RESTful接⼝服务及单元测试

    Spring Boot开发RESTful接⼝服务及单元测试 常用注解解释说明: @Controller :修饰class,⽤来创建处理http请求的对象 @RestController :Spring ...

  3. ASP.NET Core Web API 开发-RESTful API实现

    ASP.NET Core Web API 开发-RESTful API实现 REST 介绍: 符合REST设计风格的Web API称为RESTful API. 具象状态传输(英文:Representa ...

  4. 应用Spring MVC发布restful服务是怎样的一种体验

            摘要:“约定优于配置”这是一个相当棒的经验,SOAP服务性能差.基于配置.紧耦合,restful服务性能好.基于约定.松耦合,现在我就把使用Spring MVC发布restful服务的 ...

  5. 用Spring MVC开发简单的Web应用

    这个例子是来自于Gary Mak等人写的Spring攻略(第二版)第八章Spring @MVC中的一个例子,在此以学习为目的进行记录. 问题:想用Spring MVC开发一个简单的Web应用, 学习这 ...

  6. flask开发restful api系列(8)-再谈项目结构

    上一章,我们讲到,怎么用蓝图建造一个好的项目,今天我们继续深入.上一章中,我们所有的接口都写在view.py中,如果几十个,还稍微好管理一点,假如上百个,上千个,怎么找?所有接口堆在一起就显得杂乱无章 ...

  7. flask开发restful api

    flask开发restful api 如果有几个原因可以让你爱上flask这个极其灵活的库,我想蓝图绝对应该算上一个,部署蓝图以后,你会发现整个程序结构非常清晰,模块之间相互不影响.蓝图对restfu ...

  8. 使用Spring boot开发RestFul 风格项目PUT/DELETE方法不起作用

    在使用Spring boot 开发restful 风格的项目,put.delete方法不起作用,解决办法. 实体类Student @Data public class Student { privat ...

  9. 描述怎样通过flask+redis+sqlalchemy等工具,开发restful api

    flask开发restful api系列(8)-再谈项目结构 摘要: 进一步介绍flask的项目结构,使整个项目结构一目了然.阅读全文 posted @ 2016-06-06 13:54 月儿弯弯02 ...

随机推荐

  1. 【Linux-vim】vim文件:查看某几行,把某几行复制到另一个文件中

    一.查看文件的某几行1.使用cat命令(1)查看文件的前10行: cat filename |head -n 10(2)查看文件后10行: cat filename |tail -n 10(3)查看文 ...

  2. 在网页中预览excel表格文件

    项目需求在前端页面中实现预览excel表格的功能,上网了解之后大致总结为一下几种方法. 1.office文档转换为pdf,再转swf,然后通过网页加载flash进行预览 2.通过 xlsx.js,js ...

  3. python-杨辉三角形

    [题目描述]输出n(0<n)行杨辉三角形,n由用户输入. [练习要求]请给出源代码程序和运行测试结果,源代码程序要求添加必要的注释. [输入格式]一行中输入1个整数n. [输出格式]输出n行杨辉 ...

  4. 大数据学习之路之ambari配置(三)

    添加了虚拟机内存空间 重装ambari

  5. js 生成 pdf 文件

    话不多说好吧, 直接上demo图: 直接上代码好吧:(要引入的两个js  链接我放最后) <!DOCTYPE html> <html> <head> <met ...

  6. 面试问题整理之flex 布局

  7. Java习题

    public class ClassTest{ String str = new String("hello"); char[] ch = {'a','b','c'}; publi ...

  8. MongoDB 数据库开发规范

    MongoDB 数据库开发规范 转载自-落雨_ https://developer.aliyun.com/article/255536 简介: mongoDB库的设计 mongodb数据库命名规范:d ...

  9. mybatis plus @TableId注解 type属性的含义

    首先该注解用在主键id上,它的type属性有8种类型 AUTO(0),NONE(1),INPUT(2),ASSIGN_ID(3),ASSIGN_UUID(4),ID_WORKER(3),ID_WORK ...

  10. Protoc安装

    系统:linux 记住,千万别混乱版本,一般protoc可执行文件在/usr/local/bin/或/usr/bin/下 个人这里使用3.13版本示例,下面两部分命令可以写入shell脚本,记住执行要 ...