atom 에디터의 장점은 mac command line에서 command 로 atom을 사용할 수 있다는 점인데.


vscode는 이게 없다. 



그러나,


.bash_rc 또는 .bash_profile에


 alias code='open $@ -a "Visual Studio Code"' 를 추가하면 비슷한 기능이 된다.



Posted by '김용환'
,


특정 서비스의 systemd를 설정했더라도 부팅 후에 자동으로 데몬을 띄우지 않기 때문에 

부팅 이후에도 잘 동작시키려면 enable/disable을 사용해야 한다.


확인하는 작업은 is-enabled를 사용한다.


$ sudo systemctl is-enabled kafka

disabled



부팅 후에 자동으로 데몬을 띄우게 하려면 enable를 사용한다.

$ sudo systemctl enable kafka

Created symlink from /etc/systemd/system/multi-user.target.wants/kafka.service to /usr/lib/systemd/system/kafka.service.



확인한다.


$ sudo systemctl is-enabled kafka

enabled


부팅 후에 자동으로 데몬을 안 띄우게 하려면 disable를 사용한다.


$ sudo systemctl disable kafka

Removed symlink /etc/systemd/system/multi-user.target.wants/kafka.service.





systemctl --failed를 사용해 부팅안된 서비스를 확인할 수 있다. 

만약 kafka.service가 실행 중에 문제가 생겼다면 다음과 같이 보일 것이다.


$ systemctl --failed

  UNIT            LOAD   ACTIVE SUB    DESCRIPTION

● kafka.service loaded failed failed Postfix Mail Transport Agent


LOAD   = Reflects whether the unit definition was properly loaded.

ACTIVE = The high-level unit activation state, i.e. generalization of SUB.

SUB    = The low-level unit activation state, values depend on unit type.


1 loaded units listed. Pass --all to see loaded but inactive units, too.

To show all installed unit files use 'systemctl list-unit-files'.


Posted by '김용환'
,




"아름다운 것들은 관심을 바라지 않지" 


"Beautiful things don't ask for attention" 



- 월터의 상상은 현실이 되다.



'영화를 보고' 카테고리의 다른 글

어벤져스 엔드게임 중에 좋았던 대사  (0) 2019.08.20
[더스토리] 영화의 기억나는 대사  (0) 2018.06.26
레디 플레이어 원  (0) 2018.04.04
칠드런 오브 맨  (0) 2016.10.25
[마이크롭 앤 가솔린]  (0) 2016.07.02
Posted by '김용환'
,




백프레셔(Backpressure) – 스트리밍 작업을 처리하다가 폭발적인 데이터(예, 사건/사고, 이벤트)가 발생하면 처리 시스템은 폭발적인 데이터를 우아하게 처리할 수 있어야 한다. 


처리 시간이 배치 간격보다 커지면 다음 배치 잡에서는 지연이 생기고 불안정해 진다. 따라서 불안정 상태가 지속되면 백프레셔에 의해 입력율(input rate)를 줄여 처리량과 처리 시간을 줄인다. 따라서 지연이 0이 될 것이다. 


폭발적인 데이터가 갑자기 카프카에 저장되어 스파크 스트리밍의 카프카 컨슈머에 리턴하는 배치 크기를 제한하고 싶을 수 있다. 이럴 때 스파크 스트리밍 백프레셔를 적용할 수 있다. (이는 대부분의 스트리밍 처리 플랫폼(예, storm, flink)에서 제공된다.)



spark.streaming.backpressure.enabled와 spark.streaming.backpressure.initialRate를 사용하면 된다. 

spark.streaming.backpressure.initialRate 기본값은 not set이고,  spark.streaming.backpressure.enabled 기본값은  disabled이다. 



https://spark.apache.org/docs/latest/configuration.html#spark-streaming 설정에 잘 설명되어 있다.


스파크 스트리밍은 spark.streaming.backpressure.enabled를 통해 현재 배치 스케줄링 지연과 처리 시간을 기준으로 수신 속도를 제어할 수 있기 때문에 시스템이 최대한 빠르게 처리할 수 있다. 내부적으로는 수신자의 최대 수신 속도가 동적으로 설정된다. 이 속도는 spark.streaming.receiver.maxRate와 spark.streaming.kafka.maxRatePerPartition 상한 값으로 설정된다.


첫 번째 배치를 제어하거나 좀 더 구체적으로 첫 번째 배치의 메시지 수를 설정하고 싶다면, spark.streaming.backpressure.initialRate을 사용할 수 있다. spark.streaming.backpressure.initialRate은 백프레셔 메커니즘이 활성화(spark.streaming.backpressure.enabled=true)되었을 때 각 리시버가 첫번째 배치에 대한 데이터를 수신하는 최대 수신 속도이다. 


