비동기 방식이란?

동기 VS 비동기

비동기 방식에 대해서 이야기하기전에 동기와 비동기의 차이부터 짚고 넘어가야 할 것 같다.

1. 동기(Synchronous : 동시에 일어나는)

동기는 말 그대로 동시에 일어난다는 뜻입니다. 요청과 그 결과가 동시에 일어난다는 것입니다.

장점

  • 설계가 간단하고 직관적이다.

단점

  • 결과가 주어질 때까지 아무것도 못하고 대기해야 한다.

2. 비동기(Asynchronous : 동시에 일어나지 않는)

비동기는 동시에 일어나지 않는다는 뜻입니다. 요청과 결과가 동시에 일어나지 않은것이라는 하나의 약속입니다.

단점

  • 설계가 동기방식보다 비교적 복잡하다.

장점

  • 결과가 주어지는데 시간이 걸리더라도 그 시간 동안 다른 작업을 할 수 있다.

동기 방식을 비동기 방식으로 바꾸다

AliExpress 크롤링프로젝트

  1. Issue - CPU 점유율 증가

    • 해당 프로젝트는 초기에 동기방식으로 설계를 진행했는데, CPU 사용량 이슈때문에 비동기방식으로 튜닝을 진행했다.
  2. Approach - Cycle 속도를 늦춰보자

    • 초기 동기방식 설계의 프로그램은 평균 CPU 점유율이 50% 내외를 유지하였다. 이러한 문제 때문에, 실사용에서 프로그램을 구동중에는 다른 작업을 하기 힘들었다. 문제를 해결하기 위해서 Thread.sleep(long delay) 를 이용해서 사이클 속도를 늦추면 어떨까라는 생각에 적용 및 테스트를 진행하였는데 당연하게도 CPU 점유율은 낮아졌다. 30%내외를 유지하였으며 Main thread를 제외하고 3개의 thread가 동작하고 있기 때문에 눈에띄는 효과를 얻을 수 있었다.
  3. Another Issue - 전체 속도 저하

    • Cycle 속도 저하를 통해서 첫 번째 이슈는 처리했다. 하지만, 이 때문에 다른 문제가 생겼다. 자연스레 처리 속도까지 저하된 것이다. 하지만 처리속도를 높이자니 CPU 점유율 issue를 되돌릴 수는 없는 노릇이다.
  4. Approach - 비동기 설계로의 튜닝

    • 처리 속도는 유지하면서, 점유율 문제까지 해결해보자. Java에서는 Thread에 wait과 notify라는 형태의 비동기 방식을 지원한다. 쓰레드와 상태제어 - programmers 를 참고해보면 ‘wait과 notify는 동기화된 블록안에서 사용해야 한다. wait를 만나게 되면 해당 쓰레드는 해당 객체의 모니터링 락에 대한 권한을 가지고 있다면 모니터링 락의 권한을 놓고 대기한다.’

비동기 설계로 바꾼 후

1
2
3
4
5
6
7
8
9
10
11
/*Crawler.java*/
+ private static Crawler instance;

public Crawler() {
setDaemon(true);
+ instance = this;
}

+ public static Crawler getInstance() {
+ return instance;
+ }

+는 Git의 commit 기록을 보며 추가된 부분을 표시한 것이다.
위의 Crawler.java를 보면 어딘가 친숙한 코드를 볼 수 있다. 바로 Singleton pattern이다. 좀 더 정확하게 하자면, Singleton pattern 을 변형하여 같은 코드블럭에 있지 않더라도 접근 할 수 있도록 설계를 수정한 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*Writer.java*/

