'2018/05'에 해당되는 글 5건

  1. 2018.05.30 [Apache Airflow] 설치 및 간단 테스트
  2. 2018.05.09 [Elasticsearch] Adaptive Replica Selection 기능
  3. 2018.05.09 [Elasticsearch] GET API 에 대해서
  4. 2018.05.03 [Python] virtualenv 구성 맛보기
  5. 2018.05.02 [Spring Framework] SpringMVC + Gradle 템플릿 + Tomcat 연동

[Apache Airflow] 설치 및 간단 테스트

ITWeb/개발일반 2018. 5. 30. 09:35

Airflow 를 정상적으로 사용하기 위해서는 MySQL 과 같은 DB 구성이 되어 있어야 합니다.

(설치 되어 있지 않더라도 기본적인 Hello World 수준의 테스트는 가능 합니다.)


Reference)

https://airflow.apache.org/start.html


$ pip install apache-airflow

or 

$ sudo -H pip install apache-airflow --ignore-installed matplotlib

# initdb 하기 전에 mysql 구성 및 database 생성을 해야 합니다.

$ airflow initdb

$ airflow webserver -p 8080


# MySQL 연결

# The SqlAlchemy connection string to the metadata database.

# SqlAlchemy supports many different database engine, more information

# their website

sql_alchemy_conn = mysql+pymysql://root:000000@localhost/airflow


# MySQL Connection URL Syntax

https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-jdbc-url-format.html

protocol//[hosts][/database][?properties]


# Database 와 Table 에 대한 charset 을 아래와 같이 생성해 줘야 합니다.

ALTER DATABASE database_name CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;

ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;


# 터미널에서 실행하고 결과 확인 하기

$ airflow test airflow_tutorial_v01 print_hello 2018-05-08


import datetime as dt


from airflow import DAG

from airflow.operators.bash_operator import BashOperator

from airflow.operators.python_operator import PythonOperator



def print_world():

    print('world')



default_args = {

    'owner': 'me',

    'start_date': dt.datetime(2017, 6, 1),

    'retries': 1,

    'retry_delay': dt.timedelta(minutes=5),

}



with DAG('airflow_tutorial_v01',

         default_args=default_args,

         schedule_interval='0 * * * *',

         ) as dag:


    print_hello = BashOperator(task_id='print_hello',

                               bash_command='echo "hello"')

    sleep = BashOperator(task_id='sleep',

                         bash_command='sleep 5')

    print_world = PythonOperator(task_id='print_world',

                                 python_callable=print_world)



print_hello >> sleep >> print_world


일부 테스트가 부족한 부분이 있기 때문에 그냥 참고 정도만 하세요.

:

[Elasticsearch] Adaptive Replica Selection 기능

Elastic/Elasticsearch 2018. 5. 9. 17:06

6.x 에 추가된 API 중 맘에 드는 것이 있어서 글을 퍼왔습니다.


Reference)

https://www.elastic.co/guide/en/elasticsearch/reference/current/search.html


Adaptive Replica Selection

As an alternative to requests being sent to copies of the data in a round robin fashion, you may enable adaptive replica selection. This allows the coordinating node to send the request to the copy deemed "best" based on a number of criteria:


  • Response time of past requests between the coordinating node and the node containing the copy of the data
  • Time past search requests took to execute on the node containing the data
  • The queue size of the search threadpool on the node containing the data

This can be turned on by changing the dynamic cluster setting cluster.routing.use_adaptive_replica_selection from false to true:


PUT /_cluster/settings

{

    "transient": {

        "cluster.routing.use_adaptive_replica_selection": true

    }

}



위에 기술 되어 있는 것 처럼,

- 기본적으로는 Round Robin 방식으로 동작 합니다.

- 하지만 3가지 기준을 가지고 이 기능은 동작을 합니다.

1. Coordinating node 와 Data node 간의 응답 시간

2. Data node 에서 수행된 시간

3. Data node 의 threadpool queue 크기