spark.streaming.kafka.maxRatePerPartition의 기본값은 not set인고, 카프카 direct stream API를 사용할 때 각 카프카 파티션에서 데이터를 읽을 최대 속도(초당 레코드 수)으로 설정한다.



예)

spark.streaming.kafka.maxRatePerPartition = "100"

spark.streaming.backpressure.enabled = "true"






Posted by '김용환'
,

[펌] kafka burrow api

kafka 2018. 11. 20. 14:46


카프카 api stat, metric 지표를 보기 위한 burrow(https://github.com/linkedin/Burrow/wiki/HTTP-Endpoint)라는 오픈 소스 툴이 있다.



kafka에서 사용하고 싶은 http endpoint를 보고 호출한다.


https://github.com/linkedin/Burrow/wiki/HTTP-Endpoint#request-endpoints


예)

http://burrow.google.io:8000/v3/kafka/mycluster/topic/logs

{"error":false,"message":"topic offsets returned","offsets":[152223734586,152224559773,152224774276,152224723888,152224644847,152224641838,152224508250,152224383547,152215033727,152215053018,152214434045,152214530227,152215253175,152214990582,152215431432,152213601661,152215273301,152215092394,152214795862,152215123946,152215194674,152215391037,152214877453,152215137734,152215680569,152215360097,152214928462,152215484025,152214933673,152214661665,152214049830,152215021533,152215748420,152214945335,152215126831,152215051384,152214863230,152214966710,152215634739,152214820473,152215165668,152215071434,152214866458,152214865355,152214934334,152214662023,152214830751,152214573022,152215197587,152214836785],"request":{"url":"/v3/kafka/mycluster/topic/logs","host":"burrow-google"}}







Posted by '김용환'
,


apache phoenix 에서 timestamp 타입을 utc로 저장했다.



이를 현재 시간(gmt +9)로 보고 싶다면 아래와 같은 쿼리를 실행한다.




select CONVERT_TZ(timestamp, 'UTC', 'Asia/Seoul')




기타 phoenix 함수는 아래를 참조한다.


https://phoenix.apache.org/language/functions.html


Posted by '김용환'
,



코드를 커밋하기 전에 coding convention에 맞지 않으면 에러를 내고 commit을 안되게 하는 pre-commit이란 툴이 있다. 


pre-commit이 지원하는 언어/툴은 다음과 같다. 자바, 스칼라는 없지만, go, python, ruby, rust, swift 등이 있다.




docker

docker_image

fail

golang

node

python

python_venv

ruby

rust

swift

pcre

pygrep

script

system






아래와 같이 설치를 진행한다.




$ pip install pre-commit


$ cd kemi_api_디렉토리


$ pre-commit


$ vi .pre-commit-config.yaml

repos:

-   repo: https://github.com/pre-commit/pre-commit-hooks

    rev: v2.0.0

    hooks:

    -   id: trailing-whitespace

    -   id: end-of-file-fixer

    -   id: fix-encoding-pragma

    -   id: double-quote-string-fixer

    -   id: requirements-txt-fixer


$ pre-commit install




/common/exceptions.py 파일에 '# -*- coding: utf-8 -*-' 이 포함되어 있는데. 

일부러 그 주석만 삭제하고 다음 커맨드를 실행한다.




$ git add .

$ git commit -m 'test'

gTrim Trailing Whitespace.................................................Passed

Fix End of Files.........................................................Passed

Fix python encoding pragma...............................................Failed

hookid: fix-encoding-pragma


Files were modified by this hook. Additional output:


Added `# -*- coding: utf-8 -*-` to /common/exceptions.py


Fix double quoted strings................................................Passed

Fix requirements.txt.................................(no files to check)Skipped



파이썬 인코딩 부분에서 failed되고 알려준다. 

때로는 고쳐주기도 한다.




프로젝트 디렉토리의 .git/hook/pre-commit 파일이 하나 생긴다. 


[프로젝트/.git/hooks] ls -al pre-commit

-rwxr-xr-x  1 samuel.kim  staff  5257 11  9 18:19 pre-commit




이 파일로 인해서 git commit을 미리 체크한다.


hook에 대한 내용은 https://git-scm.com/book/ko/v1/Git%EB%A7%9E%EC%B6%A4-Git-%ED%9B%85를 참조한다.



#!/usr/bin/env python

"""File generated by pre-commit: https://pre-commit.com"""

