本文为实战 SpringCloud 响应式微服务系列教程第十章, 本章给出响应式 RESTful 服务完整代码示例. 建议没有之前基础的童鞋, 先看之前的章节, 章节目录放在文末.
1. 搭建响应式 RESTful 服务.
在前面章节中我们讲了如何使用 Spring Initializer 初始化响应式 web 应用, 本节中就不再做过多介绍 (请回顾第九章内容).
在学习本章内容之前需要了解 MongoDB 以及 Redis,MongoDB 以及 Redis 可查阅相关资料进行全面了解, 并在本地环境搭建 MongoDB 和 Redis.
2.application.YAML 文件配置
- server:
- port: 9801
- spring:
- application:
- name: advert
- data:
- MongoDB:
- uri: MongoDB://localhost:27017/db_advert
- http:
- encoding:
- force: true
- charset: UTF-8
- enabled: true
- Redis:
- host: 127.0.0.1
- password: 123456
- logback:
- level: info
以上配置代码是我们目前学习的响应式 RESTful 服务的全部配置, 配置比较简单, spring.data.MongoDB.uri: MongoDB://localhost:27017/db_advert 和 spring.data.Redis 是我们服务的核心配置, 我们知道传统的数据库是不支持响应式数据读取的, 所以这里使用 MongoDB 和 Redis 代替.
3. 集成响应式的 MongoDB
springboot 本身提供了 cassandra/couchbase/MongoDB/Redis 这几个 NoSQL 数据库的响应式驱动:
阻塞式的 spring-boot-starter-data-MongoDB 改为响应式的 MongoDB 依赖 spring-boot-starter-data-MongoDB-reactive:
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-MongoDB-reactive</artifactId>
- </dependency>
(1) 编写实体类:
- /**
- * 广告投放
- */
- @Document(collection="advert")// 集合名
- @Data
- @Builder
- @NoArgsConstructor
- @AllArgsConstructor
- public class Advert implements Serializable {
- private static final long serialVersionUID = -8985545025018238754L;
- /**
- * 主键
- */
- @Id
- private String id;
- /**
- * 内容
- */
- private String content;
- /**
- * 发布人
- */
- private Long userId;
- /**
- * 创建时间
- */
- private Date creatData;
- /**
- * 图片地址
- */
- private String imgUrl;
- /**
- * 视频地址
- */
- private String videoUrl;
- /**
- * 广告类型 (视频图片)
- */
- private String advertType;
- /**
- * 今日投放地区
- */
- private String launchArea;
- /**
- * 投放时长 (小时为单位)
- */
- private int durationTime;
- /**
- * 广告类型
- */
- private int classify;
- /**
- * 计费方式
- */
- private int billingMode;
- /**
- * 展示位置 (首页轮播, 其他轮播, 首页其他位置, 其他)
- */
- private int displayPosition;
- /**
- * 广告主题
- */
- private String advertTitle;
- /**
- * 是否需要自定义展示页面
- */
- private int isCustom;
- /**
- * 索引关键词
- */
- private String keyWords;
- }
其中 @document 把一个 java 类声明为 MongoDB 的文档, 可以通过 collection 参数指定这个类对应的文档, 标注在实体类上, 类似于 hibernate 的 entity 注解. 其他注解均为 lombok 的注解.
(2) 编写 repository 接口
- import com.shmc.advert.model.po.Advert;
- import org.springframework.data.MongoDB.repository.ReactiveMongoRepository;
- import org.springframework.data.MongoDB.repository.Tailable;
- import org.springframework.stereotype.Repository;
- import reactor.core.publisher.Flux;
- @Repository
- public interface AdvertRepository extends ReactiveMongoRepository<Advert,Long> {
- @Tailable
- Flux<Advert> findBy();
- }
其中 @Repository 是 org.springframework.stereotype.Repository 的注解, 这个大家应该都很熟悉了不做解释.
从以上代码中我们可以清楚看到 AdvertRepository 继承了 ReactiveMongoRepository,ReactiveMongoRepository 正是我们依赖的 maven 响应式 MongoDB-reactive 中的类, 其中 @Tailable 注解, 该注解类似于 Linux 中的 tail , 可以将 DB 的变化以响应式流的方式获取到并推送给前端.
除了可以继承 ReactiveMongoRepository 之外我们还可以通过注入 MongoTemplate 来操作 MongoDB, 但是 MongoTemplate 做不到实时监控和主动推送. 如:
- @Autowired
- MongoTemplate mongoTemplate;
(3) 编写 Service 接口以及实现类
Service 接口:
- import com.shmc.advert.model.po.Advert;
- import reactor.core.publisher.Flux;
- import reactor.core.publisher.Mono;
- public interface AdvertService {
- Mono<Advert> saveAdvert(Advert advert);
- Mono<Advert> findById(String id);
- Flux<Advert> findAll();
- Flux<Advert> findByAll();
- }
Service 实现类:
- @Service
- public class AdvertServiceImpl implements AdvertService {
- @Autowired
- MongoTemplate mongoTemplate;
- @Autowired
- private AdvertRepository advertRepository;
- @Override
- public Mono<Advert> saveAdvert(Advert advert){
- advert.setId(new IdWorker().nextId());
- advert.setCreatData(new Date());
- mongoTemplate.insert(advert);
- return Mono.just(advert);
- }
- @Override
- public Mono<Advert> findById(String id){
- Query query = new Query(Criteria.where("id").is(id));
- return Mono.just(mongoTemplate.findOne(query,Advert.class));
- }
- @Override
- public Flux<Advert> findAll(){
- return advertRepository.findAll();
- }
- @Override
- public Flux<Advert> findByAll(){
- return advertRepository.findBy();
- }
- }
(4) 编写 Controller
- import com.shmc.advert.model.po.Advert;
- import com.shmc.advert.service.AdvertService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.http.MediaType;
- import org.springframework.Web.bind.annotation.*;
- import reactor.core.publisher.Flux;
- import reactor.core.publisher.Mono;
- import java.time.Duration;
- @RestController
- @RequestMapping("/advert")
- public class AdvertController {
- @Autowired
- private AdvertService advertService;
- @PostMapping("/saveAdvert")
- public Mono<Advert> saveAdvert(@RequestBody Advert advert){
- return advertService.saveAdvert(advert);
- }
- @GetMapping("/findById/{id}")
- public Mono<Advert> findById(@PathVariable String id){
- return advertService.findById(id);
- }
- /**
- * 以 stream+JSON 流的方式推送到客户端
- * @return
- */
- @GetMapping(value = "/findAllPreSec", produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
- public Flux<Advert> findAllPreSec() {
- return advertService.findAll().delayElements(Duration.ofSeconds(1));
- }
- /**
- * 数据变更
- * @return
- */
- @GetMapping(value = "/findByAll", produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
- public Flux<Advert> findByAll(){
- return advertService.findByAll();
- }
- }
至此基于 RESTful 的响应式服务我们全部完成了, 启动程序访问 "/advert/findAllPreSec" 接口和 "/advert/findByAll" 接口就可以看到响应式的数据推送了.
findAllPreSec 这个方法. 使用了 delayElements 使得每隔一秒钟获取一条数据发送给客户端, 以 "异步响应式流" 的方式逐条推送.
这里指定了 MediaType 是 APPLICATION_STREAM_JSON, 即 application/stream+JSON 格式.
在浏览器中就可以看到每隔一秒出现一条记录. 运行程序, 访问 findByAll 接口, 然后测试调用 save 接口添加 advert 数据, 或者直接通过 MongoDB Compass 客户端添加 Stu 数据, 就会看到在页面中实时看到新添加的数据了.
下一章会吧代码上传到 gitee, 各位看官如有需要请自行下载.
系列章节目录
实战 SpringCloud 响应式微服务系列教程 (第一章)
实战 SpringCloud 响应式微服务系列教程 (第二章)
实战 SpringCloud 响应式微服务系列教程 (第三章)
实战 SpringCloud 响应式微服务系列教程 (第四章)
实战 SpringCloud 响应式微服务系列教程 (第五章)
实战 SpringCloud 响应式微服务系列教程 (第六章)
实战 SpringCloud 响应式微服务系列教程 (第七章)
实战 SpringCloud 响应式微服务系列教程 (第八章)
实战 SpringCloud 响应式微服务系列教程 (第九章)
来源: https://www.cnblogs.com/javazhiyin/p/11797349.html