黑马头条-day10-xxl-job热点文章计算

目录

一.需求分析

 实现思路

 定时计算

定时任务框架-xxljob

二. 学习目录

分布式任务调度

1 xxl-Job简介

2 XXL-Job-环境搭建

2.1调度中心环境要求

2.2源码仓库地址

 2.3 初始化“调度数据库”

2.4配置部署“调度中心”

3.配置部署调度中心-docker安装

 4.简单实例

创建xxljob-demo项目,导入依赖

application.yml配置

新建配置类

测试-单节点

4.2任务详解-执行器

4.3任务详解-基础配置

4.4路由策略(轮询)-案例

4.4路由策略(分片广播)

4.4.1 路由策略(分片广播)-案例

三.热点文章-定时计算

实现思路

1.频道列表远程接口准备

2.热文章业务层

3.xxl-job定时计算-步骤

四.查询文章接口改造

1. 思路分析

2. 功能实现

2.1 在ApArticleService中新增方法

2.2 修改控制器


一.需求分析

用户在看文章时,应该是查询出来热点文章,而不是根据时间查询最新的文章,热点文章,也就是观看量,点赞量,评论数等,

目前实现的思路:从数据库直接按照发布时间倒序查询

  • 问题1:

    如何访问量较大,直接查询数据库,压力较大

  • 问题2:

    新发布的文章会展示在前面,并不是热点文章

 实现思路

把热点数据存入redis进行展示

判断文章是否是热点,有几项标准: 点赞数量,评论数量,阅读数量,收藏数量

计算文章热度,有两种方案:

  • 定时计算文章热度

  • 实时计算文章热度

 定时计算

  • 根据文章的行为(点赞、评论、阅读、收藏)计算文章的分值,利用定时任务每天完成一次计算

  • 把分值较大的文章数据存入到redis中

  • App端用户查询文章列表的时候,优先从redis中查询热度较高的文章数据

定时任务框架-xxljob

spring传统的定时任务@Scheduled,但是这样存在这一些问题 :

  • 做集群任务的重复执行问题

  • cron表达式定义在代码之中,修改不方便

  • 定时任务失败了,无法重试也没有统计

  • 如果任务量过大,不能有效的分片执行

解决这些问题的方案为:

xxl-job 分布式任务调度框架

二. 学习目录

  • xxl-job概述

  • xxl-job入门案例

  • xxl-job高级部分

  • 热点文章定时计算

  • 查询文章接口改造

分布式任务调度

 当前软件的架构已经开始向分布式架构转变,将单体结构拆分为若干服务,服务之间通过网络交互来完成业务处理。在分布式架构下,一个服务往往会部署多个实例来运行我们的业务,如果在这种分布式系统环境下运行任务调度,我们称之为分布式任务调度

将任务调度程序分布式构建,这样就可以具有分布式系统的特点,并且提高任务的调度处理能力:

1、并行任务调度

并行任务调度实现靠多线程,如果有大量任务需要调度,此时光靠多线程就会有瓶颈了,因为一台计算机CPU的处理能力是有限的。

如果将任务调度程序分布式部署,每个结点还可以部署为集群,这样就可以让多台计算机共同去完成任务调度,我们可以将任务分割为若干个分片,由不同的实例并行执行,来提高任务调度的处理效率。

2、高可用

若某一个实例宕机,不影响其他实例来执行任务。

3、弹性扩容

当集群中增加实例就可以提高并执行任务的处理效率。

4、任务管理与监测

对系统中存在的所有定时任务进行统一的管理及监测。让开发人员及运维人员能够时刻了解任务执行情况,从而做出快速的应急处理响应。

分布式任务调度面临的问题:  

当任务调度以集群方式部署,同一个任务调度可能会执行多次,例如:电商系统定期发放优惠券,就可能重复发放优惠券,对公司造成损失,信用卡还款提醒就会重复执行多次,给用户造成烦恼,所以我们需要控制相同的任务在多个运行实例上只执行一次。常见解决方案:

  • 分布式锁,多个实例在任务执行前首先需要获取锁,如果获取失败那么就证明有其他服务已经在运行,如果获取成功那么证明没有服务在运行定时任务,那么就可以执行。

  • ZooKeeper选举,利用ZooKeeper对Leader实例执行定时任务,执行定时任务的时候判断自己是否是Leader,如果不是则不执行,如果是则执行业务逻辑,这样也能达到目的。

