当前位置:网站首页>Number 16, top posts
Number 16, top posts
2022-07-31 02:40:00 【A snicker】
1、Post scores are time-dependent,而且点赞,Collections and other operations are very high frequency,Generally, post scores are calculated through timed tasks(分布式定时任务 Spring Quartz)
2、Put posts with changed scores in the cache,Time to time to calculate the posts in the cache(Redis)
1、Put the post that needs to calculate the scoreRedis中
1、生成RedisKey
2、When an action that affects the post score occurs,Put the post inRedis
RedisKeyUtil.java
// 帖子分数
public static String getPostScoreKey() {
return PREFIX_POST + SPLIT + "score";
}
DiscussPostController.java
A)When posting a new post,将帖子id放入Redis(Give the post an initial score):RedisKey:postId 存放在Set里.
@PostMapping("/add")
@ResponseBody
public String addDiscussPost(String title, String content) {
...
// 计算帖子分数
String redisKey = RedisKeyUtil.getPostScoreKey();
redisTemplate.opsForSet().add(redisKey, post.getId());
}
B)加精、评论、When liking the action,Also put the post inRedis
// 加精 HomeController.java
@PostMapping("/wonderful")
@ResponseBody
public String setWonderful(int id) {
discussPostService.updateStatus(id, 1);
...
// 计算帖子分数
String redisKey = RedisKeyUtil.getPostScoreKey();
redisTemplate.opsForSet().add(redisKey, id);
}
// 评论 CommentController.java
@PostMapping("/add/{discussPostId}")
public String addComment(@PathVariable("discussPostId") int discussPostId, Comment comment) {
if (comment.getEntityType() == ENTITY_TYPE_POST) {
...
// 计算帖子分数
String redisKey = RedisKeyUtil.getPostScoreKey();
redisTemplate.opsForSet().add(redisKey, discussPostId);
}
2、使用Quartz实现定时任务
1、Job接口实现
quartz包下 PostScoreRefreshJob 类
public class PostScoreRefreshJob implements Job, CommunityConstant {
private static final Logger LOGGER = LoggerFactory.getLogger(PostScoreRefreshJob.class);
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private DiscussPostService discussPostService;
@Autowired
private LikeService likeService;
@Autowired
private 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);
}
}
@Override
public 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);
}
}
- Niu Ke era domain:private static final Date epoch,由于epoch只需要初始化一次,Initialize in a static code block
- 使用BoundSetOperations 绑定 Key,BoundSetOperationsis a bindingkey的对象,We can use this object to ANDkey相关的操作.
- 从Redis里取数据,若没有数据,Return directly after logging.若Set为空,对每个postId执行 refresh 方法.
refreshA method to refresh a post:
1、根据帖子id查找post实体
2、Determine whether to add refined、评论数量、点赞数量,计算分数(getTime() Get the millisecond value)
3、更新帖子分数
4、同步搜索数据
5、对Quartz进行配置:Time interval in milliseconds
对Quartz进行配置
// 配置 -> 数据库 -> 调用
@Configuration
public class QuartzConfig {
// 刷新帖子分数任务
@Bean
public JobDetailFactoryBean postScoreRefreshJobDetail() {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(PostScoreRefreshJob.class);
factoryBean.setName("postScoreRefreshJob");
factoryBean.setGroup("communityJobGroup");
factoryBean.setDurability(true);
factoryBean.setRequestsRecovery(true);
return factoryBean;
}
// 配置Trigger(SimpleTriggerFactoryBean, CronTriggerFactoryBean)
@Bean
public 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;
}
}
3、Home page interface processing
1、Sort posts by hottest
对 selectDiscussPostsThe method can be refactored
@Mapper
public interface DiscussPostMapper {
List<DiscussPost> selectDiscussPosts(int userId, int offset, int limit, int orderMode);
// 增加了一个orderMode参数: orderMode=0 时按照“最新” 排序,orderMode=1 时按照“最热” 排序
}
discusspost-mapper.xml
<select id="selectDiscussPosts" resultType="DiscussPost">
select <include refid="selectFields"></include>
from discuss_post
where status != 2
<if test="userId!=0">
and user_id = #{
userId}
</if>
<if test="orderMode==0">
order by type desc, create_time desc
</if>
<if test="orderMode==1">
order by type desc, score desc, create_time desc
</if>
limit #{
offset}, #{
limit}
</select>
2、所有调用 selectDiscussPosts 的地方进行修改
3、HomeController:
从前端获取ordermode参数,and spell in path returned to the template
@RequestMapping(path = "/index", method = RequestMethod.GET)
public String getIndexPage(Model model, Page page,
@RequestParam(name = "orderMode", defaultValue = "0") int orderMode) {
// 1.默认orderMode=0,For example, when visiting the home page
// 方法调用钱,SpringMVC会自动实例化Model和Page,并将Page注入Model.
// 所以,在thymeleaf中可以直接访问Page对象中的数据.
page.setRows(discussPostService.findDiscussPostRows(0));
page.setPath("/index?orderMode=" + orderMode); // 2.修改Page的路径,加上orderMode参数
// 3.Use the refactored onediscussPostService
List<DiscussPost> list = discussPostService
.findDiscussPosts(0, page.getOffset(), page.getLimit(), orderMode);
List<Map<String, Object>> discussPosts = new ArrayList<>();
if (list != null) {
for (DiscussPost post : list) {
Map<String, Object> map = new HashMap<>();
map.put("post", post);
User user = userService.findUserById(post.getUserId());
map.put("user", user);
long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, post.getId());
map.put("likeCount", likeCount);
discussPosts.add(map);
}
}
model.addAttribute("discussPosts", discussPosts);
model.addAttribute("orderMode", orderMode);
return "/index";
}
4、index.html
1)th:href Hyperlink modification,传入orderMode 参数
2)由orderMode The parameter determines whether there is active 参数
<!-- 筛选条件 -->
<ul class="nav nav-tabs mb-3">
<li class="nav-item">
<a th:class="|nav-link ${orderMode==0?'active':''}|" th:href="@{/index(orderMode=0)}">最新</a>
</li>
<li class="nav-item">
<a th:class="|nav-link ${orderMode==1?'active':''}|" th:href="@{/index(orderMode=1)}">最热</a>
</li>
</ul>
边栏推荐
- Fiddler captures packets to simulate weak network environment testing
- Word/Excel fixed table size, when filling in the content, the table does not change with the cell content
- coldfusion8 background scheduled tasks take shell
- JetPack组件Databinding
- CMOS和TTL的区别?
- BAT卖不动「医疗云」:医院逃离、山头林立、行有行规
- Calculate S=a+aa+…+aa…a
- 公司官网建站笔记(六):域名进行公安备案并将备案号显示在网页底部
- MPPT太阳能充放电控制器数据采集-通过网关采集电池电压容量电量SOC,wifi传输
- How to do a startup CTO?
猜你喜欢
STM32CUBEMX develops GD32F303 (11) ---- ADC scans multiple channels in DMA mode
【Android】Room —— SQLite的替代品
7. List of private messages
mmdetection训练一个模型相关命令
经典链表OJ强训题——快慢双指针高效解法
Discourse Custom Header Links
The whole process scheduling, MySQL and Sqoop
Hanyuan Hi-Tech 8-channel HDMI integrated multi-service high-definition video optical transceiver 8-channel HDMI video + 8-channel two-way audio + 8-channel 485 data + 8-channel E1 + 32-channel teleph
Introduction to flask series 】 【 flask - using SQLAlchemy
Software accumulation -- Screenshot software ScreenToGif
随机推荐
Hanyuan Hi-Tech 8-channel HDMI integrated multi-service high-definition video optical transceiver 8-channel HDMI video + 8-channel two-way audio + 8-channel 485 data + 8-channel E1 + 32-channel teleph
【Android】Room —— SQLite的替代品
Software Testing Defect Reporting - Definition, Composition, Defect Lifecycle, Defect Tracking Post-Production Process, Defect Tracking Process, Purpose of Defect Tracking, Defect Management Tools
SQL注入 Less54(限制次数的SQL注入+union注入)
真正的CTO,是一个懂产品的技术人
AtCoder Beginner Contest 261 部分题解
Brute Force/Adjacency Matrix Breadth First Directed Weighted Graph Undirected Weighted Graph
16. Registration Center-consul
Unity3D Button 鼠标悬浮进入与鼠标悬浮退出按钮事件
SQL注入 Less46(order by后的注入+rand()布尔盲注)
Problems that need to be solved by the tcp framework
print task sorting js od huawei
cudaMemcpy study notes
The effective square of the test (one question of the day 7/29)
LeetCode 1161 最大层内元素和[BFS 二叉树] HERODING的LeetCode之路
Inter-vlan routing + static routing + NAT (PAT + static NAT) comprehensive experiment
1. Non-type template parameters 2. Specialization of templates 3. Explanation of inheritance
汉源高科8路HDMI综合多业务高清视频光端机8路HDMI视频+8路双向音频+8路485数据+8路E1+32路电话+4路千兆物理隔离网络
19. Support Vector Machines - Intuitive Understanding of Optimization Objectives and Large Spacing
Layer 2 broadcast storm (cause + judgment + solution)