【黑马头条】-day10热点文章定时计算-xxl-job


文章目录

  • 1 今日内容
    • 1.1 需求分析
    • 1.2 解决方案
    • 1.3 定时计算
    • 1.4 定时任务方案对比
  • 2 分布式任务调度
  • 3 xxl-job
    • 3.1 简介
    • 3.2 环境搭建
      • 3.2.1 配置maven
      • 3.2.2 源码说明
    • 3.3 配置部署调度中心
      • 3.3.1 运行sql脚本
      • 3.3.2 修改配置application.properties
      • 3.3.3 启动引导类
    • 3.4 docker配置xxl-job调度中心
      • 3.4.1 拉取mysql镜像
      • 3.4.2 创建mysql容器,初始化xxl-job的SQL脚本
      • 3.4.3 连接mysql57
      • 3.4.4 运行sql脚本
      • 3.4.5 拉取调度中心镜像
      • 3.4.6 创建xxl-job容器
      • 3.4.7 访问调度中心
  • 4 xxl-job入门案例
    • 4.1 在任务中心中配置任务设置
    • 4.2 创建xxl-job-demo
      • 4.2.1 导入依赖
      • 4.2.2 创建application.yaml
      • 4.2.3 新建配置类XxlJobConfig
      • 4.2.4 创建任务
      • 4.2.5 创建启动类
      • 4.2.6 启动启动类和启动任务
    • 4.3 任务详解
      • 4.3.1 执行器管理
      • 4.3.2 任务管理
        • 4.3.2.1 基础配置
        • 4.3.2.2 调度配置
        • 4.3.2.3 任务配置
        • 4.3.2.4 高级配置
    • 4.4 路由策略案例
      • 4.4.1 轮询
      • 4.4.2 分片广播
        • 4.4.2.1 新增分片执行器
        • 4.4.2.2 创建分片任务
        • 4.4.2.3 修改application.yaml
        • 4.4.2.4 为工作任务添加新的任务
        • 4.4.2.5 启动两个实例并执行一次任务
  • 5 热点文章定时计算
    • 5.1 需求分析
    • 5.2 实现思路
    • 5.3 热点文章具体实现
      • 5.3.1 查询前五天文章
        • 5.3.1.1 创建查询前五天文章的mapper
        • 5.3.1.2 创建热点文章的Service
      • 5.3.2 计算文章分值
        • 5.3.2.1 创建HotArticleVo
        • 5.3.2.2 创建文章的点赞收藏关注的权重常量
        • 5.3.2.3 完善实现类
      • 5.3.3 为每个频道缓存30条分值较高的文章
        • 5.3.3.1 添加feign远程调用接口
        • 5.3.3.2 实现远程接口
        • 5.3.3.3 添加feign的注解类扫描
        • 5.3.3.4 完善实现类
      • 5.3.4 测试
    • 5.4 定时计算具体实现
      • 5.4.1 创建执行器
      • 5.4.2 创建任务
      • 5.4.3 文章微服务集成xxl-job
        • 5.4.3.1 导入依赖
        • 5.4.3.2 添加配置类
        • 5.4.3.3 添加nacos配置
      • 5.4.4 创建具体任务
      • 5.4.5 测试
  • 6 查询文章改造
    • 6.1 修改Service
    • 6.2 修改controller
  • 7 综合测试


1 今日内容

1.1 需求分析

在这里插入图片描述

1.2 解决方案

在这里插入图片描述

1.3 定时计算

在这里插入图片描述

1.4 定时任务方案对比

在这里插入图片描述

2 分布式任务调度

在这里插入图片描述

3 xxl-job

3.1 简介

在这里插入图片描述

3.2 环境搭建

在这里插入图片描述

将xxl-job拷贝到与heima-leadnews同级的目录下

在这里插入图片描述

在idea中打开

在这里插入图片描述

3.2.1 配置maven

根据自己的配置进行修改

在这里插入图片描述

3.2.2 源码说明

在这里插入图片描述

3.3 配置部署调度中心

在这里插入图片描述

在这里插入图片描述

3.3.1 运行sql脚本

在这里插入图片描述

3.3.2 修改配置application.properties

修改src/main/resources/application.properties

### xxl-job, datasource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123sjbsjb
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

3.3.3 启动引导类

启动引导类com.xxl.job.admin.XxlJobAdminApplication类