@Override
public void run() {
while (crawlerAlive.get() || !crawledData.isEmpty()) {
+ synchronized (Crawler.getInstance()) {
+ try {
+// asynchronous control
+ if(crawledData.isEmpty())
+ Crawler.getInstance().wait(1000 * 30);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
Data d = crawledData.poll();
if (d != null) {
if (wroteCount.get() % 500 == 0)
copyExcel();
writeExcel(d);
wroteCount.getAndIncrement();
}
}
writerAlive = false;
}
dev

형상관리(구성관리)

소프트웨어 구성 관리(영어: Software Configuration Management) 또는 형상 관리는 소프트웨어의 변경사항을 체계적으로 추적하고 통제하는 것으로, 형상 관리는 일반적인 단순 버전관리 기반의 소프트웨어 운용을 좀 더 포괄적인 학술 분야의 형태로 넓히는 근간을 이야기한다.
일반적으로 형상 항목(영어: Configuration Item)이라는 형태로 작업 산출물을 선정하고, 형상 항목 간의 변경 사항 추적과 통제 정책을 수립하고 관리한다.
구성관리 - wikipedia

GIT

깃(Git /ɡɪt/)은 컴퓨터 파일의 변경사항을 추적하고 여러 명의 사용자들 간에 해당 파일들의 작업을 조율하기 위한 분산 버전 관리 시스템이다. 소프트웨어 개발에서 소스 코드 관리에 주로 사용되지만 어떠한 집합의 파일의 변경사항을 지속적으로 추적하기 위해 사용될 수 있다. 기하학적 불변 이론을 바탕으로 설계됐고, 분산 버전 관리 시스템으로서 빠른 수행 속도에 중점을 두고 있는 것이 특징이며 데이터 무결성, 분산, 비선형 워크플로를 지원한다.

깃은 2005년에 리눅스 커널 개발을 위해 초기 개발에 기여한 다른 커널 개발자들과 함께 2005년에 리누스 토르발스가 처음 개발한 것이다. 2005년부터 지금까지 주니오 하마노(Junio Hamano)가 소프트웨어의 유지보수를 맡고 있다.

다른 대부분의 분산 버전 관리 시스템처럼, 또 대부분의 클라이언트-서버 시스템과 달리, 모든 노드의 모든 깃 디렉터리는 네트워크 접속이나 중앙 서버와는 독립적으로 동작하는 완전한 이력 및 완전한 버전 추적 기능을 갖춘 성숙한 저장소이다.

깃은 GNU 일반 공중 사용 허가서 v2 하에 배포되는 자유 소프트웨어이다.
깃(소프트웨어)wikipedia)

github VS bitbucket

github

현지시각으로 2018년 6월 4일 오전 6시에 MS 트위터를 통해서 GitHub 인수 소식이 정식 발표되었다.

github는 Github Inc의 대표적인 형상관리 소프트웨어이다.

이 블로그 같은 username.github.io 형식의 static webpage를 지원하기도 하며 svn과 더불어 가장 많이 사용되는 git 플랫폼의 선두주자라고 할 수 있다.

github는 github-desktop이라는 프로그램을 통해 terminal이 익숙하지 않은 사용자를 배려하고 있다.

bitbucket

2008년 Atlassian이라는 회사에서 출범하였으며, github에 비하면 약 1년가량 늦다. 제공하는 기능은 github와 매우 유사하다.

github과 비슷하게 atlassian sourcetree라는 프로그램을 통해 terminal이 익숙하지 않은 사용자를 배려하고 있으며, 사실 두 프로그램 모두 한글 번역은 좀 떨어지는 편이다.

bitbucket repo와 연결한 sourcetree에서의 commit 장면

github와 bitbucket의 차이점

github는 private repository를 제공하지 않는다. 철저하게 open-source 진영이라는 얘기라고도 볼 수 있다. private repository를 구성하기 위해서는 credit을 지불해야 하는데, 이러한 점이 bitbucket과 다르다.

bitbucket은 credit을 지불하지 않고도 private repository를 구성할 수 있으며 repository 구성단계 부터 Access level :: This is a private repository
라는 문구가 보인다. 물론 ‘::’ 대신에 체크박스가 존재한다.

또 다른 차이점은 paid plan이다. 자세하게 들여다 보지는 않았지만, plan에서 차이가 많이 난다. 어느곳이 더 싸고 비싸고 정도의 단순한 비교를 넘어서 제공하는 기능과 그 기능에대한 pricing이 다르며, github 같은 경우는 좀 더 open source project를 지향하는 지향점이 다르다고 생각한다.

마지막으로 가장 좋은 차이점이라고 생각하는 것은 trello이다.
trello는 atlassian 이 갖고 있는 협업 웹서비스(혹은 웹 애플리케이션)인데, trello를 애용하는 편이라 repo에 필요한 board를 연결해두면 아주 편리하다.

bitbucket에 연결한 trello

실수

자주하는 실수로… readme.md 의 추가로 initializing을 해놓은 후, 내 프로젝트 구성 중 필요없는 파일을 뺄 수 있는 .gitignore 파일을 먼저 commit -> push 혹은 해당파일과 동시에 first commit을 해야하는데 일단 먼저 local repo를 commit하고나서 아… 맞다.. 하는 경우가 굉장히 많다.
이번에도 repo 구성단계에서 이번 프로젝트는 eclipse, maven, java 프로젝트이므로 target directory라던가 .class (바이너리 파일) 을 제외해야하는데 미리 올려놓고 이걸 어쩐다…했다.
결국에는 remote repository를 제거한 후에 다시 commit/push 함으로써 해결을 했지만, 또 다시 이런 일이 발생하지 않게 유의해야한다.

git
11월 3 2018

AliExpress 크롤링 프로젝트

프로젝트 정보

  • 시작일자 : 2018.11.01

  • 사용언어 : Java, JS(아주 조금)

  • 사용기술

    • JAVAFX
    • JAVA-Maven
    • Selenium
  • 개발목적 : 외주

개발 상황 정보

TODO

DONE

  • UI 디자인
  • Reader 구현
  • Crawler 구현
  • ImageResize Util
  • Image Differ 구현
  • Excel Writer 구현
  • 가격 설정 구현
  • 자동종료 + 재시작
  • 수집 완료시 Modal창
  • 비동기 튜닝

SCREENSHOT

crawler-ui-capture

문제점(?)

  1. thread를 daemon으로 설정하여도, main thread가 종료될 때, finalize를 호출하지 않는 듯 하다. 그래서, selenium과 chromedriver를 이용하는 특성 때문에 직접적으로 reader.exit() 메소드를 호출 한 후에 main thread를 종료하도록 설정하였다.

  2. aliexpress를 js 사용 없이 확인해보면 lazy - loading이 적용되어있는 것을 확인 할 수 있는데, img 태그에 image-src 라는 속성을 잠시동안 줌으로써 page의 빠른 로딩을 꾀하였다.

  3. java의 (iterable.)forEach method는 for문과 같이 동작하는게 아니라서 break를 쓸 수 없다. 그래서 만든것이 RuntimeException을 상속하여 BreakException이라는 custom Exception을 내부적으로 사용했다. Exception이 발생하면 그것을 catch해 forEach구문을 탈출할 수 있게 한 것이다. 이것이 for/while loop + break 와 비교했을 때 어떤 성능저하를 불러올지는 모르겠지만, 편하게 forEach를 사용하기 위해서 꼭 필요한 존재임은 확실하다.

“BreakException.java”

1
2
3
4
5
6
7
8
9
10
package utils;

public class BreakException extends RuntimeException{

private static final long serialVersionUID = -2431473453374656968L;

public BreakException(String arg) {
super(arg);
}
}

“ImageDiffer.java” 중 두 이미지를 비교하는 와중 중복되는 이미지를 발견했을 시 삽입한 코드.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
try {
if (targetImage != null) {
imgs.forEach(imgName -> {
double diffPercent = ImageDiff.getDifferencePercent(imageDiffPath + "\\" + imgName,
targetImage);
if (diffPercent < 10.00) {
throw new BreakException("same img found :: " + imgName);
}
});
passedCount.getAndIncrement();
String name = getNextImgName();
saveImg(imageSavePath, name, targetImage);
saveImg(imageDiffPath, name, targetImage);
imgs.add(name);
d.setMainImg(name);
// System.out.println("DIFF :: " + d);
passedData.add(d);
}
} catch (BreakException e) {
System.out.println(e.getMessage());
continue;
// 같은 이미지 발견
} catch (IOException e) {
e.printStackTrace();
}

  1. javaFX 관련 문제점
    javaFX Thread가 아닌 Thread (따로 작성한 thread class, 익명 thread class 등..) 에서 javaFX UI에 관련된 명령을 실행할 경우

<java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-9>

위와 같은 Exception이 발생하게 되어있다. 따라서, 해당 명령을 javaFX Thread가 아닌 곳에서 실행시키고 싶을 경우
다음과 같은 코드로 익명 javaFX thread를 생성 및 실행할 수 있다.

1
2
3
4

Platform.runLater( ()-> { // java lambda expression
doStuffRelatedJavaFx();
});
  1. javaFX application의 재시작 구성

“AECController.java” 에서의 restart() method

1
2
3
4
5
public void restart() {
reader.exit(); // reader 내에 chromeDriver를 다루고 있기 때문에 정상동작을 위해서는 reader를 종료한 후에 동작해야 한다.
saveConfs();
Main.restart();
}

“Main.java” 의 구성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

package application;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.fxml.FXMLLoader;


public class Main extends Application {

private static Stage stage; // stage를 종료하기 위해서는 static으로 설정하는것이 좋다.

@Override
public void start(Stage primaryStage) {
try {
AnchorPane root = (AnchorPane)FXMLLoader.load(getClass().getResource("ali.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setResizable(false);
stage = primaryStage;
} catch(Exception e) {
e.printStackTrace();
}
}


public static void restart() {
stage.close();
Platform.runLater( ()->{
new Main().start( new Stage() );
});
}

public static void main(String[] args) {
launch(args);
}
}

COMMENT

[2018-11-03] 개발에 지칠땐 포스팅이 최고인듯 하다.
[2018-11-06] 일단 구현하려했던 것들을 모두 구현하였다. 추가적으로 작업할 것은 남아있지만 이 정도 개발속도면 나름 만족스럽다.
[2018-11-27] 마지막으로 기능 추가요청과 함께, 프로그램 사용에 따른 전체 PC 속도 저하문제에 관한 분석 요청을 해주셨는데 그에 따라 단순 multi-threading 을 비동기방식으로 튜닝하여 CPU usage reducing에 성공하였다.

dev
10월 29 2018
10월 25 2018

이미지 로딩을 위한 저장소에 대한 고민

첫 번째 : 단순 markdown + git

나는 haroopad를 통해 markdown 문서를 작성한다. 그래서, 첫 번째로 생각한 것이 일반 markdown 문서에 local img url 을 적어놓고 generate, deploy를 하면 어떻게 될까였다.
결과는 생각보다 참담했다.
hexo new [layout|type] ‘post name’ 명령을 통해 문서를 작성하면 다음과 같은 경로에 파일이 생성되게 된다.
경로
그래서 생각했던 것이 source 폴더내에 imgs 폴더를 생성한 후에 이미지 경로를 ../imgs/이미지명 과 같이 문서를 작성하면 되지 않을까였는데 너무 단순하게 생각하였다.
hexo는 generate 명령어를 이용하여 ejs를 통해 rendering된 html 파일을 github에 deploy함으로써 동작한다. 이미 첫 포스트에 적어놓았지만 말이다.

두 번째 : github에 이미지를 올려놓고 사용하자

위의 시행착오를 바탕으로 이미지를 github에 올려두고 ‘username.github.io/imgs/image.png’ 와 같은 형식으로 불러오면 어떨까 생각해보았다.
이런 생각을 하게 된 계기는 script 파일이나 stylesheet의 경로가 위와 같았기 때문인데, 결과는 마찬가지로 404였다.

세 번째 : google drive를 이용해 보자

구글 드라이브에 굉장히 친숙했던 터라, 이미지를 업로드해두고 불러올 생각까지 하게 되었다.
이런 식으로 말이다.
google-drive
물론, google에서 제공하는 api를 사용하는 웹서버에 경우에는 원하던 그림을 그릴 수 있을 것이라 생각한다. 하지만 결과는 마찬가지였다.
구글 드라이브에서 이미지를 업로드한 후 공유가능한 링크를 복사하면 다음과 같은 uri가 보여진다.
“drive.google.com/open?id=********fq8c4cQmkskq12xLbZA-MOOL”

링크만 봐도 <img src.. 같은 태그로는 어림도 없을 것 같은 느낌이다.

네 번째 : 클라우드 서비스 이용(feat.NHN)

어떻게 이런 생각을 하게 되었냐면, 물론 많은 사람들이 생각하겠지만 KAKAO 기술 블로그를 뒤적거리다가 하게 되었다.
좀 더 정확하게 말하자면 결정을 내리게 된 이유이다. 최근 KAKAO 기술 블로그는 Jekyll을 사용하는 형태로 github.io로 이주하였는데, 기술 블로그에 삽입되는 이미지의 host url을 자세히 보면 블로그의 host url과는 완전히 다른 cdn을 사용한다는 것을 알 수 있다.
그래서, 클라우드 서비스를 제공하는 기업 대표적으로 4곳을 찾아보았다. 구글, AWS, 네이버, NHN 이렇게 4곳이다.

  • 구글 클라우드

    그 중에서 구글 클라우드 서비스는 번역 api를 이용해본 경험이 있기 때문에(당시 기억으로 console이 굉장히 복잡함. 여전히 그러하다.) 사용하지 않았다.
  • AWS

    사실 블로그를 구성할 때, 나는 Spring framework를 배운 기억이 있기 때문에 AWS 서버를 이용하여 deploy하고자 했었다. 하지만, 생각보다 불친절한 AWS의 장벽에 가로막혀 여기도 사용하지 않았다.
  • 네이버 클라우드

    나는 네이버를 굉장히 좋아한다. 어릴 때부터 네이버에 입사해서 일을하고 싶어했다. 그 초록창.. 다이스키
  • NHN 클라우드 서비스

    하지만, 나는 NHN 클라우드 서비스인 TOAST를 선택했다. 왜냐하면 2017년 하반기, 2018년 하반기 Pre-test 1차 전형을 통과해서 그냥 기분이 좋기 때문이다.
    사실, TOAST 서비스를 들여다 보면 일단 한국 리전의 이점과 사용자를 위한 UI 디자인을 만날 수 있다.

#Hexo와 친해지기

Hexo란?

Hexo 소개 페이지 를 보면 빠르고 단순하며 게다가 강력하기까지한 blog framework라고 소개하고 있다.
hexo introduce in hexo document
Markdown (혹은 다른 언어)로 posting을 진행할 수 있다고 하는데, 좀 더 소개하자면 Jekyll (한국인이기 때문에 ko page를 링크해두었다.) 은 Ruby를 주언어로 사용하는데에 반해 Hexo는 node.js를 주언어로 삼고 있다.
구성 파일을 보면 ejs가 들어있는 것도 그 이유다.

Ejs란?

간단하게 해당 이미지를 보고 가는게 좋을 것 같다.
ejs description in ejs.co
ejs.co로 접속해보면 해당 문구를 발견할 수 있는데, 풀이해보면 EJS는 plain JavaScript와 함께 HTML을 만들어내는 단순한 템플릿 언어라고 한다. 이와 비슷하게 php, jsp, asp 정도가 있을 것이다.

Hexo의 동작

post 하나를 만들어내본 후 이해한 바로는 이러한 방식으로 동작하는 것이다.

  1. Markdown 파일 생성
  2. ejs를 통한 렌더링

참 쉽죠?

그러면, github.io로와의 연동은 어떻게 되느냐..?

위와 비슷합니다.

  1. Markdown 파일 생성
  2. ejs를 통한 렌더링
  3. 렌더링된 html 파일을 정적 파일로 저장
  4. deploy

wtf?!

정말로 이렇게 간단할까..? 하는 의문이 생긴다면 직접 해보시길 바랍니다.

1
Powered by Hexo
Original Theme Weightless
Owner Dev.secr3t