目录
一、Redis的基本使用
在Windows系统中,当安装了Redis后,会自动启动Redis的服务,每次开机时Redis就会启动,是可以直接使用的,检验的标准可以通过“登录Redis客户端的控制台”来判断:
如果已经登录了,但并不确定当前Redis是否正在运行,也可以通过ping
命令来判断:
除了
ping
以外,也可以通过其它命令来判断,例如是否可以存入数据,或是否可以取出数据等。
当登录Redis客户端的控制台之后,可以通过exit
退出:
在登录Redis客户端的控制台之后,可以通过set
命令向Redis中存入数据,也可以通过get
命令取出数据:
关于Redis存储数据的结构,可以想像成Java中的
Map
,每个数据需要有Key
和Value
,其中,Key
是唯一的,如果反复使用同一个Key
存入数据,最终在Redis中只有最后一次存入的数据!所以,Redis中的set
命令相当于Java中操作Map
的put()
方法。
由于取出数据时需要使用到数据的Key
,如果不明确当前Redis中有哪些数据,可以通过keys *
来查看所有的Key
:
其实,Redis可以理解为一个数据库(Database),在Redis中默认存在16个数据表,当登录Redis时,默认使用的是第1个数据表,这些数据表使用0
~15
作为索引,通过select index
可以切换使用数据表:
当需要清除Redis中的数据时,可以通过flushdb
或flushall
来清除:
二、SpringBoot中的计划任务
计划任务适用于每间隔一段时间就执行一次某项目任务,例如“定时更新热门话题”等。
在SpringBoot中,如果需要使用计划任务,需要:
- 在配置类的声明之前添加
@EnableScheduling
注解; - 计划任务方法必须放在某个组件类中;
- 计划任务方法的声明之前必须添加
@Scheduled
注解,并在注解中配置执行时间。
例如,先在启动类(启动类就是一个配置类)的声明之前添加注解:
然后,在cn.tedu.straw.api.question.schedule
包中创建TestSchedule
类,用于测试使用计划任务:
package cn.tedu.straw.api.question.schedule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class TestSchedule {
private int i = 0;
@Scheduled(fixedRate = 3 * 1000)
public void task() {
log.debug("测试执行计划任务:" + ++i);
}
}
完成后,启动StrawApiQuestionApplication
项目,在控制台可以看到计划任务输出的日志。
三、定期向Redis中更新标签列表
先准备计划任务,在cn.tedu.straw.api.question.schedule
包中创建RedisTagSchedule
类,并添加计划任务方法:
package cn.tedu.straw.api.question.schedule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class RedisTagSchedule {
@Scheduled(fixedRate = 10 * 1000)
public void updateRedisTag() {
log.debug("准备更新Redis服务器中缓存的标签列表");
}
}
要使用Redis的API,需要先添加spring-boot-starter-data-redis
的依赖,其参考代码是:
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
基于以上参考代码调整父级项目和当前项目的pom.xml
。
然后,还需要配置一个RedisTemplate
对象,该对象的API就是操作Redis中存取数据的!
可以在任意配置类中配置这个类的对象,则在启动类中:
由于RedisTemplate
的API不够简洁,所以,一般会将RedisTemplate
的API进行再次封装,以便于后续的调用,所以,在cn.tedu.straw.api.question.util
包中创建RedisUtils
工具类:
package cn.tedu.straw.api.question.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.io.Serializable;
/**
* 操作Redis服务器中的数据的工具类
*/
@Component
public class RedisUtils {
@Autowired
RedisTemplate<String, Serializable> redisTemplate;
/**
* 删除Redis中的某个数据
*
* @param key 被删除的数据的Key
* @return 删除操作是否成功
*/
public Boolean delete(String key) {
return redisTemplate.delete(key);
}
/**
* 向Redis中的某个List集合中添加集合元素
*
* @param key 在Redis中的List集合类型的数据的Key
* @param value 需要存入到Redis的List集合中的元素值
*/
public Long rightPush(String key, Serializable value) {
ListOperations<String, Serializable> ops = redisTemplate.opsForList();
return ops.rightPush(key, value);
}
}
回到计划类中,在执行计划任务时,先删除Redis中可能已经存在的标签列表,然后重新添加标签列表:
package cn.tedu.straw.api.question.schedule;
import cn.tedu.straw.api.question.service.ITagService;
import cn.tedu.straw.api.question.util.RedisUtils;
import cn.tedu.straw.commons.vo.TagVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
@Component
@Slf4j
public class RedisTagSchedule {
@Autowired
RedisUtils redisUtils;
@Autowired
ITagService tagService;
@Scheduled(fixedRate = 10 * 1000)
public void updateRedisTag() {
// 日志
log.debug("[{}] 准备更新Redis服务器中缓存的标签列表"
, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
// 在Redis中“标签列表”数据的Key
String tagListKey = "tags";
// 删除“标签列表”数据,避免反复增加列表元素,或某些列表元素在MySQL中更新后在Redis中无法处理
redisUtils.delete(tagListKey);
// 从数据库中读取新的“标签列表”
List<TagVO> tags = tagService.getTagList();
// 遍历“标签列表”并向Redis中逐一添加数据
for (TagVO tag : tags) {
// 向Redis中添加数据
redisUtils.rightPush(tagListKey, tag);
}
// 日志
log.debug("[{}] 更新Redis服务器中缓存的标签列表,完成!"
, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
}
}
完成后,启动当前项目,通过日志可以观察计划任务在定期执行,同时,登录Redis客户端的控制台,也可以看到标签列表数据已经成功的添加到了Redis服务器中:
四、从Redis中获取标签列表
如果需要从Redis中获取列表数据,首先,需要在RedisUtils
中补充定义获取列表数据的方法:
/**
* 获取Redis中某List集合
*
* @param key 需要获取的数据在Redis中的Key
* @return List集合
*/
public List<Serializable> listRange(String key) {
ListOperations<String, Serializable> ops = redisTemplate.opsForList();
long start = 0;
long end = ops.size(key);
return ops.range(key, start, end);
}
/**
* 获取Redis中某List集合中的数据片段
*
* @param key 需要获取的数据在Redis中的Key
* @param start 获取的数据片段在List集合中的起始位置
* @param end 获取的数据片段在List集合中的结束位置
* @return List集合中的数据片段
*/
public List<Serializable> listRange(String key, long start, long end) {
ListOperations<String, Serializable> ops = redisTemplate.opsForList();
return ops.range(key, start, end);
}
此前,在开发straw-api-user
时创建了R
类,用于表示向客户端响应的JSON结果的类型,目前,在straw-api-question
中也需要使用到这个类型,所以,应该将R
类移动到straw-commons
中的cn.tedu.straw.commons.util
包中!
注意:当把R
类从straw-api-user
移动到straw-commons
中之后,需要检查straw-api-user
的cn.tedu.straw.api.user.controller
包中代码,使用到R
类的import
语句都需要修正。
目前,当控制器向客户端响应R
类型的结果时,无法封装“数据”,所以,应该先在R
类中声明新的属性,表示“操作成功时响应到客户端的数据”,由于R
类型是希望广泛使用的,无论客户端提交的是哪种请求都可以响应R
类型,例如“获取当前登录的用户资料”、“获取标签列表”、“获取问题列表”、“获取问题详情”等,所以,补充添加的“数据”属性的类型是不确定的,则应该使用Object
类型,或使用泛型,本例中暂且使用泛型:
同时,为了能够更加简洁的创建R
对象,还应该添加方法:
最终,这些数据应该被控制器对外提供访问,在cn.tedu.straw.api.question.controller
包中创建TagController
用于对外提供访问:
package cn.tedu.straw.api.question.controller;
import cn.tedu.straw.api.question.util.RedisUtils;
import cn.tedu.straw.commons.util.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/tags")
public class TagController {
@Autowired
RedisUtils redisUtils;
// http://localhost:8081/tags
@GetMapping("")
public R getTagList() {
String key = "tags";
return R.ok(redisUtils.listRange(key));
}
}
完成后,重启straw-api-question
项目,打开浏览器,通过以上测试URL进行访问,应该可以看到标签列表数据,例如:
可以看到,以上JSON结果中还包含"message": null
这一条属性,这个属性不是需要响应到客户端的,在使用Jackson框架处理服务器端响应到客户端的JSON数据时,在SpringBoot项目中,可以添加配置:
# 服务器端向客户端响应的JSON数据中将不包含为null的属性与值
spring.jackson.default-property-inclusion=non_null
五、通过网关转发访问以上控制器
要使得当前straw-api-question
项目整合到集群中,首先,需要在当前项目中添加eureka-client
使之成为Eureka客户端,后续,在启动项目时就会在Eureka注册中心进行注册,然后,还应该在网关路由中配置新的转发规则,使得用户通过网关可以转发来访问当前项目!
则先在straw-api-question
的pom.xml
中添加eureka-client
的依赖:
然后,在straw-api-question
的application.properties
中补充Eureka Client的相关配置(从其它项目中复制过来即可,无需修改各配置值):
至此,先启动straw-eureka-server
项目,再启动straw-api-question
项目,在 http://loalhost:8761 Eureka状态页面即可看到当前项目已经成功注册:
然后,需要实现路由转发,在straw-gateway
的application.properties
配置转发规则:
为了避免转换路径的冲突,并统一转发规则的配置,以上代码中,将原有的api-user
转发规则中的原路径/api/**
改成了/api-user/**
,由于在此前完成的“注册”功能是访问了straw-api-user
的控制器的,所以,应该在register.js
中修改请求路径:
在网关的WebSecurityConfigurer
中的“白名单”的路径也跟随调整:
另外,为了使得访问的URL风格是统一的,在TagController
中,将类的声明之前使用@RequestMapping
配置的路径也添加/v1
前缀:
完成后,重启straw-api-question
,并分别启动straw-api-user
、straw-gateway
项目,最终,在浏览器中,通过 http://localhost/api-question/v1/tags 可以访问到标签列表。
由于修改了“注册”相关的URL、配置等信息,应该再次尝试注册新用户,以检验“注册”功能是否仍正常可用!
六、标签列表---前端页面
在index.html
的偏顶部位置需要显示标签列表:
首先,打开index.html
,找到显示标签列表的区域,在这个区域的标签上配置id
属性,以对应Vue对象:
接下来,在resources/static/js
中创建commons
文件夹,在该文件夹下创建tags.js
文件,用于编写此处将使用的程序:
并在index.html
中引用该文件:
然后,在tags.js
中创建Vue对象,并添加模拟数据:
然后,在index.html
中,使用v-for
语句显示以上数据:
关于
v-for
语法,它是用于遍历某个标签的,例如将v-for
配置在<p>
标签上,则表示需要遍历这个<p>
标签及其所有子级标签(从<p>
开始,直到对应的</p>
),最终,在页面中会出现若干个<p>
标签!关于
v-for
的取值,简单语法是xx in arr
,其中,arr
应该是一个JS中的数组,是被遍历的对象,而xx
就是遍历过程中数组元素的值,该语法与Java中的for (User user : users)
这类语法是相似的!