21:00:58.444 logback [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path '/xxl-job-admin'

访问端口8080

访问url http://localhost:8080/xxl-job-admin

在这里插入图片描述

进入任务调度中心

在这里插入图片描述

3.4 docker配置xxl-job调度中心

在这里插入图片描述

3.4.1 拉取mysql镜像

docker pull mysql:5.7

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

docker run -p 3306:3306 --name mysql57 \
-v /opt/mysql/conf:/etc/mysql \
-v /opt/mysql/logs:/var/log/mysql \
-v /opt/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7

3.4.3 连接mysql57

在这里插入图片描述

3.4.4 运行sql脚本

运行tables_xxl_job.sql

在这里插入图片描述

3.4.5 拉取调度中心镜像

docker pull xuxueli/xxl-job-admin:2.3.0

3.4.6 创建xxl-job容器

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

3.4.7 访问调度中心

http://192.168.204.129:8888/xxl-job-admin

账户admin密码123456

在这里插入图片描述

4 xxl-job入门案例

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.1 在任务中心中配置任务设置

在这里插入图片描述

4.2 创建xxl-job-demo

4.2.1 导入依赖

<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>

4.2.2 创建application.yaml

server:
  port: 8881

xxl:
  job:
    admin:
      addresses: http://192.168.204.129:8888/xxl-job-admin
    executor:
      appname: xxl-job-executor-sample
      port: 9999

4.2.3 新建配置类XxlJobConfig

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;
 
 
    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        logger.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
        xxlJobSpringExecutor.setAppname(appname);
        xxlJobSpringExecutor.setPort(port);
        return xxlJobSpringExecutor;
    }
}

4.2.4 创建任务

创建com.heima.xxljob.job.HelloJob

@Component
public class HelloJob {

    @XxlJob("demoJobHandler")
    public void helloJob(){
        System.out.println("简单任务执行了。。。。");

    }
}

4.2.5 创建启动类

创建com.heima.xxljob.XxlJobApplication

@SpringBootApplication
public class XxlJobApplication {
    public static void main(String[] args) {
        SpringApplication.run(XxlJobApplication.class, args);
    }
}

4.2.6 启动启动类和启动任务

在这里插入图片描述

并没有打印出来我们的简单任务,是因为我们的任务还在STOP阶段

在这里插入图片描述

原神,启动!

在这里插入图片描述

成功打印

在这里插入图片描述

4.3 任务详解

4.3.1 执行器管理

  • 执行器:
    • 任务的绑定的执行器,任务触发调度时将会*自动发现注册成功的执行器, 实现任务自动发现功能;
  • 另一方面也可以方便的进行任务分组。每个任务必须绑定一个执行器.

在这里插入图片描述

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

  • AppName:是每个执行器集群的唯一标示AppName, 执行器会周期性以AppName为对象进行自动注册。可通过该配置自动发现注册成功的执行器, 供任务调度时使用;

  • 名称:执行器名称, 因为AppName限制字母数字等组成,可读性不强, 名称为了提高执行器的可读性;如分片广播执行器

  • 排序:执行器的排序, 系统中需要执行器的地方,如任务新增, 将会按照该排序读取可用的执行器列表;

  • 注册方式:调度中心获取执行器地址的方式;

  • 机器地址:注册方式为"手动录入"时有效,支持人工维护执行器的地址信息;

4.3.2 任务管理

在这里插入图片描述

4.3.2.1 基础配置
  • 执行器:每个任务必须绑定一个执行器,方便给任务进行分组;
  • 任务描述:任务的描述信息,便于任务管理;
  • 负责人:任务的负责人;
  • 报警邮件:
    • 任务调度失败时邮件通知的邮箱地址,支持配置,配置多个邮箱地址时用逗号分割
4.3.2.2 调度配置

在这里插入图片描述

调度配置

  • 调度类型:
    • 无:该类型不会主动触发调度;
    • CRON:该类型将会通过CRON,触发任务调度;
    • 固定速度:该类型将会以固定速度,触发任务调度;按照固定的间隔时间,周期性触发,比如一秒一次。
4.3.2.3 任务配置

在这里插入图片描述

  • 运行模式:BEAN模式(Spring管理):任务以JobHandler方式维护在执行器端;需要结合 “JobHandler” 属性匹配执行器中任务;

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

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

4.3.2.4 高级配置

在这里插入图片描述

