Deview 2015 Day 1에 참가하게 되었습니다.

요번년도 Deview의 Topic은 웹,모바일,웹스케일 컴퓨팅,빅데이터,선행기술, 테크 스타트업,오픈소스,개발자 문화,로봇,검색 이렇게 10가지로 구성되어 있었는데요.

일단 Deview 참가 신청을 하기 전에 주제를 살펴보았습니다.하나 하나씩 살펴보니 전보다 난이도가 높아서, 제가 발표를 듣고 완전히 이해 할 수 있을까 하는 생각이 들었습니다.하지만 Deview 참가자로 추첨이 되었을 때 어떤 주제를 들을지 고민하는 상황은 앞에 달콤한 사탕을 두고 어떤 것을 고를지 고민하는 상황 같았습니다.


행사날이 다가오고 Deview 행사장에 딱 들어갔을 때 행사장의 느낌은 먼가 제작년 Deview 보다 고급진(?) 느낌이 들었습니다. 자유롭게 앉아서 토의 할 수 있는 인테리어와 볼거리가 많았던 부스까지 더 다양하고 풍부해진 느낌이 들었습니다.

가기전에 어떤 섹션을 들을지 고민을 많이 하긴 했지만 최종적으로 아래 5가지의 섹션을 들었습니다.

1. Developing Android Libraries: Lessons from Realm 

2. Quality without QA

3. Packetbeat 와 Elasticsearch를 이용한 실시간 모니터링

4. React Everywhere

5. 데이터사이언스팀 빌딩 그리고 시스템 구축


Developing Android Libraries: Lessons from Realm 과 Quality without QA는 외국인 연사가 발표를 하셨는데, Deview 측에서 동시통역을 제공해 주셨습니다.

5가지 섹션 중 가장 쉽게 다가갈 수 있었던 섹션은 Quality without QA 입니다.나머지 4가지 섹션의 내용도 좋았지만 아직 부족한게 많은 주니어라 발표를 듣고 완벽히 이해 할 수는 없었던 섹션 이었습니다. 2~3주 뒤에 Deview측에서 영상을 올려 준다고 했기 때문에 다시 들어봐야 겠습니다.


[Quality without QA 섹션을 들으면서]


Quality without QA 섹션의 주제는 개발자 중심에서의 QA에 관한 이야기였습니다. 크게 3가지를 다루었고 그 내용은 아래와 같습니다


1. Git branch를 이용하면 독립적이고, 안정적으로 테스트를 하는데 크게 도움이 된다. 

2. 자동화된 테스트와 코드리뷰가 필요하다.

3. Dogfooding은 regression 버그를 잡는 중요한 도구이다.


Dogfooding의 어원 및 자세한 내용은 다음 링크를 참고해보세요!  


이야기 중 코드리뷰에 관한 이야기가 있어서 반가웠습니다.


[Quality without QA 발표자료 중 코드리뷰에 관한 내용]


제가 다니는 회사에서도 저의 사수님께서 코드리뷰를 해주시는데요.첫 코드 리뷰때 난도질 당하던 저의 코드는 잊을 수가 없었죠;;



하지만 코드리뷰를 하고 나면 주니어 개발자 입장에서는 좋은 코드를 보고 고치고 느낌으로써 성장하고 있다는 것을 몸소 깨닫게 되고 코드 리뷰를 하면서 전달받는 지식과 경험들은 저에게 피가 되고 살이 되더군요.또한 초반엔 코드리뷰를 할 때 보다 점점더 지날수록 배포과정이 좀 더 빠르게 진행되는 느낌이 들었습니다.실제로 발표 내용을 보니 Atlassian에서 조사한 통계에 따르면 코드리뷰를 했을 때와 안했을 때를 비교 했을 때 시간이 지날수록 코드리뷰를 했을때의 배포속도가 더 빨라진다는 조사결과가 나왔다고 합니다


[코드리뷰 유뮤에 따른 배포 속도 비교 그래프]

Tim Pettersen는 Pull Requests를 위한 10가지 팁도 소개 해줬는데요. 이에 대한 자세한 내용은 블로터 기사 에서 확인해 보실 수 있습니다.


섹션 중간중간에 부스투어도 했는데 이것저것 많이 주시더군요. 행사 끝나고 집에 돌아오는 길에 가방을 보니 두둑해져 있었습니다.



Deview 2013,2015 모두 참가해 보았지만 매번 제 자신에게 느끼는 건 '더 공부해야겠구나' 란 생각이 든다는 것입니다. Deview 2013 보다 Deview 2015 에선 세션을 듣고 이해 할 수 있는 비중이 높아 지긴 했지만 그래도 저에게 부족함을 느끼고 있습니다. 다른 대형 개발자 컨퍼런스가 요즘 많이 없어졌던데 Deview는 아직 남아 있어서 새로운 기술들을 알게 되고, 다른 개발자들이 공유해 주는 지식과 경험을 들을 수 있어서 즐거웠습니다. 내년 Deview도 기대합니다

Posted by 알 수 없는 사용자
,
Posted by 알 수 없는 사용자
,
On-ly 2.0 project review pt 자료 입니다.




Posted by 알 수 없는 사용자
,
이번 포스팅에서는 Django Web Framework의 설치와 환경 설정에 대한 알아볼까 한다.

Django (web framework)

From Wikipedia, the free encyclopedia

Django (/ˈæŋɡ/ jang-goh)[4] is a free and open source web application framework, written in Python, which follows the model–view–controller (MVC) architectural pattern.[5][6] It is maintained by the Django Software Foundation (DSF), an independent organization established as a 501(c)(3) non-profit.

Django's primary goal is to ease the creation of complex, database-driven websites. Django emphasizesreusability and "pluggability" of components, rapid development, and the principle of don't repeat yourself. Python is used throughout, even for settings, files, and data models. Django also provides an optional administrative create, read, update and delete interface that is generated dynamically through introspectionand configured via admin models.


위키백과를 참고해 찾아보면 장고 혹은 쟁고라 불리우며 무료 오픈소스 웹 어플리케이션 Framework라고 되어 있다. 또한 MVC ( Model-View-Controller) 아키텍처 패턴을 따른다고 되어있다. 특히 장고는 데이터베이스 기반의 웹 프레임 워크 개발에 용이하게 되어 있다고 한다.

그렇다면 Django를 사용하기 위한 설치 과정과 환경 설정 과정을 알아보도록 하자.

  1. Django Install

  • Django 설치

easy_install을 이용해 Django를 설치 한다.


 $ sudo easy_install Django


  • Django 설치 확인

아래 명령어를 통해 Django가 제대로 설치 되었는지 확인한다.

$ python

>>> import django

>>> print(django.get_version())

1.8.x 


  • Django Project 생성

아래 명령어를 통해 Django Project를 생성한다.

 $ django-admin startproject project_name

mysite/
    manage.py
    mysite/
        __init__.py
        settings.py
        urls.py
        wsgi.py


  • Django app 생성

아래 명령어를 통해 Django app을 생성한다.

 $ python manage.py startapp app_name

mysite/
    manage.py
    mysite/
        __init__.py
        settings.py
        urls.py
        wsgi.py

app_name/ __init__.py admin.py migrations/ __init__.py models.py tests.py views.py





  1. VirtualEnv Install (가상환경)

VirtualEnv를 사용 하는 이유는 아래와 같다.

프로젝트만의 가상 공간을 만들고 필요한 모듈들을 따로 설치 및 관리 할 수 있다.

프로젝트 간 Python 버전 충돌, 모듈 버전 충돌로부터 해방 될 수 있다.

실제 적용할 서버와 동일한 환경을 만들어 작업을 진행 할 수 있다.


  • virtualenv 설치

아래 명령어를 통해 virtualenv를 설치 해준다.


 $ sudo pip install virtualenv

# 만약 pip가 없다면

$ sudo easy_install pip



  • setting

아래 명령어를 통해 virtualenv를 사용하기 위한 설정을 해준다.


$ cd project_name

$ virtualenv virtualenv_name

# “virtualenv_name”이라는 이름의 새로운 디렉터리가 생성된 것 확인.


  • 가상 공간 활성화

해당 프로젝트 내에서 아래의 명령어를 통해 virtualenv 를 활성/비활성화 시켜준다.


$ . virtualenv_name/bin/activate

# 프롬프트가 달라진 것 확인.

(venv)$

# 비활성화 하고 싶을 때는

$ deactivate



  1. Django settings.py 설정 하기

  • Logging(Debug) 사용하기

  • Logging은 디버깅 기능을 사용하고, 로그를 기록하기 위해 제공 되는 모듈 이다.

1. Django Install 에서 생성한 project에 접근 한다.

/project_name/ 경로의 settings.py 파일에 편집기를 통해 아래 코드를 추가한다.



LOGGING = {

   'version': 1,

   'disable_existing_loggers': False,

   'formatters': {                        # message 출력 포맷 형식

       'verbose': {

           'format' : "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",

           'datefmt' : "%d/%b/%Y %H:%M:%S"

       },

       'simple': {

           'format': '%(levelname)s %(message)s'

       },

   },

   'handlers': {

       'file': {

           'level': 'DEBUG',

           'class': 'logging.FileHandler',

           'filename': 'file_name.log',        # message가 저장될 파일명(파일명 변경 가능)

           'formatter': 'verbose'

       },

   },

   'loggers': {

       'django': {

           'handlers':['file'],

           'propagate': True,

           'level':'DEBUG',

       },

       'app_name': {                   # Project에서 생성한 app의 이름

           'handlers': ['file'],          # 다른 app을 생성 후 해당 app에서도

           'level': 'DEBUG',          # 사용하고자 할 경우 해당 app 이름으로

       },                                      # 좌측 코드를 추가 작성해서 사용

   }

}