1 xxl-Job简介

针对分布式任务调度的需求,市场上出现了很多的产品:

1) TBSchedule:淘宝推出的一款非常优秀的高性能分布式调度框架,目前被应用于阿里、京东、支付宝、国美等很多互联网企业的流程调度系统中。但是已经多年未更新,文档缺失严重,缺少维护。

2) XXL-Job:大众点评的分布式任务调度平台,是一个轻量级分布式任务调度平台, 其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。

3)Elastic-job:当当网借鉴TBSchedule并基于quartz 二次开发的弹性分布式任务调度系统,功能丰富强大,采用zookeeper实现分布式协调,具有任务高可用以及分片功能。

4)Saturn: 唯品会开源的一个分布式任务调度平台,基于Elastic-job,可以全域统一配置,统一监 控,具有任务高可用以及分片功能。

XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。

源码地址:xxl-job: 一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。

文档地址:分布式任务调度平台XXL-JOB

特性

  • 简单灵活 提供Web页面对任务进行管理,管理系统支持用户管理、权限控制; 支持容器部署; 支持通过通用HTTP提供跨平台任务调度;

  • 丰富的任务管理功能 支持页面对任务CRUD操作; 支持在页面编写脚本任务、命令行任务、Java代码任务并执行; 支持任务级联编排,父任务执行结束后触发子任务执行; 支持设置指定任务执行节点路由策略,包括轮询、随机、广播、故障转移、忙碌转移等; 支持Cron方式、任务依赖、调度中心API接口方式触发任务执行

  • 高性能 任务调度流程全异步化设计实现,如异步调度、异步运行、异步回调等,有效对密集调度进行流量削峰;

  • 高可用 任务调度中心、任务执行节点均 集群部署,支持动态扩展、故障转移 支持任务配置路由故障转移策略,执行器节点不可用是自动转移到其他节点执行 支持任务超时控制、失败重试配置 支持任务处理阻塞策略:调度当任务执行节点忙碌时来不及执行任务的处理策略,包括:串行、抛弃、覆盖策略

  • 易于监控运维 支持设置任务失败邮件告警,预留接口支持短信、钉钉告警; 支持实时查看任务执行运行数据统计图表、任务进度监控数据、任务完整执行日志;

2 XXL-Job-环境搭建

2.1调度中心环境要求
  • Maven3+

  • Jdk1.8+

  • Mysql5.7+

2.2源码仓库地址
源码仓库地址Release Download
GitHub - xuxueli/xxl-job: A distributed task scheduling framework.(分布式任务调度平台XXL-JOB)Download
xxl-job: 一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。Download

 2.3 初始化“调度数据库”

请下载项目源码并解压,获取 “调度数据库初始化SQL脚本” 并执行即可。

位置:/xxl-job/doc/db/tables_xxl_job.sql 共8张表

 

- xxl_job_lock:任务调度锁表;
- xxl_job_group:执行器信息表,维护任务执行器信息;
- xxl_job_info:调度扩展信息表: 用于保存XXL-JOB调度任务的扩展信息,如任务分组、任务名、机器地址、执行器、执行入参和报警邮件等等;
- xxl_job_log:调度日志表: 用于保存XXL-JOB任务调度的历史信息,如调度结果、执行结果、调度入参、调度机器和执行器等等;
- xxl_job_logglue:任务GLUE日志:用于保存GLUE更新历史,用于支持GLUE的版本回溯功能;
- xxl_job_registry:执行器注册表,维护在线的执行器和调度中心机器地址信息;
- xxl_job_user:系统用户表;

调度中心支持集群部署,集群情况下各节点务必连接同一个mysql实例;

如果mysql做主从,调度中心集群节点务必强制走主库;

2.4配置部署“调度中心”

 调度中心项目:xxl-job-admin

作用:统一管理任务调度平台上调度任务,负责触发调度执行,并且提供任务管理平台。

步骤一:调度中心配置

调度中心配置文件地址:/xxl-job/xxl-job-admin/src/main/resources/application.properties

数据库的连接信息修改为自己的数据库

 启动调度中心,默认登录账号 “admin/123456”, 登录后运行界面如下图所示。

