Боевая система Spring Boot 2.X — Spring Boot интегрирует Redis

Spring Boot

Репозиторий исходного кода:GitHub.com/Округ Чжашуй/…

Облако кода:git ee.com/иди туда/узнай…

В предыдущем разделе мы в основном реализовали глобальную обработку исключений Spring Boot.В этом разделе основное внимание уделяется интеграции Spring Boot с Redis, которая будет реализовывать следующие два контента:

  • Spring Boot напрямую управляет Redis для реализации набора K-V NoSQL и получения
  • Кэш базы данных Spring Boot

1. Введение и установка Redis

Redis является наиболее широко используемой и известной базой данных NoSQL в Интернете, а также наиболее широко используемым промежуточным программным обеспечением в системах хранения.

Redis — это сервер структуры данных в памяти с открытым исходным кодом (под лицензией BSD), который можно использовать, среди прочего, в качестве базы данных, кэша и посредника очередей сообщений. Он поддерживает несколько типов структур данных, таких как строки, хэши, списки, наборы, отсортированные наборы. Встроенная репликация, сценарии Lua, вытеснение LRU, транзакции и различные уровни сохраняемости диска, а также обеспечение высокой доступности с помощью Redis Sentinel и автоматическое разбиение на разделы с помощью Redis Cluster. «Ссылка 1»

1.1 Сценарии использования Redis

После появления микросервисов и распространения Redis все шире используется в интернет-компаниях, и сценариев использования становится все больше:

  • тайник: это наиболее часто используемая область Redis. Redis хранит все данные непосредственно в памяти, и его скорость доступа намного выше, чем у баз данных, таких как MySQL, которые необходимо запрашивать с жесткого диска. Если вы записываете данные, обычно используемые в SQL в кеш можно значительно повысить производительность системы, снизить нагрузку на базу данных. В то же время обмен данными между процессами также может быть достигнут с помощью кэширования Redis;
  • Упорство: Например: для каждого компонента микросервиса встроенный механизм сеанса не может быть разделен между процессами. Если сеанс записан в Redis, можно реализовать совместное использование сеанса различными процессами микросервиса. Путем сохранения его на жестком диске через Redis, этого можно избежать, сессия сервера теряется из-за простоя и перезапуска сервера;
  • Распределенная блокировка: для данных, совместно используемых процессами, необходимо избегать генерации грязных данных с помощью блокировок.Однопоточная функция Redis может использоваться для блокировки и освобождения общих данных;
  • опубликовать, подписаться: Да, Redis можно использовать как облегченную очередь сообщений;
  • Более: ограничения на доступ к интерфейсу, кешированная история поведения пользователей, количество лайков контента, списки лидеров и т. д.

1.2, установка Redis

Здесь используется Linux.Для платформ Windows вы можете использовать WSL или виртуальную машину для установки дистрибутива Linux.

# 安装编译所需的软件
sudo apt install gcc g++ make

Официальный сайт Redis:redis.io/download

# 下载 Redis 
wget http://download.redis.io/releases/redis-5.0.8.tar.gz
tar -xvf redis-5.0.8.tar.gz
cd redis-5.0.8
# 编译
mark

Простая конфигурация для Redis:vim redis.conf

# 添加, Redis 密码,默认为空
requirepass springboot
# 注释掉 bind,允许远程访问,或者空格隔开添加运行远程访问的 ip
# bind 127.0.0.1
# 修改为 no,保护模式关闭,允许通过 IP 访问
protected-mode no

Старт Redis

# 后台以服务的方式运行
src/redis-server redis.conf --daemonize yes
# 使用密码登录默认端口号 6379
src/redis-cli -p 6379 -a springboot
 # 写入 Key = name,value = springboot
127.0.0.1:6379> set name springboot
OK
127.0.0.1:6379> get name
"springboot"
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379>exit
# 关闭 Redis 服务,并把数据保存到硬盘
./src/redis-cli -p 6379 -a springboot  shutdown save

2. Начните использовать Redis

Новый проект10-spring-boot-redis, добавьте следующие зависимости:

// Gradle
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.1'
// 刚开始是 MyBatis,但缓存部分写的有问题,删掉换了 JPA,最后有加上了 MyBatis,所以有两个
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'mysql:mysql-connector-java'