from __future__ import print_function


import distutils.spawn

import os

import subprocess

import sys


# work around https://github.com/Homebrew/homebrew-core/issues/30445

os.environ.pop('__PYVENV_LAUNCHER__', None)


HERE = os.path.dirname(os.path.abspath(__file__))

Z40 = '0' * 40

ID_HASH = '138fd403232d2ddd5efb44317e38bf03'

# start templated

CONFIG = '.pre-commit-config.yaml'

HOOK_TYPE = 'pre-commit'

INSTALL_PYTHON = '/Users/samuel.kim/.pyenv/versions/3.7.0/bin/python3.7'

SKIP_ON_MISSING_CONFIG = False

# end templated



class EarlyExit(RuntimeError):

    pass



class FatalError(RuntimeError):

    pass



def _norm_exe(exe):

    """Necessary for shebang support on windows.


    roughly lifted from `identify.identify.parse_shebang`

    """

    with open(exe, 'rb') as f:

        if f.read(2) != b'#!':

            return ()

        try:

            first_line = f.readline().decode('UTF-8')

        except UnicodeDecodeError:

            return ()


        cmd = first_line.split()

        if cmd[0] == '/usr/bin/env':

            del cmd[0]

        return tuple(cmd)



def _run_legacy():

    if HOOK_TYPE == 'pre-push':

        stdin = getattr(sys.stdin, 'buffer', sys.stdin).read()

    else:

        stdin = None


    legacy_hook = os.path.join(HERE, '{}.legacy'.format(HOOK_TYPE))

    if os.access(legacy_hook, os.X_OK):

        cmd = _norm_exe(legacy_hook) + (legacy_hook,) + tuple(sys.argv[1:])

        proc = subprocess.Popen(cmd, stdin=subprocess.PIPE if stdin else None)

        proc.communicate(stdin)

        return proc.returncode, stdin

    else:

        return 0, stdin



def _validate_config():

    cmd = ('git', 'rev-parse', '--show-toplevel')

    top_level = subprocess.check_output(cmd).decode('UTF-8').strip()

    cfg = os.path.join(top_level, CONFIG)

    if os.path.isfile(cfg):

        pass

    elif SKIP_ON_MISSING_CONFIG or os.getenv('PRE_COMMIT_ALLOW_NO_CONFIG'):

        print(

            '`{}` config file not found. '

            'Skipping `pre-commit`.'.format(CONFIG),

        )

        raise EarlyExit()

    else:

        raise FatalError(

            'No {} file was found\n'

            '- To temporarily silence this, run '

            '`PRE_COMMIT_ALLOW_NO_CONFIG=1 git ...`\n'

            '- To permanently silence this, install pre-commit with the '

            '--allow-missing-config option\n'

            '- To uninstall pre-commit run '

            '`pre-commit uninstall`'.format(CONFIG),

        )



def _exe():

    with open(os.devnull, 'wb') as devnull:

        for exe in (INSTALL_PYTHON, sys.executable):

            try:

                if not subprocess.call(

                        (exe, '-c', 'import pre_commit.main'),

                        stdout=devnull, stderr=devnull,

                ):

                    return (exe, '-m', 'pre_commit.main', 'run')

            except OSError:

                pass


    if distutils.spawn.find_executable('pre-commit'):

        return ('pre-commit', 'run')


    raise FatalError(

        '`pre-commit` not found.  Did you forget to activate your virtualenv?',

    )



def _rev_exists(rev):

    return not subprocess.call(('git', 'rev-list', '--quiet', rev))



def _pre_push(stdin):

    remote = sys.argv[1]


    opts = ()

    for line in stdin.decode('UTF-8').splitlines():

        _, local_sha, _, remote_sha = line.split()

        if local_sha == Z40:

            continue

        elif remote_sha != Z40 and _rev_exists(remote_sha):

            opts = ('--origin', local_sha, '--source', remote_sha)

        else:

            # ancestors not found in remote

            ancestors = subprocess.check_output((

                'git', 'rev-list', local_sha, '--topo-order', '--reverse',

                '--not', '--remotes={}'.format(remote),

            )).decode().strip()

            if not ancestors:

                continue

            else:

                first_ancestor = ancestors.splitlines()[0]

                cmd = ('git', 'rev-list', '--max-parents=0', local_sha)

                roots = set(subprocess.check_output(cmd).decode().splitlines())

                if first_ancestor in roots:

                    # pushing the whole tree including root commit

                    opts = ('--all-files',)

                else:

                    cmd = ('git', 'rev-parse', '{}^'.format(first_ancestor))

                    source = subprocess.check_output(cmd).decode().strip()

                    opts = ('--origin', local_sha, '--source', source)


    if opts:

        return opts

    else:

        # An attempt to push an empty changeset

        raise EarlyExit()