3.配置部署调度中心-docker安装

 1.创建mysql容器,初始化xxl-job的SQL脚本

docker run -p 3306:3306 --name mysql57 \
-v /usr/local/mysql/conf:/etc/mysql \
-v /usr/local/mysql/log:/var/log/mysql \
-v /usr/local/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7

2.拉取镜像

docker pull xuxueli/xxl-job-admin:2.3.0

 3.创建容器

docker run -e PARAMS="--spring.datasource.url=jdbc:mysql://192.168.81.128:3306/xxl_job?Unicode=true&characterEncoding=UTF-8 \
--spring.datasource.username=root \
--spring.datasource.password=123456" \
-p 8888:8080 -v /tmp:/data/applogs \
--name xxl-job-admin --restart=always  -d xuxueli/xxl-job-admin:2.3.0

 4.简单实例

创建xxljob-demo项目,导入依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--xxl-job--><dependency><groupId>com.xuxueli</groupId><artifactId>xxl-job-core</artifactId><version>2.3.0</version></dependency>
</dependencies>
application.yml配置
server:port: 8881xxl:job:admin:addresses: http://192.168.200.130:8888/xxl-job-adminexecutor:appname: xxl-job-executor-sampleport: 9999
新建配置类
/*** xxl-job config** @author xuxueli 2017-04-28*/
@Configuration
public class XxlJobConfig {private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);@Value("${xxl.job.admin.addresses}")private String adminAddresses;@Value("${xxl.job.executor.appname}")private String appname;@Value("${xxl.job.executor.port}")private int port;@Beanpublic XxlJobSpringExecutor xxlJobExecutor() {logger.info(">>>>>>>>>>> xxl-job config init.");XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();xxlJobSpringExecutor.setAdminAddresses(adminAddresses);xxlJobSpringExecutor.setAppname(appname);xxlJobSpringExecutor.setPort(port);return xxlJobSpringExecutor;}}

 任务代码,重要注解:@XxlJob(“JobHandler”)

测试-单节点
  • 启动微服务

  • 在xxl-job的调度中心中启动任务

4.2任务详解-执行器

  • 执行器:任务的绑定的执行器,任务触发调度时将会自动发现注册成功的执行器, 实现任务自动发现功能;

  • 另一方面也可以方便的进行任务分组。每个任务必须绑定一个执行器

以下是执行器的属性说明:  

属性名称说明
AppName是每个执行器集群的唯一标示AppName, 执行器会周期性以AppName为对象进行自动注册。可通过该配置自动发现注册成功的执行器, 供任务调度时使用;
名称执行器的名称, 因为AppName限制字母数字等组成,可读性不强, 名称为了提高执行器的可读性;
排序执行器的排序, 系统中需要执行器的地方,如任务新增, 将会按照该排序读取可用的执行器列表;
注册方式调度中心获取执行器地址的方式;
机器地址注册方式为"手动录入"时有效,支持人工维护执行器的地址信息;

自动注册和手动注册的区别和配置  

4.3任务详解-基础配置

基础配置

  • 执行器:每个任务必须绑定一个执行器, 方便给任务进行分组

  • 任务描述:任务的描述信息,便于任务管理;

  • 负责人:任务的负责人;

  • 报警邮件:任务调度失败时邮件通知的邮箱地址,支持配置多邮箱地址,配置多个邮箱地址时用逗号分隔

调度配置

  • 调度类型:

    • 无:该类型不会主动触发调度;

    • CRON:该类型将会通过CRON,触发任务调度;

    • 固定速度:该类型将会以固定速度,触发任务调度;按照固定的间隔时间,周期性触发;

任务配置

  • 运行模式:

BEAN模式:任务以JobHandler方式维护在执行器端;需要结合 "JobHandler" 属性匹配执行器中任务;

  • JobHandler:运行模式为 "BEAN模式" 时生效,对应执行器中新开发的JobHandler类“@JobHandler”注解自定义的value值;

  • 执行参数:任务执行所需的参数;

阻塞处理策略

阻塞处理策略:调度过于密集执行器来不及处理时的处理策略;

  • 单机串行(默认):调度请求进入单机执行器后,调度请求进入FIFO(First Input First Output)队列并以串行方式运行;

  • 丢弃后续调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,本次请求将会被丢弃并标记为失败;

  • 覆盖之前调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,将会终止运行中的调度任务并清空队列,然后运行本地调度任务;

 

