当前位置:   article > 正文

springboot2学习-webflux整合redis_webflux redis

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代码如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.jack</groupId>
  6. <artifactId>webflux_redis</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. <packaging>jar</packaging>
  9. <name>webflux_redis</name>
  10. <description>Demo project for Spring Boot</description>
  11. <parent>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-starter-parent</artifactId>
  14. <version>2.0.2.RELEASE</version>
  15. <relativePath/> <!-- lookup parent from repository -->
  16. </parent>
  17. <properties>
  18. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  19. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  20. <java.version>1.8</java.version>
  21. </properties>
  22. <dependencies>
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework.boot</groupId>
  29. <artifactId>spring-boot-starter-webflux</artifactId>
  30. </dependency>
  31. <!-- Spring Boot 响应式 MongoDB 依赖 -->
  32. <dependency>
  33. <groupId>org.springframework.boot</groupId>
  34. <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
  35. </dependency>
  36. <dependency>
  37. <groupId>org.projectlombok</groupId>
  38. <artifactId>lombok</artifactId>
  39. <optional>true</optional>
  40. </dependency>
  41. <dependency>
  42. <groupId>org.springframework.boot</groupId>
  43. <artifactId>spring-boot-starter-test</artifactId>
  44. <scope>test</scope>
  45. </dependency>
  46. <dependency>
  47. <groupId>io.projectreactor</groupId>
  48. <artifactId>reactor-test</artifactId>
  49. <scope>test</scope>
  50. </dependency>
  51. </dependencies>
  52. <build>
  53. <plugins>
  54. <plugin>
  55. <groupId>org.springframework.boot</groupId>
  56. <artifactId>spring-boot-maven-plugin</artifactId>
  57. </plugin>
  58. </plugins>
  59. </build>
  60. </project>

 application.properties的配置如下:

  1. ## Redis 配置
  2. ## Redis服务器地址
  3. spring.redis.host=xx.xx.xx.xx
  4. ## Redis服务器连接端口
  5. spring.redis.port=6379
  6. ## Redis服务器连接密码(默认为空)
  7. spring.redis.password=xxxxxxxx
  8. # 连接超时时间(毫秒),这里注意的是连接超时时间不能太少或者为 0,不然会引起异常 RedisCommandTimeoutException: Command timed out
  9. spring.redis.timeout=5000
  1. #mongodb的配置
  2. spring.data.mongodb.host=192.168.0.104
  3. spring.data.mongodb.database=admin
  4. spring.data.mongodb.port=27017
  5. spring.data.mongodb.username=admin
  6. spring.data.mongodb.password=admin

2,实体类代码如下:

  1. package com.jack.webflux_redis.domain;
  2. import lombok.Data;
  3. import org.springframework.data.annotation.Id;
  4. import java.io.Serializable;
  5. /**
  6. * create by jack 2018/5/13
  7. * City 必须实现序列化,因为需要将对象序列化后存储到 Redis。如果没实现 Serializable,
  8. * 会引出异常:java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable
  9. * payload but received an object of type。
  10. 如果不是用默认的序列化,需要自定义序列化实现,只要实现 RedisSerializer 接口去实现即可,
  11. 然后在使用 RedisTemplate.setValueSerializer 方法去设置你实现的序列化实现,支持 JSON、XML 等。
  12. */
  13. @Data
  14. public class City implements Serializable {
  15. private static final long serialVersionUID = -1L;
  16. /**
  17. * 城市编号
  18. * @Id 注解标记对应库表的主键或者唯一标识符。因为这个是我们的 DO,数据访问对象一一映射到数据存储。
  19. */
  20. @Id
  21. private Long id;
  22. /**
  23. * 省份编号
  24. */
  25. private Long provinceId;
  26. /**
  27. * 城市名称
  28. */
  29. private String cityName;
  30. /**
  31. * 描述
  32. */
  33. private String description;
  34. }

3,redis的测试代码如下:

redis的测试controller如下,此代码不具有reactive特性:

  1. package com.jack.webflux_redis.controller;
  2. import com.jack.webflux_redis.domain.City;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.data.redis.core.RedisTemplate;
  5. import org.springframework.data.redis.core.ValueOperations;
  6. import org.springframework.web.bind.annotation.*;
  7. import reactor.core.publisher.Mono;
  8. import java.util.concurrent.TimeUnit;
  9. /**
  10. * create by jack 2018/5/13
  11. *
  12. * 使用 @Autowired 注入 RedisTemplate 对象,这个对象和 Spring 的 JdbcTemplate 功能十分相似,RedisTemplate 封装了 RedisConnection,具有连接管理、序列化和各个操作等,还有针对 String 的支持对象 StringRedisTemplate。
  13. 删除 Redis 某对象,直接通过 key 值调用 delete(key)。
  14. Redis 操作视图接口类用的是 ValueOperations,对应的是 Redis String/Value 操作,get 是获取数据;set 是插入数据,可以设置失效时间,这里设置的失效时间是 60 s。
  15. 还有其他的操作视图,ListOperations、SetOperations、ZSetOperations 和 HashOperations。
  16. */
  17. @RestController
  18. @RequestMapping(value = "/city")
  19. public class CityWebFluxController {
  20. /**
  21. * RedisTemplate 实现操作 Redis,但操作是同步的,不是 Reactive 的
  22. */
  23. @Autowired
  24. private RedisTemplate redisTemplate;
  25. @GetMapping(value = "/{id}")
  26. public Mono<City> findCityById(@PathVariable("id") Long id) {
  27. String key = "city_" + id;
  28. ValueOperations<String, City> operations = redisTemplate.opsForValue();
  29. boolean hasKey = redisTemplate.hasKey(key);
  30. City city = operations.get(key);
  31. if (!hasKey) {
  32. return Mono.create(monoSink -> monoSink.success(null));
  33. }
  34. return Mono.create(monoSink -> monoSink.success(city));
  35. }
  36. @PostMapping()
  37. public Mono<City> saveCity(@RequestBody City city) {
  38. String key = "city_" + city.getId();
  39. ValueOperations<String, City> operations = redisTemplate.opsForValue();
  40. operations.set(key, city, 60, TimeUnit.SECONDS);
  41. return Mono.create(monoSink -> monoSink.success(city));
  42. }
  43. @DeleteMapping(value = "/{id}")
  44. public Mono<Long> deleteCity(@PathVariable("id") Long id) {
  45. String key = "city_" + id;
  46. boolean hasKey = redisTemplate.hasKey(key);
  47. if (hasKey) {
  48. redisTemplate.delete(key);
  49. }
  50. return Mono.create(monoSink -> monoSink.success(id));
  51. }
  52. }
运行程序打开postman,添加一个城市信息,

通过redis获取一个城市代码如下:



4,添加一个reactive的redis操作

上面的redis操作不具有reactive的特性,下面是具有reactive特性的redis操作,代码如下:

  1. package com.jack.webflux_redis.controller;
  2. import com.jack.webflux_redis.domain.City;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.data.redis.core.ReactiveRedisTemplate;
  5. import org.springframework.data.redis.core.ReactiveValueOperations;
  6. import org.springframework.web.bind.annotation.*;
  7. import reactor.core.publisher.Mono;
  8. /**
  9. * create by jack 2018/5/13
  10. */
  11. @RestController
  12. @RequestMapping(value = "/city2")
  13. public class CityWebFluxReactiveController {
  14. /**
  15. *
  16. * 持 Reactive 的操作类为 ReactiveRedisTemplate
  17. * @Autowired 注入 ReactiveRedisTemplate 对象。
  18. ReactiveValueOperations 是 String(或 value)的操作视图,
  19. 操作视图还有 ReactiveHashOperations、ReactiveListOperations、ReactiveSetOperations 和 ReactiveZSetOperations 等。
  20. 不一样的是,操作视图 set 方法是操作 City 对象,但可以 get 回 Mono 或者 Flux 对象
  21. */
  22. @Autowired
  23. private ReactiveRedisTemplate reactiveRedisTemplate;
  24. @GetMapping(value = "/{id}")
  25. public Mono<City> findCityById(@PathVariable("id") Long id) {
  26. String key = "city_" + id;
  27. ReactiveValueOperations<String, City> operations = reactiveRedisTemplate.opsForValue();
  28. Mono<City> city = operations.get(key);
  29. return city;
  30. }
  31. @PostMapping
  32. public Mono<City> saveCity(@RequestBody City city) {
  33. String key = "city_" + city.getId();
  34. ReactiveValueOperations<String, City> operations = reactiveRedisTemplate.opsForValue();
  35. return operations.getAndSet(key, city);
  36. }
  37. @DeleteMapping(value = "/{id}")
  38. public Mono<Long> deleteCity(@PathVariable("id") Long id) {
  39. String key = "city_" + id;
  40. return reactiveRedisTemplate.delete(key);
  41. }
  42. }

测试结果和上面的redis测试结果一样,只是具有reactive的特性,是异步非阻塞的。


5,整合redis和mongodb

dao层的代码如下:

  1. package com.jack.webflux_redis.dao;
  2. import com.jack.webflux_redis.domain.City;
  3. import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
  4. import org.springframework.stereotype.Repository;
  5. /**
  6. * create by jack 2018/5/12
  7. */
  8. @Repository
  9. public interface CityRepository extends ReactiveMongoRepository<City, Long> {
  10. }

新增一个handler,代码如下:

  1. package com.jack.webflux_redis.handler;
  2. import com.jack.webflux_redis.dao.CityRepository;
  3. import com.jack.webflux_redis.domain.City;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.data.redis.core.RedisTemplate;
  8. import org.springframework.data.redis.core.ValueOperations;
  9. import org.springframework.stereotype.Component;
  10. import reactor.core.publisher.Flux;
  11. import reactor.core.publisher.Mono;
  12. /**
  13. * create by jack 2018/5/13
  14. */
  15. @Component
  16. public class CityHandler {
  17. private static final Logger LOGGER = LoggerFactory.getLogger(CityHandler.class);
  18. @Autowired
  19. private RedisTemplate redisTemplate;
  20. private final CityRepository cityRepository;
  21. @Autowired
  22. public CityHandler(CityRepository cityRepository) {
  23. this.cityRepository = cityRepository;
  24. }
  25. public Mono<City> save(City city) {
  26. return cityRepository.save(city);
  27. }
  28. /**
  29. * 如果缓存存在,从缓存中获取城市信息;
  30. 如果缓存不存在,从 DB 中获取城市信息,然后插入缓存
  31. * @param id
  32. * @return
  33. */
  34. public Mono<City> findCityById(Long id) {
  35. // 从缓存中获取城市信息
  36. String key = "city_" + id;
  37. ValueOperations<String, City> operations = redisTemplate.opsForValue();
  38. // 缓存存在
  39. boolean hasKey = redisTemplate.hasKey(key);
  40. if (hasKey) {
  41. City city = operations.get(key);
  42. LOGGER.info("CityHandler.findCityById() : 从缓存中获取了城市 >> " + city.toString());
  43. return Mono.create(cityMonoSink -> cityMonoSink.success(city));
  44. }
  45. // 从 MongoDB 中获取城市信息
  46. Mono<City> cityMono = cityRepository.findById(id);
  47. if (cityMono == null)
  48. return cityMono;
  49. // 插入缓存
  50. cityMono.subscribe(cityObj -> {
  51. operations.set(key, cityObj);
  52. LOGGER.info("CityHandler.findCityById() : 城市插入缓存 >> " + cityObj.toString());
  53. });
  54. return cityMono;
  55. }
  56. public Flux<City> findAllCity() {
  57. return cityRepository.findAll().cache();
  58. }
  59. public Mono<City> modifyCity(City city) {
  60. Mono<City> cityMono = cityRepository.save(city);
  61. // 缓存存在,删除缓存
  62. String key = "city_" + city.getId();
  63. boolean hasKey = redisTemplate.hasKey(key);
  64. if (hasKey) {
  65. redisTemplate.delete(key);
  66. LOGGER.info("CityHandler.modifyCity() : 从缓存中删除城市 ID >> " + city.getId());
  67. }
  68. return cityMono;
  69. }
  70. /**
  71. * 如果缓存存在,删除;
  72. 如果缓存不存在,不操作
  73. * @param id
  74. * @return
  75. */
  76. public Mono<Long> deleteCity(Long id) {
  77. cityRepository.deleteById(id);
  78. // 缓存存在,删除缓存
  79. String key = "city_" + id;
  80. boolean hasKey = redisTemplate.hasKey(key);
  81. if (hasKey) {
  82. redisTemplate.delete(key);
  83. LOGGER.info("CityHandler.deleteCity() : 从缓存中删除城市 ID >> " + id);
  84. }
  85. return Mono.create(cityMonoSink -> cityMonoSink.success(id));
  86. }
  87. }

新增一个controller,代码如下:

  1. package com.jack.webflux_redis.controller;
  2. import com.jack.webflux_redis.domain.City;
  3. import com.jack.webflux_redis.handler.CityHandler;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.*;
  6. import reactor.core.publisher.Flux;
  7. import reactor.core.publisher.Mono;
  8. /**
  9. * create by jack 2018/5/13
  10. */
  11. @RestController
  12. @RequestMapping(value = "/city3")
  13. public class CityWebFluxMongodbController {
  14. @Autowired
  15. private CityHandler cityHandler;
  16. @GetMapping(value = "/{id}")
  17. public Mono<City> findCityById(@PathVariable("id") Long id) {
  18. return cityHandler.findCityById(id);
  19. }
  20. @GetMapping()
  21. public Flux<City> findAllCity() {
  22. return cityHandler.findAllCity();
  23. }
  24. @PostMapping()
  25. public Mono<City> saveCity(@RequestBody City city) {
  26. return cityHandler.save(city);
  27. }
  28. @PutMapping()
  29. public Mono<City> modifyCity(@RequestBody City city) {
  30. return cityHandler.modifyCity(city);
  31. }
  32. @DeleteMapping(value = "/{id}")
  33. public Mono<Long> deleteCity(@PathVariable("id") Long id) {
  34. return cityHandler.deleteCity(id);
  35. }
  36. }
运行测试,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。






声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/715793
推荐阅读
相关标签
  

闽ICP备14008679号