Это 22-й день моего участия в августовском испытании обновлений.Подробности о событии:Испытание августовского обновления
1. Знакомство с часто используемыми клиентами Redis
После Spring Boot 2.x салат используется по умолчанию для поддержки подключения к Redis.
Интернет-адрес Jedis API:tool.OSCHINA.net/uploads/API…
Адрес официального сайта салата:lettuce.io
концепция:
Jedis: это клиент Java-реализации старомодного Redis, который обеспечивает более полную поддержку команд Redis;
Redisson: реализует распределенную и масштабируемую структуру данных Java;
Lettuce: высокоуровневый клиент Redis для потокобезопасного синхронного, асинхронного и реактивного использования, поддерживающий кластеры, Sentinel, конвейеры и кодировщики.
преимущество:
Jedis: всесторонне предоставляет операционные функции Redis;
Redisson: он способствует разделению внимания пользователей на Redis и предоставляет множество распределенных связанных операционных сервисов, таких как распределенные блокировки, распределенные коллекции и очереди с задержкой, которые могут поддерживаться через Redis;
Lettuce: управляемый временем коммуникационный уровень, основанный на платформе Netty, вызовы его методов являются асинхронными, а API Lettuce является потокобезопасным, поэтому одно соединение Lettuce может использоваться для выполнения различных операций.
2. Spring Boot интегрирует Jedis
Когда мы используем Spring Boot для создания микросервисов, нам по-прежнему нужен кеш Redis для кэширования некоторых данных и хранения некоторых часто используемых данных. Использовать Redis напрямую сложнее. Здесь мы используем jedis для реализации кеша Redis для достижения цели эффективного кэширования. .
2.1 Знакомство с зависимостями Jedis
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
2.2 Настройка application.yml
spring:
redis:
port: 6379
password: 2436
host: 112.124.1.187
jedis:
pool:
max-idle: 6 # 连接池最大空闲连接数,默认 8
max-active: 10 # 连接池最大连接数(使用负值便是没有限制),默认 8
min-idle: 2 # 连接池最小空闲连接数,默认 0
timeout: 2000
Spring Boot не интегрирует Jedis, поэтому вам нужно написать свой собственный класс конфигурации и настроить JedisPool
2.3 Запись конфигурации
@Configuration
public class JedisConfig {
private Logger logger = LoggerFactory.getLogger(JedisConfig.class);
@Value("${spring.redis.port}")
private Integer port;
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.jedis.pool.max-idle}")
private Integer maxIdle;
@Value("${spring.redis.jedis.pool.max-active}")
private Integer maxActive;
@Value("${spring.redis.jedis.pool.min-idle}")
private Integer minIdle;
@Value("${spring.redis.timeout}")
private Integer timeout;
@Bean
public JedisPool jedisPool(){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setMaxTotal(maxActive);
JedisPool jedisPool = new JedisPool(jedisPoolConfig,host,port,timeout,password);
logger.info("Jedis连接成功:" + host + ":" + port);
return jedisPool;
}
}
2.4 Тест 1: Тип строки
Требование: пользователь вводит ключ, чтобы сначала определить, существуют ли данные в Redis. Если они существуют, запросите их в Redis и верните. Если они не существуют, выполните запрос в базе данных MySQL, назначьте результат Redis и верните
// UserService.java
public interface UserService {
/**
* 需求:用户输入一个key
* 先判断Redis中是否存在该数据
* 如果存在,在Redis中进行查询,并返回
* 如果不存在,在MySQL数据库查询,将结果赋给Redis,并返回
*
*/
public String getString(String key);
}
=======================
// UserServiceImpl.java
@Service
@Log // 相当于 Logger logger = LoggerFactory.getLogger(JedisConfig.class);
public class UserServiceImpl implements UserService {
@Autowired
JedisPool jedisPool;
@Override
public String getString(String key) {
String value = "";
// 1.得到Jedis对象
Jedis jedis = jedisPool.getResource();
// 2.判断该key在redis中是否存在
if(jedis.exists(key)){
// 2.1 Redis中存在,
value = jedis.get(key);
log.info("查询Redis中的数据!");
} else{
// 2.2 Redis中不存在,从数据库中查询,并存入Redis中
value = "MySQL中的数据";
log.info("查询MySQL中的数据: " + value);
jedis.set(key,value);
}
// 3. 关闭Jedis连接
jedis.close();
return value;
}
}
=======================
// UserController.java
@Controller
public class UserController {
@Autowired
UserService userService;
@RequestMapping("/getString")
@ResponseBody
public String getString(String key){
return userService.getString(key);
}
}
2.5 Инструменты
// JedisUtil.java
@Component
public class JedisUtil {
@Autowired
private JedisPool jedisPool;
/**
* 获取Jedis资源
* @return
*/
public Jedis getJedis(){
return jedisPool.getResource();
}
/**
* 释放Jedis连接
*/
public void close(Jedis jedis){
if(jedis!=null){
jedis.close();
}
}
......
}
2.6 Тест 2: Тип строки
Требование: Пользователь вводит данные redis, срок действия ключа 30 секунд
// UserService.java
public interface UserService {
/**
* 测试String类型
* 需求:用户输入一个redis数据,该key的有效期为 30 秒
*/
public String expireStr(String key,String value);
}
=======================
// UserServiceImpl.java
@Service
@Log // 相当于 Logger logger = LoggerFactory.getLogger(JedisConfig.class);
public class UserServiceImpl implements UserService {
@Autowired
JedisPool jedisPool;
@Autowired
JedisUtil jedisUtil;
/**
* 测试String类型
* 需求:用户输入一个redis数据,该key的有效期为 30 秒
*/
public String expireStr(String key,String value){
Jedis jedis = jedisUtil.getJedis();
if(!jedis.exists(key)){
// 1.在Redis中存入数据
jedis.set(key,value);
// 2.设置该值过期时间
jedis.expire(key,30);
log.info("将" + key + "有效时间设置为:30秒。");
}
// 3.查询key的有效时间
Long time = jedis.ttl(key);
jedisUtil.close(jedis);
return "该" + key + " : " + value + "的有效时间剩余: " + time;
}
}
=======================
@RestController
public class UserController {
@Autowired
UserService userService;
@RequestMapping("/expireStr")
public String expireStr(String key,String value){
return userService.expireStr(key,value);
}
}
2.7 Тест 3: Тип хэша
Требование: запрашивать информацию о пользователе на основе идентификатора пользователя.
Сначала определите, существует ли он в Redis:
Если он существует, возьмите его прямо из Redis;
Если он не существует, удалите его из MySQL и сохраните в Redis.
// User.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private String id;
private String name;
private Integer age;
}
=======================
public interface UserService {
/**
* 测试Hash类型
* 需求:根据用户ID查询用户信息
* 先判断是否在Redis中存在:
* 如果存在,直接从Redis中取出
* 如果不存在,从MySQL中取出,并存入Redis中
*/
public User findById(String id);
}
=======================
/* Hash 测试*/
@Service
@Log // 相当于 Logger logger = LoggerFactory.getLogger(JedisConfig.class);
public class UserServiceImpl implements UserService {
@Autowired
JedisPool jedisPool;
@Autowired
JedisUtil jedisUtil;
@Override
public User findById(String id) {
String key = "user:" + id; // 实体类名称:id
Jedis jedis = jedisUtil.getJedis();
User user = null;
if(jedis.exists(key)){ // 存在
user = new User();
Map<String, String> map = jedis.hgetAll(key);
user.setId(map.get("id"));
user.setName(map.get("name"));
user.setAge(Integer.parseInt(map.get("age")));
log.info("===================》从Redis中查询数据");
} else{ // 不存在
// 从MySQL中查询数据
user = new User(id,"xiaojian",22);
log.info("===================》从MySQL中查询数据" + user);
// 存入Redis
Map<String, String> map = new HashMap<>();
map.put("id",user.getId());
map.put("name",user.getName());
map.put("age",user.getAge()+"");
jedis.hmset(key,map);
log.info("===================》存入Redis中");
}
jedisUtil.close(jedis);
return user;
}
}
=======================
@RestController
public class UserController {
@Autowired
UserService userService;
@RequestMapping("/findById")
public String findById(String id){
return userService.findById(id).toString();
}
}
3. Салат интеграции Spring Boot 2.x
Lettuce: высокоуровневый клиент Redis для потокобезопасного синхронного, асинхронного и реактивного использования, поддерживающий кластеры, Sentinel, конвейеры и кодировщики.
Коммуникационный уровень, управляемый временем, основан на платформе Netty, вызовы его методов являются асинхронными, а API Lettuce является потокобезопасным, поэтому одно соединение Lettuce может использоваться для выполнения различных операций.
3.1 Импорт зависимостей
<!-- 默认是lettuce客户端-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis依赖 commons-pool ,这个依赖一定要加-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
3.2 Файл конфигурации
spring:
redis:
port: 6379
password: 2436
host: 112.124.1.187
lettuce:
pool:
max-active: 10
max-idle: 6
min-idle: 2
max-wait: 1000
shutdown-timeout: 100
3.3 Класс конфигурации
// 添加使用RedisTemplate模板,不书写,使用Spring Boot 默认
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory factory){
RedisTemplate<String,Object> template = new RedisTemplate();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// 在使用注解@Bean返回RedisTemplate的时候,同时配置hashKey与hashValue的序列化方式
// key 采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// value 采用jackson的序列化方式
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash 的key 也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// hash 的value采用jackson的序列化方式
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
3.3.1 Проблемы с конфигурацией
@Bean
public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory factory){
RedisTemplate<String,Object> template = new RedisTemplate();
template.setConnectionFactory(factory);
// ****** 改2 ******
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// Jackson 格式
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 方法过期,改 1 时注释掉这里,正常 或 改 2 时使用
// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// ****** 改1 ******,其他情况下注释掉
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance);
jackson2JsonRedisSerializer.setObjectMapper(om);
// String 类型格式
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// 在使用注解@Bean返回RedisTemplate的时候,同时配置hashKey与hashValue的序列化方式
// key 采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// value 采用jackson的序列化方式,使用 ****** 改 2 ****** 对象
template.setValueSerializer(genericJackson2JsonRedisSerializer);
// hash 的key 也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// hash 的value采用jackson的序列化方式,使用 ****** 改 2 ****** 对象
template.setHashValueSerializer(genericJackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
хеш-тип данных
- Исходный файл конфигурации дает результат (как отображается RedisDesktopManager):
["com.xiaojian.pojo.User",{"id":"1103","name":"修心","age":22}]
- изменить 1
{"id":"1105","name":"修心","age":22}
- изменить 2
{"@class":"com.xiaojian.pojo.User","id":"1106","name":"修心","age":22}
3.4 Тестирование
========
@Service
@Slf4j
public class UserServiceImpl {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
public String getString(){
System.out.println(redisTemplate);
log.info("RedisTemplate--------->测试");
return null;
}
}
==========
@SpringBootTest
class BootLettuceApplicationTests {
@Autowired
private UserServiceImpl userService;
@Test
void contextLoads() {
userService.getString();
}
}
PS: Китайский запрос в linux отображается в шестнадцатеричном виде.Вы можете войти в клиент, добавив --raw после redis-cli
[root@xiaojian bin]# ./redis-cli -a 2436 --raw
3.5 Тест 1: Тип строки
Требование: пользователь вводит ключ, чтобы сначала определить, существуют ли данные в Redis. Если они существуют, запросите их в Redis и верните. Если они не существуют, выполните запрос в базе данных MySQL, назначьте результат Redis и верните
@Service
@Slf4j
public class UserServiceImpl {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
/**
* Lettuce --> RedisTemplate 进一步的封装
* RedisTemplate 的方法和命令不一样
*
* Redis String 类型
* 需求:用户输入一个key
* 先判断Redis中是否存在该数据
* 如果存在,在Redis中进行查询,并返回
* 如果不存在,在MySQL数据库查询,将结果赋给Redis,并返回
*
* @return
*/
public String getString(String key){
String val = "";
if(redisTemplate.hasKey(key)){ // exist
val = (String) redisTemplate.opsForValue().get(key);
log.info("-----> 从Redis中查询出数据:" + val);
} else{
val = "MYSQL中查询出来的数据";
log.info("-----> 从MySQL中查询出的数据:" + val);
redisTemplate.opsForValue().set(key,val);
log.info("-----> 把从MySQL中查询出来的数据存入Redis");
}
return val;
}
}
=======================
@SpringBootTest
class BootLettuceApplicationTests {
@Autowired
private UserServiceImpl userService;
@Test
void contextLoads() {
String result = userService.getString("lettuce");
System.out.println(result);
}
}
3.6 Тест 2: Тип строки
Требование: Пользователь вводит данные redis, срок действия ключа 30 секунд
@Service
@Slf4j
public class UserServiceImpl {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
/**
* 测试 String 类型
* 需求:用户输入一个redis数据,该key的有效期为20小时
* @return
*/
public void expireStr(String key,String value){
redisTemplate.opsForValue().set(key,value);
// 定时,可以指定单位:天,时,分,秒
redisTemplate.expire(key,20, TimeUnit.HOURS);
}
}
================
@SpringBootTest
class BootLettuceApplicationTests {
@Autowired
private UserServiceImpl userService;
@Test
void t2() {
userService.expireStr("timeout","午时已到!");
}
}
3.7 Тест 3: Тип хеша (id должен быть строкой)
Требование: запрашивать информацию о пользователе на основе идентификатора пользователя.
Сначала определите, существует ли он в Redis:
Если он существует, возьмите его прямо из Redis;
Если он не существует, удалите его из MySQL и сохраните в Redis.
// 首先,在 RedisConfig 类中添加hash的序列化配置
...
// hash 的key 也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// hash 的value采用jackson的序列化方式
template.setHashValueSerializer(jackson2JsonRedisSerializer);
...
================
@Service
@Slf4j
public class UserServiceImpl {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
/**
* 测试 Hash
* @param id
* @return
*
* 根据 Id 查询用户对象信息
* 先判断Redis中是否存在该key
* 如果不存在,查询MySQL 数据库,并将结果添加到 Redis 中,并返回
* 如果存在,直接将结果在Redis查询并返回
*/
public User findById(String id){
User user = null;
if(redisTemplate.opsForHash().hasKey("user",id)){
log.info("----->从 Redis 中取出数据!");
user = (User)redisTemplate.opsForHash().get("user",id);
} else{
// 从 MySQL 中取出数据
user = new User();
user.setId(id);
user.setName("修心");
user.setAge(22);
log.info("----->从 MySQL 中取出数据");
/**
@ param h 用户实体,user
@ param hk 用户主键
@ param hv 整个对象
*/
redisTemplate.opsForHash().put("user",id,user);
log.info("----->将 map 数据存入 Redis中");
}
return user;
}
}
========================
@SpringBootTest
class BootLettuceApplicationTests {
@Autowired
private UserServiceImpl userService;
@Test
void t3() {
User user = userService.findById("1143");
System.out.println(user);
}
}
ПС: вопрос
Проблема 1: Есть много одинаковых строк ---- > извлеченных
Решение 1. Класс инструментов
Решение 2. Entity Bean объявляет метод, возвращающий строку этого класса.
Проблема 2: Проблема принуждения и повторная запись в течение длительного времени redisTemplate.opsForHash()
Решение: объявите переменную над бизнес-классом и замените redisTemplate.opsForHash() именем переменной.
@Resource(name = "redisTemplate")
private ValueOperations<String, String> redisString;
@Resource(name = "redisTemplate")
private HashOperations<String, String, User> redisHash; // K:"user"; HK:"ID"; HV: Object
4. Общие приложения Redis
4.1 Функция проверки мобильного телефона
нужно:
Пользователь вводит номер мобильного телефона на клиенте и нажимает «Отправить», чтобы сгенерировать четырехзначный код, действительный в течение 60 секунд.
Введите код подтверждения, нажмите «Подтвердить», верните сообщение об успешном или неудачном завершении, и каждый номер мобильного телефона может быть проверен только 3 раза в течение 5 минут. и дать соответствующую информацию
4.2 Ограничение входа в систему
нужно:
Пользователю разрешается ввести неправильный пароль только 5 раз в течение 2 минут;
Если оно превысит указанное количество раз, вход в систему будет ограничен на 1 час. (Необходимо давать соответствующее приглашение каждый раз, когда не удается войти в систему)