-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[#31] 기간별 인기 피드 #32
[#31] 기간별 인기 피드 #32
Changes from 14 commits
5263c13
37fffa3
38b4e53
0bd5a48
e9810dd
ee59359
5d632ce
a5d1fdc
59e31df
9f412ce
2bf9736
e24aa60
51a51d0
f4d1144
8f3ed12
c4bae8f
124e3e0
082c0e2
2e77773
eef16d7
2c0e01d
8d106bf
3155b96
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,12 @@ | ||
package com.stoury.config.batch; | ||
|
||
import com.stoury.domain.Feed; | ||
import com.stoury.repository.FeedRepository; | ||
import com.stoury.repository.LikeRepository; | ||
import com.stoury.repository.RankingRepository; | ||
import com.stoury.utils.CacheKeys; | ||
import jakarta.persistence.EntityManagerFactory; | ||
import jakarta.persistence.PersistenceContext; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.batch.core.Job; | ||
import org.springframework.batch.core.Step; | ||
|
@@ -11,15 +15,22 @@ | |
import org.springframework.batch.core.job.flow.Flow; | ||
import org.springframework.batch.core.repository.JobRepository; | ||
import org.springframework.batch.core.step.builder.StepBuilder; | ||
import org.springframework.batch.item.ItemProcessor; | ||
import org.springframework.batch.item.ItemWriter; | ||
import org.springframework.batch.item.database.JpaPagingItemReader; | ||
import org.springframework.batch.item.database.builder.JpaPagingItemReaderBuilder; | ||
import org.springframework.batch.repeat.RepeatStatus; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.data.domain.PageRequest; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.util.Pair; | ||
import org.springframework.scheduling.annotation.EnableScheduling; | ||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | ||
import org.springframework.transaction.PlatformTransactionManager; | ||
|
||
import java.time.temporal.ChronoUnit; | ||
import java.util.List; | ||
|
||
@Configuration | ||
|
@@ -29,6 +40,7 @@ public class BatchConfig { | |
Pageable pageable = PageRequest.of(0, 10); | ||
private final FeedRepository feedRepository; | ||
private final RankingRepository rankingRepository; | ||
private final EntityManagerFactory entityManagerFactory; | ||
|
||
@Bean | ||
public Step stepUpdatePopularDomesticCities(JobRepository jobRepository, PlatformTransactionManager tm) { | ||
|
@@ -69,4 +81,131 @@ public Job jobUpdatePopularSpots(JobRepository jobRepository, PlatformTransactio | |
.end() | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public JpaPagingItemReader<Feed> feedReader() { | ||
return new JpaPagingItemReaderBuilder<Feed>() | ||
.name("feedReader") | ||
.pageSize(100) | ||
.entityManagerFactory(entityManagerFactory) | ||
.queryString("select f from Feed f") | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public ItemProcessor<Feed, Pair<String, Long>> dailyFeedProcessor(LikeRepository likeRepository) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 얘네 빈으로 사용되고 있지 않은 것 같은데, 그냥 feedProcessor 호출하도록 만들어도 되지 않을까요 ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 배치 설정 예시를 보면 아이템 리더,프로세서,라이터 하나하나를 빈으로 설정하길래 일단 따라했는데 그렇게 안해도 되는 거였군요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 빈으로 해도 되긴 하는데 지금은 빈 등록 후 빈으로 안쓰고 있는 것 같아서 말씀드렸어요 :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 배치는 보통 젠킨스같은곳에서 돌 때 해당 스케줄에 해당 잡만 수행되도록 구성하기 때문에 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 젠킨스에서 여러개의 인스턴스를 배포할 때 특정 잡을 돌리고 싶은 인스턴스만 지정해서 배포하면, 하나의 인스턴스만 그 잡을 수행하게 하는 그런 메커니즘이 맞나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 음 .. 정확히는 하나의 인스턴스에 파일을 올려두고 .. 맞나 ? 인스턴스가 하난지는 잘 기억이 안나네요 워커가 여러개긴 한데 그게 별도의 인스턴스였는지 가물가물해서 .. 아무튼 배치 파일 배포는 jar 파일을 업로드 하는 걸 말하고, 모든 젠킨스 잡은 같은 파일을 사용하게 돼요. 그래서 파일 실행 시 java - |
||
return feedProcessor(likeRepository, ChronoUnit.DAYS); | ||
} | ||
|
||
@Bean | ||
public ItemProcessor<Feed, Pair<String, Long>> weeklyFeedProcessor(LikeRepository likeRepository) { | ||
return feedProcessor(likeRepository, ChronoUnit.WEEKS); | ||
} | ||
|
||
@Bean | ||
public ItemProcessor<Feed, Pair<String, Long>> monthlyFeedProcessor(LikeRepository likeRepository) { | ||
return feedProcessor(likeRepository, ChronoUnit.MONTHS); | ||
} | ||
|
||
public ItemProcessor<Feed, Pair<String, Long>> feedProcessor(LikeRepository likeRepository, ChronoUnit chronoUnit) { | ||
return feed -> { | ||
String feedId = feed.getId().toString(); | ||
|
||
if (!likeRepository.existsByFeedId(feedId)) { | ||
return Pair.of(feedId, 0L); | ||
} | ||
long currentLikes = likeRepository.getCountByFeedId(feedId); | ||
long prevLikes = likeRepository.getCountSnapshotByFeed(feedId, chronoUnit); | ||
|
||
return Pair.of(feedId, currentLikes - prevLikes); | ||
}; | ||
} | ||
|
||
@Bean | ||
public ItemWriter<Pair<String, Long>> dailyFeedWriter() { | ||
return feedWriter(ChronoUnit.DAYS); | ||
} | ||
|
||
@Bean | ||
public ItemWriter<Pair<String, Long>> weeklyFeedWriter() { | ||
return feedWriter(ChronoUnit.WEEKS); | ||
} | ||
|
||
@Bean | ||
public ItemWriter<Pair<String, Long>> monthlyFeedWriter() { | ||
return feedWriter(ChronoUnit.MONTHS); | ||
} | ||
|
||
public ItemWriter<Pair<String, Long>> feedWriter(ChronoUnit chronoUnit) { | ||
return list -> { | ||
for (Pair<String, Long> pair : list) { | ||
String feedId = pair.getFirst(); | ||
Long likeIncrease = pair.getSecond(); | ||
|
||
if (likeIncrease > 0) { | ||
rankingRepository.saveHotFeed(feedId, likeIncrease, chronoUnit); | ||
} | ||
} | ||
}; | ||
} | ||
|
||
@Bean | ||
public Step stepDailyFeed(JobRepository jobRepository, PlatformTransactionManager tm, | ||
ThreadPoolTaskExecutor taskExecutor, LikeRepository likeRepository) { | ||
return new StepBuilder("stepDailyFeed", jobRepository) | ||
.<Feed, Pair<String, Long>>chunk(100, tm) | ||
.reader(feedReader()) | ||
.processor(dailyFeedProcessor(likeRepository)) | ||
.writer(dailyFeedWriter()) | ||
.taskExecutor(taskExecutor) | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public Step stepWeeklyFeed(JobRepository jobRepository, PlatformTransactionManager tm, | ||
ThreadPoolTaskExecutor taskExecutor, LikeRepository likeRepository) { | ||
return new StepBuilder("stepWeeklyFeed", jobRepository) | ||
.<Feed, Pair<String, Long>>chunk(100, tm) | ||
.reader(feedReader()) | ||
.processor(weeklyFeedProcessor(likeRepository)) | ||
.writer(weeklyFeedWriter()) | ||
.taskExecutor(taskExecutor) | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public Step stepMonthlyFeed(JobRepository jobRepository, PlatformTransactionManager tm, | ||
ThreadPoolTaskExecutor taskExecutor, LikeRepository likeRepository) { | ||
return new StepBuilder("stepMonthlyFeed", jobRepository) | ||
.<Feed, Pair<String, Long>>chunk(100, tm) | ||
.reader(feedReader()) | ||
.processor(monthlyFeedProcessor(likeRepository)) | ||
.writer(monthlyFeedWriter()) | ||
.taskExecutor(taskExecutor) | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public Job jobDailyFeed(JobRepository jobRepository, PlatformTransactionManager tm, | ||
ThreadPoolTaskExecutor taskExecutor, LikeRepository likeRepository) { | ||
return new JobBuilder("jobDailyFeed", jobRepository) | ||
.start(stepDailyFeed(jobRepository, tm, taskExecutor, likeRepository)) | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public Job jobWeeklyFeed(JobRepository jobRepository, PlatformTransactionManager tm, | ||
ThreadPoolTaskExecutor taskExecutor, LikeRepository likeRepository) { | ||
return new JobBuilder("jobWeeklyFeed", jobRepository) | ||
.start(stepWeeklyFeed(jobRepository, tm, taskExecutor, likeRepository)) | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public Job jobMonthlyFeed(JobRepository jobRepository, PlatformTransactionManager tm, | ||
ThreadPoolTaskExecutor taskExecutor, LikeRepository likeRepository) { | ||
return new JobBuilder("jobMonthlyFeed", jobRepository) | ||
.start(stepMonthlyFeed(jobRepository, tm, taskExecutor, likeRepository)) | ||
.build(); | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package com.stoury.controller; | ||
|
||
import com.stoury.dto.feed.FeedResponse; | ||
import com.stoury.service.RankingService; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import java.time.temporal.ChronoUnit; | ||
import java.util.List; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
public class RankingController { | ||
private final RankingService rankingService; | ||
|
||
@GetMapping("/rank/abroad-spots") | ||
public List<String> getPopularAbroadSpots() { | ||
return rankingService.getPopularAbroadSpots(); | ||
} | ||
|
||
@GetMapping("/rank/domestic-spots") | ||
public List<String> getPopularDomesticSpots() { | ||
return rankingService.getPopularDomesticSpots(); | ||
} | ||
|
||
@GetMapping("/rank/daily-hot-feeds") | ||
public List<FeedResponse> getDailyHotFeeds() { | ||
return rankingService.getHotFeeds(ChronoUnit.DAYS); | ||
} | ||
|
||
@GetMapping("/rank/weekly-hot-feeds") | ||
public List<FeedResponse> getWeeklyHotFeeds() { | ||
return rankingService.getHotFeeds(ChronoUnit.WEEKS); | ||
} | ||
|
||
@GetMapping("/rank/monthly-hot-feeds") | ||
public List<FeedResponse> getMonthlyHotFeeds() { | ||
return rankingService.getHotFeeds(ChronoUnit.MONTHS); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 파일 패키지를 config 밑이 아니라 batch로 따로 빼는게 좋을 것 같아요 ~