路由策略

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

  • FIRST(第一个):固定选择第一个机器;
  • LAST(最后一个):固定选择最后一个机器;
  • ROUND(轮询)
  • RANDOM(随机):随机选择在线的机器;
  • CONSISTENT_HASH(一致性HASH):每个任务按照Hash算法固定选择某一台机器,且所有任务均匀散列在不同机器上。
  • LEAST_FREQUENTLY_USED(最不经常使用):使用频率最低的机器优先被选举;
  • LEAST_RECENTLY_USED(最近最久未使用):最久未使用的机器优先被选举;
  • FAILOVER(故障转移):按照顺序依次进行心跳检测,第一个心跳检测成功的机器选定为目标执行器并发起调度;
  • BUSYOVER(忙碌转移):按照顺序依次进行空闲检测,第一个空闲检测成功的机器选定为目标执行器并发起调度;
  • SHARDING_BROADCAST(分片广播):广播触发对应集群中所有机器执行一次任务,同时系统自动传递分片参数;可根据分片参数开发分片任务;

阻塞处理策略

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

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

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

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

4.4 路由策略案例

4.4.1 轮询

将当前任务转为轮询

在这里插入图片描述

修改application.yaml

server:
  port: ${port:8881}

xxl:
  job:
    admin:
      addresses: http://192.168.204.129:8888/xxl-job-admin
    executor:
      appname: xxl-job-executor-sample
      port: ${executor.port:9999}

设置另外一个实例

在这里插入图片描述

修改工作任务以区分两个实例

@Component
public class HelloJob {
    @Value("${server.port}")
    private String port;

    @XxlJob("demoJobHandler")
    public void helloJob(){
        //获取当前时间yy-MM-dd HH:mm:ss
        Date date = new Date();
        System.out.println("hello xxl-job,当前时间:"+date+",端口号:"+port);
    }
}

启动两个实例,并启动任务,显示交叉进行轮询访问

在这里插入图片描述

4.4.2 分片广播

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.4.2.1 新增分片执行器

在这里插入图片描述

4.4.2.2 创建分片任务

在这里插入图片描述

在这里插入图片描述

4.4.2.3 修改application.yaml
server:
  port: ${port:8881}

xxl:
  job:
    admin:
      addresses: http://192.168.204.129:8888/xxl-job-admin
    executor:
      appname: xxl-job-sharding-executor
      port: ${executor.port:9999}
4.4.2.4 为工作任务添加新的任务
@XxlJob("shardingJobHandler")
public void shardingJobHandler(){
    //获取分片参数
    int shardIndex = XxlJobHelper.getShardIndex();
    int shardTotal = XxlJobHelper.getShardTotal();

    //业务逻辑
    List<Integer>list = getList();
    for(Integer i:list){
        if(i%shardTotal==shardIndex){
            System.out.println(new Date()+":当前第"+shardIndex+"分片,总分片数:"+shardTotal+",当前任务项:"+i);
        }
    }
}

public List<Integer> getList(){
    List<Integer> list = new ArrayList<>();
    for(int i=0;i<10000;i++){
        list.add(i);
    }
    return list;
}
4.4.2.5 启动两个实例并执行一次任务

在这里插入图片描述

发现基本执行时间一致,有效缓解集群压力

5 热点文章定时计算

5.1 需求分析

在这里插入图片描述

在这里插入图片描述

5.2 实现思路

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

5.3 热点文章具体实现

5.3.1 查询前五天文章

5.3.1.1 创建查询前五天文章的mapper

在com.heima.article.mapper.ApArticleMapper中创建新方法

public List<ApArticle> findArticleListByLast5days(@Param("dayParam") Date dayParam);

转到其sql语句

<select id="findArticleListByLast5days" resultMap="resultMap">
    SELECT
    aa.*
    FROM
    `ap_article` aa
    LEFT JOIN ap_article_config aac ON aa.id = aac.article_id
    <where>
        and aac.is_delete != 1
        and aac.is_down != 1
        <if test="dayParam != null">
            and aa.publish_time <![CDATA[>=]]> #{dayParam}
        </if>
    </where>
</select>
5.3.1.2 创建热点文章的Service

在heima-leadnews-article中创建com.heima.article.service.HotArticleService接口

public interface HotArticleService {
    /**
     * 计算热点文章
     */
    public void computeHotArticle();
}

创建接口的实现类com.heima.article.service.impl.HotArticleServiceImpl类

