spring.datasource.password=1234
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.25
docker pull xuxueli/xxl-job-admin:2.3.0
docker run -e PARAMS="--spring.datasource.url=jdbc:mysql://192.168.174.133: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 -d xuxueli/xxl-job-admin:2.3.0
<dependency>
<groupId>com.xuxueligroupId>
<artifactId>xxl-job-coreartifactId>
<version>2.3.0version>
dependency>
server:
port: 8881
xxl:
job:
admin:
addresses: http://192.168.174.133:8888/xxl-job-admin
executor:
appname: xxl-job-executor-sample
port: 9999
XxlJobConfig.java@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;
}
/**
* 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
*
* 1、引入依赖:
*
* org.springframework.cloud
* spring-cloud-commons
* ${version}
*
*
* 2、配置文件,或者容器启动变量
* spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
*
* 3、获取IP
* String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
*/
}
@Component
public class HelloJob {
@XxlJob("demoJobHandler")
public void demoJobHandler()
{
XxlJobHelper.log("XXL-JOB, Hello World.");
System.out.println("简单任务执行...");
}
}
xxl-job-sharding-sample
@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;
}
@Slf4j
@Service
@Transactional
public class HotArticleServiceImpl implements HotArticleService {
@Autowired
private ApArticleMapper apArticleMapper;
/**
* 计算热门文章
*/
@Override
public void computeHotArticle() {
// 1. 查询前5天的文章数据
Date dayParam = DateTime.now().minusDays(5).toDate();
List<ApArticle> articleList = apArticleMapper.findArticleListByLast5days(dayParam);
// 2. 计算文章的分值
List<HotArticleVo> hotArticleVoList = getHotArticleVoList(articleList);
// 3. 为每个频道缓存30条分值较高的文章
cacheTagToRedis(hotArticleVoList);
}
@Autowired
private IWemediaClient wemediaClient;
@Autowired
private CacheService cacheService;
/**
* 为每个频道缓存30条分值较高的文章
* @param hotArticleVoList
*/
private void cacheTagToRedis(List<HotArticleVo> hotArticleVoList) {
// 为每个频道缓存30条分值较高的文章
ResponseResult responseResult = wemediaClient.getChannels();
if(responseResult.getCode().equals(200)){
String jsonString = JSON.toJSONString(responseResult.getData());
List<WmChannel> wmChannelList = JSON.parseArray(jsonString, WmChannel.class);
// 检索出每个频道的文章
if(wmChannelList!=null){
for (WmChannel wmChannel : wmChannelList) {
List<HotArticleVo> hotArticleVos = hotArticleVoList.stream().filter(item ->
item.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 articleList
* @return
*/
private List<HotArticleVo> getHotArticleVoList(List<ApArticle> articleList) {
List<HotArticleVo> articleVoList = new ArrayList<>();
if(articleList!=null) {
for (ApArticle apArticle : articleList) {
HotArticleVo hotArticleVo = new HotArticleVo();
BeanUtils.copyProperties(apArticle, hotArticleVo);
Integer score = computeArticleScore(apArticle);
hotArticleVo.setScore(score);
articleVoList.add(hotArticleVo);
}
}
return articleVoList;
}
/**
* 计算文章分数
* @param apArticle
* @return
*/
private Integer computeArticleScore(ApArticle apArticle) {
Integer score = 0;
if(apArticle.getLikes() != null){
score += ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT*apArticle.getLikes();
}
if(apArticle.getComment() != null){
score += ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT*apArticle.getComment();
}
if(apArticle.getCollection() != null){
score += ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT*apArticle.getCollection();
}
if(apArticle.getViews() != null){
score += apArticle.getViews();
}
return score;
}
}
// ApArticleMapper.java
List<ApArticle> findArticleListByLast5days(@Param("dayParam") Date dayParam);
// ApArticleMapper.xml
<select id="findArticleListByLast5days" resultType="com.heima.model.article.pojos.ApArticle">
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>
总结:
ApArticleMapper.java
中的形参必须添加@Param注解, 否则在ApArticleMapper.xml
中会将dayParam解释为Date的属性然后报错.leadnews-hot-article-executor
@Component
@Slf4j
public class ComputeHotArticleJob {
@Autowired
private HotArticleService hotArticleService;
@XxlJob("computeHotArticleJob")
public void handle()
{
log.info("热文章分值计算调度任务开始执行...");
hotArticleService.computeHotArticle();
log.info("热文章分值计算调度任务执行结束...");
}
}
// article-Controller
public class ArticleHomeController {
...
public ResponseResult load(@RequestBody ArticleHomeDto articleHomeDto)
{
// return apArticleService.load(articleHomeDto, ArticleConstants.LOADTYPE_LOAD_MORE);
return apArticleService.load2(articleHomeDto, ArticleConstants.LOADTYPE_LOAD_MORE, true);
}
...
}
// article-Service
public ResponseResult load2(ArticleHomeDto dto, Short type, boolean firstPage) {
if(firstPage==true){
String jsonString = cacheService.get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
if(StringUtils.isNotBlank(jsonString)){
List<HotArticleVo> hotArticleVoList = JSON.parseArray(jsonString, HotArticleVo.class);
return ResponseResult.okResult(hotArticleVoList);
}
}
return load(dto, type);
}
黑马程序员. 黑马头条
https://gitee.com/yu-ba-ba-ba/leadnews