내용
• 리뷰1(오늘)
• 시작: 기능 목록 초안, 초기 컴포넌트 식별
• 0.1 개발: 핵심 기능 구현, CLI 구현
• 일부 설계 과정, 일부 구현 결과물
• 리뷰2 (다음)
• 0.2 개발: 웹 구현
• 일부 설계/구현 결과물
• 테스트 관련 생각할 거리: 단위 vs 통합, E2E 테스트
2
3.
이야기의 시작, 반복(중복)
[커맨드라인]
행렬 분해
[커맨드라인]
사용자용 데이터
생성
[커맨드라인]
아이템용 데이터
생성
[자바]
데이터 변환 후
저장
반복
여러 알고리즘에 대해 과정 유사
3
4.
자동화 욕구
4
[커맨드라인]
행렬 분해
[커맨드라인]
사용자용 데이터 생성
[커맨드라인]
아이템용 데이터 생성
[자바]
데이터 변환 후
저장
반복
자동화
[프로세스]
경로, 타입, 숫자 등 많은 설정
단계별 실패 확인
데이터 변환
과정, 실패 처리 등
프로그램
설정 정보
경로, 타입, 숫자 등
5.
기능 목록
버전내용
0.1 1개 알고리즘에 대한 서비스 구현
콘솔에서 서비스 실행: 설정 파일 사용
5
0.2
웹 인터페이스
- 작업 설정 생성
- 작업 설정을 이용해서 백그라운드로 작업 실행
- 실행 내역 추적
0.3 다른 알고리즘 추가
주요 모델
•분석 실행(프로세스)
• 입력: 실행에 필요한 데이터
• 알고리즘 실행: 입력을 이용해서 특정 알고리즘 수행
• 결과 저장: 실행 결과를 원하는 형태로 보관
7
8.
0.1 버전 상위수준 설계 초안
입력 데이터
경로 제공 결과 보관
8
주요 인터페이스
핵심 컴포넌트
알고리즘 실행
설정 파일을 이용해서
분석 기능 실행
9.
요구 상세
•Mahout을 이용해서 분석 1개 실행
• 로컬에 설치된 Mahout을 사용
• 기능 실행은 쉘스크립트를 이용해서 실행
• Mahout 실행 환경
• 로컬: 로컬 실행, 로컬 입력 파일
• 하둡: 하둡 클러스터(MR) 실행, HDFS 입력 파일
• 로컬에 설치된 하둡을 사용
• 결과 저장
• 로컬에 JSON 파일, (단순 테스트 목적) 콘솔 출력
9
10.
요구 상세
•Mahout을 이용해서 분석 1개 실행
• 로컬에 설치된 Mahout을 사용
• 기능 실행은 쉘스크립트를 이용해서 실행
10
• Mahout 실행 환경
JVM에서 외부 쉘을 실행
사용자가 쉘을 마음대로 변경하면
안 되므로, 런타임에 쉘을 동적 생성
• 로컬: 로컬 실행, 로컬 입력 파일
• 하둡: 하둡 클러스터(MR) 실행, HDFS 입력 파일
• 로컬에 설치된 하둡을 사용
• 결과 저장
• 로컬에 JSON 파일, (단순 테스트 목적) 콘솔 출력
Mahout 실행시, 하둡 클러스
터 설정 가능하도록
입력 파일로 로컬 경로와 HDFS
경로를 사용할 수 있도록 하둡 결과 파일을 읽어와 처리
핵심 부분 설계시작
12
class RecAnalyticsServiceSpec
extends Specification {
def “생성”() {
def RecAnalyticsService service =
new RecAnalyticsService()
}
}
public class RecAnalyticsService {
!}
* 공간의 제약으로 이름 등 일부 변경
class RecAnalyticsServiceSpec
extends Specification {
def service = new RecAnalyticService()
!
def “로그가 없을 경우”() {
when:
service.createRecommendation()
then:
thrown(NoDataException)
}
}
public class RecAnalyticsService {
public void createRecommendation() {
throw new NoDataException();
}
}
!
public class NoDataException extends RuntimeEx…{
}
13.
13
def service= new RecAnalyticService()
!
def “로그가 없을 경우”() {
when:
service.createRecommendation()
then:
thrown(NoDataException)
}
!
def “로그가 있다면”() {
setup:
def ActivityStorage activityStorage = Mock()
service.setActivityStorage(activityStorage)
when:
service.createRecomendation()
then: “로그가 있으면 NoDataException이 발생하지 않음”
1 * activityStorage.hasActivityData() >> true
notThrown(NoDataException)
}
!
public interface ActivityStorage {
boolean hasActivityData();
}
!
public class RecAnalyticsService {
private ActivityStorage activityStorage;
!
public void createRecommendation() {
if (!activityStorage.hasActivityData())
throw new NoDataException();
}
!
public void setActivityStorage(ActivityStorage …) {
this.activityStorage = …;
}
}
사용자 입장에서 메서드도출
UserItemSource와 SimItemSource의 사용자
23
▼
“ResultSaver의 콘크리트 클래스”
public class ConsoleResultSaver implements … {
!
public void saveUserItem(UserItemSource source) {
List<UserItem> userItems = source.getUserItems();
for (UserItem ui: userItems) { … }
}
!
public void saveUserItem(UserItemSource source) {
Iterator<UserItem> userItems = source.getIterator();
while(userItems.hasNext()) {
UserItem ui = userItems.next();
…
}
}
1안
2안
대량 결과 데이터를 처리할 수 있어야 하기 때문에,
2안 선택
24.
2안으로 구현
쉘실행 결과로 만들어진 파일로부터
데이터를 읽어오는 Iterator 구현 필요
24
public interface UserItemSource {
Iterator<UserItem> iterator();
}
25.
2안, Iterator 구현예
25
private class FileSystemSimItemIterator
implements Iterator<SimItem> {
private final SequenceFile.Reader reader;
private final IntWritable key;
private final VectorWritable value ;
private SimItem nextItem;
!
public FileSystemSimItemIterator(
FileSystem fileSystem, Path simItemFile) {
reader = createSequenceFileReader(
fileSystem, similarItemFile, new Configuration());
key = new IntWritable();
value = new VectorWritable();
moveNext();
}
!
@Override
public boolean hasNext() {
return nextItem != null;
}
!
@Override
public SimilarItems next() {
checkNextItemExists();
SimItem result = nextItem;
moveNext();
return result;
}
private void checkNextItemsExists() {
if (nextItem == null) throw new NoSuchElementException();
}
!
private void moveNext() {
boolean isNextRead = readNextKeyValue();
if (!isNextRead) {
closeReader();
nextItem = null;
return;
}
createNextItems();
}
!
private boolean readNextKeyValue() {
try {
return reader.next(key, value);
} catch (…) {…}
}
!
private void createNextItem() {
List<ItemValue> allSimilarItems = new ArrayList<>();
for (Vector.Element ele : value.get().nonZeroes())
allSimilarItems.add(new ItemValue(ele.index(), ele.get()));
nextItem = new SimItem(key.get(), allSimilarItems);
}
private void closeReader() { … }
}
Factory
설정 정보의값에 따라
모든 객체를 생성하고 조립
public class RecAnalyticServiceFactoryImpl implements RecAnalyticServiceFactory {
@Override
public RecommendationAnalyticService create(RecommendationConfig config) {
checkAnalyticServiceType(config);
MahoutRecAnalyticService service = new MahoutRecAnalyticService();
ServiceColleboratorFactory factory = ServiceColleboratorFactory.create(config);
service.setActivityDataStorage(factory.createActivityDataStorage());
service.setMahoutRunner(factory.createMahoutRunner());
service.setResultSaver(factory.createResultSaver());
return service;
}
37
38.
기타
• analytic-service
• 런타임에 쉘 생성: Velocity 이용해서 템플릿 처리
• CLI
• 설정 파일 로딩시 플레이스홀더 변환 처리
• ${env.환경변수}, ${자바시스템프로퍼티}
• e2e 테스트 (로컬, 하둡 클러스터 환경)
• 메이븐 이용 멀티 프로젝트 사용
• API 위주 서브 프로젝트: spi-*
• 주요 서브 프로젝트
• mahout-analytic-service, simple-data-storage
• cli
• 배포판 생성 서브 프로젝트: zip 파일로 생성
38