路由策略

当执行器集群部署时,提供丰富的路由策略,包括;

  • FIRST(第一个):固定选择第一个机器;

  • LAST(最后一个):固定选择最后一个机器;

  • ROUND(轮询)

  • RANDOM(随机):随机选择在线的机器;

  • CONSISTENT_HASH(一致性HASH):每个任务按照Hash算法固定选择某一台机器,且所有任务均匀散列在不同机器上。

  • LEAST_FREQUENTLY_USED(最不经常使用):使用频率最低的机器优先被选举;

  • LEAST_RECENTLY_USED(最近最久未使用):最久未使用的机器优先被选举;

  • FAILOVER(故障转移):按照顺序依次进行心跳检测,第一个心跳检测成功的机器选定为目标执行器并发起调度;

  • BUSYOVER(忙碌转移):按照顺序依次进行空闲检测,第一个空闲检测成功的机器选定为目标执行器并发起调度;

  • SHARDING_BROADCAST(分片广播):广播触发对应集群中所有机器执行一次任务,同时系统自动传递分片参数;可根据分片参数开发分片任务;

4.4路由策略(轮询)-案例

 1.修改任务为轮询

 2.启动多个微服务

修改yml配置文件  

server:port: ${port:8881}xxl:job:admin:addresses: http://192.168.200.130:8888/xxl-job-adminexecutor:appname: xxl-job-executor-sampleport: ${executor.port:9999}

3.启动多个微服务

每个微服务轮询的去执行任务

4.4路由策略(分片广播)

执行器集群部署时,任务路由策略选择”分片广播”情况下,一次任务调度将会广播触发对应集群中所有执行器执行一次任务  

执行器集群部署时,任务路由策略选择”分片广播”情况下,一次任务调度将会广播触发对应集群中所有执行器执行一次任务  

4.4.1 路由策略(分片广播)-案例

需求:让两个节点同时执行10000个任务,每个节点分别执行5000个任务

①:创建分片执行器

 

 ②:创建任务,路由策略为分片广播

③:分片广播代码

分片参数

index:当前分片序号(从0开始),执行器集群列表中当前执行器的序号;

total:总分片数,执行器集群的总机器数量;

 修改yml配置

server:port: ${port:8881}xxl:job:admin:addresses: http://192.168.200.130:8888/xxl-job-adminexecutor:appname: xxl-job-sharding-executorport: ${executor.port:9999}

代码

package com.heima.xxljob.job;import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;@Component
public class HelloJob {@Value("${server.port}")private String port;@XxlJob("demoJobHandler")public void helloJob(){System.out.println("简单任务执行了。。。。"+port);}@XxlJob("shardingJobHandler")public void shardingJobHandler(){//分片的参数int shardIndex = XxlJobHelper.getShardIndex();int shardTotal = XxlJobHelper.getShardTotal();//业务逻辑List<Integer> list = getList();for (Integer integer : list) {if(integer % shardTotal == shardIndex){System.out.println("当前第"+shardIndex+"分片执行了,任务项为:"+integer);}}}public List<Integer> getList(){List<Integer> list = new ArrayList<>();for (int i = 0; i < 10000; i++) {list.add(i);}return list;}
}

④:测试

启动多个微服务测试,一次执行可以执行多个任务

三.热点文章-定时计算

需求:为每个频道缓存热度较高的30条文章优先展示  

判断文章热度较高的标准是什么?

文章:阅读,点赞,评论,收藏

实现思路

1.频道列表远程接口准备

计算完成新热数据后,需要给每个频道缓存一份数据,所以需要查询所有频道信息

① 在heima-leadnews-feign-api定义远程接口

package com.heima.apis.wemedia;import com.heima.model.common.dtos.ResponseResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;@FeignClient("leadnews-wemedia")
public interface IWemediaClient {@GetMapping("/api/v1/channel/list")public ResponseResult getChannels();
}

heima-leadnews-wemedia端提供接口

package com.heima.wemedia.feign;import com.heima.apis.wemedia.IWemediaClient;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.wemedia.service.WmChannelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class WemediaClient implements IWemediaClient {@Autowiredprivate WmChannelService wmChannelService;@GetMapping("/api/v1/channel/list")@Overridepublic ResponseResult getChannels() {return wmChannelService.findAll();}
}