документapplication.propertiesКонфигурация соединения Redis, соединения MySQL, MyBatis, JPA

# Redis host ip
spring.redis.host=wsl
# Redis 服务器连接端口
spring.redis.port=6379
# Redis 数据库索引(默认为 0)
spring.redis.database=0
# Redis 服务器连接密码(默认为空)
spring.redis.password=springboot
#连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=500
# 使用 Redis 缓存
spring.cache.type=redis

# 数据库 URL、用户名、密码、JDBC Driver更换数据库只需更改这些信息即可
# MySQL 8 需要指定 serverTimezone 才能连接成功
spring.datasource.url=jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.password=xiaoxian
spring.datasource.username=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# Hibernate 的一些配置
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
# 是否在 Log 显示 SQL 执行语句
spring.jpa.show-sql=true
# hibernate.ddl-auto 配置对数据库表的操作
spring.jpa.hibernate.ddl-auto=none

# MyBatis 驼峰命名转换
mybatis.configuration.map-underscore-to-camel-case=true
# 显示 Mybatis 的 SQL,Mapper 所在的包打印 debug 级别的代码
logging.level.org.xian.redis.repository=debug

стартовый классRedisApplication.javaНастройте картограф MyBatis и включите конфигурацию кэша.

@SpringBootApplication
@EnableCaching
@MapperScan("org.xian.redis.repository")
public class RedisApplication ......

2.1, конфигурация Redis

Новый RedisConfig.java

