10.Quartz实现定时打分 热帖排行
1.Spring Quartz
(1)简介
核心组件
scheduler 接口:核心调度工具,所有任务由这一接口调用
job:定义任务,重写execute方法
JobDetail接口:配置描述
Trigger接口:什么时候运行,以什么样的频率运行
(2)Spring 整合
引入依赖
将配置表导入数据库
qrtz_job_details:job详情配置描述表
qrtz_simple_triggers:触发器相关配置简述
qrtz_jtriggers:触发器相关完整配置
qrtz_scheduler:定时器状态
qrtz_locks:定时器锁
QuartzPropertie
spring.quartz.job-store-type=jdbc
spring.quartz.scheduler-name=communityScheduler
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO
#spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
spring.quartz.properties.org.quartz.jobStore.class=org.springframework.scheduling.quartz.LocalDataSourceJobStore
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
spring.quartz.properties.org.quartz.jobStore.isClustered=true
spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
spring.quartz.properties.org.quartz.threadPool.threadCount=5
BUG:从spring5.3.6使用org.quartz.impl.jdbcjobstore.JobStoreTX定义quartz的默认数据源支持,如下
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
实现我们自己的job,重写execute方法
在QuartzConfig里
配置 JobDetail
// 配置JobDetail
@Bean
public JobDetailFactoryBean alphaJobDetail() {JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();factoryBean.setJobClass(AlphaJob.class);factoryBean.setName("alphaJob");factoryBean.setGroup("alphaJobGroup");factoryBean.setDurability(true);factoryBean.setRequestsRecovery(true);return factoryBean;
}
实例化JobDetailFactoryBean
声明管理的管理的是谁.setJobClass
声明job任务的名字.setName
声明任务属于的组.setGroup
声明任务是否长久保存.setDurability
声明任务是否可恢复.setRequestsRecovery
配置Trigger
// 配置Trigger(SimpleTriggerFactoryBean, CronTriggerFactoryBean)
@Bean
public SimpleTriggerFactoryBean alphaTrigger(JobDetail alphaJobDetail) {SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();factoryBean.setJobDetail(alphaJobDetail);factoryBean.setName("alphaTrigger");factoryBean.setGroup("alphaTriggerGroup");factoryBean.setRepeatInterval(3000);factoryBean.setJobDataMap(new JobDataMap());return factoryBean;
}
实例化SimpleTriggerFactoryBean
声明管理的管理的是谁.setJobClass
声明job任务的名字.setName
声明任务属于的组.setGroup
声明任务执行的频率.setRepeatInterval
声明Trigger的存储状态类型.setJobDataMap
热帖排行
用一个set将分数变化的帖子的id装起来,定时从中取出 更新
(1)统计发生分数变化的帖子
点赞 评论 精华 发布时间会影响分数
RedisKeyUtil
添加方法统计帖子分数
// 帖子分数
public static String getPostScoreKey() {return PREFIX_POST + SPLIT + "score";
}
DiscussPostController
发布帖子 设置精华 的方法里补充
// 计算帖子分数
String redisKey = RedisKeyUtil.getPostScoreKey();
redisTemplate.opsForSet().add(redisKey, post.getId());
CommentController
addComment添加评论时做处理
LikeController
like 点赞时 做处理
(2)写定时任务Job
public class PostScoreRefreshJob implements Job, CommunityConstant {private static final Logger logger = LoggerFactory.getLogger(PostScoreRefreshJob.class);@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate DiscussPostService discussPostService;@Autowiredprivate LikeService likeService;@Autowiredprivate ElasticsearchService elasticsearchService;// 牛客纪元private static final Date epoch;static {try {epoch = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2014-08-01 00:00:00");} catch (ParseException e) {throw new RuntimeException("初始化牛客纪元失败!", e);}}@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {String redisKey = RedisKeyUtil.getPostScoreKey();BoundSetOperations operations = redisTemplate.boundSetOps(redisKey);if (operations.size() == 0) {logger.info("[任务取消] 没有需要刷新的帖子!");return;}logger.info("[任务开始] 正在刷新帖子分数: " + operations.size());while (operations.size() > 0) {this.refresh((Integer) operations.pop());}logger.info("[任务结束] 帖子分数刷新完毕!");}private void refresh(int postId) {DiscussPost post = discussPostService.findDiscussPostById(postId);if (post == null) {logger.error("该帖子不存在: id = " + postId);return;}// 是否精华boolean wonderful = post.getStatus() == 1;// 评论数量int commentCount = post.getCommentCount();// 点赞数量long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, postId);// 计算权重double w = (wonderful ? 75 : 0) + commentCount * 10 + likeCount * 2;// 分数 = 帖子权重 + 距离天数double score = Math.log10(Math.max(w, 1))+ (post.getCreateTime().getTime() - epoch.getTime()) / (1000 * 3600 * 24);// 更新帖子分数discussPostService.updateScore(postId, score);// 同步搜索数据post.setScore(score);elasticsearchService.saveDiscussPost(post);}}
(3)配置 JobDetails Trigger
@Configuration
public class QuartzConfig {@Beanpublic JobDetailFactoryBean postScoreRefreshJobDetail() {JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();factoryBean.setJobClass(PostScoreRefreshJob.class);factoryBean.setName("postScoreRefreshJob");factoryBean.setGroup("communityJobGroup");factoryBean.setDurability(true);factoryBean.setRequestsRecovery(true);return factoryBean;}@Beanpublic SimpleTriggerFactoryBean postScoreRefreshTrigger(JobDetail postScoreRefreshJobDetail) {SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();factoryBean.setJobDetail(postScoreRefreshJobDetail);factoryBean.setName("postScoreRefreshTrigger");factoryBean.setGroup("communityTriggerGroup");factoryBean.setRepeatInterval(1000 * 60 * 5);factoryBean.setJobDataMap(new JobDataMap());return factoryBean;}
}