 在ApArticleMapper.xml新增方法

<select id="findArticleListByLast5days" resultMap="resultMap">SELECTaa.*FROM`ap_article` aaLEFT JOIN ap_article_config aac ON aa.id = aac.article_id<where>and aac.is_delete != 1and aac.is_down != 1<if test="dayParam != null">and aa.publish_time <![CDATA[>=]]> #{dayParam}</if></where>
</select>

 修改ApArticleMapper类

package com.heima.article.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.pojos.ApArticle;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.util.Date;
import java.util.List;@Mapper
public interface ApArticleMapper extends BaseMapper<ApArticle> {/*** 加载文章列表* @param dto* @param type  1  加载更多   2记载最新* @return*/public List<ApArticle> loadArticleList(ArticleHomeDto dto,Short type);public List<ApArticle> findArticleListByLast5days(@Param("dayParam") Date dayParam);
}

2.热文章业务层

定义业务层接口  

package com.heima.article.service;public interface HotArticleService {/*** 计算热点文章*/public void computeHotArticle();
}

 修改ArticleConstans

package com.heima.common.constants;public class ArticleConstants {public static final Short LOADTYPE_LOAD_MORE = 1;public static final Short LOADTYPE_LOAD_NEW = 2;public static final String DEFAULT_TAG = "__all__";public static final String ARTICLE_ES_SYNC_TOPIC = "article.es.sync.topic";public static final Integer HOT_ARTICLE_LIKE_WEIGHT = 3;public static final Integer HOT_ARTICLE_COMMENT_WEIGHT = 5;public static final Integer HOT_ARTICLE_COLLECTION_WEIGHT = 8;public static final String HOT_ARTICLE_FIRST_PAGE = "hot_article_first_page_";
}

 创建一个vo接收计算分值后的对象

package com.heima.model.article.vos;import com.heima.model.article.pojos.ApArticle;
import lombok.Data;@Data
public class HotArticleVo extends ApArticle {/*** 文章分值*/private Integer score;
}

 业务层实现类

package com.heima.article.service.impl;import com.alibaba.fastjson.JSON;
import com.heima.apis.wemedia.IWemediaClient;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.HotArticleService;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.redis.CacheService;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.wemedia.pojos.WmChannel;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;@Service
@Slf4j
@Transactional
public class HotArticleServiceImpl implements HotArticleService {@Autowiredprivate ApArticleMapper apArticleMapper;/*** 计算热点文章*/@Overridepublic void computeHotArticle() {//1.查询前5天的文章数据Date dateParam = DateTime.now().minusDays(50).toDate();List<ApArticle> apArticleList = apArticleMapper.findArticleListByLast5days(dateParam);//2.计算文章的分值List<HotArticleVo> hotArticleVoList = computeHotArticle(apArticleList);//3.为每个频道缓存30条分值较高的文章cacheTagToRedis(hotArticleVoList);}@Autowiredprivate IWemediaClient wemediaClient;@Autowiredprivate CacheService cacheService;/*** 为每个频道缓存30条分值较高的文章* @param hotArticleVoList*/private void cacheTagToRedis(List<HotArticleVo> hotArticleVoList) {//每个频道缓存30条分值较高的文章ResponseResult responseResult = wemediaClient.getChannels();if(responseResult.getCode().equals(200)){String channelJson = JSON.toJSONString(responseResult.getData());List<WmChannel> wmChannels = JSON.parseArray(channelJson, WmChannel.class);//检索出每个频道的文章if(wmChannels != null && wmChannels.size() > 0){for (WmChannel wmChannel : wmChannels) {List<HotArticleVo> hotArticleVos = hotArticleVoList.stream().filter(x -> x.getChannelId().equals(wmChannel.getId())).collect(Collectors.toList());//给文章进行排序,取30条分值较高的文章存入redis  key:频道id   value:30条分值较高的文章sortAndCache(hotArticleVos, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + wmChannel.getId());}}}//设置推荐数据//给文章进行排序,取30条分值较高的文章存入redis  key:频道id   value:30条分值较高的文章sortAndCache(hotArticleVoList, ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ArticleConstants.DEFAULT_TAG);}/*** 排序并且缓存数据* @param hotArticleVos* @param key*/private void sortAndCache(List<HotArticleVo> hotArticleVos, String key) {hotArticleVos = hotArticleVos.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());if (hotArticleVos.size() > 30) {hotArticleVos = hotArticleVos.subList(0, 30);}cacheService.set(key, JSON.toJSONString(hotArticleVos));}/*** 计算文章分值* @param apArticleList* @return*/private List<HotArticleVo> computeHotArticle(List<ApArticle> apArticleList) {List<HotArticleVo> hotArticleVoList = new ArrayList<>();if(apArticleList != null && apArticleList.size() > 0){for (ApArticle apArticle : apArticleList) {HotArticleVo hot = new HotArticleVo();BeanUtils.copyProperties(apArticle,hot);Integer score = computeScore(apArticle);hot.setScore(score);hotArticleVoList.add(hot);}}return hotArticleVoList;}/*** 计算文章的具体分值* @param apArticle* @return*/private Integer computeScore(ApArticle apArticle) {Integer scere = 0;if(apArticle.getLikes() != null){scere += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;}if(apArticle.getViews() != null){scere += apArticle.getViews();}if(apArticle.getComment() != null){scere += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;}if(apArticle.getCollection() != null){scere += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;}return scere;}
}

