使用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"), "\n");
stubFor(get(urlPathEqualTo(url))
.willReturn(aResponse().withBody(content).withStatus(200)));
}
}

使用Spring MVC开发RESTful API(续)的更多相关文章
- 使用Spring MVC开发RESTful API
第3章 使用Spring MVC开发RESTful API Restful简介 第一印象 左侧是传统写法,右侧是RESTful写法 用url描述资源,而不是行为 用http方法描述行为,使用http状 ...
- Spring Boot开发RESTful接⼝服务及单元测试
Spring Boot开发RESTful接⼝服务及单元测试 常用注解解释说明: @Controller :修饰class,⽤来创建处理http请求的对象 @RestController :Spring ...
- ASP.NET Core Web API 开发-RESTful API实现
ASP.NET Core Web API 开发-RESTful API实现 REST 介绍: 符合REST设计风格的Web API称为RESTful API. 具象状态传输(英文:Representa ...
- 应用Spring MVC发布restful服务是怎样的一种体验
摘要:“约定优于配置”这是一个相当棒的经验,SOAP服务性能差.基于配置.紧耦合,restful服务性能好.基于约定.松耦合,现在我就把使用Spring MVC发布restful服务的 ...
- 用Spring MVC开发简单的Web应用
这个例子是来自于Gary Mak等人写的Spring攻略(第二版)第八章Spring @MVC中的一个例子,在此以学习为目的进行记录. 问题:想用Spring MVC开发一个简单的Web应用, 学习这 ...
- flask开发restful api系列(8)-再谈项目结构
上一章,我们讲到,怎么用蓝图建造一个好的项目,今天我们继续深入.上一章中,我们所有的接口都写在view.py中,如果几十个,还稍微好管理一点,假如上百个,上千个,怎么找?所有接口堆在一起就显得杂乱无章 ...
- flask开发restful api
flask开发restful api 如果有几个原因可以让你爱上flask这个极其灵活的库,我想蓝图绝对应该算上一个,部署蓝图以后,你会发现整个程序结构非常清晰,模块之间相互不影响.蓝图对restfu ...
- 使用Spring boot开发RestFul 风格项目PUT/DELETE方法不起作用
在使用Spring boot 开发restful 风格的项目,put.delete方法不起作用,解决办法. 实体类Student @Data public class Student { privat ...
- 描述怎样通过flask+redis+sqlalchemy等工具,开发restful api
flask开发restful api系列(8)-再谈项目结构 摘要: 进一步介绍flask的项目结构,使整个项目结构一目了然.阅读全文 posted @ 2016-06-06 13:54 月儿弯弯02 ...
随机推荐
- cpu指令如何读写硬盘
我们提到cpu的主要作用之一就是控制设备之间的数据交互.这其中自然也包括了硬盘.系统的所有数据基本都在硬盘中,所以知道怎么读写硬盘,对程序来说非常重要,所以我们先来探索下传说中的pio模式. cpu要 ...
- APICloud重磅支持Atom编辑器,并建立开发工具核心库
APICloud技术再次升级,不仅支持Atom编辑器开发工具,并推出核心开发工具库,使开发者进行App开发更便捷高效. APICloud支持Atom编辑器开发工具 APICloud始终坚持多开发工具支 ...
- react开发教程(三)组件的构建
什么是组件 组件化就好像我们的电脑装机一样,一个电脑由显示器.主板.内存.显卡.硬盘,键盘,鼠标.... 组件化开发有如下的好处:降低整个系统的耦合度,在保持接口不变的情况下,我们可以替换不同的组件快 ...
- PAT B1076 Wifi密码
题目描述: 下面是微博上流传的一张照片:"各位亲爱的同学们,鉴于大家有时需要使用 wifi,又怕耽误亲们的学习,现将 wifi 密码设置为下列数学题答案:A-1:B-2:C-3:D-4:请同 ...
- CSS简单样式练习(七)
运行效果: 源代码: 1 <!DOCTYPE html> 2 <html lang="zh"> 3 <head> 4 <meta char ...
- ThinkCMF[仿骑呗共享单车官网]
学习Thinkcmf内容管理系统(Thinkphp3.2.3框架)时候,用来练手的,简单的模仿骑呗官网首页,并对后台管理做了点小修改. 安装: 下载地址:https://pan.baidu.com/s ...
- Blazor组件自做六 : 使用JS隔离封装Baidu地图
1. 运行截图 演示地址 2. 在文件夹wwwroot/lib,添加baidu子文件夹,添加baidumap.js文件 2.1 跟上一篇类似,用代码方式异步加载API,脚本生成新的 body > ...
- JS正则表达式学习记录
JS:正则表达式学习记录 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&q ...
- Java实现单链表的反转
思路1:初始化一个新的头节点reverseHead,然后遍历旧链表,利用头插法向reverseHead进行插入 思路2: 1.反转相当于数据的更换(1和n,2和n-1,3和n-2)n为链表的长度 2. ...
- spring配置数据源(加载properties文件)
1.在spring中引入properties配置文件需要引入context的命名空间和真实地址 2.然后加载文件 需要注意的是这是采用的是set注入方式,所以name属性值必须是连接池set方法名去掉 ...