이 중에서 가장 좋은 기준을 바탕으로 동작 하게 되는 것입니다.


:

[Elasticsearch] GET API 에 대해서

Elastic/Elasticsearch 2018. 5. 9. 14:22

평소 Get API 에 대해서 설명을 할 때 document id 로 lookup 을 하기 때문에 모든 shard 로 request 를 보내지 않아 빠르다고 설명을 했습니다.

이 과정에서 놓치기 쉬운 부분이 있는데 기본적으로 Get API 는 realtime  동작을 합니다.

즉, refresh 동작과 상관 없이 기본적으로 최신 정보를 가져오게 되는 것입니다.


다시 말해, 내부적으로 refresh 동작을 수행한다는 의미가 됩니다.

그래서 Get API 에서는 realtime 이라는 변수를 통해서 realtime Get 을 사용 할 것인지 말 것인지를 정의 할 수 있습니다.


아래는 위 설명을 이해 하는데 도움을 주기 위해서 소스 코드의 일부를 발췌 하였습니다.


[GetRequest.java]

private boolean refresh = false;

boolean realtime = true;
/**
* Should a refresh be executed before this get operation causing the operation to
* return the latest value. Note, heavy get should not set this to {@code true}. Defaults
* to {@code false}.
*/
public GetRequest refresh(boolean refresh) {
this.refresh = refresh;
return this;
}

public boolean refresh() {
return this.refresh;
}

public boolean realtime() {
return this.realtime;
}

@Override
public GetRequest realtime(boolean realtime) {
this.realtime = realtime;
return this;
}


[RestGetAction.java]

@Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
final boolean includeTypeName = request.paramAsBoolean("include_type_name", true);
final String type = request.param("type");
if (includeTypeName == false && MapperService.SINGLE_MAPPING_NAME.equals(type) == false) {
throw new IllegalArgumentException("You may only use the [include_type_name=false] option with the get APIs with the " +
"[{index}/_doc/{id}] endpoint.");
}
final GetRequest getRequest = new GetRequest(request.param("index"), type, request.param("id"));
getRequest.refresh(request.paramAsBoolean("refresh", getRequest.refresh()));
getRequest.routing(request.param("routing"));
getRequest.preference(request.param("preference"));
getRequest.realtime(request.paramAsBoolean("realtime", getRequest.realtime()));
if (request.param("fields") != null) {
throw new IllegalArgumentException("the parameter [fields] is no longer supported, " +
"please use [stored_fields] to retrieve stored fields or [_source] to load the field from _source");
}
final String fieldsParam = request.param("stored_fields");
if (fieldsParam != null) {
final String[] fields = Strings.splitStringByCommaToArray(fieldsParam);
if (fields != null) {
getRequest.storedFields(fields);
}
}

getRequest.version(RestActions.parseVersion(request));
getRequest.versionType(VersionType.fromString(request.param("version_type"), getRequest.versionType()));

getRequest.fetchSourceContext(FetchSourceContext.parseFromRestRequest(request));

return channel -> client.get(getRequest, new RestToXContentListener<GetResponse>(channel) {
@Override
protected RestStatus getStatus(final GetResponse response) {
return response.isExists() ? OK : NOT_FOUND;
}
});
}


[TransportGetAction.java]

@Override
protected void asyncShardOperation(GetRequest request, ShardId shardId, ActionListener<GetResponse> listener) throws IOException {
IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex());
IndexShard indexShard = indexService.getShard(shardId.id());
if (request.realtime()) { // we are not tied to a refresh cycle here anyway
listener.onResponse(shardOperation(request, shardId));
} else {
indexShard.awaitShardSearchActive(b -> {
try {
super.asyncShardOperation(request, shardId, listener);
} catch (Exception ex) {
listener.onFailure(ex);
}
});
}
}

@Override
protected GetResponse shardOperation(GetRequest request, ShardId shardId) {
IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex());
IndexShard indexShard = indexService.getShard(shardId.id());

