使用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 ...
随机推荐
- SVN在idea中操作解析图
进入的位置
- 【AD】Altium Designer 原理图的绘制
原理图设置基础 原理图的设置 设置原理图图纸大小 在原理图的绘制过程中,各个元件的大小是不能调整的. 如果原理图纸张放不下,需要对图纸进行设置:设计->文档选项,右键->选项-> ...
- html5不熟悉的标签全称
<dl></dl> 定义列表(英文全称:DefinitionList) <dt> 放在每个定义术语词前(定义术语.英文全称:DefinitionTerm) 名称 & ...
- 体验javascript之美6:如果你觉得什么都会了或者不知道js学什么了看这里-面向对象编程
概述 当大家已经把js的语言基础理解了,然后能够写出一些简单的例子了,这个时候基本上达到了一年工作经验的水平,而自己能够独立的写一些小功能,完成一些小效果,或者临摹修改一些比较复杂的插件的时候差不多就 ...
- 用css动态实现圆环百分比分配——初探css3动画
最近的小程序项目有个设计图要求做一个圆环,两种颜色分配,分别代表可用金额和冻结金额.要是就直接这么显示,感觉好像挺没水平??于是我决定做个动态! 在mdn把新特性gradients(渐变).trans ...
- canvas 1px 出现模糊解决方法及原理
关于canvas绘制1像素出现模糊的原因及解决方法 canvas是html5中非常强大的功能,但是在绘制的时候如果出现1像素,例如画一条1像素的线可能出现模糊情况. 一.解决方法 网上比较常见的解决方 ...
- 前端react+redux+koa写的博客推荐
React-Node搭建的博客 曾经用的php+mysql+js写的博客,现在看来已经很low了,所以用目前最火的react+koa框架重构一下.先上地址吧:目前线上版本http://www.liuw ...
- Leetcode1/242/383-HashMap常用方法以及遍历排序方式
HashMap常用方法以及遍历排序方式 常用方法 map.containsKey() map.put() map1.equals(map2) 遍历方式 Iterator<Map.Entry< ...
- Python 图_系列之基于邻接炬阵实现广度、深度优先路径搜索算法
图是一种抽象数据结构,本质和树结构是一样的. 图与树相比较,图具有封闭性,可以把树结构看成是图结构的前生.在树结构中,如果把兄弟节点之间或子节点之间横向连接,便构建成一个图. 树适合描述从上向下的一对 ...
- 将word文件转为excel文件
有些word文件里的数据是有顺序或者规律,想转成表格的形式,下面就以我要转的word为例. 我的word文件是这样的 1.word转txt(文本文件) 文件--->另存为--->路径--- ...