@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
    @Bean
    public RedisCacheConfiguration cacheConfiguration() {
        // 设置缓存过期时间为 120 秒后
        return RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(120)).disableCachingNullValues();
    }
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
        // 使用 RedisCacheManager 作为缓存管理器
        return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration()).transactionAware().build();
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // Jackson 序列方式
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        // Jackson 默认自动识别 Public 修饰的成员变量、getter、setter
        // private、protected、public 修饰的成员变量都可以自动识别,无需都指定 getter、setter 或者 public。
        // 参考 https://blog.csdn.net/sdyy321/article/details/40298081
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 对于 8 种基本数据类型及其封装类和 String ,其他的类型在序列化的时候带上该类型和值
        // 参考 https://www.jianshu.com/p/c5fcd2a1ab49
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // Redis 链接
        template.setConnectionFactory(redisConnectionFactory);
        // Redis Key - Value 序列化使用 Jackson
        template.setKeySerializer(jackson2JsonRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // Redis HashKey - HashValue 序列化使用 Jackson
        template.setHashKeySerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    @ConditionalOnMissingBean(StringRedisTemplate.class)
    public StringRedisTemplate stringRedisTemplate(
            RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

RedisTemplate по умолчанию — это RedisTemplate, то есть и ключ, и значение являются универсальными, а ключ настроен как строка.

Используйте Jackson в качестве сериализатора, потому что по умолчанию он используетJdkSerializationRedisSerializer, который сериализуется в двоичный файл и хранится в Redis, что не способствует просмотру исходного контента. Используя JSON в качестве инструмента сериализации, можно легко получить доступ и к другим языкам программирования.

2.2, Redis получить и установить

Здесь мы покажем, как хранить и читать данные Redis с помощью RedisTemplate и StringRedisTemplate.

@RestController
@CacheConfig(cacheNames = "users")
public class RedisController {
    @Resource StringRedisTemplate stringTemplate;
    @Resource RedisTemplate<String, User> redisTemplate;

    @RequestMapping("/setString")
    public String setString(@RequestParam(value = "key") String key,
                            @RequestParam(value = "value") String value) {
        stringTemplate.opsForValue().set(key, value);
        return "ok";
    }
    
    @RequestMapping("/getString")
    public String getString(@RequestParam(value = "key") String key) {
        return stringTemplate.opsForValue().get(key);
    }
    // User 类set、get 和时间限制
    @RequestMapping(value = "/setUser")
    public String setUser(@RequestBody User user) {
        // 1 分钟后过期
        redisTemplate.opsForValue().set(user.getUsername(), user, Duration.ofMinutes(1));
        return "ok";
    }
    
    @RequestMapping("/getUser")
    public User getUser(@RequestParam(value = "key") String key) {
        return redisTemplate.opsForValue().get(key);
    }
    
    @RequestMapping("/delUser")
    public User delUser(@RequestParam(value = "key") String key) {
        User user = redisTemplate.opsForValue().get(key);
        // 删除
        redisTemplate.delete(key);
        return user;
    }
}

Анализ кода: set(key, value, time_out), время истечения срока действия Redis можно указать через третий параметр.

Запустите проект, посетите вышеуказанные URL-адреса соответственно и проверьте их действие.

2.2, кеш базы данных

документUser.java, сценарий SQL находится вuser.sql, Insert Controller здесь не прописан, а сервисный слой опущен.

@Entity
@Table(name = "user", schema = "spring")
public class User implements Serializable {
    private static final long serialVersionUID = 413797298970501130L;
    @Id private String username;
    private Byte age;
    // 省略 Getter setter
}

Файл UserMapper:

package org.xian.redis.repository;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.xian.redis.entity.User;

public interface UserMapper {
    @Select("Select username, age From user Where username=#{username}")
    User selectByUsername(String username);
    
    @Update("Update user Set age=#{age} Where username=#{username}")
    void update(User user);

    @Delete("Delete From user where username=#{username}")
    void delete(String username);
}

существуетRedisControllerДобавьте следующее

@RequestMapping("/deleteAllCache")
@CacheEvict(allEntries = true)
public String deleteAllCache() {
    // 删除所有缓存
    return "OK";
}

@RequestMapping("/mySelect")
@Cacheable(key = "#username")
public User mySelect(@RequestParam(value = "username") String username) {
    return userMapper.selectByUsername(username);
}

@RequestMapping("/myUpdate")
@CachePut(key = "#user.username")
public User myUpdate(@RequestBody User user) {
    userMapper.update(user);
    return userMapper.selectByUsername(user.getUsername());
}

@RequestMapping("/myDelete")
@CacheEvict(key = "#username")
public User myDelete(@RequestParam(value = "username") String username) {
    userMapper.delete(username);
    return userMapper.selectByUsername(username);
}

Разбор кода: кеширование трех часто используемых аннотаций.

@Cacheable(key = "#username")Указывает, что имя пользователя используется в качестве ключа, а результат записывается в кэш Redis. Если это значение ключа уже есть в кеше, оно вернется напрямую без выполнения SQL-запроса.Когда в кеше нет данных, SQL-запрос будет выполнен. То есть сначала посмотрите, есть ли кеш, верните его напрямую, если есть, и запросите базу данных, если нет.

@CachePut(key = "#user.username"), а также@CacheableТочно так же функция находится в myUpdate(), и содержимое в кеше не будет записано или обновлено до окончания выполнения.

@CacheEvict(key = "#username")Удалить содержимое в кеше на основе значения ключа. Удаляет, если указано allEntries = true@CacheConfig(cacheNames = "users")Все кэши под cacheNames.

Запустите проект, откройте /mySelect?username=boot , при первом доступе или доступе после истечения срока действия кеша терминал распечатает оператор SQL, запросит SQL, получит доступ в течение срока действия кеша и напрямую вернет содержимое в кеше. без обращения к базе данных.

Доступ к /select?username=boot, API-интерфейсу, использующему JPA, также не запрашивает базу данных в течение срока действия кэша. Заинтересованные друзья могут попробовать его, и соответствующий код был загружен в репозиторий GitHub.

Другие интерфейсы здесь не перечислены.

Ступай на яму: Кэшированный @Cache* используется не в интерфейсе Repository или Mapper, а в определенном методе. Например, на уровне службы интерфейса службы или на уровне интерфейса контроллера.

Кроме того, кеш может указать генератору ключей автоматически генерировать значение ключа, что здесь не реализовано, а задается вручную по ключу = "#username".

В соответствии с дополнительными операционными API Redis, такими как массивы, наборы, хэш, рекомендуется прочитать Справочник 1.

Ссылка 1: Понимание Redis и использование Redis в проектах Spring BootWoohoo. IBM.com/developer Я…"

Содержание следующего раздела — интеграция Spring Boot с MangoDB (еще один широко используемый NoSQL) или краткое введение в распределенные блокировки Redis. Для получения дополнительной информации вы можете обратить внимание на CSDN-блог Xiaoxian «Big Data Xiaoxian» или публичный аккаунт «Advanced Programming Technology».