if (request.refresh() && !request.realtime()) {
indexShard.refresh("refresh_flag_get");
}

GetResult result = indexShard.getService().get(request.type(), request.id(), request.storedFields(),
request.realtime(), request.version(), request.versionType(), request.fetchSourceContext());
return new GetResponse(result);
}

보시면 아시겠지만 refresh 와 realtime 의 동작에는 차이가 있습니다.

이해 하기 쉽게 정리 하면,

- refresh 는 get 수행 전에 relevant shard 를 대상으로 refresh 동작을 먼저 하기 때문에 성능 저하가 있을 수 있습니다.

- realtime 은 수행 시점에 refresh 동작을 수행 하게 됩니다. (이 경우 refresh searcher 의 대상은 internal searcher 가 됩니다.)

- 여기서 translog 에서 데이터를 읽어야 하는 경우가 있는데 이 경우는 update API 에서 사용 되게 됩니다.


[InternalEngine.java]

@Override
public GetResult get(Get get, BiFunction<String, SearcherScope, Searcher> searcherFactory) throws EngineException {
assert Objects.equals(get.uid().field(), IdFieldMapper.NAME) : get.uid().field();
try (ReleasableLock ignored = readLock.acquire()) {
ensureOpen();
SearcherScope scope;
if (get.realtime()) {
VersionValue versionValue = null;
try (Releasable ignore = versionMap.acquireLock(get.uid().bytes())) {
// we need to lock here to access the version map to do this truly in RT
versionValue = getVersionFromMap(get.uid().bytes());
}
if (versionValue != null) {
if (versionValue.isDelete()) {
return GetResult.NOT_EXISTS;
}
if (get.versionType().isVersionConflictForReads(versionValue.version, get.version())) {
throw new VersionConflictEngineException(shardId, get.type(), get.id(),
get.versionType().explainConflictForReads(versionValue.version, get.version()));
}
if (get.isReadFromTranslog()) {
// this is only used for updates - API _GET calls will always read form a reader for consistency
// the update call doesn't need the consistency since it's source only + _parent but parent can go away in 7.0
if (versionValue.getLocation() != null) {
try {
Translog.Operation operation = translog.readOperation(versionValue.getLocation());
if (operation != null) {
// in the case of a already pruned translog generation we might get null here - yet very unlikely
TranslogLeafReader reader = new TranslogLeafReader((Translog.Index) operation, engineConfig
.getIndexSettings().getIndexVersionCreated());
return new GetResult(new Searcher("realtime_get", new IndexSearcher(reader)),
new VersionsAndSeqNoResolver.DocIdAndVersion(0, ((Translog.Index) operation).version(), reader, 0));
}
} catch (IOException e) {
maybeFailEngine("realtime_get", e); // lets check if the translog has failed with a tragic event
throw new EngineException(shardId, "failed to read operation from translog", e);
}
} else {
trackTranslogLocation.set(true);
}
}
refresh("realtime_get", SearcherScope.INTERNAL);
}
scope = SearcherScope.INTERNAL;
} else {
// we expose what has been externally expose in a point in time snapshot via an explicit refresh
scope = SearcherScope.EXTERNAL;
}

// no version, get the version from the index, we know that we refresh on flush
return getFromSearcher(get, searcherFactory, scope);
}
}


[코드의 이해]

RestGetAction -> TransportGetAction (TransportSingleShardAction) -> IndexShard ->

ShardGetService -> InternalEngine (Engine) -> SearcherManager(ReferenceManager)


:

[Python] virtualenv 구성 맛보기

ITWeb/개발일반 2018. 5. 3. 12:07

먼저 무작정 하다 보면 에러 부터 경험 할때가 많이 있습니다.

Anaconda 를 설치 해 놓고 잊고 있었는데 에러 부터 나와서 뭐지 했내요.


python virtualenv 구성 시 아레와 같은 에러 발생시 해결 방법



$ virtualenv venv