 在ArticleApplication的引导类中添加以下注解

@EnableFeignClients(basePackages = "com.heima.apis")

 现在数据库中准备点数据

package com.heima.article.service.impl;import com.heima.article.ArticleApplication;
import com.heima.article.service.HotArticleService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;@SpringBootTest(classes = ArticleApplication.class)
@RunWith(SpringRunner.class)
public class HotArticleServiceImplTest {@Autowiredprivate HotArticleService hotArticleService;@Testpublic void computeHotArticle() {hotArticleService.computeHotArticle();}
}

3.xxl-job定时计算-步骤

①:在heima-leadnews-article中的pom文件中新增依赖  

<!--xxl-job-->
<dependency><groupId>com.xuxueli</groupId><artifactId>xxl-job-core</artifactId><version>2.3.0</version>
</dependency>

② 在xxl-job-admin中新建执行器和任务

新建执行器:leadnews-hot-article-executor

 

新建任务:路由策略为轮询,Cron表达式:0 0 2 * * ?  

 

③ leadnews-article中集成xxl-job

XxlJobConfig

package com.heima.article.config;import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** xxl-job config** @author xuxueli 2017-04-28*/
@Configuration
public class XxlJobConfig {private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);@Value("${xxl.job.admin.addresses}")private String adminAddresses;@Value("${xxl.job.executor.appname}")private String appname;@Value("${xxl.job.executor.port}")private int port;@Beanpublic XxlJobSpringExecutor xxlJobExecutor() {logger.info(">>>>>>>>>>> xxl-job config init.");XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();xxlJobSpringExecutor.setAdminAddresses(adminAddresses);xxlJobSpringExecutor.setAppname(appname);xxlJobSpringExecutor.setPort(port);return xxlJobSpringExecutor;}}

 在nacos配置新增配置

xxl:job:admin:addresses: http://192.168.200.130:8888/xxl-job-adminexecutor:appname: leadnews-hot-article-executorport: 9999

④:在article微服务中新建任务类

package com.heima.article.job;import com.heima.article.service.HotArticleService;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
@Slf4j
public class ComputeHotArticleJob {@Autowiredprivate HotArticleService hotArticleService;@XxlJob("computeHotArticleJob")public void handle(){log.info("热文章分值计算调度任务开始执行...");hotArticleService.computeHotArticle();log.info("热文章分值计算调度任务结束...");}
}

四.查询文章接口改造

1. 思路分析

 

2. 功能实现

2.1 在ApArticleService中新增方法

/*** 加载文章列表* @param dto* @param type  1 加载更多   2 加载最新* @param firstPage  true  是首页  flase 非首页* @return*/
public ResponseResult load2(ArticleHomeDto dto,Short type,boolean firstPage);

