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 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。






猜你喜欢

转载自blog.csdn.net/wj903829182/article/details/80297336