New python executable in /Users/xxx/Work/apps/venv/bin/python

Installing setuptools, pip, wheel...

  Complete output from command /Users/xxx/Work/apps/venv/bin/python - setuptools pip wheel:

  Traceback (most recent call last):

  File "<stdin>", line 4, in <module>

  File "/Users/xxx/anaconda2/lib/python2.7/tempfile.py", line 32, in <module>

    import io as _io

  File "/Users/xxx/anaconda2/lib/python2.7/io.py", line 51, in <module>

    import _io

ImportError: dlopen(/Users/xxx/Work/apps/venv/lib/python2.7/lib-dynload/_io.so, 2): Symbol not found: __PyCodecInfo_GetIncrementalDecoder

  Referenced from: /Users/xxx/Work/apps/venv/lib/python2.7/lib-dynload/_io.so

  Expected in: flat namespace

 in /Users/xxx/Work/apps/venv/lib/python2.7/lib-dynload/_io.so

----------------------------------------

...Installing setuptools, pip, wheel...done.

Traceback (most recent call last):

  File "/Users/xxx/anaconda2/bin/virtualenv", line 11, in <module>

    sys.exit(main())

  File "/Users/xxx/anaconda2/lib/python2.7/site-packages/virtualenv.py", line 712, in main

    symlink=options.symlink)

  File "/Users/xxx/anaconda2/lib/python2.7/site-packages/virtualenv.py", line 953, in create_environment

    download=download,

  File "/Users/xxx/anaconda2/lib/python2.7/site-packages/virtualenv.py", line 904, in install_wheel

    call_subprocess(cmd, show_stdout=False, extra_env=env, stdin=SCRIPT)

  File "/Users/xxx/anaconda2/lib/python2.7/site-packages/virtualenv.py", line 796, in call_subprocess

    % (cmd_desc, proc.returncode))

OSError: Command /Users/xxx/Work/apps/venv/bin/python - setuptools pip wheel failed with error code 1


Reference)

https://github.com/certbot/certbot/issues/2850


$ pip uninstall virtualenv

$ conda install virtualenv


# 아래와 같이 하면 실행한 위치에 venv 라는 폴더가 생성이 됩니다.

$ virtualenv venv --distribute

New python executable in /Users/xxx/Work/apps/venv/bin/python

copying /Users/xxx/anaconda2/bin/python => /Users/xxx/Work/apps/venv/bin/python

copying /Users/xxx/anaconda2/bin/../lib/libpython2.7.dylib => /Users/xxx/Work/apps/venv/lib/libpython2.7.dylib

Installing setuptools, pip, wheel...done.


# 가상환경 실행

$ source venv/bin/activate

(venv) MacBook-Pro-2:apps xxx$


# 가상환경 빠져나오기

(venv) MacBook-Pro-2:apps xxx$ deactivate


# python 버전별 가상환경 실행

$ virtualenv venv --python=python2.7

or

$ python -m virtualenv venv


$ virtualenv venv --python=python3.6

or

$ python3 -m virtualenv venv


# 가상환경에서 필요한 package 구성 후 설치 모듈을 구성 합니다. (requirements.txt)

$ virtualenv venv2.7 --python=python2.7

Running virtualenv with interpreter /Users/xxx/anaconda2/bin/python2.7

New python executable in /Users/xxx/Work/apps/venv2.7/bin/python2.7

copying /Users/xxx/anaconda2/bin/python2.7 => /Users/xxx/Work/apps/venv2.7/bin/python2.7

copying /Users/xxx/anaconda2/bin/../lib/libpython2.7.dylib => /Users/xxx/Work/apps/venv2.7/lib/libpython2.7.dylib

Also creating executable in /Users/xxx/Work/apps/venv2.7/bin/python

Installing setuptools, pip, wheel...done.

MacBook-Pro-2:apps xxx$ source venv2.7/bin/activate

(venv2.7) MacBook-Pro-2:apps xxx$