@Service
@Slf4j
@Transactional
public class HotArticleServiceImpl implements HotArticleService {
    @Autowired
    private ApArticleMapper apArticleMapper;
    @Override
    public void computeHotArticle() {
        //1. 查询前五天的文章数据
        Date date = DateTime.now().minusDays(5).toDate();
        List<ApArticle> articleListByLast5days = apArticleMapper.findArticleListByLast5days(date);
        //2. 计算文章的分值

        //3. 为每个频道缓存30条分值较高的文章

    }
}

5.3.2 计算文章分值

5.3.2.1 创建HotArticleVo

因为最后计算的文章中并不包含数值字段,因此需要添加一个vo字段

在heima-leadnews-model中创建com.heima.model.article.vos.HotArticleVo类

@Data
public class HotArticleVo extends ApArticle {
    /**
     * 文章分值
     */
    private Integer score;
}
5.3.2.2 创建文章的点赞收藏关注的权重常量

在heima-leadnews-common中的com.heima.common.constants.ArticleConstants添加新的常量

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_INDEX_TOPIC = "article.es.syn.topic";

    public static final Integer HOT_ARTICLE_VIEW_WEIGHT = 1;
    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_";
}
5.3.2.3 完善实现类
@Service
@Slf4j
@Transactional
public class HotArticleServiceImpl implements HotArticleService {
    @Autowired
    private ApArticleMapper apArticleMapper;
    @Override
    public void computeHotArticle() {
        //1. 查询前五天的文章数据
        Date date = DateTime.now().minusDays(5).toDate();
        List<ApArticle> articleListByLast5days = apArticleMapper.findArticleListByLast5days(date);
        //2. 计算文章的分值
        List<HotArticleVo> hotArticleVos = computeHot5dayArticle(articleListByLast5days);

        //3. 为每个频道缓存30条分值较高的文章

    }

    /**
     * 计算热点文章
     * @param articleListByLast5days
     */
    private List<HotArticleVo> computeHot5dayArticle(List<ApArticle> articleListByLast5days) {
        List<HotArticleVo> hotArticleVoList = new ArrayList<>();
        if (articleListByLast5days != null && articleListByLast5days.size() > 0) {
            for (ApArticle apArticle : articleListByLast5days) {
                HotArticleVo hotArticleVo = new HotArticleVo();
                BeanUtils.copyProperties(apArticle, hotArticleVo);
                Integer score = computeScore(apArticle);
                hotArticleVo.setScore(score);
                hotArticleVoList.add(hotArticleVo);
            }
        }
        return hotArticleVoList;
    }