def _opts(stdin):

    fns = {

        'commit-msg': lambda _: ('--commit-msg-filename', sys.argv[1]),

        'pre-commit': lambda _: (),

        'pre-push': _pre_push,

    }

    stage = HOOK_TYPE.replace('pre-', '')

    return ('--config', CONFIG, '--hook-stage', stage) + fns[HOOK_TYPE](stdin)



def main():

    retv, stdin = _run_legacy()

    try:

        _validate_config()

        return retv | subprocess.call(_exe() + _opts(stdin))

    except EarlyExit:

        return retv

    except FatalError as e:

        print(e.args[0])

        return 1



if __name__ == '__main__':

    exit(main())




만약 flake8라는 파이썬 코딩 컨벤션 강제 툴을 적용하려면. .pre-commit-config.yaml 파일의 hooks id를 추가한다.



$ vi .pre-commit-config.yaml

repos:

-   repo: https://github.com/pre-commit/pre-commit-hooks

    rev: v2.0.0

    hooks:

    -   id: trailing-whitespace

    -   id: end-of-file-fixer

    -   id: fix-encoding-pragma

    -   id: double-quote-string-fixer

    -   id: requirements-txt-fixer

    -   id: flake8






Posted by '김용환'
,



flask에서는 json encoder를 사용해서 json 응답을 보내줘야 한다.


@app.route("/getEmployeeList") def getEmployeeList(): try: # Initialize a employee list employeeList = [] # create a instances for filling up employee list for i in range(0,2): empDict = { 'firstName': 'Roy', 'lastName': 'Augustine'} employeeList.append(empDict) # convert to json data jsonStr = json.dumps(employeeList) except Exception ,e: print str(e) return jsonify(Employees=jsonStr)


https://codehandbook.org/create-json-using-python-flask/





그러나 flask에 flask-restful을 추가해 설치한후,, 아래와 같이 설정한다면..


from flask import Flask
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app)

class HelloWorld(Resource):
    def get(self):
        return {'hello': 'world'}

api.add_resource(HelloWorld, '/')

if __name__ == '__main__':
    app.run(debug=True)



그냥 기본 타입과 collection은 자동으로 json으로 변환한다. 그 이유가 멀까?


json.dump(aaa) 이런 코드가 필요없어서 참 좋았다.







https://github.com/flask-restful/flask-restful/blob/master/flask_restful/__init__.py#L474


make_response()에서 default decorator로 json을 출력한다.





아래 코드를 보면, indent 4칸에 newline으로 예쁘게 출력하는 코드가 있다. 


https://github.com/flask-restful/flask-restful/blob/master/flask_restful/representations/json.py


from __future__ import absolute_import
from flask import make_response, current_app
from flask_restful.utils import PY3
from json import dumps


def output_json(data, code, headers=None):
    """Makes a Flask response with a JSON encoded body"""

    settings = current_app.config.get('RESTFUL_JSON', {})

    # If we're in debug mode, and the indent is not set, we set it to a
    # reasonable value here.  Note that this won't override any existing value
    # that was set.  We also set the "sort_keys" value.
    if current_app.debug:
        settings.setdefault('indent', 4)
        settings.setdefault('sort_keys', not PY3)

    # always end the json dumps with a new line
    # see https://github.com/mitsuhiko/flask/pull/1262
    dumped = dumps(data, **settings) + "\n"

    resp = make_response(dumped, code)
    resp.headers.extend(headers or {})
    return resp






Posted by '김용환'
,




okhttp3와 moshi만 있으면 자바/스칼라 http 통신이 완전 편해진다.. 



okhttp3와 moshi는 json serialization/deserialization 개발 공부를 크게 낮춘다.





https://github.com/square/okhttp/wiki/Recipes


https://github.com/square/moshi


Posted by '김용환'
,




python으로 해결하는 JSONP 파싱 예시이다.



>>> import requests

>>> url = '...'

>>> jsonp = requests.get(url % 1000)

>>> jsonp.content

b'callback({"status":{

...

})' 

>>> import json

>>> pure_json = jsonp.text[jsonp.text.index('(') + 1 : jsonp.text.rindex(')')]

>>> dealers = json.loads(pure_json)

>>> dealers.keys()

dict_keys(['status'])

>>> dealers['count']

10 



Posted by '김용환'
,