(venv2.7) MacBook-Pro-2:apps xxx$ pip install boto3

Collecting boto3

  Downloading https://files.pythonhosted.org/packages/24/ec/7e3c056775ac725b31a7d5d38e6f7f93ac793f50c35a2cd36b4446be426c/boto3-1.7.12-py2.py3-none-any.whl (128kB)

    100% |████████████████████████████████| 133kB 518kB/s

Collecting s3transfer<0.2.0,>=0.1.10 (from boto3)

  Using cached https://files.pythonhosted.org/packages/d7/14/2a0004d487464d120c9fb85313a75cd3d71a7506955be458eebfe19a6b1d/s3transfer-0.1.13-py2.py3-none-any.whl

Collecting jmespath<1.0.0,>=0.7.1 (from boto3)

  Using cached https://files.pythonhosted.org/packages/b7/31/05c8d001f7f87f0f07289a5fc0fc3832e9a57f2dbd4d3b0fee70e0d51365/jmespath-0.9.3-py2.py3-none-any.whl

Collecting botocore<1.11.0,>=1.10.12 (from boto3)

  Downloading https://files.pythonhosted.org/packages/8e/d7/8b4b54f82a1a137f520d8b04f6c7b87b5f4762818100d1f38a0234fbf17c/botocore-1.10.12-py2.py3-none-any.whl (4.2MB)

    100% |████████████████████████████████| 4.2MB 2.6MB/s

Collecting futures<4.0.0,>=2.2.0; python_version == "2.6" or python_version == "2.7" (from s3transfer<0.2.0,>=0.1.10->boto3)

  Downloading https://files.pythonhosted.org/packages/2d/99/b2c4e9d5a30f6471e410a146232b4118e697fa3ffc06d6a65efde84debd0/futures-3.2.0-py2-none-any.whl

Collecting python-dateutil<3.0.0,>=2.1; python_version >= "2.7" (from botocore<1.11.0,>=1.10.12->boto3)

  Downloading https://files.pythonhosted.org/packages/0c/57/19f3a65bcf6d5be570ee8c35a5398496e10a0ddcbc95393b2d17f86aaaf8/python_dateutil-2.7.2-py2.py3-none-any.whl (212kB)

    100% |████████████████████████████████| 215kB 3.8MB/s

Collecting docutils>=0.10 (from botocore<1.11.0,>=1.10.12->boto3)

  Downloading https://files.pythonhosted.org/packages/50/09/c53398e0005b11f7ffb27b7aa720c617aba53be4fb4f4f3f06b9b5c60f28/docutils-0.14-py2-none-any.whl (543kB)

    100% |████████████████████████████████| 552kB 3.8MB/s

Collecting six>=1.5 (from python-dateutil<3.0.0,>=2.1; python_version >= "2.7"->botocore<1.11.0,>=1.10.12->boto3)

  Downloading https://files.pythonhosted.org/packages/67/4b/141a581104b1f6397bfa78ac9d43d8ad29a7ca43ea90a2d863fe3056e86a/six-1.11.0-py2.py3-none-any.whl

Installing collected packages: six, python-dateutil, jmespath, docutils, botocore, futures, s3transfer, boto3

Successfully installed boto3-1.7.12 botocore-1.10.12 docutils-0.14 futures-3.2.0 jmespath-0.9.3 python-dateutil-2.7.2 s3transfer-0.1.13 six-1.11.0

(venv2.7) MacBook-Pro-2:apps xxx$ pip freeze > requirements.txt

(venv2.7) MacBook-Pro-2:apps xxx$ deactivate

MacBook-Pro-2:apps xxx$ cat requirements.txt

boto3==1.7.12

botocore==1.10.12

docutils==0.14

futures==3.2.0

jmespath==0.9.3

python-dateutil==2.7.2

s3transfer==0.1.13

six==1.11.0


만들어진 requirements.txt 파일을 가지고 설치는 아래와 같이 하시면 됩니다.


$ pip install -r requirements.txt 