logging을 사용할 app의 파일에 아래의 코드를 추가 한 뒤 사용하면 된다.


import logging

logger = logging.getLogger(__name__)

#       logger.debug()



console 에 아래의 명령어를 통해 logging을 실행 시킨다.

 $ tail -f filename.log


  • Installed_apps 설정

project_name/settings.py 파일에서 'INSTALLED_APPS’에 아래와 같이 app_name을 추가한다.

# Application definition

INSTALLED_APPS = (

   'django.contrib.admin',

   'django.contrib.auth',

   'django.contrib.contenttypes',

   'django.contrib.sessions',

   'django.contrib.messages',

   'django.contrib.staticfiles',

   'app_name',                         # ',' 빠트리지 않도록 주의한다.

) 


  • Templates 설정

project_name/settings.py 파일에서 'DIRS’ 부분을 아래와 같이 templates 파일 경로를 설정해준다.


TEMPLATES = [

 {

       'BACKEND': 'django.template.backends.django.DjangoTemplates',

       'DIRS': [os.path.join(BASE_DIR, 'templates')],

       'APP_DIRS': True,

       'OPTIONS': {

           'context_processors': [

               'django.template.context_processors.debug',

               'django.template.context_processors.request',

               'django.contrib.auth.context_processors.auth',

               'django.contrib.messages.context_processors.messages',

           ],

       },

   },

]


  • Database 설정

project_name/settings.py 파일에서 ‘DATABASES’ 부분을 다음 과 같이 설정해 준다. 아래는 mysql을 사용할 경우 이다.

# Database

# https://docs.djangoproject.com/en/1.8/ref/settings/#databases


DATABASES = {

   'default': {

       'ENGINE': 'django.db.backends.mysql',    # 사용할 DB Engine의 종류

       'NAME': 'database_name',                          # 해당 Engine에서 사용할 DB name

       'USER': 'database_user_id',                       # 해당 Engine에 접근 하기 위한 user_id (ex. root)

       'PASSWORD': 'database_password',        # 해당 Engine에 접근 하기 위한 password

       'HOST': 'localhost',

       'PORT': '',

   }

}

DATABASE_OPTIONS = {'charset': 'utf-8'}       # Database encoding 설정


다른 종류의 DB를 사용 할 경우 Django의 Documentation가 제공하는 아래 링크를 참고 하면 된다.

https://docs.djangoproject.com/en/1.8/ref/settings/#std:setting-DATABASES


  • Language 설정

project_name/settings.py 파일에서 'LANGUAGE_CODE' 부분을 다음과 같이 설정해준다.

LANGUAGE_CODE = 'ko-kr'


TIME_ZONE = 'Asia/Seoul'




  • Static files 설정

project_name/collected_statics 디렉터리를 생성 해준다.

project_name/settings.py 파일에서 'STATIC’  부분을 다음과 같이 설정 해준다.

# Static files (CSS, JavaScript, Images)


# https://docs.djangoproject.com/en/1.8/howto/static-files/


STATIC_ROOT = os.path.join(BASE_DIR, 'collected_statics')     #


STATIC_URL = '/assets/'


STATICFILES_DIRS =(

       os.path.join(BASE_DIR, 'static'),

)




프로젝트에 사용할 js, css파일은 project_name/static/ 의 경로에 넣어준다.

project_name/template 경로에 있는 HTML로 작성된 template 파일에 아래와 같이 적용해 사용한다.

{% load static %}

<link href="{% static 'css/bootstrap.css' %}" rel="stylesheet">

<script src="{% static 'js/jquery-2.1.3.js' %}"></script> 



아래 명령어를 통해 prject_name/static/ 에 있는 js, css 파일을 collected_statics 파일로 모아준다.

 $ python manage.py collectstatic

명령어 실행 시 아래와 같이 이미 존재하는 파일을 덮어씌울 것이냐는 질문을 하게 되면 'yes'라고 입력해주면 된다.

< 그림 Collectstatic 명령어 실행 결과 >



  1. Django url

  • Template에서 Static 파일을 사용하기 위해 4) Django setting의 Static files 설정과 더불어 project_name/urls.py 파일에도 아래와 같이 코드를 추가해 주어야 한다.


from django.conf.urls import include, url, patterns

from django.contrib import admin

from django.conf import settings

from django.conf.urls.static import static

from django.contrib.staticfiles.urls import staticfiles_urlpatterns


urlpatterns = [

       url(r'^admin/', include(admin.site.urls)),

] + static(settings.STATIC_URL, document_root = settings.STATIC_ROOT)


urlpatterns += staticfiles_urlpatterns()


이것으로 Django를 설치하고 환경설정을 위해 Setting 파일의 install_app, Template, DB, Static File, Language, Time Zone을 비롯해 디버깅과 로그 기록을 위한 Logging 모듈설정을 마치도록 하겠다.






'Newbie's Log' 카테고리의 다른 글

Deview 2015 Day 1참가 후기  (0) 2015.09.22
On-ly 2.0 project review  (0) 2015.08.18
Evernote API 사용하기  (0) 2015.07.29
주니어 개발자의 On-ly 2.0 프로젝트 경험기-개발편  (0) 2015.07.21
On-ly 소개 Prezi  (0) 2015.07.16
Posted by 알 수 없는 사용자
,



이번 포스팅은 Evernote 에서 무료로 제공하고 있는 Evernote API를 소개 하고

Evernote-SDK에 포함되어 있는 Sample 코드를 실행해보고자 한다.

Evernote API는 개발자들이 본인들의 프로젝트에 Evernote 연동을 할 수 있도록 제공해주는 오픈소스이다. 

Evernote의 기능 중 note, notebook 과 관련해서 작성, 판독, 갱신 및 삭제 기능등을 모두 연동해 사용할 수 있도록 

Thrift 방식으로 제공 하고 있다.

이번 포스팅에서는 Evernote API를 사용하기 위해 제공되는 Evernote-SDK에 포함된 Sample 코드중 EDAMTest.py 

라는 파일을 이용해 진행을 하려고 한다.


https://dev.evernote.com/

위 URL은 Evernote API 이용을 위한 SDK파일과 Documentation을 제공 하고 있는 에버노트 개발자 홈페이지 이다. 홈페이지에 접속 하게 되면 아래와 같은 페이지를 볼 수 있다. 

<Evernote 개발자 홈페이지>

1. 가운데에 있는   버튼을 통해 다운로드 페이지로 이동 할 수 있다.


Evernote API는 이 포스팅에서 진행할 Python용 외에도 IOS, Android와 같은 모바일 용과 Ruby, C++, Perl 과 같은 다른 언어를 위한 SDK파일도 제공하고 있다. 

우리는 Python을 이용 할 것이기 때문에 가장 위에 있는 Python용 Install SDK 와 Quick-start-Guide 를 이용하면 된다.


2. Install SDK를 클릭하면 Evernote-SDK-Python이 올려져 있는 GitHub로 이동된다. 이곳에서 SDK 파일을 다운로드 해 주도록 한다. 다운로드를 완료 했다면 그 다음 단계는 일종의 Tutorial 과정인 Quick-start-Guide 이다. 

zip파일을 받아 압축을 풀어도 되고 아래와 같이 git명령어를 이용해도 된다.


$ git clone  https://github.com/evernote/evernote-sdk-python.git



3. Quick-Start-Guide를 클릭하면 위에서 다운로드한 Evernote-SDK의 기본적인 사용법을 Tutorial 형식으로 제공하는 페이지로 이동하게 된다.


4. Tutorial 및 Evernote API를 이용하기 위한 준비

첫번째 단계는 준비 과정이다. 

1) https://sandbox.evernote.com (test용 Evernote 샌드박스 사이트) 의 계정을 만들어야 한다. 사이트에 접속하게되면 아래와 같은 설명을 볼 수 있는데 살펴 보면, 해당 사이트는 test용이고, 실제 Evernote와 별개로 운영되는 사이트 임을 알 수 있다. 

계정을 생성하였다면 그다음 단계는 Evernote API KEY를 발급 받아야한다.

2) 두번째 항목의 'get one here'를 클릭하거나 우측 상단의  를 클릭하게되면 아래와 같이 API KEY를 발급 받을 수 있는 화면을 볼 수 있다. 

좌측은 직접 작성을 하면 된다. 그리고 우측을 보면 API Permissions가 있다. 이는 Evernote API를 이용해 접근해서 처리할 수 있는 권한에 따라 2가지로 나뉜 것이다. Basic Access의 경우 생성하고 정보를 불러와서 읽는 것만 가능하고, Full Access의 경우 basic 기능에 추가로 업데이트와 공유 노트북의 생성까지 가능하다. 사용하려는 용도에 맞게 선택하면 된다. 우리는 우선 Full Access로 선택 하도록 한다. 

다음으로 바로 아래에 있는 3개의 check box를 살펴보면 첫번째는 사용자Data 동기화와 관련된 것이고 두번째는 Evernote에서 제공하는 업데이트 정보를 email로 구독할 것인지에 대한 것이고 세번째는 API KEY를 발급 받기위해 약관에 동의하는 것과 관련된 것이다. 

우리는 첫번째와 두번째는 제외하고 세번째만 체크 한뒤 Request Key 버튼을 클릭하면된다.

Evernote API KEY인 Consumer KEY와 Consumet Secret이 발급 된 것을 확인 할 수 있을 것이다. 발급 받은 KEY는 이후 진행할 Sample Script에서 사용 해야 되니 따로 적어 놓도록 하자. 해당 KEY는 발급 과정에서 작성한 e-mail을 통해 언제든지 다시 확인 할 수있으니 위 페이지를 벗어 났더라도 걱정 하지 말자.

3) Quick-start-guide의 What you need 에는 설명되어 있지 않지만 한가지 더 준비해야 될 것이 있다. 

바로 Developer Token을 발급 받는 것이다. Developer Token은 Evernote 계정에 접근해 작업을 할 수 있도록 승인 받기 위해 꼭 필요한 것이다. Developer Token은 아래 링크를 통해 발급 받을 수 있다. 

두개의 링크 중 우리는 Sample Script를 실행해볼 것이기 때문에 sandbox용을 발급 받도록 한다. Production용은 실제 개발에 적용할 경우 이용면 된다.

https://sandbox.evernote.com/api/DeveloperToken.action - Sandbox 용

https://www.evernote.com/api/DeveloperToken.action - Production 용

Developer Token의 경우 반드시 따로 적어서 저장해놓도록 하자. 해당 토큰은 잃어 버렸을 경우 재발급 받는 방법 밖에 없다. 아래 그림에서 가운데 위치한 Developer Token 을 저장해 놓으면 된다.

이것으로 준비과정은 끝이 났다. 이제부터는 위에서 준비한 1)Evernote-SDK 2) API KEY 3) Developer Token 을 가지고 Sample Script를 진행해 보자.


5. 우리는 위에서 Evernote-SDK를 이미 다운 받았기 때문에 다운 받은 Evernote-SDK-Python 파일이 있는 곳으로 이동해 진행을 하면 된다.

아래와 같이 해당 폴더로 이동했다면 setup.py 라는 파일이 있는 것을 확인 할 수 있을 것이다.

아래의 명령어를 실행한다.

해당 명령어는 Evernote SDK 에있는 Library들을 python directory에 위치 시켜 사용할 수 있게 해준다.

$ python setup.py install


6.  위의 install 이 제대로 진행 되었는 지 확인 하기위해  아래의 명령어를 실행해본다. Error 메세지가 없이 조용히 종료 된다면 제대로 install된 것이다.

$ python -c 'from evernote.api.client import EvernoteClient'


7.  ./sample/client/ 경로로 이동한다.

EDAMTest.py 파일이 있는 것을 확인 할 수 있다. 해당 파일을 열어서 아래와 같이 첫번째 auth_token 부분에 위에서 발급 받은 dev_token 을 입력한 뒤 저장 해준다.

다음 명령어를 실행해준다.

$ export PYTHONPATH=../../lib; python EDAMTest.py

명령어를 실행하게 되면 1개의 노트북을 발견하였고 노트북의 이름은 test1 이고 새로운 노트를 생성하였다는 메세지와 함께 새로 생성한 노트의 GUID를 출력하는 것을 볼 수 있을 것이다. 단지 콘솔에 메세지만 출력된 것이 아니고

https://sandbox.evernote.com/    샌드박스용 Evernote에 로그인하면 아래와 같이 새로운 노트가 생성된 것을 확인 할 수 있다.

노트가 생성되는 것 까지는 확인 하였지만 어떻게 활용을 해야될지 감이 잘 안올 것이다. 아래의 코드는 노트생성을 위해 가장 기본이 되는 형식이다. 

client = EvernoteClient(token=auth_token, sandbox=True)

note_store = client.get_note_store()

notebooks = note_store.listNotebooks()


# To create a new note, simply create a new Note object and fill in

# attributes such as the note's title.

note = Types.Note()

note.title = "Test note from EDAMTest.py"

note.content = '<?xml version="1.0" encoding="UTF-8"?>'

note.content += '<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'

note.content += '<en-note>Here is the Evernote logo:<br/>'

note.content += '<en-media type="image/png" hash="' + hash_hex + '"/>'

note.content += '</en-note>'

created_note = note_store.createNote(note)


노트생성을 위해서는 사용자의 정보가 우선 있어야 한다. 이때 사용되는 것이 auth_token (dev_token)이다. 토큰을 이용해 노트를 작성할 계정의 사용자임을 확인시킨다.

client = EvernoteClient(token=auth_token, sandbox=True)

그다음 필요한 것은 노트저장소의 정보이다. 위에서 확인한 사용자의 노트저장소의 정보를 가져온다.

note_store = client.get_note_store()

노트 저장소에는 노트북이 있을 것이다. 한개일 수도 있고 여러개 일 수도 있다. Evernote-sandbox 계정을 생성하고 로그인 했을 때 노트북을 한개 만들었을 것이다. 해당 노트북이 기본 노트북으로 지정되고 노트를 생성할 노트북을 우리가 따로 지정해주지 않는다면 기본 노트북에 자동으로 생성이 될것이다.

notebooks = note_store.listNotebooks()

노트를 생성할 위치와 계정 정보를 확인 했다면 노트를 만들어보자. 노트를 만들기 위해 노트 객체를 가져온다.

note = Types.Note()

노트의 제목을 설정해준다.

note.title = "Test note from EDAMTest.py"

노트의 본문내용은 Evernote 에서 자체적으로 사용 하는 ENML 이라는 markup language를 사용해야된다. 때문에 3번째 줄의 '본문 내용' 을 제외한 나머지 코드의 형식을 맞춰 주어야 생성이 가능하다.

note.content = '<?xml version="1.0" encoding="UTF-8"?>'

note.content += '<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'

note.content += '<en-note>본문 내용<br/>'

note.content += '</en-note>'

이제 위에서 작성한 제목과 본문내용을 가진 노트를 생성하면 된다.

created_note = note_store.createNote(note)


Evernote API 사용에 있어서 가장 기본이 되는 노트 생성과정을 알아보았다. Evernote API를 사용하기 위해 기억해야될 것은
auth_token (developer_token)과 모든 기능 사용에 기본이 되는 사용자의 정보를 확인시켜주고 노트저장소의 정보를 가져오는 것이다. 이 세가지를 잘 활용한다면 Evernote API 사용에 많은 도움이 될 것이다.





'Newbie's Log' 카테고리의 다른 글

On-ly 2.0 project review  (0) 2015.08.18
Django 설치 및 환경설정  (0) 2015.08.03
주니어 개발자의 On-ly 2.0 프로젝트 경험기-개발편  (0) 2015.07.21
On-ly 소개 Prezi  (0) 2015.07.16
Ubuntu에서 OracleJDK설치하기  (0) 2015.06.23
Posted by 알 수 없는 사용자
,

Reference

1. Storm-starter : https://github.com/apache/storm/tree/master/examples/storm-starter

2. Storm : https://github.com/apache/storm

3. Implementing Real-Time Trending Topics With a Distributed Rolling Count Algorithm in Storm : http://www.michael-noll.com/blog/2013/01/18/implementing-real-time-trending-topics-in-storm/


소개

회사 업무로 Apache Storm(이하 Storm)과 관련된 프로젝트를 완료하고 휴식중에, Storm 기초를 설명할 수 있을만한 예제를 만들어보기 위해서 Storm Starter를 참조하여 간단한 프로젝트를 만들었다.

이 프로젝트는 Twitter Sample Public Status API(https://dev.twitter.com/streaming/reference/get/statuses/sample)를 사용하여 Twitter realtime stream data의 일부를 Input으로 하고, HashTag 정보를 추출한 후 일정 시간 간격(emit frequency)으로 일정 시간 동안(window length)의 일정 갯수(TOP_N)의 Top HashTag를 생성하여 출력하는 프로젝트이다.

Storm-starter project에서 많은 소스코드를 가져 왔으며 Twitter Library는 Twitter4J를 사용한다.

Project source : https://github.com/forcemax/storm_twitter_hashtag


실행하기

0. Prerequisites

Java 1.7 이상, Storm 0.9.5, Maven, Git

(Twitter API를 사용하기 위한 consumerKey, consumerSecret, accessToken, accessTokenSecret을 변경하지 않으면 실행이 안된다.)

1. 소스 가져오기

git clone https://github.com/forcemax/storm_twitter_hashtag

2. 소스 빌드하기

$ mvn clean package

3. Storm Cluster에 Topology submit

$ storm jar StormTwitterHashtag-0.0.1-SNAPSHOT-jar-with-dependencies.jar com.embian.forcemax.twitter.StormTwitterHashtagTopologyRunner server

4. Storm UI에서 확인하기



그림. topology 목록



그림. topology 상세 정보


설명

- Rolling Count Algorithm은 Reference 3번 사이트를 참조

- Topology는 다음과 같이 구성되어 있다. Twitter API를 사용하기 위해서는 Twitter에 App 등록을 해야하며, App 등록을 하면 consumerKey, consumerSecret, accessToken, accessTokenSecret 값을 얻을 수 있다. 다음 코드에 해당 값을 넣어서 사용한다.



그림. Topology 구성도

- TwitterSpout은 Twitter4J Library를 사용하며, LinkedBlockingQueue를 사용해서 새로운 Public Status가 있을때 Queue에 저장한다. nextTuple() 호출시에 Queue에서 꺼내서 ExtractHashTagBolt에 넘긴다.

- ExtractHashTagBolt는 받은 Public Status에서 HashTag만 뽑아내서, RollingCountBolt로 넘긴다. HashTag의 갯수 만큼 emit이 발생한다.

- RollingCountBolt는 생성할 때 인자로 받은 window length와 emit frequency 값을 바탕으로, emit frequency마다 window length에 속하는 데이터에서 word별 count를 계산해서 IntermediateRankingsBolt로 넘긴다. 이때, emit frequency마다 emit을 하기 위해서 TickTuple을 사용하는데, TickTuple은 Storm 0.8에 새로 들어간 기능이며 Component(Spout, Bolt)내에서 일정 주기 마다 Tuple을 발생시키는 기능이다.

IntermediateRankingsBolt와 TotalRankingsBolt는 생성할때 인자로 topN, emit frequency를 받으며, 입력된 word별 count를 바탕으로 상위 topN개의 word와 count를 뽑아내고 emit frequency 마다 emit한다. IntermediateRankingsBolt는 parallelism hint를 크게 주어 map-reduce 구조에서 map의 역할을 하고, TotalRankingsBolt는 parallelism hint를 1로 주고 reduce의 역할을 한다. emit frequency마다 emit을 하기 위해서 RollingCountBolt와 마찬가지로 TickTuple을 사용한다.

- 마지막으로 PrinterBolt는 TotalRankingsBolt에서 emit한 Tuple을 출력하기 위해서 사용하며 특별한 기능은 없다.


이 프로젝트는 Storm의 Spout(nextTuple과 open), Bolt(execute, prepare)와 Topology wiring에 대한 이해만 있다면 코드를 보는데 아무 무리가 없을 정도로 간단한 예제이다. 그러나 외부 서비스(Twitter)과의 연계를 통한 Spout 구성, Atomic하게 역할을 분리한 Bolt, parallelism hint를 조절하여 성능을 향상시키는 방법을 확인해 보기에 알맞은 예제이다.

Twitter Sample Public Status API는 10분에 20000 statuses 정도의 데이터밖에 제공하지 않으므로, 한대의 Storm에서 처리하기에 충분하다.

초보자가 알아보기 쉽게 코드가 구성되어 있으니, Storm을 이용하여 realtime CEP 엔진을 공부하려는 분들에게 많은 도움이 되었으면 한다.


posted by forcemax

Posted by 알 수 없는 사용자
,

오늘은 "주니어 개발자의 On-ly 2.0 프로젝트 경험기-개발편"을 포스팅한다.

"주니어 개발자의 On-ly 2.0 프로젝트 경험기-기획편"이 궁금하면 이곳을 클릭하길 바란다.


다사다난했던 On-ly 2.0 기획 단계를 거치고 직접 기획했기에 순조로울 것만 같았던 개발 과정은 결코 순조롭지 못했다.


그중 첫번째. 버블 차트

우리가 많은 시간을 잡고 있었던 과정 중 하나가  친구와 나의 게임 플레이 시간 데이터를 버블차트로 보여주는 부분이였다. 

초기에 기획 했을 때 아래 그림과 같은 버블차트 + 알파 를 원했다.


<나와 사용자의 게임 플레이 시간 버블 차트>  


버블차트에 버블 하나하나가 생동감이 있길 바랬고, 거슬리지 않을 만큼의 애니메이션 효과도 들어가길 원했다. 또한 난 최근에 본부장님의 소개로 D3.js라는 것을 알게 되었고(참고,D3 소개), D3.js를 On-ly에서 사용하고 싶었다. 하지만 회사내에서 D3.js 말고 안드로이드 차트 라이브러리를 쓰자는 의견이 나왔고,  Hellocharts 라이브러리 를 사용하게 되었다. 이 라이브러리를 사용하기에는 먼가 아쉬움이 많이 남았지만 그래도 최대한  잘 시각화해서 보여주고 싶었다. 아래 그림과 같이 말이다.


<D3.js를 사용한 버블 차트의 예>



아래는 버블차트를 이용한 1차 버전이다. 일주일간의 나의 게임플레이시간 데이터와 친구의 게임플레이시간의 데이터를 seekbar를 통해 보여주도록 했는데 컬러의 일관성이 없어 산만하고 한 눈에 들어오지 않는다는 평을 받았다. 


    

<3일치 버블차트 1차 버전>


그래서 데이터를 어떻게 표현 할 것인지 오랜 시간 동안 고민을 했는데 최종적으로 선택한 것이 아래 그림과 같다. 


    

<3일치 버블차트 최종 버전>


컬러를 덜 사용하고, top1 사용자와 친구 그리고 나에 집중하여 1차 버전보다는 눈에 잘 들어온다 . 하지만 아쉬운건 사실이었다. 좀 더 생동감있고, 전체적으로 앱과 어울리는 느낌이 부족해 보였다. 버블차트 작업으로 인해 일정을 많이 잡아먹어서 현 상태에 일단 만족해야했고 더 발전된 방향에 대해서는 다음 버전을 기약해야 했다. 


두번째는. 파이차트


On-ly 개발을 하면서 갑자기 문득 들었던 생각이 "내가 가장 많이 하는 게임은 무엇일까?"라는 원초적인 질문에 대해서는 On-ly에서 아무런 데이터를 보여주고 있지 않다는 것을 깨달았다. 

그래서 우리는 사용자가 많이 하는 top6 게임에 대해 파이차트로 보여주는 기능을 추가 했고, 이 기능을 구현하기 위해 Hellocharts라이브러리를 제외한 다른 차트 라이브러리를 찾아보았다.

그러다 발견한 것이 MPAndroidChart !  

이 차트라이브러리에 대한 소개는 아래 영상과 같다. 




차트의 x축, y축 조정 부터, label기능, 다양한 에니메이션 효과,거기다 오픈소스인 MPAndroidChart가 상당히 마음에 들어서 MPAndroidChart github에 별을 쏴주고, 바로 On-ly에 적용하였다. 

요렇게!


(MPAndroidChart를 이용한 파이차트)


세번째, weibo연동하기. 


On-ly가 샤오미와 이야기가 오고가서 샤오미 앱스토어에 On-ly를 배포해야 했다. 일단 중국에선 facebook이 막혀있으니 weibo연동이 필요했다. weibo api문서는 영문을 지원해 줬지만 나머진 죄다 중국어로 되어 있으니 난감 했다(중국어를 빨리 공부해야겠다는 생각이 가장 먼저 앞섰다.). 거기다 weibo sdk version이 froyo여서 안드로이드 버전차이로 인한 삽질을 하게 되었다. 언제적 froyo를...


친절하신 카네기멜론 대학 학생이 Sina Weibo API GUIDE를 작성했는데 개발하는데 상당히 많이 도움이 되어서 그 링크를 공유한다.현재 중국시장에서는 weibo 친구들과 함께 On-ly를 즐길 수 있으며, On-ly는 샤오미 스토어에 올라가 있다.

>>>>>>>On-ly 샤오미 스토어로 이동


마무리하며..

On-ly2.0을 기획하고 배포하는데 5개월의 기간이 지나갔다.회사분들과 함께 기획하고, 개발하고, 테스트 하며 한땀한땀 정성을 쏟아부었다. 이 앱에 애정을 많이 쏟아 부어서 그런지 On-ly 2.0을 Release하고 나서 아쉬운 점과 보완 해야 할 점(User Interface변경 등)이 속속히 생겨났다. 다음 버전에는 그러한 아쉬움들을 반영하고 싶다.

간단한 On-ly 자료를 소개로 주니어 개발자의 On-ly 2.0 프로젝트 경험기를 마무리한다.


(On-ly 소개자료)

-끝-     



'Newbie's Log' 카테고리의 다른 글

Django 설치 및 환경설정  (0) 2015.08.03
Evernote API 사용하기  (0) 2015.07.29
On-ly 소개 Prezi  (0) 2015.07.16
Ubuntu에서 OracleJDK설치하기  (0) 2015.06.23
주니어 개발자의 On-ly 2.0 프로젝트 경험기-기획편  (0) 2015.06.17
Posted by 알 수 없는 사용자
,

On-ly 소개 Prezi

Newbie's Log 2015. 7. 16. 14:49
Posted by 알 수 없는 사용자
,

사장님 소개로 Agar.io라는 게임을 해보았습니다.

Javascript와 WebSocket을 이용한 웹게임인데, 간단하면서도 중독성 있는 게임으로 시간가는줄 모르고 하게되었네요.

하지만, 워낙 게임치 인데다가 나이까지 40줄에 들어서니 순발력도 떨어져서 살아남는 것이 쉽지가 않더군요.

그래서 Agar.io Bot이라는 것을 찾아서 설치해 보았습니다.

혼자서 다른 유저들로부터 잘 도망다니면서 쑥쑥 크더군요.그런데, Bot이 자동으로 알아서 게임하는걸 보고만 있자니.... 이건 내가 게임을 하고 있는건지, 게임이 나를 가지고 노는 건지...zzzzzz

그래서 자동사냥 위주의 모바일 게임에서 힌트를 얻어 최소한의 사용자 액션을 Bot에 추가 해보기로 했습니다.


1.Agar.io와 Bot에 대해서

Agar.io(http://agar.io)는 자신보다 큰 세포(세포인지 뭔지 모르겠지만 그냥 편하게 세포하고 합시다.)는 피해 다니면서 작은 세포를 먹어서 자신의 몸집을 키워나가는 게임 입니다.

클라이언트는 모두 javascript로 되어 있으며, 서버와는 WebSocket을 통해 실시간으로 통신하는 것으로 보입니다. 그래서 당연히 Bot도 모두 Javascript로 되어 있으며, 소스도 공개되어 있어서 누구나 수정 가능 합니다.

Agar.io는 그냥 보기에는 굉장히 간단한 게임인 것처럼 보이지만, 게임 감각이 비루하거나 순발력이 심히 떨어지는 사람들은 다른 사람들의 먹이 신세를 벋어나지 못하는 어찌보면 참 슬픈 게임입니다.

Agar.io Bot은 이러한 슬픈 몸뚱이를 가진 사람들을 위한 자동사냥 프로그램입니다. 혼자서 알아서 잘 피해 다니면서 알아서 잘 큽니다.

물론 완벽하지는 않습니다. 가끔 죽음을 피할 수 없는 상황에 놓이기도 하고 어이없는 행동(버그?)으로 죽기도 합니다. 결국 상위랭커로 가기 위해서는 사람의 손길이 필요한 것 같습니다.

그리고 Bot의 최대 단점은..... 당연히 직접 게임을 하는 것이 아니기 때문에 금방 흥미가 떨어집니다.

Agar.io Bot의 소스는 GitHub에 공개되어 있습니다.


2. 추가할 기능에 대해서

자동사냥을 기본으로 하는 요즘 모바일 게임을 보면 대부분의 게임 플레이는 자동사냥 봇이 하면서 사용자는 지루하지 않게 스킬 버튼만 눌러주는 방식이 유행인것 같습니다.

모바일 환경의 특성 때문에 케릭터를 직접 컨트롤하는게 어렵기도 하고 피곤하기도 해서 나온 새로운 게임 플레이 패턴인 듯 합니다.

Agar.io Bot에도 이런 방식을 도입해 보도록 하죠. 조금이나마 다이나믹한 플레이를 위해~~~

우선, Bot의 움직임을 간접적으로 제어할 수 있는 버튼을 하나 추가해 보기로 하죠.

Bot의 기본 움직임을 "수비적"이라고 정하고 여기에 "공격적"으로 움직일 수 있는 옵션을 하나 추가하고, 버튼하나를 추가해서 클릭할 때마다 "수비적 움직임"과 "공격적 움직임"이 서로 스위칭 되도록 해보겠습니다.


[그림 1] Agar.io Original


[그림 2] Agar.io With Bot



3. 1단계 - Agar.io Bot 설치하기


(1) 사용자 스크립트 실행을 위한 브라우저 확장 프로그램 설치

Agar.io Bot을 설치하기 위해서는 브라우저에 추가앱을 설치해야 합니다.

[Firefox를 사용할 경우]

Greasemonkey 설치

https://addons.mozilla.org/ko/firefox/addon/greasemonkey/

위의 링크를 통해서 Firefox의 Add-on인 Greasemonkey를 설치합니다.

[Chrome을 사용할 경우]

Tampermonkey 설치

https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=ko

위의 링크를 통해서 Chrome의 확장프로그램인 Tampermonkey를 설치합니다.

참고로 Greasemonkey나 Tampermonkey라는 프로그램은 원하는 웹페이지가 로딩된 후 사용자가 작성한 스크립트 파일이 실행되도록 해주는 Plug-in 프로그램 입니다. (자세한건 구글신에게...)


이 다음부터는 Firefox나 Chrome 모두 진행 과정은 거의 비슷하기 때문에 Firefox를 기준으로 설명해 드리도록 하겠습니다. (IE는 모릅니다. Agar.io Bot 은 Firefox, Chrome, Opera만 지원하는 것 같습니다. 죄송)

사용자 스크립트를 실행시킬수 있는 프로그램이 브라우저에 설치가 되었다면, 이 다음 단계는 Bot 소스를 설치하는 단계입니다.


(2) Agar.io Bot 설치

https://github.com/Apostolique/Agar.io-bot

위의 링크로 들어가면, Agar.io Bot의 자바스크립트 소스가 있는 GitHub 사이트가 나옵니다.

Bot을 실행하기 위해서는 "bot.user.js" 파일과 "launcher.user.js" 파일이 필요합니다.

먼저, "bot.user.js" 파일을 클릭하면 전체 소스가 출력되며, 소스 상단에 있는 3개의 버튼중 "Raw"버튼을 클릭하면 (1)에서 설치한 확장프로그램의 스크립트 설치화면이 출력됩니다.

"install" 버튼을 클릭하면 "bot.user.js" 스크립트 설치가 완료 됩니다.

"launcher.user.js" 스크립트도 같은 방식으로 설치 합니다.

참고로,

"bot.user.js" 파일은 bot의 자동사냥을 위한 공식, 행동 규칙 등이 정의 되어 있는 파일이고, "launcher.user.js"파일은 agar.io의 원래 스크립트 파일에 "bot.user.js"을 이용해 실제 Bot을 띄워줘는 스크립트를 추가한 파일 입니다.

자, 이제 두개의 파일의 설치를 마쳤으면, http://agar.io에 접속하면 서버 접속부터 사냥 까지 모두 자동으로 진행되는 것을 확인 하실 수 있습니다. (닉네임까지도 봇에 정해진 닉네임으로 플레이가 되버립니다.)



4. 2단계 - Agar.io Bot에 새로운 기능 추가하기

새로운 기능을 추가하기 위해서는 (2)에서 설치한 스크립트 파일을 수정해야 합니다.

스크립트 파일을 수정할려면 스크립트를 편집기에 띄워야 겠죠. 스크립트를 편집기에 띄우는 방법은 다음과 같습니다.

[Firefox]

부가기능 => User Scripts(화면 왼쪽 메뉴) => Launcher의 환경설정 => 이 유저 스크립트 편집 클릭(화면 하단)

[Chrome]

Toolbar에서 Tampermonkey 아이콘 클릭 => Dashboard => Launcher 클릭


이제 launcher.user.js 스크립트 파일이 편집기에 출력이 되었을겁니다.

먼저, 코드를 추가할 위치를 찾아야 합니다.

console.log("Running Bot Launcher!");
(function (h, f) {

// 인자 변수인 h, f는 사람들 마다 다를 수 있습니다.

// 원본 agar.io 의 소스는 자바스크립트 압축 프로그램을 거쳐 나온 소스다 보니

// 변수명과 함수명은 1~2글자의 알파벳으로 치환되어 있고, 이마저도 버전따라 다를 수 있습니다.

// ======= 이부분에 추가 코드를 삽입하시면 됩니다. ==============

.......

다음의 라인을 추가해 주세요.

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // by yam-Embian start
  var toggleDefence = true;   
  window.getToggleDefence = function() {
    return toggleDefence;
  }
  var toolbox = window.jQuery("<div id='toolbox'></div>");
  toolbox.css({"position":"fixed","width":"100%","height":"90px","bottom":"0px","text-align":"right","padding":"10px"});
  window.jQuery("body").append(toolbox);
 
  var offBtn = window.jQuery("<input type='button' id='offBtn' value='공격적으로 플레이하기' />");
  offBtn.css({"width":"150px","height":"70px"});
 
  offBtn.click(function(){
    toggleDefence = !toggleDefence;
    window.setDarkTheme(!getDarkBool());
    if (toggleDefence) {
        offBtn.val("공격적으로 플레이하기");
    }
    else {
        offBtn.val("수비적으로 플레이하기");
    }
  });
  toolbox.append(offBtn);
  // by yam-Embian end
  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


사실 원본 소스를 모두 분석할 수 있으면 참 좋겠지만, agar.io의 원본 소스는 압축프로그램을 거쳐 나온 소스이기 때문에 변수명과 함수명이 모두 의미 없는 알파벳으로 이루어져 있어서 분석하는게 쉽지 않습니다.

하지만, 시간이 된다면 천천히 분석해 가면서 변수명과 함수명을 의미있는 나만의 값으로 치환해 나가는 작업을 해보고 이것도 따로 포스팅 해보고 싶은 욕심이 있습니다......만 할 수 있을 지는 잘 모르겠습니다.


위의 코드를 추가하셨다면 다시 한번 agar.io에 접속해 보세요.

화면 하단에 추가 기능 활성화를 위한 버튼이 출력된것을 보실 수 있습니다. 한번 클릭해 보세요. 화면 테마가 Night 버전으로 바뀌게 됩니다. 오호~~

아직은 Bot의 움직임을 바꿔주는 코드가 들어가지 않아서 테마만 바뀌게 됩니다.

처음에는 움직임 규칙이 바뀔때마다 큰 글씨로 바뀐 규칙을 출력해 줄려고 했으나, 귀찮아서 아에 테마를 바꿔주는 방식으로 수정했습니다.

테마를 바꿔주는 코드는 다음 라인입니다.

window.setDarkTheme(!getDarkBool());


움직임 규칙이 바뀔때 테마를 바꾸는게 아니라 다른 액션을 추하고 싶으시면 저 라인 대신 다른 코드를 직접 적으시면 됩니다.


이제 실제 움직임 규칙을 적용해 보도록 하겠습니다.

세포의 실제 움직임을 결정하는 코드는 "bot.user.js" 파일에 있습니다.

위에 launcher.user.js파일을 열었던것 처럼 bot.user.js 파일을 열어주세요.

수정할 곳을 딱 한군데 입니다.

코드를 추가할 위치는 findDestination 함수입니다.

"function findDestination"으로 검색하시면 한방에 찾으실 수 있습니다.

function findDestination() {
  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // by yam-Embian
  splitDistance = f.getToggleDefence() ? 700: 20;
  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ...
  ...


agar.io bot은 자신을 공격할 수 있는 세포들은 + 710 크기 만큼의 영향력 범위를 추가로 가지도록 해두었습니다.

[그림 3] 나를 공격 가능한 세포의 영향력 범위


그림을 보시면 여러개의 원이 어지럽게 그려진것을 보실 수 있는데, 초록색 운은 내 세포가 도망갈 수 있는 방향을 나타냅니다.

초록색 반대쪽에 있는 주황색 반원은 공격가능한 세포의 범위에 들어왔을 나타나며, 주황색 반원이 없어질때까지 도망을 가게 되어 있습니다.

왼쪽에 태극기 세포가 보이실 겁니다. 태극기 세포 주위에 연한 주황색 원이 있는데, 이 세포는 현재 분열을 할 수 없는 상태로 공격 범위가 주황색 원까지라는 말입니다.(이게 버그인지는 모르겠지만, 가끔 주황색 원을 가지는 세포가 분열해서 저를 먹는 경우도 있었습니다.)

화면에 보이는 빨간색 원이 분열 가능한 세포의 공격가능 범위 입니다. 실제 세포는 보이지 않지만 그 공격 범위에 제 세포가 들어가서 피하는 모습입니다.


위에 추가한 코드는 결국 공격가능 세포의 공격력 범위를 조작하는 코드입니다.

실제로 공격범위에 들어갔다고 해서 모든 세포들이 내 세포를 공격하는 것도 아닌데, 큰 세포들 주위에 있으면, 공격범위에서 벋어나기 위해 회피 기동만 하다가 오히려 코너에 몰려서 죽는 경우를 자주 보게 됩니다.

그래서 정말 피해야 할 상황인지 아닌지 플레이어가 직접 판단해서 결정할 수 있도록 세포들의 공격 범위를 조작할 수 있는 코드를 추가한 것입니다.


이제 필요한 코드 수정은 모드 끝났습니다.

다시 한번 플레이를 해볼까요?


[그림 4] 공격적인 모드를 선택했을 때 화면


공격적인 모드일 떄 화면을 보시면 제 세포 뿐만 아니라 상대방 세포 주위의 원이 아직 작아 진것을 보실 수 있습니다.

공격적인 모드 일때는 상대방 세포가 정말 가까이 접근하기 전까지는 회피 기동을 하지 않고 먹이를 먹는데 집중하게 됩니다.

"수비적으로 플레이하기" 버튼을 클릭하면 [그림 3]처럼 화면 모드나 하얀색으로 다시 바뀌고 원래 방식대로 세포가 움직입니다.


기본은 공격모드로 해서 플레이 하다가 위험할 것 같으면 수비모드로 바꾸는 등 다이나믹한 플레이가 가능해 졌습니다. ㅎㅎㅎ



5. 3단계 - 부가 요소 추가하기

위에 내용까지만 따라 하셔도 충분히 재미 있으실 것 같은데, 그래도 재미를 위해 몇가지 요소를 더 추가해 보도록 하겠습니다.


첫번째는 내 세포의 이름 입니다.

Bot은 무조건 닉네임이 "NotReallyBot"으로 고정이 되어 있습니다. (이름참 넌센스 합니다.ㅡㅡ;;)

Bot의 닉네임을 정해주는 파일은 launcher.user.js 파일 입니다.

"NotReallyBot"이라고 검색하면 딱 한군데 있습니다. 원하는 닉네임으로 수정해 주시면 됩니다.

//names = ["NotReallyABot"],

names = ["배고파 배고파"],


두번째는 "분열 가능 세포 함수 무시하기" 입니다.

앞에서 잠깐 설명 드렸었는데, 세포중 어떤 세포는 일반적인 공격범위를 가지고 있지 않은 세포들이 보이실 겁니다.(노란색 원) 소스 코드를 보면 canSplit 함수에서 false를 리턴받은 세포들은 일정한 크기의 다른 공격범위를 가지게 되는데요.

이게 좀 애매한 부분이 있습니다. 실제로 플레이 해보시면 이런 노란색 원의 공격범위를 가지는 세포들이 실제로 분열을 해서 우리의 세포를 먹어버리는 경우를 자주 격게 되는데요.

그래서 아에 canSplit이 항상 true를 리턴하도록 수정을 해 보았더니, 실제로 생존률이 높아졌습니다.(통계를 내서 내린 결론은 아닙니다.)

bot.user.js파일에서 canSplit 함수를 찾아 항상 false를 리턴하도록수정하시면 됩니다.

function canSplit(player1, player2) {
      return true;
      //return compareSize(player1, player2, 2.30) && !compareSize(player1, player2, 9);
}


지금까지 agar.io bot에 새로운 기능을 추가하는 방법에 대해서 알아보았습니다.

사실 제목에 "공부하기"라고 적어놓고는 공부하고는 거리가 먼 게임 이야기만 했는데, 억지로 공부하는 것보다는 이렇게 재미있는 주제를 가지고 조금씩 지식을 넓혀 가는 것도 좋은 방법이지 않을까 생각합니다.


다음에 시간이 되면 앞에서 말씀 드렸던 agar.io 원본 소스 분석에 대한 글도 적어보도록 하죠. 감사합니다.


Posted by 알 수 없는 사용자
,

eBay Pulsar를 분석하면서 받은 느낌은 다음과 같다.

"eBay Pulsar를 바로 써먹는 것은 불가능해보인다. Jetstream을 이용하면 CEP 시스템을 쉽게 구축할 수 있을 것 같고 eBay Pulsar는 Jetstream을 활용하기 위해서 참고하는 소스 정도로 사용하면 될 것 같다."

문제는 Jetstream의 문서를 봐도 이해가 잘 되도록 되어있지는 않다는 것이다.


Jetstream을 이용해서 간단한 Pipeline을 구성하면서 Jetstream에 대해서 이해할 수 있도록 하자.


Jetstream App 만들기

maven이 설치되어있다면 다음과 같은 명령으로 Jetstream App을 만들 수 있다.

mvn archetype:generate -DarchetypeGroupId=com.ebay.jetstream.archetype -DarchetypeArtifactId=simpleapp -DarchetypeVersion=4.0.2

위의 명령을 실행시키면 groupId, artifactId 등을 물어본다. 적당히 입력하면 Jetstream Sample App이 만들어진다.

$ mvn archetype:generate -DarchetypeGroupId=com.ebay.jetstream.archetype -DarchetypeArtifactId=simpleapp -DarchetypeVersion=4.0.2

....

Define value for property 'groupId': : com.embian.test

Define value for property 'artifactId': : pulsar-test

Define value for property 'version': 1.0-SNAPSHOT:

Define value for property 'package': com.embian.test:

Confirm properties configuration:

groupId: com.embian.test

artifactId: pulsar-test

version: 1.0-SNAPSHOT

package: com.embian.test

...

mvn을 돌린 위치에 artifactId로 입력했던 pulsar-test라는 디렉토리가 생성된 것을 확인할 수 있다.

pulsar-test는 자신이 사용하는 IDE에 맞게 설정해서 Import하면 된다. 


XML파일 이해하기

Jetstream에서 사용하는 주요한 XML은 다음과 같다.

buildsrc/JetstreamConf/appwiring.xml

InboundChannel과 OutboundChannel에 대한 정의를 가지고 있다.

기본으로 생성된 App의 설정파일은 다음과 같다. XML을 한번에 보면 머리아프기 때문에 역할별로 떼어서 보도록 하겠다.

XML의 제일 상단 부분에 위치한 내용이다. 따로 설명이 필요 없으니 Pass.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"

    default-lazy-init="false">

그 후에 나오는 부분은 다음과 같다.

   <bean id="InboundMessageBinder"

        class="com.ebay.jetstream.event.support.channel.ChannelBinding"

        depends-on="MessageService">

        <property name="channel" ref="InboundMessages"/>

    </bean>

    

    <bean id="InboundMessages"

        class="com.ebay.jetstream.event.channel.messaging.InboundMessagingChannel">

        <property name="address" ref="InboundChannelAddress" />

        <property name="waitTimeBeforeShutdown" value="15000"/>

        <property name="eventSinks">

            <list>

                <ref bean="SampleProcessor" />

            </list>

        </property>

    </bean>


    <bean id="InboundChannelAddress"

        class="com.ebay.jetstream.event.channel.messaging.MessagingChannelAddress">

        <property name="channelTopics">

            <list>

                <value>Jetstream.sample/SampleEvent</value>

            </list>

        </property>

    </bean>

    

    <bean id="SampleProcessor" class="com.embian.test.processor.SampleProcessor">

        <property name="eventSinks">

            <list>

                <ref bean="OutboundMessages" />

            </list>

        </property>

    </bean>

Inbound에 대한 설정이다. 

InboundMessageBinder(첫번째 Bean)에서는 Message 전송 방식을 어떤식으로 쓸 것인지를 정의한다. depends-on="MessageService" 부분이 이 부분이다. MessageService에 대해서는 appwiring.xml에 나와있지 않다. 해당 정보는 나중에 볼 messagecontext.xml에서 확인할 수 있다.

아무튼.. MessageService라는 방식으로 Message를 전송하면서 Channel은 InboundMessages라는 Bean을 사용한다는 것이다. 

두번째 Bean인 InboundMessages를 보면 InboundChannelAddress에 주소가 정의되어있고 eventSinks로 SampleProcessor를 추가한 것을 확인할 수 있다.

그 밑에는 각각 InboundChannelAddress와 SampleProcessor에 대해서 정의되어있다.

InboundChannelAddress는 topic으로 Jetstream.sample/SampleEvent를 가지는 채널을 의미하고 SampleProcessor는 com.embian.test.processor.SampleProcessor 클래스를 나타내고 있다. 


종합해서 보면 Inbound는 Jetstream.sample/SampleEvent라는 토픽을 가지고 있는 통로를 통해서 메시지를 받아오고 받아온 메시지는 SampleProcessor에서 처리한다는 것이다.

SampleProcessor부분에 보면 eventSinks로 OutboundMessages가 정의되어있다. SampleProcessor에서 처리를 끝낸 것은 OutboundMessages를 통해서 전달된다는 것을 의미한다.


여기까지의 내용을 그림으로 표시하면 다음과 같다.


<그림 1. pulsar-test의 Inbound 설정 내용>

Inbound에 대한 설정 바로 밑에는 Outbound에 대한 설정이 있다.

<bean id="OutboundMessageChanneBinder" class="com.ebay.jetstream.event.support.channel.ChannelBinding"

        depends-on="MessageService">

        <property name="channel" ref="OutboundMessages" />

    </bean>

    

    <bean id="OutboundMessages"

        class="com.ebay.jetstream.event.channel.messaging.OutboundMessagingChannel"

        depends-on="MessageService">

        <property name="address" ref="OutboundMessageChannelAddress" />

    </bean>


    <bean id="OutboundMessageChannelAddress"

        class="com.ebay.jetstream.event.channel.messaging.MessagingChannelAddress">

        <property name="channelTopics">

            <list>

                <value>Jetstream.sample2/SampleEvent</value>

            </list>

        </property>

    </bean>

OutboundMessageChannelBinder에서 OutboundMessages를 채널로 정의하고 있고 OutboundMessages의 주소는 OutboundMessageChannelAddress에서 정의하고 있다. OutbountMessageChannelBinder와 OutboundMessages에 대한 정의는 MessageService를 참고하고 있다는 것을 알 수 있다.

요약하자면.. OutboundMessage는 Jetstream.sample2/SampleEvent를 토픽으로 가지고 있는 통로를 사용한다는 것이다.

Outbound까지의 내용을 <그림 1>에 추가하면 다음과 같다.


여기까지 해서 Pipeline 구성에 대한 가장 기본적인 wiring이 끝난 것이다.

outbound에 대한 설정 밑에는 Shutdown에 대한 설정이 들어가있다.

<bean id="ShutDownOrchestrator" class="com.ebay.jetstream.event.support.ShutDownOrchestrator"

        lazy-init="false">

        <property name="shutDownComponent">

            <list>

                <ref bean="InboundMessages" />

                <ref bean="SampleProcessor" />

                <ref bean="OutboundMessages" />

            </list>

        </property>

    </bean>

오류가 발생한 경우 Shutdown을 시키는 processor에 대해서 정의한 것이다.

Shutdown이 발생하면 채널과 processor를 닫는 방법에 대해서 추가할 수 있도록 한 것이다.

그리고 appwiring.xml은 이해하기 쉽게 이름을 변경하는 것도 가능하다.


buildsrc/JetstreamConf/messagecontext.xml

appwiring.xml은 Processor와 Channel간의 연결에 대해서 설정하는 반면에 messagecontext.xml은 실제 메시지 전달을 위해서 어떤 방식으로 전달할 것인가를 나타내는 설정이다.

appwiring.xml에서 언급했던 MessageService에 대해서 찾아보면 다음과 같이 되어있다.

<bean id="MessageService"

        class="com.ebay.jetstream.messaging.config.MessageServiceConfiguration"

        depends-on="SystemPropertiesConfiguration">

        <property name="messageServiceProperties" ref="MessageServiceProperties" />

    </bean>

MessageServiceProperties를 확인해보면 Jetstream.sample과 Jetstream.sample2에 대해서 정의되어있는 것을 확인할 수 있다.

<bean id="MessageServiceProperties"

        class="com.ebay.jetstream.messaging.config.MessageServiceProperties">

        <property name="nicUsage" ref="NICUsage" />

        <property name="transports">

            <list>

                <ref bean="zookeeper" />


                <bean

                    class="com.ebay.jetstream.messaging.transport.netty.config.NettyTransportConfig">

                    <property name="transportClass"

                        value="com.ebay.jetstream.messaging.transport.netty.NettyTransport" />

                    <property name="transportName" value="netty" />

                    <property name="protocol" value="tcp" />

                    <property name="contextList">

                        <list>

                            <bean

                                class="com.ebay.jetstream.messaging.transport.netty.config.NettyContextConfig">

                                <!--  TODO: Change to your context name -->

                                <property name="contextname" value="Jetstream.sample" />

                                <property name="port" value="15590" />

                                <property name="scheduler" ref="consistenthashingaffinityscheduler"/>

                            </bean>

                        </list>

                    </property>

                    <property name="sendbuffersize" value="1048576" />

                    <property name="receivebuffersize" value="1048576" />

                    <property name="downstreamDispatchQueueSize" value="262144" />

                    <property name="connectionTimeoutInSecs" value="10" />

                    <property name="asyncConnect" value="true" />

                    <property name="numAcceptorIoProcessors" value="1" />

                    <property name="numConnectorIoProcessors" value="1" />

                    <property name="requireDNS" value="false" />

                    <property name="netmask" value="#{systemProperties['jetstream.runtime.netmask'] ?: '127.0.0.1/8'}" />

                    <property name="connectionPoolSz" value="1" />

                    <property name="maxNettyBackLog" value="20000" />

                    <property name="idleTimeoutInSecs" value="8640000"/>

                    <property name="enableCompression" value="false" />

                    <property name="tcpKeepAlive" value="true"/>

                </bean>


                <bean

                    class="com.ebay.jetstream.messaging.transport.netty.config.NettyTransportConfig">

                    <property name="transportClass"

                        value="com.ebay.jetstream.messaging.transport.netty.NettyTransport" />

                    <property name="transportName" value="netty" />

                    <property name="protocol" value="tcp" />

                    <property name="contextList">

                        <list>

                            <bean

                                class="com.ebay.jetstream.messaging.transport.netty.config.NettyContextConfig">

                                <!--  TODO: Change to your context name -->

                                <property name="contextname" value="Jetstream.sample2" />

                                <property name="port" value="15591" />

                                <property name="scheduler" ref="consistenthashingaffinityscheduler"/>

                            </bean>

                        </list>

                    </property>

                    <property name="sendbuffersize" value="1048576" />

                    <property name="receivebuffersize" value="1048576" />

                    <property name="downstreamDispatchQueueSize" value="262144" />

                    <property name="connectionTimeoutInSecs" value="10" />

                    <property name="asyncConnect" value="true" />

                    <property name="numAcceptorIoProcessors" value="1" />

                    <property name="numConnectorIoProcessors" value="1" />

                    <property name="requireDNS" value="false" />

                    <property name="netmask" value="#{systemProperties['jetstream.runtime.netmask'] ?: '127.0.0.1/8'}" />

                    <property name="connectionPoolSz" value="1" />

                    <property name="maxNettyBackLog" value="20000" />

                    <property name="idleTimeoutInSecs" value="8640000"/>

                    <property name="enableCompression" value="false" />

                    <property name="tcpKeepAlive" value="true"/>

                </bean>

            </list>

        </property>

        <property name="upstreamDispatchQueueSize" value="300000" />

        <property name="upstreamDispatchThreadPoolSize" value="2" />

    </bean>


Jetstream.sample은 15590 포트를, Jetstream.sample2는 15591 포트를 통해서 통신하도록 정의되어있다.


Processor 만들기

XML 파일을 통해서 Inbound와 Outbound에 대한 연결 설정이 끝나면 해당 App이 Inbound를 통해서 들어온 Message를 어떻게 처리한 후 Outbound로 보낼지에 대해서 구성해야 한다. 

Inbound의 설정 중 eventSinks에서 정의한 Processor Class에서 Data의 처리를 수행하게 된다. 아래의 XML은 appwiring.xml에서 Processor에 대한 정의 부분이다. 이미 앞에서 알아봤던 XML에 기록되어있는 것을 다시한번 확인해보면 다음과 같다.

<bean id="InboundMessages"

        class="com.ebay.jetstream.event.channel.messaging.InboundMessagingChannel">

        <property name="address" ref="InboundChannelAddress" />

        <property name="waitTimeBeforeShutdown" value="15000"/>

        <property name="eventSinks">

            <list>

                <ref bean="SampleProcessor" />

            </list>

        </property>

    </bean>


<bean id="SampleProcessor" class="com.embian.test.processor.SampleProcessor">

        <property name="eventSinks">

            <list>

                <ref bean="OutboundMessages" />

            </list>

        </property>

    </bean>

위의 XML에서는 Inbound로 들어오는 Message를 SampleProcessor로 전달한다는 것을 의미한다. 그리고 SampleProcessor는 결과를 OutboundMessages로 보내도록 정의되어있다.


SampleProcessor는 com.embian.test.processor.SampleProcessor라는 Custom Processor를 의미한다. Processor에 EsperProcessor를 붙여서 바로 EPL처리를 하는 것도 가능하다. 먼저 Custom Processor에 대해서 알아본 후 EsperProcessor에 대해서 알아보도록 하겠다.


- CustomProcessor

CustomProcessor는 Jetstream의 AbstractEventProcessor를 상속받아서 구현할 수 있다. SampleProcessor의 내용을 보면 다음과 같다.

package com.embian.test.processor;


import org.springframework.context.ApplicationEvent;

import org.springframework.jmx.export.annotation.ManagedResource;



import com.ebay.jetstream.event.EventException;

import com.ebay.jetstream.event.JetstreamEvent;

import com.ebay.jetstream.event.support.AbstractEventProcessor;

import com.ebay.jetstream.management.Management;


@ManagedResource(objectName = "Event/Processor", description = "SampleProcessor")

public class SampleProcessor extends AbstractEventProcessor {

    private JetstreamEvent lastEvent;

    

    @Override

    public void afterPropertiesSet() throws Exception {

        Management.addBean(getBeanName(), this);

    }


    public JetstreamEvent getLastEvent() {

        return lastEvent;

    }


    @Override

    public int getPendingEvents() {

        return 0;

    }


    @Override

    public void pause() {

        

    }


    @Override

    protected void processApplicationEvent(ApplicationEvent event) {

        

    }


    @Override

    public void resume() {

        

    }


    @Override

    public void sendEvent(JetstreamEvent event) throws EventException {

        lastEvent = event;

        super.incrementEventRecievedCounter();

        super.fireSendEvent(event);

        super.incrementEventSentCounter();

    }


    public void setLastEvent(JetstreamEvent lastEvent) {

        this.lastEvent = lastEvent;

    }


    @Override

    public void shutDown() {

        

    }


}


위의 코드는 Jetstream App을 만들면 기본적으로 생성되는 코드이다. 각 method들이 유용하게 사용될 수 있지만 여기서는 가장 기본적인 method에 대해서만 알아보도록 하겠다.


afterPropertiesSet method

afterPropertiesSet method는 이름 그대로 Processor가 properties를 설정한 직후 호출되는 method이다. 초기에 설정 등이 필요한 부분은 여기에서 처리하면 된다. 

sendEvent method

sendEvent method는 XML에서 정의된 eventSinks로 message를 보낼 때 사용된다. 코드 내에서  fireSendEvent method를 사용하는 것을 볼 수 있다. 이 method가 eventSinks로 message를 보내는 역할을 한다. 만약 Pulsar의 예제처럼 IP를 통해서 국가 코드를 얻고자 하는 경우 fireSendEvent를 하기 전에 필요한 정보를 추가하면 된다.

event에서 정보를 읽거나 쓸때는 event.put, event.get method를 사용할 수 있다.


- EsperProcessor

만약 위의 예제에서 SampleProcessor를 CustomProcessor가 아닌 EsperProcessor를 사용하도록 하기 위해서는 appwiring.xml을 다음과 같이 수정하면 된다.

<bean id="SampleProcessor"

        class="com.ebay.jetstream.event.processor.esper.EsperProcessor">

        <property name="esperEventListener" ref="EsperEventListener" />

        <property name="configuration" ref="EsperConfiguration" />

        <property name="epl" ref="EPL" />

        <property name="eventSinks">

            <list>

                <ref bean="OutboundMessages" />

            </list>

        </property>

        <property name="esperExceptionHandler">

            <bean id="esperExceptionHandler"

                class="com.ebay.jetstream.event.processor.esper.EsperExceptionHandler"></bean>

        </property>

    </bean>

property 중 esperEventListener와 configuration, epl은 bean으로 등록되어있어야 한다.

이 bean들을 등록하기 위한 XML은 다음과 같다.

EsperSetup.xml

<?xml version="1.0" encoding="UTF-8"?>

<!--

Pulsar

Copyright (C) 2013-2015 eBay Software Foundation

Licensed under the GPL v2 license.  See LICENSE for full terms.

  -->

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"

    default-lazy-init="true">


    <bean id="EsperEventListener"

        class="com.ebay.jetstream.event.processor.esper.EsperEventListener">

    </bean>


      <bean id="EsperConfiguration"

        class="com.ebay.jetstream.event.processor.esper.EsperConfiguration">

        <property name="internalTimerEnabled" value="true" />

        <property name="msecResolution" value="1" />

        <property name="timeSourceNano" value="false" />

        <property name="declaredEvents" ref="EventDefinition" />

        <property name="listenerDispatchTimeout" value="1000" />

        <property name="listenerDispatchPreserveOrder" value="false" />

        <property name="insertIntoDispatchTimeout" value="100" />

        <property name="insertIntoDispatchPreserveOrder" value="false" />

        <property name="threadPoolSize" value="2" />

        <property name="queueSizeLimit" value="30000" />

        <property name="executionLogging" value="true" />

        <property name="timerLogging" value="true" />

        <property name="exceptionHandlerFactoryClass"

            value="com.ebay.jetstream.event.processor.esper.JetstreamExceptionHandlerFactory" />

        <property name="autoImport">

            <list>

            </list>

        </property>

    </bean>


    <bean id="EventDefinition"

        class="com.ebay.jetstream.event.processor.esper.EsperDeclaredEvents">

        <property name="eventTypes">

            <list>

                <bean

                    class="com.ebay.jetstream.event.processor.esper.MapEventType">

                    <property name="eventAlias" value="SampleEvent" />

                    <property name="eventFields">

                        <map>

                            <entry key="data" value="java.lang.String" />

                        </map>

                    </property>

                </bean>

            </list>

        </property>

    </bean>

</beans>


EsperEventListener는 Jetstream의 Esper Listener 설정을 나타내고 EsperConfiguration은 기본적인 Esper 설정을 나타낸다. EsperSetup.xml에서 관심있게 봐야 하는 부분은 EventDefinition이라는 bean이다.

EPL을 사용하기 위해서 Event Type을 정의하는 부분이다. eventTypes라는 property에는 EPL에서 사용할 Event의 Type이 정의된다. 여기서는 string값을 갖는 data라는 이름의 항목만 있는 SampleEvent라는 이벤트를 정의했다.


EPL.xml

<?xml version="1.0" encoding="UTF-8"?>

<!--

Pulsar

Copyright (C) 2013-2015 eBay Software Foundation

Licensed under the GPL v2 license.  See LICENSE for full terms.

  -->

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"

    default-lazy-init="true">


    <bean id="EPL" class="com.ebay.jetstream.event.processor.esper.EPL">

        <property name="statementBlock">

            <value>

        <![CDATA[

               

        

            @OutputTo("OutboundMessages")

            select data, count(*) as cnt from SampleEvent.win:time(10)  group by data;

        ]]>

            </value>

        </property>

    </bean>

</beans>

EPL은 위와 같이 설정된다. @OutputTo라는 Annotation을 통해서 해당 EPL의 결과를 eventSinks에서 정의된 목록 중 어디로 보낼지를 결정할 수 있다. 위의 예제는 10초동안 data가 종류별로 몇개가 들어왔는지를 세어보는 EPL이다.


이와 같이 설정되어있을 경우.. Inbound를 통해서 들어온 이벤트는 다음과 같이 정의가 되어있다는 것을 알 수 있다.

{

     "data" : 문자열

}

이 이벤트가 EPL을 통해서 OutboundMessage로 전달될 때는 다음과 같은 형태가 된다.

{

"data": 문자열1,

"cnt": 문자열개수

}

결과 이벤트는 위의 내용 말고도 어떤 이벤트 타입인지가 js_ev_type이라는 이름 등을 통해서 추가된다.


Custom Channel 만들기

이벤트가 생성되는 시점이나 이벤트를 저장소 등에 저장하고 싶을 때 Custom Channel을 통해서 처리할 수 있다. Jetstream에서는 AbstractInboundChannel을 상속받아서 Custom Channel을 만드는 것이 가능하다. 


여기서는 0.5초마다 단어를 선택해서 outbound로 던지는 예제를 보면서 Custom Channel을 만들어보도록 하겠다.

package com.embian.channel;


import java.util.Timer;

import java.util.TimerTask;


import org.springframework.context.ApplicationEvent;

import org.springframework.jmx.export.annotation.ManagedOperation;


import com.ebay.jetstream.event.EventException;

import com.ebay.jetstream.event.JetstreamEvent;

import com.ebay.jetstream.event.channel.AbstractInboundChannel;

import com.ebay.jetstream.event.channel.ChannelAddress;

import com.ebay.jetstream.management.Management;


public class WordCountChannel extends AbstractInboundChannel {

private Timer timer = new Timer("Timer");

private TimerTask timerTask;

private Boolean channelOpened;

private class TimingTask extends TimerTask {

public void run() {

System.err.println("!!!!!!!!!!! task run");

generate();

}

}

    @Override

    public void afterPropertiesSet() throws Exception {

     Management.removeBeanOrFolder(getBeanName(), this);

Management.addBean(getBeanName(), this);

    timerTask = new TimingTask();

timer.schedule(timerTask, 30 * 1000);

        init();

    }

    

    private void init() {

    channelOpened = false;

    }


    @ManagedOperation

    public void generate() {

// TODO Auto-generated method stub

    String[] wordArray = {"WORD1", "WORD2", "WORD3", "WORD4"};

    int idx= 0;

    while(true) {

    if (channelOpened) {

    JetstreamEvent event = new JetstreamEvent();

       event.setEventType("SampleEvent");

       event.put("data", a[idx]);

       fireSendEvent(event);

       idx= (idx+ 1) % wordArray.length;

       

    }

    try {

Thread.sleep(1 * 500);

} catch (InterruptedException e) {

e.printStackTrace();

}

    }

}


@Override

    public void close() throws EventException {

        super.close();

        shutDown();

    }


    @Override

    public void flush() throws EventException {

        

    }


    @Override

    public ChannelAddress getAddress() {

        return null;

    }


    

    @Override

    public int getPendingEvents() {

        return 0;

    }


    @Override

    public void open() throws EventException {

        super.open();

        channelOpened = true;         

    }


    @Override

    public void pause() {

        close();

    }


    @Override

    protected void processApplicationEvent(ApplicationEvent event) {

        

    }


    @Override

    public void resume() {

        open();

    }


    @Override

    public void shutDown() {

       

    }

}


위의 코드에서 관심있게 봐야 할 부분은 afterPropertiesSet method와 open method이다.

afterPropertiesSet method

afterPropertiesSet method는 이름 그대로 Processor가 properties를 설정한 직후 호출되는 method이다. 초기에 설정 등이 필요한 부분은 여기에서 처리하면 된다. 

open method

Channel이 열릴때 호출되는 method이다.


매우 간단한 코드이기 때문에 약간의 시간만 투자하면 파악할 수 있다. afterPropertiesSet method에 보면 Management.removeBeanOrFolder를 한 후 다시 Management.addBean을 하는 것을 볼 수 있다. 이건 Pulsar 예제에서 이렇게 사용하고 있어서 똑같이 넣어준 것이다. (왜 remove한 후에 add한 것인지 이유는 모름 -_-)



실행하기

Jetstream을 실행하기 위해서는 먼저 Zookeeper가 동작중이어야 한다. 그리고 실행할 때 설정해줘야 하는 정보는 다음과 같다.


- Main Class : com.ebay.jetstream.application.JetstreamApplication

- arguments : -p 8009 -n appname -cv 1.0

-p : 모니터링 포트

-n : App 이름

-cv : Config 버전, Config를 사용하지 않을때는 그냥 1.0 정도로 넣으면 된다.

- VM arguments : -Djetstream.runtime.zkserver.host=127.0.0.1 -Djetstream.runtime.zkserver.port=2181 -Djetstream.runtime.netmask=0.0.0.0/0

jetstream.runtime.zkserver.host : Zookeeper Host

jetstream.runtime.zkserver.port : Zookeeper Port

jetstream.runtime.netmask : Listen할 netmask

- 시스템 환경변수 

JETSTREAM_HOME : appwiring.xml이 위치하는 buildsrc 디렉토리의 경로

MONGO_HOME : Mongodb의 접속 주소, Config를 사용하지 않을때는 설정하지 않아도 된다.



Posted by 알 수 없는 사용자
,