 实现方法

/*** 加载文章列表* @param dto* @param type      1 加载更多   2 加载最新* @param firstPage true  是首页  flase 非首页* @return*/
@Override
public ResponseResult load2(ArticleHomeDto dto, Short type, boolean firstPage) {if(firstPage){String jsonStr = cacheService.get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());if(StringUtils.isNotBlank(jsonStr)){List<HotArticleVo> hotArticleVoList = JSON.parseArray(jsonStr, HotArticleVo.class);ResponseResult responseResult = ResponseResult.okResult(hotArticleVoList);return responseResult;}}return load(type,dto);
}

2.2 修改控制器

/*** 加载首页* @param dto* @return*/
@PostMapping("/load")
public ResponseResult load(@RequestBody ArticleHomeDto dto){//        return apArticleService.load(dto, ArticleConstants.LOADTYPE_LOAD_MORE);return apArticleService.load2(dto, ArticleConstants.LOADTYPE_LOAD_MORE,true);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/578851.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

每周一算法:邻值查找

给定一个长度为 n n n的序列 A A A&#xff0c; A A A中的数各不相同。 对于 A A A 中的每一个数 A i A_i Ai​&#xff0c;求&#xff1a; m i n 1 ≤ j < i ∣ A i − A j ∣ min_{1≤j<i}|A_i−A_j| min1≤j<i​∣Ai​−Aj​∣&#xff0c;以及令上式取到最小值的…

40G多模光模块QSFP-40G-SR4优势及应用领域介绍

QSFP-40G-SR4光模块是一种常用的光纤传输解决方案。传输速率40G&#xff0c;SR代表短距离多模光纤&#xff08;Short Range Multimode Fiber&#xff09;&#xff0c;4表示有四个光纤通道。这种光模块采用MPO/MTP多模光纤连接器来实现高速传输&#xff0c;传输距离可以达到300米…

WU反走样算法

WU反走样算法 由离散量表示连续量而引起的失真称为走样&#xff0c;用于减轻走样现象的技术成为反走样&#xff0c;游戏中称为抗锯齿。走样是连续图形离散为想想点后引起的失真&#xff0c;真实像素面积不为 零。走样是光栅扫描显示器的一种固有现象&#xff0c;只能减轻&…

【Echarts】使用echarts和echarts-wordcloud生成词云图

一、下载echarts和echarts-wordcloud 地址&#xff1a;https://download.csdn.net/download/qq_25285531/88663006 可直接下载放在项目中使用 二、词云数据 词云数据是对象的格式&#xff0c;可以从后端获取&#xff0c;这里以下面数据为例 {"visualMap": 199,&…

基于多反应堆的高并发服务器【C/C++/Reactor】(中)EventLoop初始化

这个Dispatcher是一个事件分发模型&#xff0c;通过这个模型,就能够检测对应的文件描述符的事件的时候,可以使用epoll/poll/select,前面说过三选一。另外不管是哪一个底层的检测模型,它们都需要使用一个数据块,这个数据块就叫做DispatcherData。除此之外,还有另外一个部分,因为…

vue3 vue3-print-nb 实现打印功能

vue3 vue3-print-nb 实现打印功能 效果 vue3-print-nb 文档 安装 pnpm add vue3-print-nbtypescript 中 xx.d.ts declare module "vue3-print-nb";配置 全局配置 src/main.ts import print from "vue3-print-nb";const app createApp(App); app.us…

Leetcode2928. 给小朋友们分糖果 I

Every day a Leetcode 题目来源&#xff1a;2928. 给小朋友们分糖果 I 解法1&#xff1a;暴力 枚举 3 位小朋友的糖果数&#xff0c;范围为 [0, limit]&#xff0c;分别记为 i、j、k。 当满足 i j k n 时&#xff0c;答案 1。 代码&#xff1a; /** lc appleetcode.c…

引领制造业迈向智能化新时代,SIA上海智能工厂展助力行业蓬勃发展

当今制造业迎来了数字化革命的潮流&#xff0c;其中智能工厂作为其中的一个关键角色&#xff0c;正在推动着制造业的转型和升级。智能工厂不仅仅是一个集成了先进技术的制造厂房&#xff0c;更是数字化时代制造业发展的崭新范本。本文将探讨智能工厂在现代制造业中的重要作用。…

SecuSphere:一款功能强大的一站式高效DevSecOps安全框架

关于SecuSphere SecuSphere是一款功能强大的一站式高效DevSecOps解决方案&#xff0c;DevSecOps作为一个经过针对性设计的集中式平台&#xff0c;可以帮助广大研究人员管理和优化漏洞管理、CI/CD管道集成、安全评估和DevSecOps实践。 SecuSphere是一个功能全面的DevSecOps平台…

RK3568驱动指南|第八篇 设备树插件-第83章 设备树插件驱动分析实验

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

IDEA 控制台中文出现乱码问题解决

一、问题概述 请看下图 二、问题分析 IDEA控制台输出乱码一般会有三种来源&#xff1a; ① IDEA本身编码错误 ② Tomcat日志输出编码错误 ③ 项目本身原因。 终极原因&#xff1a;IDEA编码和Tomcat编码不一致&#xff0c;统一设置为UTF-8即可。 三、解决思路 修改…

Superset二次开发之环境部署(Windows版)

本地环境版本介绍: 编号 名称 版本 1supersetv3.0.0rc32Pythonv3.10.123Nodev16.20.24npmv8.19.45Anacondav3 1.下载源码 #下载源码 git clone https://github.com/apache/superset.git ##速度慢可替换为 https://gitclone.com/github.com/apache/superset.git #进入项目目录…

在Go中解析HTTP请求中的表单数据

想象一下&#xff0c;你收到了一封神秘的信件&#xff0c;信上写着&#xff1a;“在HTTP请求的表单数据中&#xff0c;隐藏着一座宝藏&#xff0c;等待勇敢的冒险者前来寻找。” 你知道&#xff0c;这封信正是冲着你来的&#xff01;今天&#xff0c;我们将使用Go语言作为我们的…

VMware17Pro虚拟机安装Linux CentOS 7.9(龙蜥)教程(超详细)

目录 1. 前言2. 下载所需文件3. 安装VMware3.1 安装3.2 启动并查看版本信息3.3 虚拟机默认位置配置 4. 安装Linux4.1 新建虚拟机4.2 安装操作系统4.2.1 选择 ISO 映像文件4.2.2 开启虚拟机4.2.3 选择语言4.2.4 软件选择4.2.5 禁用KDUMP4.2.6 安装位置配置4.2.7 网络和主机名配置…

web自动化测试的智能革命:AI如何推动软件质量保证的未来

首先这个标题不是我取的&#xff0c;是我喂了关键字让AI给取的&#xff0c;果然非常的标题党&#xff0c;让人印象深刻&#xff0c;另外题图也是AI自动生成的。 先简单回顾一下web自动化测试的一些发展阶段 QTP时代 很多年前QTP横空出世的时候&#xff0c;没有人会怀疑这种工…

java判断时间是否为节假日(或指定的日期),是的话返回true,否返回false

这个方法属实有点老套&#xff0c;先定义set&#xff0c;需要手动输入要判断的时间才行。 这个代码&#xff0c;就是输入一个日期&#xff0c;来判断这个日期是否为你指定的日期。如果是的话&#xff0c;返回true&#xff0c;否的话返回false。 代码&#xff1a; package Lx…

软考高级考完了,怎么评职称?

每年考试结束后&#xff0c;总有朋友问我&#xff0c;考完后怎么才能评上职称呢&#xff1f;获得证书就意味着获得了职称吗&#xff1f;让我们一起来看看具体情况吧&#xff01; 01\职称获取途径 获得职称可以通过认定、评审、国家统一考试(以考代评)、职业资格对应等多种方式…

轻松设置CentOS IP地址的最终指南:详细的分步说明

轻松设置CentOS IP地址的最终指南 一、引言二、准备工作三、手动设置IP地址四、自动分配IP地址(DHCP)五、使用网络管理工具设置IP地址5.1、使用nmtui工具进行图形化设置5.2、使用nmcli命令行工具进行设置 六、常见问题和解决方案七、总结 一、引言 CentOS操作系统是一种基于Li…

LLM(八)| Gemini语言能力深度观察

论文地址&#xff1a;https://simg.baai.ac.cn/paperfile/fc2138ce-cadb-4a36-b9f7-c4000dea3369.pdf 谷歌最近发布的Gemini系列模型是第一个在各种任务与OpenAI GPT系列相媲美的模型。在本文中&#xff0c;作者对Gemini的语言能力做了深入的探索&#xff0c;做出了两方面的贡献…