or

$ pip3 install -r requirements.txt 


:

[Spring Framework] SpringMVC + Gradle 템플릿 + Tomcat 연동

ITWeb/개발일반 2018. 5. 2. 18:33

매니저 역할을 오래 하다 보면 개발 감각이 떨어 지는건 어쩔수 없는 일인 것 같습니다.

SpringMVC 를 이용한 기본 웹 개발도 이제는 거북이 수준이 되어 가고 있는 것 같아 잊지 않기 위해 기록해 봅니다.


요즘 워낙 좋은 Tool, IDE 들이 많이 나와 있고. Git 검색만 해봐도 찾던 소스가 너무나도 많이 나옵니다.

아래는 Intellij community 버전을 이용해서 웹프로젝트 템플릿을 생성하는 예제 입니다.


각 단계별 코드별 설명은 작성 하지 않았습니다.

이유는 여긴 제가 그냥 혼자 기록하고 공부하는 공간이고, 누구를 가르치기에 너무 많은 무림고수 들이 계셔서 어쭙잖은 설명은 그냥 아래 레퍼런스로 대신 합니다.


References)

https://github.com/HowookJeong/springmvc-gradle-template

https://tomcat.apache.org/tomcat-9.0-doc/config/context.html

https://docs.spring.io/spring/docs/4.3.16.RELEASE/spring-framework-reference/htmlsingle/#spring-web


Step 1)

Intellij 를 실행 하고 매뉴에서 아래와 같이 선택을 합니다.


File -> New -> Project -> Gradle -> Java -> Next


GroupId -> org.project.web

ArtifactId -> springmvc-gradle-template

Next


Step 2)

SpringMVC Framework 틀을 만들기 위해 필요한 폴더와 파일들을 아래와 같이 생성을 합니다.


2-1) Community 버전으로는 gradle webapp 추가가 안되기 때문에 별도로 생성 합니다.

Make webapp directory

src/main/webapp


2-2) Make WEB-INF directory

src/main/webapps/WEB-INF

web.xml

dispatcher-servlet.xml


2-3) jsp template 사용을 위해서 WEB-INF 아래 jsp 폴더를 생성 합니다.

Make jsp directory

src/main/webapps/WEB-INF/jsp


2-4) Resource 구성을 합니다.

Make resources directory

src/main/resources/beans

bo-bean.xml

dao-bean.xml

src/main/resources/context

applicationContext.xml

applicationContext-mybatis.xml

src/main/resources/mybatis

config.xml

jdbc.properties

src/main/resources/sql

example.xml

log4j.xml


2-5) Package 구성을 합니다.

Make packages

org.project.web.bo

org.project.web.controllor

org.project.web.dao

org.project.web.model


여기까지 하셨으면 기본 empty project 생성은 끝났습니다.

이제 필요한 코드와 설정을 해보도록 하겠습니다.


Step 3)

이제 필요한 코드들을 생성해 보도록 하겠습니다.


3-1) web.xml 설정하기



3-2) dispatcher-servlet.xml 설정하기



3-3) log4j.xml 설정하기



3-4) mybatis/config.xml 설정하기



3-5) mybatis/jdbc.properties 설정하기



3-6) build.gradle 설정하기



이제 application 구현을 위한 코드 생성을 해보겠습니다.


3-7) controller/ExampleController


3-8) bo/ExampleBO


3-9) bo/ExampleBOImpl


3-10) dao/ExampleDAO


3-11dao/ExampleDAOImpl


3-12) model/ExampleModel



코드 구현을 했으니 이제 resources 설정을 하도록 하겠습니다.


3-13) beans/bo-bean.xml


3-14) beans/dao-bean.xml


3-15) context/applicationContext.xml


3-16) context/applicationContext-mybatis.xml


3-17) sql/example.xml


화면 구성을 위한 데코레이션으로 jsp 코드를 작성해 보겠습니다.


3-18) WEB-INF/jsp/exampleView.jsp


: