springboot2学习-webflux整合redis

springboot2学习-webflux整合redis

前面学习整合了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 outspring.redis.timeout=5000
#mongodb的配置spring.data.mongodb.host=192.168.0.104spring.data.mongodb.database=adminspring.data.mongodb.port=27017spring.data.mongodb.username=adminspring.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 等。 */@Datapublic 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 */@Repositorypublic 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 */@Componentpublic 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。

免责声明:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕。
相关文章
返回顶部