赞
踩
前面学习整合了mongodb和 Thymeleaf,这里来整合redis和mongodb。学习参考:http://gitbook.cn/gitchat/column/5acda6f6d7966c5ae1086f2b/topic/5acdaa62d7966c5ae108708a
这里需要用到mongodb,和redis,这两个都可以使用docker来启动redis和mongodb的容器,设置好用户名和密码启动容器就可以建立连接了,具体的操作略掉了,网上资料很多。mongodb容器可以采用之前博客建立的mongodb容器
1,创建一个springboot2的项目
pom.xml代码如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <groupId>com.jack</groupId>
- <artifactId>webflux_redis</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <packaging>jar</packaging>
-
- <name>webflux_redis</name>
- <description>Demo project for Spring Boot</description>
-
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.0.2.RELEASE</version>
- <relativePath/> <!-- lookup parent from repository -->
- </parent>
-
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
- <java.version>1.8</java.version>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-webflux</artifactId>
- </dependency>
-
- <!-- Spring Boot 响应式 MongoDB 依赖 -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>io.projectreactor</groupId>
- <artifactId>reactor-test</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
-
-
- </project>
application.properties的配置如下:
- ## Redis 配置
- ## Redis服务器地址
- spring.redis.host=xx.xx.xx.xx
- ## Redis服务器连接端口
- spring.redis.port=6379
- ## Redis服务器连接密码(默认为空)
- spring.redis.password=xxxxxxxx
- # 连接超时时间(毫秒),这里注意的是连接超时时间不能太少或者为 0,不然会引起异常 RedisCommandTimeoutException: Command timed out
- spring.redis.timeout=5000
-
- #mongodb的配置
- spring.data.mongodb.host=192.168.0.104
- spring.data.mongodb.database=admin
- spring.data.mongodb.port=27017
- spring.data.mongodb.username=admin
- spring.data.mongodb.password=admin
2,实体类代码如下:
- package com.jack.webflux_redis.domain;
-
- import lombok.Data;
- import org.springframework.data.annotation.Id;
-
- import java.io.Serializable;
-
- /**
- * create by jack 2018/5/13
- * City 必须实现序列化,因为需要将对象序列化后存储到 Redis。如果没实现 Serializable,
- * 会引出异常:java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable
- * payload but received an object of type。
- 如果不是用默认的序列化,需要自定义序列化实现,只要实现 RedisSerializer 接口去实现即可,
- 然后在使用 RedisTemplate.setValueSerializer 方法去设置你实现的序列化实现,支持 JSON、XML 等。
- */
- @Data
- public class City implements Serializable {
-
- private static final long serialVersionUID = -1L;
-
- /**
- * 城市编号
- * @Id 注解标记对应库表的主键或者唯一标识符。因为这个是我们的 DO,数据访问对象一一映射到数据存储。
- */
- @Id
- private Long id;
-
- /**
- * 省份编号
- */
- private Long provinceId;
-
- /**
- * 城市名称
- */
- private String cityName;
-
- /**
- * 描述
- */
- private String description;
- }
3,redis的测试代码如下:
redis的测试controller如下,此代码不具有reactive特性:
- package com.jack.webflux_redis.controller;
-
- import com.jack.webflux_redis.domain.City;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
- import org.springframework.web.bind.annotation.*;
- import reactor.core.publisher.Mono;
-
- import java.util.concurrent.TimeUnit;
-
- /**
- * create by jack 2018/5/13
- *
- * 使用 @Autowired 注入 RedisTemplate 对象,这个对象和 Spring 的 JdbcTemplate 功能十分相似,RedisTemplate 封装了 RedisConnection,具有连接管理、序列化和各个操作等,还有针对 String 的支持对象 StringRedisTemplate。
- 删除 Redis 某对象,直接通过 key 值调用 delete(key)。
- Redis 操作视图接口类用的是 ValueOperations,对应的是 Redis String/Value 操作,get 是获取数据;set 是插入数据,可以设置失效时间,这里设置的失效时间是 60 s。
- 还有其他的操作视图,ListOperations、SetOperations、ZSetOperations 和 HashOperations。
- */
- @RestController
- @RequestMapping(value = "/city")
- public class CityWebFluxController {
- /**
- * RedisTemplate 实现操作 Redis,但操作是同步的,不是 Reactive 的
- */
- @Autowired
- private RedisTemplate redisTemplate;
-
- @GetMapping(value = "/{id}")
- public Mono<City> findCityById(@PathVariable("id") Long id) {
- String key = "city_" + id;
- ValueOperations<String, City> operations = redisTemplate.opsForValue();
- boolean hasKey = redisTemplate.hasKey(key);
- City city = operations.get(key);
-
- if (!hasKey) {
- return Mono.create(monoSink -> monoSink.success(null));
- }
- return Mono.create(monoSink -> monoSink.success(city));
- }
-
- @PostMapping()
- public Mono<City> saveCity(@RequestBody City city) {
- String key = "city_" + city.getId();
- ValueOperations<String, City> operations = redisTemplate.opsForValue();
- operations.set(key, city, 60, TimeUnit.SECONDS);
-
- return Mono.create(monoSink -> monoSink.success(city));
- }
-
- @DeleteMapping(value = "/{id}")
- public Mono<Long> deleteCity(@PathVariable("id") Long id) {
- String key = "city_" + id;
- boolean hasKey = redisTemplate.hasKey(key);
- if (hasKey) {
- redisTemplate.delete(key);
- }
- return Mono.create(monoSink -> monoSink.success(id));
- }
- }
运行程序打开postman,添加一个城市信息,
通过redis获取一个城市代码如下:
4,添加一个reactive的redis操作
上面的redis操作不具有reactive的特性,下面是具有reactive特性的redis操作,代码如下:
- package com.jack.webflux_redis.controller;
-
- import com.jack.webflux_redis.domain.City;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.ReactiveRedisTemplate;
- import org.springframework.data.redis.core.ReactiveValueOperations;
- import org.springframework.web.bind.annotation.*;
- import reactor.core.publisher.Mono;
-
- /**
- * create by jack 2018/5/13
- */
- @RestController
- @RequestMapping(value = "/city2")
- public class CityWebFluxReactiveController {
-
- /**
- *
- * 持 Reactive 的操作类为 ReactiveRedisTemplate
- * @Autowired 注入 ReactiveRedisTemplate 对象。
- ReactiveValueOperations 是 String(或 value)的操作视图,
- 操作视图还有 ReactiveHashOperations、ReactiveListOperations、ReactiveSetOperations 和 ReactiveZSetOperations 等。
- 不一样的是,操作视图 set 方法是操作 City 对象,但可以 get 回 Mono 或者 Flux 对象
- */
- @Autowired
- private ReactiveRedisTemplate reactiveRedisTemplate;
-
- @GetMapping(value = "/{id}")
- public Mono<City> findCityById(@PathVariable("id") Long id) {
- String key = "city_" + id;
- ReactiveValueOperations<String, City> operations = reactiveRedisTemplate.opsForValue();
- Mono<City> city = operations.get(key);
- return city;
- }
-
- @PostMapping
- public Mono<City> saveCity(@RequestBody City city) {
- String key = "city_" + city.getId();
- ReactiveValueOperations<String, City> operations = reactiveRedisTemplate.opsForValue();
- return operations.getAndSet(key, city);
- }
-
- @DeleteMapping(value = "/{id}")
- public Mono<Long> deleteCity(@PathVariable("id") Long id) {
- String key = "city_" + id;
- return reactiveRedisTemplate.delete(key);
- }
- }
测试结果和上面的redis测试结果一样,只是具有reactive的特性,是异步非阻塞的。
5,整合redis和mongodb
dao层的代码如下:
- package com.jack.webflux_redis.dao;
-
- import com.jack.webflux_redis.domain.City;
- import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
- import org.springframework.stereotype.Repository;
-
- /**
- * create by jack 2018/5/12
- */
- @Repository
- public interface CityRepository extends ReactiveMongoRepository<City, Long> {
- }
新增一个handler,代码如下:
- package com.jack.webflux_redis.handler;
-
- import com.jack.webflux_redis.dao.CityRepository;
- import com.jack.webflux_redis.domain.City;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
- import org.springframework.stereotype.Component;
- import reactor.core.publisher.Flux;
- import reactor.core.publisher.Mono;
-
- /**
- * create by jack 2018/5/13
- */
- @Component
- public class CityHandler {
- private static final Logger LOGGER = LoggerFactory.getLogger(CityHandler.class);
-
- @Autowired
- private RedisTemplate redisTemplate;
-
- private final CityRepository cityRepository;
-
- @Autowired
- public CityHandler(CityRepository cityRepository) {
- this.cityRepository = cityRepository;
- }
-
- public Mono<City> save(City city) {
- return cityRepository.save(city);
- }
-
-
- /**
- * 如果缓存存在,从缓存中获取城市信息;
- 如果缓存不存在,从 DB 中获取城市信息,然后插入缓存
- * @param id
- * @return
- */
- public Mono<City> findCityById(Long id) {
-
- // 从缓存中获取城市信息
- String key = "city_" + id;
- ValueOperations<String, City> operations = redisTemplate.opsForValue();
-
- // 缓存存在
- boolean hasKey = redisTemplate.hasKey(key);
- if (hasKey) {
- City city = operations.get(key);
-
- LOGGER.info("CityHandler.findCityById() : 从缓存中获取了城市 >> " + city.toString());
- return Mono.create(cityMonoSink -> cityMonoSink.success(city));
- }
-
- // 从 MongoDB 中获取城市信息
- Mono<City> cityMono = cityRepository.findById(id);
-
- if (cityMono == null)
- return cityMono;
-
- // 插入缓存
- cityMono.subscribe(cityObj -> {
- operations.set(key, cityObj);
- LOGGER.info("CityHandler.findCityById() : 城市插入缓存 >> " + cityObj.toString());
- });
-
- return cityMono;
- }
-
- public Flux<City> findAllCity() {
- return cityRepository.findAll().cache();
- }
-
- public Mono<City> modifyCity(City city) {
-
- Mono<City> cityMono = cityRepository.save(city);
-
- // 缓存存在,删除缓存
- String key = "city_" + city.getId();
- boolean hasKey = redisTemplate.hasKey(key);
- if (hasKey) {
- redisTemplate.delete(key);
-
- LOGGER.info("CityHandler.modifyCity() : 从缓存中删除城市 ID >> " + city.getId());
- }
-
- return cityMono;
- }
-
-
- /**
- * 如果缓存存在,删除;
- 如果缓存不存在,不操作
- * @param id
- * @return
- */
- public Mono<Long> deleteCity(Long id) {
-
- cityRepository.deleteById(id);
-
- // 缓存存在,删除缓存
- String key = "city_" + id;
- boolean hasKey = redisTemplate.hasKey(key);
- if (hasKey) {
- redisTemplate.delete(key);
-
- LOGGER.info("CityHandler.deleteCity() : 从缓存中删除城市 ID >> " + id);
- }
-
- return Mono.create(cityMonoSink -> cityMonoSink.success(id));
- }
- }
-
新增一个controller,代码如下:
- package com.jack.webflux_redis.controller;
-
- import com.jack.webflux_redis.domain.City;
- import com.jack.webflux_redis.handler.CityHandler;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.*;
- import reactor.core.publisher.Flux;
- import reactor.core.publisher.Mono;
-
- /**
- * create by jack 2018/5/13
- */
- @RestController
- @RequestMapping(value = "/city3")
- public class CityWebFluxMongodbController {
- @Autowired
- private CityHandler cityHandler;
-
- @GetMapping(value = "/{id}")
- public Mono<City> findCityById(@PathVariable("id") Long id) {
- return cityHandler.findCityById(id);
- }
-
- @GetMapping()
- public Flux<City> findAllCity() {
- return cityHandler.findAllCity();
- }
-
- @PostMapping()
- public Mono<City> saveCity(@RequestBody City city) {
- return cityHandler.save(city);
- }
-
- @PutMapping()
- public Mono<City> modifyCity(@RequestBody City city) {
- return cityHandler.modifyCity(city);
- }
-
- @DeleteMapping(value = "/{id}")
- public Mono<Long> deleteCity(@PathVariable("id") Long id) {
- return cityHandler.deleteCity(id);
- }
- }
运行测试,http://127.0.0.1:8080/city3
新增一个城市
查询一个城市:http://127.0.0.1:8080/city3/3
后台输出
2018-05-13 10:52:40.283 INFO 8444 --- [ntLoopGroup-4-3] c.j.webflux_redis.handler.CityHandler : CityHandler.findCityById() : 城市插入缓存 >> City(id=3, provinceId=6410, cityName=海南, description=海南岛是一个历史不长、但风景优美的沿海岛屿,为中国第二大岛)
进行第二次查询,直接从缓存获取数据:
2018-05-13 10:54:30.253 INFO 8444 --- [ctor-http-nio-2] c.j.webflux_redis.handler.CityHandler : CityHandler.findCityById() : 从缓存中获取了城市 >> City(id=3, provinceId=6410, cityName=海南, description=海南岛是一个历史不长、但风景优美的沿海岛屿,为中国第二大岛)
源码地址:
https://github.com/wj903829182/springcloud5/tree/master/webflux_redis
总结:上面演示了webflux整合redis和mongodb的案例,代码都十分的简单和之前的webflux的CRUD差不多,只是采用了数据存储的方式,引入了nosql数据库mongodb和内存数据库redis。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。