    /**
     * 计算文章的分值
     * @param apArticle
     * @return
     */
    private Integer computeScore(ApArticle apArticle) {
        Integer score = 0;
        //1. 文章的阅读数
        if(apArticle.getViews() != null){
            score += apArticle.getViews()* ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        //2. 文章的点赞数
        if(apArticle.getLikes() != null){
            score += apArticle.getLikes()* ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        //3. 文章的评论数
        if(apArticle.getComment() != null){
            score += apArticle.getComment()* ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        //4. 文章的收藏数
        if(apArticle.getCollection() != null){
            score += apArticle.getCollection()* ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }
}

5.3.3 为每个频道缓存30条分值较高的文章

5.3.3.1 添加feign远程调用接口

因为频道信息是在自媒体中,所以需要进行远程调用

在heima-leadnews-apis中创建com.heima.apis.wemedia.IWemediaClient接口

@FeignClient(value = "leadnews-wemedia")
public interface IWemediaClient {
    @GetMapping("/api/v1/channel/list")
    public ResponseResult getChannels();
}
5.3.3.2 实现远程接口

在heima-leadnews-wemedia中创建com.heima.wemedia.feign.WemediaClient类

@RestController
public class WemediaClient implements IWemediaClient {
    @Autowired
    private WmChannelService wmChannelService;

    @GetMapping("/api/v1/channel/list")
    @Override
    public ResponseResult getChannels() {
        return wmChannelService.findList();
    }
}
5.3.3.3 添加feign的注解类扫描

因为文章微服务要调用feign远程接口获取所有频道列表,所以需要添加Feign扫描注解

为文章微服务的启动类添加@EnableFeignClients(basePackages = "com.heima.apis")

@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.heima.article.mapper")
@EnableAsync
@EnableFeignClients(basePackages = "com.heima.apis")
public class ArticleApplication {
5.3.3.4 完善实现类
@Service
@Slf4j
@Transactional
public class HotArticleServiceImpl implements HotArticleService {
    @Autowired
    private ApArticleMapper apArticleMapper;
    @Override
    public void computeHotArticle() {
        //1. 查询前五天的文章数据
        Date date = DateTime.now().minusDays(20).toDate();
        List<ApArticle> articleListByLast5days = apArticleMapper.findArticleListByLast5days(date);
        //2. 计算文章的分值
        List<HotArticleVo> hotArticleVos = computeHot5dayArticle(articleListByLast5days);

        //3. 为每个频道缓存30条分值较高的文章
        CacheChannelHotArticleToRedis(hotArticleVos);

    }

    @Autowired
    private IWemediaClient iWemediaClient;
    @Autowired
    private CacheService cacheService;
    /**
     * 为每个频道缓存30条分值较高的文章
     * @param hotArticleVos
     */
    private void CacheChannelHotArticleToRedis(List<HotArticleVo> hotArticleVos) {
        //1. 查询所有的频道
        ResponseResult response = iWemediaClient.getChannels();
        if(response.getCode().equals(200)){
            String channelJSON = JSON.toJSONString(response.getData());
            List<WmChannel> wmChannels = JSON.parseArray(channelJSON, WmChannel.class);
            //2. 检索出每个频道的文章
            if(wmChannels != null && wmChannels.size() > 0){
                for (WmChannel wmChannel : wmChannels) {
                    List<HotArticleVo> sortedArticleOfChannel = hotArticleVos.stream()
                            .filter(hotArticleVo -> hotArticleVo.getChannelId().equals(wmChannel.getId()))
                            .sorted((a, b) -> b.getScore() - a.getScore()).limit(30)
                            .collect(Collectors.toList());
                    //3. 缓存到redis
                    cacheService.set(ArticleConstants.HOT_ARTICLE_FIRST_PAGE+wmChannel.getId(),JSON.toJSONString(sortedArticleOfChannel));
                }

            }
        }
        //4. 设置推荐文章
        List<HotArticleVo> hotArticleVoList = hotArticleVos.stream()
                .sorted((a, b) -> b.getScore() - a.getScore())
                .limit(30)
                .collect(Collectors.toList());
        cacheService.set(ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ArticleConstants.DEFAULT_TAG,JSON.toJSONString(hotArticleVoList));
    }

    /**
     * 计算热点文章
     * @param articleListByLast5days
     */
    private List<HotArticleVo> computeHot5dayArticle(List<ApArticle> articleListByLast5days) {
        List<HotArticleVo> hotArticleVoList = new ArrayList<>();
        if (articleListByLast5days != null && articleListByLast5days.size() > 0) {
            for (ApArticle apArticle : articleListByLast5days) {
                HotArticleVo hotArticleVo = new HotArticleVo();
                BeanUtils.copyProperties(apArticle, hotArticleVo);
                Integer score = computeScore(apArticle);
                hotArticleVo.setScore(score);
                hotArticleVoList.add(hotArticleVo);
            }
        }
        return hotArticleVoList;
    }

    /**
     * 计算文章的分值
     * @param apArticle
     * @return
     */
    private Integer computeScore(ApArticle apArticle) {
        Integer score = 0;
        //1. 文章的阅读数
        if(apArticle.getViews() != null){
            score += apArticle.getViews()* ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        //2. 文章的点赞数
        if(apArticle.getLikes() != null){
            score += apArticle.getLikes()* ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        //3. 文章的评论数
        if(apArticle.getComment() != null){
            score += apArticle.getComment()* ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        //4. 文章的收藏数
        if(apArticle.getCollection() != null){
            score += apArticle.getCollection()* ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }
}

5.3.4 测试

在HotArticleServiceImpl上alt+enter创建测试类

在这里插入图片描述

@SpringBootTest(classes = ArticleApplication.class)
@RunWith(SpringRunner.class)
class HotArticleServiceImplTest {
    @Autowired
    private HotArticleService hotArticleService;

    @Test
    void computeHotArticle() {
        hotArticleService.computeHotArticle();
    }
}

打开redis查看

在这里插入图片描述

已经有写入redis了

5.4 定时计算具体实现

5.4.1 创建执行器

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

在这里插入图片描述

5.4.2 创建任务

在这里插入图片描述

5.4.3 文章微服务集成xxl-job

5.4.3.1 导入依赖
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>2.3.0</version>
</dependency>
5.4.3.2 添加配置类

添加配置类com.heima.article.config.XxlJobConfig

@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;
 
 
    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        logger.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
        xxlJobSpringExecutor.setAppname(appname);
        xxlJobSpringExecutor.setPort(port);
        return xxlJobSpringExecutor;
    }
}
5.4.3.3 添加nacos配置

在nacos中添加leadnews-arcticle的xxl-job的配置

修改执行器名称

xxl:
  job:
    admin:
      addresses: http://192.168.204.129:8888/xxl-job-admin
    executor:
      appname: leadnews-hot-article-executor
      port: 9999

在这里插入图片描述

5.4.4 创建具体任务

在heima-leadnews-article中创建com.heima.article.job.ComputeHotArticleJob类

@Component
@Slf4j
public class ComputeHotArticleJob {

    @Autowired
    private HotArticleService hotArticleService;

    @XxlJob("computeHotArticleJob")
    public void handle(){
        log.info("热文章分值计算调度任务开始执行...");
        hotArticleService.computeHotArticle();
        log.info("热文章分值计算调度任务结束...");

    }
}

@XxlJob中的值要与任务管理中的运行模式的名称一致

在这里插入图片描述

5.4.5 测试

启动后执行一次任务。正常显示

2024-04-18 16:46:16.099 [Thread-113] INFO  c.h.article.job.ComputeHotArticleJob - 热文章分值计算调度任务开始执行...
2024-04-18 16:46:16.696 [Thread-113] INFO  c.n.config.ChainedDynamicProperty - Flipping property: leadnews-wemedia.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2024-04-18 16:46:16.728 [Thread-113] INFO  c.n.loadbalancer.BaseLoadBalancer - Client: leadnews-wemedia instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=leadnews-wemedia,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2024-04-18 16:46:16.742 [Thread-113] INFO  c.n.l.DynamicServerListLoadBalancer - Using serverListUpdater PollingServerListUpdater
2024-04-18 16:46:16.751 [Thread-113] INFO  com.alibaba.nacos.client.naming - new ips(1) service: DEFAULT_GROUP@@leadnews-wemedia -> [{"instanceId":"192.168.204.1#51803#DEFAULT#DEFAULT_GROUP@@leadnews-wemedia","ip":"192.168.204.1","port":51803,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"DEFAULT","serviceName":"DEFAULT_GROUP@@leadnews-wemedia","metadata":{"preserved.register.source":"SPRING_CLOUD"},"ipDeleteTimeout":30000,"instanceHeartBeatTimeOut":15000,"instanceHeartBeatInterval":5000}]
2024-04-18 16:46:16.752 [Thread-113] INFO  com.alibaba.nacos.client.naming - current ips:(1) service: DEFAULT_GROUP@@leadnews-wemedia -> [{"instanceId":"192.168.204.1#51803#DEFAULT#DEFAULT_GROUP@@leadnews-wemedia","ip":"192.168.204.1","port":51803,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"DEFAULT","serviceName":"DEFAULT_GROUP@@leadnews-wemedia","metadata":{"preserved.register.source":"SPRING_CLOUD"},"ipDeleteTimeout":30000,"instanceHeartBeatTimeOut":15000,"instanceHeartBeatInterval":5000}]
2024-04-18 16:46:16.777 [Thread-113] INFO  c.n.config.ChainedDynamicProperty - Flipping property: leadnews-wemedia.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2024-04-18 16:46:16.779 [Thread-113] INFO  c.n.l.DynamicServerListLoadBalancer - DynamicServerListLoadBalancer for client leadnews-wemedia initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=leadnews-wemedia,current list of Servers=[192.168.204.1:51803],Load balancer stats=Zone stats: {unknown=[Zone:unknown;	Instance count:1;	Active connections count: 0;	Circuit breaker tripped count: 0;	Active connections per server: 0.0;]
},Server stats: [[Server:192.168.204.1:51803;	Zone:UNKNOWN;	Total Requests:0;	Successive connection failure:0;	Total blackout seconds:0;	Last connection made:Thu Jan 01 08:00:00 CST 1970;	First connection made: Thu Jan 01 08:00:00 CST 1970;	Active Connections:0;	total failure count in last (1000) msecs:0;	average resp time:0.0;	90 percentile resp time:0.0;	95 percentile resp time:0.0;	min resp time:0.0;	max resp time:0.0;	stddev resp time:0.0]
]}ServerList:com.alibaba.cloud.nacos.ribbon.NacosServerList@249de647
2024-04-18 16:46:17.659 [Thread-113] INFO  c.h.article.job.ComputeHotArticleJob - 热文章分值计算调度任务结束...

在这里插入图片描述

6 查询文章改造

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

6.1 修改Service

为com.heima.article.service.ApArticleService接口添加新方法

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

实现:

@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> hotArticleVos = JSON.parseArray(jsonStr, HotArticleVo.class);
            return ResponseResult.okResult(hotArticleVos);
        }
    }
    return load(dto, type);
}

6.2 修改controller

修改com.heima.article.controller.v1.ArticleHomeController#load方法

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

7 综合测试

小小的修改一下数据

在这里插入图片描述

启动微服务及nginx

在这里插入图片描述

查看首页

在这里插入图片描述

看看redis是否对的上

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

✌粤嵌—2024/4/18—旋转链表✌

代码实现&#xff1a; 方法一&#xff1a;在原链表中找到旋转之后的首尾&#xff0c;改变指向 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode* rotateRight(struct ListNode *head, int k) {i…

电脑怎么创建加密磁盘?方法很简单!

在电脑中创建加密磁盘&#xff0c;可以有效地保护电脑数据&#xff0c;避免数据泄露。那么&#xff0c;电脑怎么创建加密磁盘呢&#xff1f;下面我们就一起来了解一下吧。 密盘创建方法 创建密盘建议使用超级秘密磁盘3000&#xff0c;软件界面简约&#xff0c;操作简单&#x…

秋招复习笔记——八股文部分:网络HTTP

常见面试题 基本概念 HTTP 是超文本传输协议&#xff0c;也就是HyperText Transfer Protocol。HTTP 协议是一个双向协议,是一个在计算机世界里专门用来在两点之间传输数据的约定和规范。是超越了普通文本的文本&#xff0c;它是文字、图片、视频等的混合体&#xff0c;最关键…

资源管理规范

踩坑经验&#xff1a; 对于IO及池化资源(文件、线程池、网络IO(HttpClient)、磁盘IO)&#xff0c;使用之后一定要及时回收&#xff0c;好借好还&#xff0c;再借不难。 稳定关闭方式 完成I/O操作后&#xff0c;应该关闭流以释放系统资源。可以使用finally块确保流被关闭&#…

学生故事|勇于创新,拒绝“一成不变”的设计

对于JIANG MANQI而言&#xff0c;室内设计一直是她钟爱的行业选择。 在没有进入莱佛士学习之前&#xff0c;MANQI受家人的影响&#xff0c;从小就对设计行业比较感兴趣。而她选择室内设计&#xff0c;是觉得室内设计是比较有前途的一个专业&#xff0c;随着人们生活品质的提高…

数字化转型-工具变量数据集

01、数据介绍 数字化转型是指企业或个人利用数字技术&#xff0c;如大数据、云计算、人工智能等&#xff0c;对其业务流程、运营模式、决策方式等进行全面、深入的变革&#xff0c;以提高效率、降低成本、提升质量、增强竞争力。在这个过程中&#xff0c;工具变量扮演着至关重…

SpringBoot 集成Nacos注册中心和配置中心-支持自动刷新配置

SpringBoot 集成Nacos注册中心和配置中心-支持自动刷新配置 本文介绍SpringBoot项目集成Nacos注册中心和配置中心的步骤&#xff0c;供各位参考使用 1、配置pom.xml 文件 在pom.xml文件中定义如下配置和引用依赖&#xff0c;如下所示&#xff1a; <properties><pr…

MATLAB求和函数

语法 S sum(A) S sum(A,“all”) S sum(A,dim) S sum(A,vecdim) S sum(,outtype) S sum(,nanflag) 说明 示例 S sum(A) 返回沿大小大于 1 的第一个数组维度计算的元素之和。 如果 A 是向量&#xff0c;则 sum(A) 返回元素之和。 如果 A 是矩阵&#xff0c;则 sum(A) 将…

如何在Linux CentOS部署宝塔面板并实现固定公网地址访问内网宝塔

文章目录 一、使用官网一键安装命令安装宝塔二、简单配置宝塔&#xff0c;内网穿透三、使用固定公网地址访问宝塔 宝塔面板作为建站运维工具&#xff0c;适合新手&#xff0c;简单好用。当我们在家里/公司搭建了宝塔&#xff0c;没有公网IP&#xff0c;但是想要在外也可以访问内…

Spring Boot 中如何处理存取 MySQL 中 JSON 类型的字段

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的全栈工程师 欢迎分享 / 收藏 / 赞 / 在看…

K8S部署Nginx与问题

【containerd错误解决系列】failed to create shim task, OCI runtime create failed, unable to retrieve OCI... 环境 # cat /etc/redhat-release CentOS Linux release 8.0.1905 (Core) # uname -r 4.18.0-348.rt7.130.el8.x86_64 问题及现象 1、pod的状态全部都是Conta…

谷粒商城实战(013 业务-认证服务-短信验证)

Java项目《谷粒商城》架构师级Java项目实战&#xff0c;对标阿里P6-P7&#xff0c;全网最强 总时长 104:45:00 共408P 此文章包含第211p-第p219的内容 介绍 认证中心要集成 社交登录、OAuth2.0、单点登录 等功能 OAuth 2.0&#xff1a; 问题解决&#xff1a; OAuth 2.0 主要…

鸿蒙应用开发之Web组件2

前面学习了加载Web组件,在使用这个组件之前需要设置网络加载的权限,否则是不能使用Web组件,所以大家在使用这个组件时,需要仔细检查是否有设置这个权限。 如果Web组件只是默认加载一次连接,就可以使用构造时传入的参数来决定,如果想不断地变换不同的网络地址,就需要使用…

「GO基础」在Windows上安装Go编译器并配置Golang开发环境

文章目录 1、安装Go语言编译程序1.1、下载GoLang编译器1.2、安装GoLang编译器 2、配置Golang IDE运行环境2.1、配置GO编译器2.1.1、GOROOT 概述2.1.2、GOROOT 作用2.1.2、配置 GOROOT 2.2、配置GO依赖管理2.2.1、Module管理依赖2.2.2、GOPATH 管理依赖 2.3、运行GO程序2.3.1、创…

05_数组和结构体

结构体 结构体的使用(重点) 结构体值传参 传值是指将参数的值拷贝一份传递给函数&#xff0c;函数内部对该参数的修改不会影响到原来的变量 结构体地址传递 传址是指将参数的地址传递给函数&#xff0c;函数内部可以通过该地址来访问原变量&#xff0c;并对其进行修改。…

大量excel文件私密性较强 需要密码保护 如何给excel文件批量加密

一&#xff0c;前言 在现代办公环境中&#xff0c;Excel文件已成为数据存储和交流的常见工具。然而&#xff0c;随着数据泄露和信息安全问题的日益严重&#xff0c;如何保护Excel文件的安全性成为了我们关注的焦点。批量加密Excel文件成为了一种有效的解决方案&#xff0c;它可…

【光伏行业】光伏发电的应用领域

光伏发电作为一种清洁、可再生的能源技术&#xff0c;在多个领域都有广泛的应用。以下是一些光伏发电的主要应用领域&#xff1a; 住宅和商业建筑&#xff1a;光伏板可以安装在屋顶上&#xff0c;为建筑提供电力。这不仅降低了对电网的依赖&#xff0c;还减少了电费支出&#x…

Spring (四) 之配置及配置文件的操作

文章目录 1、Spring 基于注解的配置基于注解的配置引入依赖包配置实体类数据访问层业务层业务层实现测试 2、Bean和Component和Configuration的区别1 Bean:2 Component:3 Configuration:总结&#xff1a; 区别Component和Configuration区别 3、Spring读取properties配置文件准备…

【C++】开始使用优先队列

送给大家一句话: 这世上本来就没有童话&#xff0c;微小的获得都需要付出莫大的努力。 – 简蔓 《巧克力色微凉青春》 开始使用优先队列 1 前言2 优先队列2.1 什么是优先队列2.2 使用手册2.3 仿函数 3 优先队列的实现3.1 基本框架3.2 插入操作3.3 删除操作3.4 其他函数 4 总结T…

【ZYNQ】PS和PL数据交互丨AXI总线(主机模块RTL代码实现)

文章目录 一、PS-PL数据交互桥梁&#xff1a;AXI总线1.1 AXI总线和AXI4总线协议1.2 PS-PL数据传输的主要场景1.2.1 PL通过AXI_HP操作DDR3 Controller读写DDR31.2.2 PS作主机使用GP接口传输数据 1.3 AXI端口带宽理论1.4 AXI 总线的读写分离机制1.5 握手机制1.6 AXI_Lite总线1.7 …