Netty  라는 오픈소스를 살펴보다가 4.0 에 pool buffer 를 구현하는데 jemalloc 를 참고 했다는 언급이 있어

서 처음 알게되었습니다.  malloc  레벨에서 이러한 작업결과들이  있다는걸 이제서야 알게되었네요. 

jemalloc 함수는 Jason Evans라는 사람에 의해 만들어 졌습니다. (앞자를 따서 je).  일반적인 목적의 malloc 이


며, 2005년에 FreeBSD의  libc할당자로 채택되어졌습니다. 메모리단편화를 최소 화하는데 집중되었으며 멀티


프로세서/멀티쓰레드 시대에 맞게 병렬화 지원을 확장하였습니다. 이 메모리 할당자는 현재 나와있는 메모리 


할당자중 성능이 가장 좋다고 알려져 있습니다. 기본적인 malloc 함수에 비해 두 배가 넘는 성능을 보인다고 


합니다. jemalloc 에 관한 유명한 문서로  


https://www.facebook.com/notes/facebook-engineering/scalable-memory-allocation-using-jemalloc/480222803919 


가 있습니다. http://www.canonware.com/jemalloc/index.html  는 홈페이지이며 


논문은 http://people.freebsd.org/~jasone/jemalloc/bsdcan2006/jemalloc.pdf  를 참고하시면 됩니다.


 buddy allocation와 slab allocation 는 jemalloc 를 이해하기위한 기반이론입니다.

그동안 consolas 를 가장 애용했었는데 아래 2개의 글꼴도 참 좋네요.


1. hack


다운로드 : https://sourcefoundry.org/hack/

위에서 다운로드 받아서 설치해도 되고, 파일 올려둡니다.

Hack-v2_013-ttf.zip


압축 해제한다음에 각각 글꼴 파일 더블클릭하면 설치되네요 (윈도우 8.x)

 






글 폭이  좀 많이 넓어지는 느낌. (개인적 취향에는 딱 맞습니다 ^^)



2. naver d2 




http://dev.naver.com/projects/d2coding

'소프트웨어 사색' 카테고리의 다른 글

객체 복제 in Polyglot  (0) 2015.11.13
jemalloc 이란?  (0) 2015.09.09
인터프리터 언어는 컴파일 언어보다 느린가?  (0) 2015.08.01
정규표현식 (Regex) 정리  (10) 2015.07.23
주요 오픈소스 라이센스  (0) 2015.06.26

파이썬은 왜 느릴까? 느린가?     (파이썬 종류에 따라서 천차만별임을 염두..) 

https://medium.com/@cookatrice/why-python-is-slow-looking-under-the-hood-7126baf936d7

https://hbfs.wordpress.com/2009/11/10/is-python-slow/


왜 항상 자바는 c++ 보다 느릴까?  (언제 쓰여진건지 모르겠군요. 항상이란 말이 좀 깨림칙...) 

http://sungpi.postach.io/post/wae-hangsang-jabajavaneun-c-boda-neuringa


왜 인터프리트 언어는 컴파일언어보다 느릴까?

http://stackoverflow.com/questions/1694402/why-are-interpreted-languages-slow

http://stackoverflow.com/questions/7991877/why-is-an-interpreter-slower-than-a-compiler-in-practice


C++ VS Python 속도비교 

http://blog.dhananjaynene.com/2008/07/performance-comparison-c-java-python-ruby-jython-jruby-groovy/

http://www.matthiaskauer.com/2014/02/a-speed-comparison-of-python-cython-and-c/


스택 오버플로우의  답변 중 하나

All answers seem to miss the real important point here. It's the detail how "interpreted" code is implemented.

Interpreted languages are slower because their method, object and global variable space model is dynamic. This requires many many extra hash-table lookups on each access to a variable or method call. This is where most of the time is spent. It is a painful random memory lookup, which really hurts when you get a L1/L2 cache-miss.

Google's Javascript Core8 is so fast and targeting almost C speed for a simple optimization: they take the object data model as fixed and create internal code to access it like the data structure of a native compiled program. When a new variable or method is added or removed then the whole compiled code is discarded and compiled again.

The technique is well explained in the Deutsch/Schiffman paper "Efficient Implementation of the Smalltalk-80 System".

The question why php, python and ruby aren't doing this is pretty simple to answer: the technique is extremely complicated to implement.

And only Google has the money to pay for JavaScript because a fast browser-based JavaScript interpreter is their fundamental need of their billion dollar business model.


가장 심플한 답변 

compiled language like C is usually compiled directly into machine code. When you run the code, it is executed directly by the CPU.

A fully interpreted language like BASIC or PHP is usually interpreted each time it runs. When you execute your code, the CPU executes the interpreter, and the interpreter reads and executes your source code. (PHP can be classified as fully interpreted, since while it does use opcodes, they are usually thrown away after the execution.)

bytecode interpreted language like Python, is compiled from source code to bytecode that is executed by a virtual machine. The CPU runs the VM, and the VM executes each bytecode instruction. In Python, the bytecode is compiled the first time code is executed.

In Java, bytecode is compiled ahead of execution. The Java VM also has a special feature called Just-in-time compilation. This means that during execution, it may compile some of the bytecodeto machine code, which it can send to the CPU to execute directly.

In conclusion, with compiled languages, the CPU runs the code directly. In interpreted languages, the CPU usually runs the interpreter or virtual machine. This makes interpreted languages generally slower than compiled languages, due to the overhead of running the VM or interpreter.

NOTE: While we speak of interpreted and compiled languages, what we are really discussing is the usual execution style of a language. PHP can be compiled (using HipHop), and C can be interpreted (using Parrot VM).

 


정규표현식은 아주 가끔 쓰기때문에 항상 다시 볼때마다 헷갈리곤 하기에
주요 사용예를 내가 나중에 다시 봤을 때 편하도록 정리하여 보았다. 



정규 표현식의 용어들정규 표현식에서 사용되는 기호를 Meta문자라고  표현한다. 표현식에서 내부적으로 특정 의미를 가지는 문자를 말하며 간단하게 정리하면 아래의 표와 같다.

 표현식
의미 
 ^x
 문자열의 시작을 표현하며 x 문자로 시작됨을 의미한다.
x$
 문자열의 종료를 표현하며 x 문자로 종료됨을 의미한다.
 .x
 임의의 한 문자의 자리수를 표현하며 문자열이 x 로 끝난다는 것을 의미한다.
 x+
 반복을 표현하며 x 문자가 한번 이상 반복됨을 의미한다.
 x?
 존재여부를 표현하며 x 문자가 존재할 수도, 존재하지 않을 수도 있음을 의미한다.
 x*
 반복여부를 표현하며 x 문자가 0번 또는 그 이상 반복됨을 의미한다.
 x|y
 or 를 표현하며 x 또는 y 문자가 존재함을 의미한다.
 (x)
그룹을 표현하며 x 를 그룹으로 처리함을 의미한다.
 (x)(y)
그룹들의 집합을 표현하며 앞에서 부터 순서대로 번호를 부여하여 관리하고 x, y 는 각 그룹의 데이터로 관리된다.
 (x)(?:y)
그룹들의 집합에 대한 예외를 표현하며 그룹 집합으로 관리되지 않음을 의미한다. 
 x{n}
 반복을 표현하며 x 문자가 n번 반복됨을 의미한다.
 x{n,}
 반복을 표현하며 x 문자가 n번 이상 반복됨을 의미한다.
 x{n,m}
 반복을 표현하며 x 문자가 최소 n번 이상 최대 m 번 이하로 반복됨을 의미한다.

Meta 문자들 중에서 좀 더 특수하게 사용되는 문자들이 존재한다. '[]' 는 내부에 지정된 문자열의 범위 중에서 한 문자만을 선택하다는 특수한 의미를 가진다. 그리고 내부에서 Meta문자를 사용하면 다른 의미를 가지고 동작할 수 있으므로 잘 확인하고 사용해야 한다. 좀 더 특별한 용도로 사용되는 것들은 아래의 표와 같다. 

 

 표현식
의미 
[xy]
문자 선택을 표현하며 x 와 y 중에 하나를 의미한다.
 [^xy]
not 을 표현하며  x 및 y 를 제외한 문자를 의미한다.
 [x-z]
range를 표현하며 x ~ z 사이의 문자를 의미한다. 
 \^
escape 를 표현하며 ^ 를 문자로 사용함을 의미한다.
 \b
word boundary를 표현하며 문자와 공백사이의 문자를 의미한다.
 \B
non word boundary를 표현하며 문자와 공백사이가 아닌 문자를 의미한다.
 \d
digit 를 표현하며 숫자를 의미한다. 
 \D
non digit 를 표현하며 숫자가 아닌 것을 의미한다. 
 \s
space 를 표현하며 공백 문자를 의미한다. 
 \S
non space를 표현하며 공백 문자가 아닌 것을 의미한다.
 \t
tab 을 표현하며 탭 문자를 의미한다.
 \v
vertical tab을 표현하며 수직 탭(?) 문자를 의미한다.
 \w
word 를 표현하며 알파벳 + 숫자 + _ 중의 한 문자임을 의미한다. 
 \W
non word를 표현하며 알파벳 + 숫자 + _ 가 아닌 문자를 의미한다. 


정규표현식을 사용할 때 Flag 라는 것이 존재하는데 Flag를 사용하지 않으면 문자열에 대해서 검색을 한번만 처리하고 종료하게 된다. Flag는 다음과 같은 것들이 존재한다.

 Flag
의미 
 g
Global 의 표현하며 대상 문자열내에 모든 패턴들을 검색하는 것을 의미한다. 
 i
Ignore case 를 표현하며 대상 문자열에 대해서 대/소문자를 식별하지 않는 것을 의미한다.
 m
Multi line을 표현하며 대상 문자열이 다중 라인의 문자열인 경우에도 검색하는 것을 의미한다. 


사용 예제 

개별 숫자 - /[0-9]/g

전체에서  0~9사이에 아무 숫자 '하나'  찾음 

개발 문자 - /[to]/g

전체에서 t  혹은 o  를 모두 찾음 

단어 - /filter/g

전체에서  f 따로 i 따로 찾는게 아니라 'filter' 라는 단어에 매칭되는것을 찾음 

단어 제외 - /\b(?:(?!to)\w)+\b/g

전체에서 'to' 라는 단어를 빼고 다른 단어 매칭 /  확인결과 "Tutorial" 도 제외됨. 

단어 제외 - \b(?!\bto\b)\w+\b

전체에서 'to' 라는 단어를 빼고 다른 단어 매칭 /  확인결과 "Tutorial" 는 제외 안됨.  이게 더 정확하다고 볼수있을듯. 

 

이메일 - /^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/i

'시작을'  0~9 사이 숫자 or a-z A-Z 알바펫 아무거나로 시작하고  /  중간에 - _  . 같은 문자가 있을수도 있고 없을수도 있으며 / 

그 후에 0~9 사이 숫자 or a-z A-Z 알바펫중 하나의 문자가 없거나 연달아 나올수 있으며 /  @ 가 반드시 존재하고  / 

0-9a-zA-Z 여기서 하나가 있고  /  중간에 - _  . 같은 문자가 있을수도 있고 없을수도 있으며 / 그 후에 0~9 사이 숫자 or a-z A-Z 알바펫중 하나의 

문자가 없거나 연달아 나올수 있으며 /  반드시  .  이 존재하고  / [a-zA-Z] 의 문자가 2개나 3개가 존재 /   이 모든것은 대소문자 구분안함 

전화번호 - /^\d{3}-\d{3,4}-\d{4}$/

시작을 숫자 3개로하며 /   중간에 하이픈 -  하나 존재 /  숫자가 3~4개 존재하며 /  하이픈 하나 존재 /  숫자 4개로 끝남 

핸드폰 번호 - /^01([0|1|6|7|8|9]?)-?([0-9]{3,4})-?([0-9]{4})$/

시작을 숫자 01로 시작하며 그 후에 0,1,6,7,8,9 중에 하나가 나올수도 있으며 /  하이픈 - 하나 존재할수도 있으며 /  숫자 3~4개 이어지고 / 

또 하이픈 - 하나 존재할수도 있으며 /  숫자 4개가 이어짐 

URL - ^(https?):\/\/([^:\/\s]+)(:([^\/]*))?((\/[^\s/\/]+)*)?\/?([^#\s\?]*)(\?([^#\s]*))?(#(\w*))?$

^(https?):\/\/

([^:\/\s]+)

(:([^\/]*))?

((\/[^\s/\/]+)*)?\/?([^#\s\?]*)(\?([^#\s]*))?(#(\w*))?$



레퍼런스 : 

http://stackoverflow.com/questions/7820930/regex-how-to-exclude-single-word <-- 이론 
http://ccambo.blogspot.kr/2014/10/regular-expression.html   <-- 이론 
http://regexr.com/  <-- 테스트 



Actor 모델의 기본을 짚어보고  Akka 에 예제를 짧막하게 살펴본다. (굉장히 두서없는 글이 될것이다) 
Actor 모델이 굉장히 유명한데, 개인적으로 ActiveObject 패턴으로 알고있었다.
정확히 둘 간에 어떤  차이점이 있는지는  모르겠다.  패턴과 모델 ??  

내가 읽은 어느 책에서는 ActiveObject  패턴이 Actor 과 같다고 나오고, POSA2 편을 보면 (Pattern-Oriented Software Architecture 2 : http://www.cs.wustl.edu/~schmidt/POSA/POSA2에서는 ActiveObject 패턴과 Reactor / Proactor 패턴이 나온다.  분명히 다르긴 하다. 


정리


object :   객체로 호출하면 바로 반응한다. 

actor  :   능동적인 객체 ( 즉 자신의 쓰레드가 있고 큐를 가지고있다 )  호출하면 바로 응답하지 않는다. 

reactor : actor 에 추가적으로 해당 이벤트에 대한 핸들러가 매핑되어서 디스패치하는 구조. (selector 느낌)

proactor :  행위를 actor 에 넘겨서 그 행위에 대한 결과를 받은 구조.  ( IOCP 느낌)

activeobject : 행위할수 있는 actor 에게 자신의 요청 사항을 넘겨준후에 future 객체를 이용하여 결과 파악을 함. 


이번글을 쓰는 계기로 차이점에 관련된 읽을거리를 검색해봤다 ;;

http://www.carlgibbs.co.uk/blog/?p=237

http://members.unine.ch/anita.sobe/res/RR-I-AS-2014.06.1.pdf

Active Object pattern: http://www.dre.vanderbilt.edu/~schmidt/PDF/Active-Objects.pdf

Actor Model: http://en.wikipedia.org/wiki/Actor_model

솔직히 나는 저 둘의 관계에 대해  깊숙히 이해하고 싶은 생각이 없다. (지금 당장은 말이다) 
따라서 깊은 이해는 없다라는걸 미리 말한다. 대략 요약하면 


차이

-   구조적으로 두 패턴모두 스케쥴러와 큐를 가지고있다.
-   구조적으로 ActiveObject 는 프록시를 활용하며 , Future 객체를 사용한다.
-   구조적으로 Actor 패턴은  ActiveObject 에 비해서 자유성이 높다. 
-   메세징/통신측면에서 ActiveObject 패턴은  미리 정해진 함수호출을 통해서 잡을 전달한다.  
-   메세징/통신측면에서 Actor 패턴은 문자열을 보내며, 그 문자열 포맷은 매우 자유롭다.
    따라서 Actor 패턴에서 각각의 Actor 의 OnRecvier 구현은 매우 유연하다.  

-   Actor 모델은 더욱 확장하기 쉬운데 어떤 액터나 프록시가 될수있고 다른 액터와 분산해서 작업부하를 가질       수있다.

-  대부분의  AOM 구현들은 정해진 서번트와 함께 쓰레드풀에 의존적이다.  
-  양쪽 모델모두 가능한 이미 존재하는 Actor 나 ActiveObject 를 재사용한다는걸 알아둬라. 
-  보통 Actor 모델은 큐가 제한이 없으며, ActiveObject 는 큐의 크기에 제한이 있다. 


자 Active Object 패턴은 한마디로 말하면

"비동기 메세지를 처리할수있는 능동적 객체" 이다.

주목해야할 단어가 2개있다. 


첫째, "비동기" 

둘째, "능동적" 


비동기란 무엇인가??  

내가 어떤일을 해야하는데 내가 하기는 싫고 , 다른 녀석한테 시키고 싶을때 저놈은 게다가 그 일에는 전문가다. 내가 일을  

- 다른녀석한테 슬쩍 넘기고 , 나는 내일을 하는게 비동기다. (논블럭이라고도한다. 미묘한 차이가 있다)

- 다른녀석한테 넘기고 , 그 녀석이 일을 마칠때까지 기다리고 있는건 동기이다.(블럭킹 되었다고한다)


비동기메세지란 결국 메세지를 다른녀석한테 던저주고 나서 , 바로 나는 내일을 하는것을 말한다. 

* 윈도우즈의 IOCP에서 말하는 비동기 입출력(Overlapped IO) 은 조금 또 다른 느낌이다..(병렬의 느낌) 


그럼 능동적은 무엇인가?? 

기술적으로 간단하게 말하면, 나도 쓰레드를 가지고있고, 다른녀석도 자신의 쓰레드 혹은 프로세스를 가지고있는걸 말한다. 둘이 같은 쓰레드를 공유하는게 아니라는 얘기이다.


결국 위에서 말한 "나" 와  "다른 녀석" 은 각각 자신만의 "쓰레드" 를 가지고있고, 자신만의 "논블록킹" 큐를 가지고있게된다. 

따라서 어떤 일을 상대방한테 넘길때 메세지를 상대방의 큐에 넣고 , 돌아오면 "비동기" 가 되는것이고 

상대방은 스스로 쓰레드에서  while 을 돌면서  자신의 큐를 확인해서 일거리를 가지고 옮으로 "능동적"이다.

전체 맥락이 이해됬는가??

이 능동형 객체가 Akka에서 Actor 이며 , Actor 들은 자신만의 큐와 스케쥴러를  가지고있을것이다.

근데 메세지를 객체로?? 메세지가 명령인데  명령을 객체화 해서 보내는것은 알다시피 Command 패턴이다. 그리고  Future 패턴이나  큐에 잡을 던진다는것에서  producer-consumer  패턴을 포함한다.

패턴은 조금씩 서로와 연관된다.  

클라이언트측에서 어떤 “메소드 실행” 을  클라이언트와는 별개의 쓰레드 혹은 다른 컴퓨터의 큐에 던저서 그쪽에서실행되게 하자!! 값은 나중에 받자!! RPC 랑도 연관되네? Vert.x/ RabbitMQ / ActiveMQ 랑도 연관되네? 

(하둡RPC : http://brad2014.tistory.com/175)

근데 객체복사를 꽤 하니깐 작은규모에선 오버헤드가 좀 있고 이런 사소한것보단 저런 나름 복잡한 패턴을 실제 구현하는 피로감이..(그래서 akka  라이브러리가 나옴)

(Akka 는 비동기적으로 업무처리를 분담한다는것 이외에도 무상태를 지향하므로 , 데드락이나 레이스컨디션등 일반적인 객체지향언어의 멀티쓰레딩 코딩에서 나타나는 문제에 대한 보완을 한다. 물론 객체를 메세지로 보낼때 객체의 상태를 바꿀수있게 코딩한다면 의미없어 지겠지만) 



위의 ActiveObject UML 을 살펴보면  

1. 클라이언트가  ActiveObject 의 프록시로  메세지를 전달한다 ( 함수를 호출한다. 명령을 한다.)

2. ActiveOjbect 는 스케쥴러를 가지고 있다. ( 이게 "능동형" 라는 말인데, 쓰레드가  while 을 돈다는 말이다)

3. ActiveObject 는 MessageQueue 를 가지고있다. ( 이게 비동기를 위한 논블럭킹 큐이다) 

4. 클라이언트는 ActiveOjbect 를 통해 메세지를 만들고 메세지큐에 넣은다음에 퓨처 객체를 받고 자기할일함

5. 스케쥴러는 메세지큐에서 하나꺼내서 서번트를 통해서 작업을 하고, 퓨처객체에 완료 알림을 한다

6. 클라이언트는 퓨처객체에서 get 을 해서 실제 객체 (원하는 작업의 결과) 를 얻는다. 


첫번째 그림을 이해했으면 이것도 마찬가지라는걸 알수있다. Akka 의 Actor 는 다른 Actor의 메일박스에 메세지를 던지고 자기할일하고, QA 라는 Actor 는 디스패처를 통해서 일감을 가지와서 실행.

   

  여러개의 Actor 가 서로 다른 Actor 들에게 동시에 메세지(할일) 을 넘겨줄수도 있겠지..

Akka ( http://akka.io/ ) 에서 Actor   란 ?  (홈페이지 발췌) 

Actors

액터는 매우 가벼운 동시성  엔터티들이다. 이놈들은 메세지들을 event-driven receive loop  를 이용하여 비동기적으로 실행한다.  메세지에 대응되는 패턴매칭은 액터의 행동을 나타내는 굉장히 편리한 방법이며 , 추상레벨을 높혀서 Akka 를 가져다가 사용하는 개발자들이  분산/병렬  코드를 작성하기 굉장히 쉽게 해준다.

You focus on workflow—how the messages flow in the system—instead of low level primitives like threads, locks and socket IO. Learn More.

Akka with ActiveObject 패턴 

// 프록시와 서번트의 공통 인터페이스 
public interface  Printer {
  Future<String> printNonBlocking ( String msg) ;
  Option<String> print Blocking ( String msg) ;
}

// 공통인터페이스 기반으로 서번트를 만든다.
class PrinterImpl implements Printer {
  private String name ;
    public void PrinterImpl ( String name ) {
    this.name = name ;
  }
  public Futures<String>printNonBlocking ( String msg ) {
    System.out.println (msg) ;
    return Futures.successful(”Worked”);
  }
  public Option<String> print Blocking (String msg) {
    System.out.println (msg) ;
    return Option.some(”Worked”) ;
  }
} 


public class Main{
  public static void main (String [] args ) {
    ActorSystem system = ActorSystem.create (”testSystem ” ) ;

    // typedActor 가 사용되었다.
    // 첫번째 인자는 프록시에 사용될 인터페이스이고, 두번째 인자는 서번트가 들어간다 
    PrinterhelloWorld = TypedActor.get (system) .typedActorOf (
    new TypedProps<PrinterImpl>( Printer.class , PrinterImpl.class)) ;

    // 비동기호출을 하여 Futre 객체를 즉시 받는다.
    Future<String> resultNB = helloWorld.printNonBlocking ( ”Hello World!) ;
    Option<String> result  B = helloWorld.printBlocking (”Hello World !);
  }
} 


Akka with JAVA   

import akka.actor.UntypedActor;

public class PrintActor extends UntypedActor {

    @Override

    public void onReceive(Object message) throws Exception {

        Thread.sleep(500); // 테스트를 위해 0.5초 sleep

        System.out.println(message);

    }

}


ActorRef actor = Actors.actorOf(PrintActor.class);

actor.start();

// actor를 이용해서 액터에 메시지를 전달

ActorRef의 start() 메서드는 액터를 시작하며, 액터가 시작된 이후부터 액터에 메시지를 전달할 수 있게 된다.

액터에 메시지 전달하기

Actors.actorOf()를 이용해서 액터를 생성했다면, 이후 ActorRef가 제공하는 메서드를 이용해서 액터에 메시지를 전달할 수 있다. 

다음의 세 가지 방법으로 액터에 메시지를 전달할 수 있다.

1.Fire-And-Forget: 메시지를 전달하고 메시지에 대한 응답을 기다리지 않는다. 병행 및 확장에 적합한 메시지 전달 방식이다.

2.Send-And-Receive-Eventually: 메시지를 전달하고 응답을 받는다. 응답을 받을 때 까지 블록킹된다.

3.Send-And-Receive-Future: 메시지를 전달하고 응답을 받기 위한 Future를 리턴한다.


ActorRef actor = Actors.actorOf(PrintActor.class);

actor.start();

actor.sendOneWay("받아라");  //Fire-And-Forget 예  

actor.sendOneWay("받아라2");

actor.sendOneWay("받아라3");

System.out.println("비동기로 실행");


sendRequestReply() 메서드를 이용한 Send-And-Receive-Eventually 방식 메시지 전달

ActorRef.sendRequestReply() 메서드는 액터에 메시지를 전달하고, 그 메시지에 대한 응답이 올 때 까지 대기하고 싶을 때 사용된다. 액터 구현 클래스는 getContext().replyUnsafe() 메서드를 이용해서 메시지에 대해 응답할 수 있는데, ActorRef.sendRequestReply() 메서드는 이 응답을 리턴하게 된다. 예를 들어, 다음과 같이 메시지에 대해 응답하는 액터가 있다고 하자.

public class PingActor extends UntypedActor {

    @Override

    public void onReceive(Object message) throws Exception {

        getContext().replyUnsafe("응답: "+ message); // 메시지 sender에 응답

    }

}

이 경우 다음과 같이 sendRequestReply() 메서드를 이용함으로써 액터에 전달한 메시지에 대한 응답이 도착할 때 까지 대기할 수 있다.

ActorRef actor = Actors.actorOf(PingActor.class);

actor.start();

Object res = actor.sendRequestReply("헬로우"); // 액터로부터 응답이 도착할 때 까지 대기


sendRequestReplyFuture() 메서드를 이용한 Send-And-Receive-Future 방식 메시지 전달


sendRequestReplyFuture() 메서드는 메시지를 전달한 뒤 응답을 받기 위한 Future를 리턴한다. Future는 자바가 제공하는 Future가 아닌 Akka가 제공하는 akka.dispatch.Future 타입이다. Future는 주로 다음과 같은 형식으로 주로 사용된다.

Future future = actor.sendRequestReplyFuture("하이");

future.await(); // 응답을 대기. 대기 시간을 초과하면 예외 발생

..

..

어떤작업..

..

..

if (future.isCompleted()) { // 완료되었다면

    Option resultOption = future.result(); // 응답 구함

    if (resultOption.isDefined()) { // 응답 데이터가 있다면,

        Object result = resultOption.get(); // 응답 데이터 구함

        System.out.println(result);

    }

}


sendRequestReplyFuture()가 리턴한 Future의 await() 메서드는 시간이 초과될 때 까지 대기한다. 시간이 초과되기 전에 응답이 도착하면 다음으로 넘어가고, 시간이 초과되면 ActorTimeoutException 예외를 발생시킨다.



akka 홈페이지 발췌) 

  1. public class Greeting implements Serializable {
  2. public final String who;
  3. public Greeting(String who) { this.who = who; }
  4. }
  5.  
  6. public class GreetingActor extends UntypedActor {
  7. LoggingAdapter log = Logging.getLogger(getContext().system(), this);
  8.  
  9. public void onReceive(Object message) throws Exception {
  10. if (message instanceof Greeting)
  11. log.info("Hello " + ((Greeting) message).who);
  12. }
  13. }
  14.  
  15. ActorSystem system = ActorSystem.create("MySystem");
  16. ActorRef greeter = system.actorOf(Props.create(GreetingActor.class), "greeter");
  17. greeter.tell(new Greeting("Charlie Parker"), ActorRef.noSender());



Akka with Scala

akka 홈페이지 발췌) 

  1. case class Greeting(who: String)
  2.  
  3. class GreetingActor extends Actor with ActorLogging {
  4. def receive = {
  5. case Greeting(who) log.info("Hello " + who)
  6. }
  7. }
  8.  
  9. val system = ActorSystem("MySystem")
  10. val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
  11. greeter ! Greeting("Charlie Parker")


글을 쓰고나니 좀 웃음이 나는데 양심고백을 하자면  나는 요즘도 멀티쓰레드 프로그래밍을 할때 바로 쓰레드를 만들거나 자바 executors  서비스를 이용하는데 만들고나서 한참을 데드락이 있을까봐 코드를 째려보거나 가끔은 설마 하며 운에 맞기는 코딩을 한다. 아예 akka 는 생각안한다. ;;  작은 시스템에서도 충분히 아니 강력히 써야 한다고 생각은 하는데 실천이 부족했던것같다. 이 글을 계기로  당장 개발해야 하는 수요조절기능을 위한 스케쥴러에 첫삽을 떠보려한다.


처리율(throughput)   임백준님의 Akka 시작하기에서 발췌 

아카를 이용한 리팩토링을 끝마쳤을 때, 똑같은 컴퓨터 위에서 전과 동일한 몬테 카를로 시나리오를 수행하는데 걸리는 시간이 6시간에서 2시간으로 단축되었다. 66%의 시간이 절약된 것이다. 결과를 확인한 사람들은 깜짝 놀랐다. 단순히 자바 스레드에서 아카로 라이브러리를 바꾸었을 뿐인데 그렇게 엄청난 차이가 있을 수 있냐며 고개를 갸웃거렸다. 물론 이런 차이를 일반화할 수는 없다. 이런 결과 하나를 가지고 아카가 자바 스레 드보다 3배 빠르다고 말하는 어리석은 사람은 없을 것이다. 아카도 내부적으로 자 1 아카에 대하여 - 017 바 스레드를 사용하기 때문에 그런 비교 자체가 성립하지 않는다. 하지만 일반적 인 차원에서 짚고 넘어갈만한 부분도 있다. 이렇게 커다란 차이가 어디에서 비롯 되었는지 이해하려면 우선 암달의 법칙Amdahl’s law을 생각해볼 필요가 있다. 암달의 법칙은 이렇다. “멀티코어를 사용하는 프로그램의 속도는 프로그램 내부에 존재하는 순차적sequential 부분이 사용하는 시간에 의해서 제한된다.” Thread나 Task를 만들어서 ExecutorService에게 제출하는 식으로 동시성 코드를 작성하면 여러 개의 스레드가 동시에 작업을 수행한다. 하지만 프로그램 안에는 Thread나 Task가 포함하지 않는 코드가 존재한다. 여러 개의 스레드가 동시에 작업을 수행하더라도 synchronized 블록이나 데이터베이스, 네트워크 API 호출 등을 만날 때 다른 스레드와 나란히 줄을 서서 순차적으로 작업을 수행 해야 하는 경우도 있다. 암달의 법칙은 프로그램이 낼 수 있는 속도의 상한이 이런 순차적 코드가 사용하는 시간에 의해서 제한된다고 말하는 것이다. 이러한 순차적 코드의 또 다른 이름은 블로킹blocking 콜이다. 문제는 스레드 자체 가 아니라 스레드를 사용하면서 자기도 모르게 만들어내는 블로킹 콜이다. 조금 과장해서 말하자면 자바 개발자가 스레드를 이용해서 만들어내는 ‘동시성’ 코드는 일종의 신기루다. 사실은 코드 곳곳에 존재하는 블로킹 콜, 순차적 코드 때문에 전 체적인 프로그램의 처리율은 이미 상한이 정해져 있지만 여러 개의 스레드가 ‘동 시에’ 동작한다는 사실로부터 위안을 받을 뿐이다.


제공 : 한빛 네트워크
저자 : Mike Loukides
역자 : 이덕준
원문 : What is DevOps? 

아드리안 콕크로프트(Adrian Cockcroft)는 넷플릭스의 무운영(NoOps)이라는 기사로 몇 달간 타오른 논쟁의 불씨를 지폈다. 아드리안의 기사에 대한 존 올스포(John Allspaw)의 상세한 답변의 핵심은 다음과 같다. 아드리안이 "무운영"이라고 설명한 것이 실제로는 그렇지 않다는 것이다. 운영은 없어지지 않는다. 책임은 시간이 흐름에 따라 전이될 수 있으며, 전이되고 있다. 구인 공고도 마찬가지다. 책임을 어떻게 나누던지, 할 일은 여전히 해야하고, 그런 일 중 하나가 바로 운영이다. 아드리안이 넷플릭스의 무운영이라고 부르는 바로 그것이 Etsy의 운영과 전혀 다른 것이 아니다. 여기에서 질문을 하나 던져보자. 21세기의 "운영"은 무엇을 의미하는가? 무운영이 운영을 운영과 아주 유사한 무엇으로 대체하려는 움직임이라면, 혼란을 피할 수 없다. 논란으로 인한 격정이 가라앉은 지금, 운영이란 무엇인지, 시간이 흐름에 따라 어떻게 변하는지 더 잘 이해할 때가 되었다. 

얼마전 존과 점심을 같이 할때, 그는 다음과 같이 말했다. 컴퓨터 시대의 여명을 돌아보면, 개발과 운영의 구분이 없었다. 개발을 했건 운영을 했건, 테이프를 마운트하고 전면 패널에서 스위치를 켜고 껐을 것이며, 문제가 발생하면 재부팅을 했고, 어쩌면 타버린 진공관을 교체했을 수도 있다. 괴짜스러운 하얀 연구복을 입어야 했을지도 모른다. 개발과 운영이 나뉜건 60년대 부터인데, 프로그래머나 분석가가 여러 상자의 천공카드를 판독기에 부어넣으면, "컴퓨터 운영자"는 유리벽 뒤에서 IBM JCL(작업 제어 언어)에 따라 마운팅된 테이프 사이를 종종걸음으로 다녔다. 그리고 운영자가 라인 프린터의 출력물을 이름표가 붙은 보관함에 밀어넣어주면, 거기에서 각자의 이름 아래 쌓인 결과물을 받아 볼 수 있었다. 

70년대의 미니컴퓨터와 80년대의 PC의 도래로 인해 메인프레임 운영자와 사용자의 벽이 세분화되어서 1980년대와 90년대의 시스템 관리자와 네트워크 관리자가 등장했다. 이게 바로 현대의 "IT 운영자" 문화의 탄생이다. 미니컴퓨터 사용자는 위험하기에 딱 좋을만큼의 지식을 가진 컴퓨터 전문가이곤 했다(새로온 임원에게 루트 패스워드를 주고 "본인 계정을 만드세요"라고 했더니 30여명의 사용자가 나누어 쓰고 있는 VAX를 바로 망가뜨렸던게 기억난다). PC 사용자는 네트워크가 필요하고, 지원이 필요하며, 파일 서버나 메일 서버와 같은 공유 자원이 필요하다. 그리고 물론, "빌어먹을 운영자"(BOFH, "Bastard Operator from Hell")같은 소설이 요즘의 세태를 상기시켜준다. 조금 다른 방식으로, 그런 문제를 가진 사람은 당신 말고 "아무도" 없다는 얘기를 들었는데, 모든 이가 바로 동일한 문제를 가졌다는 사실을 회의를 하기 전까지 알지 못했던 일이 기억난다. 우리가 운영이 사라지길 바란다는데에 의심의 여지가 없으며, 특히, 이론적으로, 개인용 컴퓨터와 데스크톱 워크스테이션의 발전이 우리 모두가 우리 개인의 머신에 대한 책임이 있다는 것을 의미하는 그 때부터 개발자와 시스템 관리자 사이의 벽이 있기를 바랐다는데에 의심의 여지가 없다. 

하지만 누군가는 갈수록 더 중요해지는 웹사이트를 포함한 기반 체계를 운영해야한다. 회사와 전산 설비는 커지는데 반해 많은 시스템 관리자의 장애 대응에 대한 인식은 확장되지 않고 있다. 회사 전체가 386 한 대에서 운영되던(1990년도의 오라일리 같은) 때에는, 아무도 모르는 명령행 주문을 외워대는 것이 문제를 고치는 적절한 방법이었다. 하지만, 랙스페이스(Rackspace)나 아마존(Amazon)과 같이 수 백, 수 천대의 노드에 대해 얘기할 때에는 이런 방식은 통하지 않는다. 운영의 입장에서, 웹에 대한 화두는 브라우저에서 돌아가는 완전한 응용 프로그램으로의 진화가 아니다. 한 대의 서버에서 수십, 수백, 수천, (구글이나 페이스북의 경우에는) 수백만대의 서버로 성장하는 이야기다. 그런 규모의 운영을 하게되면, 명령 행에서 문제를 해결하는 방법을 선택할 수 없다. 추가적인 수정이나 패치에 대해 동기화가 되지 않은 장비를 그냥 두고 볼 수가 없다. "125대의 서버를 즉시 온라인에 붙여야하는데, 자동화할 시간이 없습니다"라는 소리를 듣는다는 것은 재난으로 가는 비결이다. 

이 문제에 대한 운영 커뮤니티의 응답은 놀랍지 않았다. 오라일리의 벨로시티 컨퍼런스의 주제 중 하나는 "코드로써의 기반 체계(Infrastructure as Code)"였다. 안정적으로 운영하려면, 이를 재생산하고 프로그래밍 가능하게 만들 필요가 있다. 그런 이유로, 가상 머신이 소프트웨어를 설정 이슈에서 보호해준다. Puppet과 Chef가 설정을 자동화해주고, 모든 머신이 똑같은 소프트웨어 설정이 된 채로 올바른 서비스를 구동하고 있음을 알 수 있다. Vagrant가 모든 가상 머신이 처음부터 똑같이 구축되었음을 보장해주고, 자동화된 모니터링 도구가 클러스터가 모두 적절하게 돌아가고 있음을 알려준다. 노드가 사설 데이터 센터에 있는지, 호스팅 설비나 공용 클라우드에 있는지 여부는 중요치 않다. 이들을 관리하는 소프트웨어를 만들지 않으면 살아남을 수 없다. 

게다가, 전통적인 하드웨어 서버나 네트워크에서 멀어질수록, 모든 단계에서 가상화된 세상으로 가면 갈수록, 구식의 시스템 관리는 중단되기 시작할 것이다. 물리적인 장비실의 물리 장비는 사라지지 않겠지만, 더 이상 그것만이 시스템 관리자가 신경써야 할 전부가 아니다. 동일 장소 배치 설비에서 가상 인스턴스의 루트 디스크 드라이브가 어디 있을까? 가상 스위치의 네트워크 포트는 어디에 있을까? 물론, 90년대의 시스템 관리자는 이런 자원을 소프트웨어로 관리했다. 펄 스크립트 포트폴리오가 없는 시스템 관리자가 제 값어치를 하는 일은 없었다. 차이점은 이제 네트워크 포트, 디스크 드라이브 또는 CPU 같은 자원 자체가 물리적인 자원일 수도 있고, 손으로 가리키거나 제거할 수 있는 물리적인 장치와는 상관 없는 소프트웨어일 수도 있다는 것이다. 이렇게 계층화된 자산을 관리하는 효과적인 방법은 소프트웨어를 통하는 것 뿐이다. 

그래서 기반체계는 코드가 되어야한다. 위에서 언급한 펄 스크립트는 80년대 후반부터 이미 기반체계가 코드가 되고 있음을 보여준다. 사실상, 펄은 시스템 관리를 자동화하는데 사용하기 위해 설계된 프로그래밍 언어였다. 앞선 기술의 시스템 관리자가 손수 작성한 설정과 재생산 불가능한 주문이 그들의 장비를 운영하는데 나쁜 방법이라는 것을 깨닫게 되는데까지 그리 오래 걸리지 않았다. 이런 경향이 아마존이나 랙스페이스 때문에 시스템을 랙에 올리는 일이 줄어든, 전통적인 시스템 관리자의 종말이라고 할 수도 있다. 그러나 이는 성장하기를 거부하고 컴퓨팅 업계의 발전을 받아들이기를 거부하는 시스템 관리자의 운명일 뿐이다. (그리고 적응을 거부한 시스템 관리자는 BOFH 협회의 수를 늘릴 것이며, 우리 대부분은 그들이 떠나는 것을 보면서 기뻐할 것이다.) 좋은 시스템 관리자는 자동화가 관리 업무의 일부 요소임이 확실하며 자동화를 채택하는 일이 보다 더 중요해 질 것이라는 것을 항상 알고 있었다. 새로운 시스템 관리자는 머신을 끄거나 고장난 디스크 드라이브를 교체하고 재부팅하고 백업해둔 디스크에서 복구하는 일을 하지 않을 것이다. 새 관리자는 EC2 인스턴스가 오작동하고 있는 것을 자동으로 찾아내서 불량 인스턴스를 종료시키고 새로운 인스턴스를 생성시키고 구성하며, 서비스 중단 없이 이 모두를 수행하는 소프트웨어를 작성할 것이다. 이 정도 수준의 자동화를 해두면, 새로운 "운영자"는 수십대의 시스템을 책임지게 되던지, 만여대의 시스템을 책임지게 되던지 신경쓰지 않을 것이다. 현대의 BOFH는 변화를 수용하지 않기로한, 구식의 시스템 관리자일 가능성이 더 높다. 

제임스 어쿼트(James Urquhart)는 클라우드에서 돌아가는 요즘 애플리케이션에 여전히 회복력과 내구성이 필요하며, 모니터링이 필요하고, 크게 변하는 부하에 대응할 필요가 있음을 설명하면서 이를 확실히 했다. 그러나 그는 예전에는 IT/기반 체계 운영에서 제공된 이런 기능들이 이제는 애플리케이션의 일부가 되어야하며, 특히 "서비스로서의 플랫폼(Platform as a service)" 환경이 되어야 한다고 언급했다. 운영은 사라지지 않으며, 개발의 일부가 된다. 빅 데이터, 웹 성능 최적화, 애플리케이션 미들웨어, 어마어마하게 분산된 환경에서의 내구성을 이해하고 있는 최고의 개발자 따위를 마음에 그리기 보다, 개발 팀에 있는 운영 전문가가 필요하다. 기반 체계는 사라지는 것이 아니며, 코드로 옮겨간다. 그리고 기반 체계, 시스템 관리자 및 기업의 IT 부서를 담당한 사람들은 발전해서 그들의 기반 체계를 유지할 수 있는 코드를 작성할 수 있다. 고립되기 보다는, 애플리케이션을 만든 개발자와 협동하고 협력할 필요가 있다. 이런 움직임이 비공식적으로 알려진 "개발운영(DevOps)"이다. 

작년 아마존의 EBS 중단 사건은 "운영"의 성격이 어떻게 바뀌고 있는지를 잘 보여준다. 많은 손실을 입고 고생한 회사도 있고 그런 회사와 뚜렷한 차이를 보이며 그 정도 중단은 대수롭지 않게 넘긴 회사도 있다. 그 차이가 무엇인가? 넷플릭스를 포함, 그 사건으로 고생하지 않은 회사들은 신뢰성을 위한 설계법을 알고 있었다. 이들 회사는 회복력, 존(zone)을 넘어선 데이터 스프레딩 및 수많은 신뢰성 공학을 이해하고 있었다. 게다가, 그들은 회복성이 애플리케이션의 속성임을 이해했으며, 네트워크 일부에서 장애가 발생해도 애플리케이션이 살아남을 수 있음을 확실히 하기 위해 개발팀과 작업했다. 아마존의 서비스에 대해 부정적인 비판을 하는 것보다 더 중요한 것은 EBS가 죽어도 애플리케이션이 작동할 수 있도록 똑똑하고 주의깊게 설계했다는 추천서이다. 넷플릭스의 카오스멍키(ChaosMonkey)는 훌륭한데, 극단적으로, 복잡한 분산 애플리케이션이 장애에도 살아남을 수 있다는 것을 보장해주는 도구의 예이다. 카오스멍키는 임의로 애플리케이션의 인스턴스와 서비스를 죽인다. 애플리케이션이 지속적으로 임의의 (그리고 고의의!) 장애를 견뎌내고 성능 저하 없이 충분히 견고한지 보장하기 위해 개발팀과 운영팀은 협업한다. 

반면, EBS 장애 동안, 아마존 직원이 아닌 그 누구도 하드웨어에 손 하나 대지 않았다. 그 때, JD Long은 EBS 장애에 대한 최선은 이를 고치려고 미친듯이 애쓰지 않는 것이라고 트윗했다. 어떻게 그럴 수 있는가. 하지만 여기서 20년, 심지어 10년 전 운영 경험과 어떤 차이가 있는지 아는 것이 중요하다. 이미 장애가 발생하기 전부터 이는 결정되어 있었던 일이다. 이런 장애을 성공적으로 다룬 사이트는 견고한 소프트웨어를 작성했고, 조심스럽게 데이터를 관리해서 단일 존에만 의존적이지 않았다. 또한 유사하게, 장애에서 복구하기 위해 허둥댄 사이트는 애플리케이션에 회복성를 구축하지 않았으며, 다른 존에 걸친 데이터 복제를 해두지 않았던 사이트었다. 

이런 책임의 재분배에 더해서, 하위 계측 스택부터 애플리케이션 그 자체까지, 비용이 재분배되고 있음을 확인할 수 있다. 운영 비용이 사라졌다고 생각하면 오산이다. 새 서버를 사는데 들인 자본은 아마존의 월단위 청구서로 대체되었지만 여전히 비용이다. 전통적인 IT 스탭은 감축되었을 것이며, 확실히 스탭당 서버의 비율은 높아졌다. 이는 일부 IT 기능이 개발 그룹으로 사라졌기 때문이다. 이런 유대는 가변적이다, 하지만 확실히 핵심은 다음과 같다. 애플리케이션이 돌아가는 서버의 위치와 애플리케이션이 어떻게 관리되는지가 모두 바뀌어도 견고하고 안정적인 애플리케이션을 고객에게 제공하는 일은 똑같다. 

운영의 중요한 일 하나는 아마존 같은 공용 클라우드, 사설 클라우드, 전통적인 동일 장소 배치, 전용 기반 체계 구축 사이에서 비용 트레이드 오프를 이해하는 것이다. 비용을 절감하고 부하의 변동에 따라 하드웨어를 할당하거나 해제해야하는 스타트업이라면 아마존을 안 쓸 수 없을 것이다. 피크의 성능을 다룰 수 있지만 대부분 시간동안 놀고 있는 거대한 클러스터를 갖고 싶지는 않을 것이다. 그러나 아마존은 값이 싸지는 않으며, 보다 큰 회사는 아마도 더 나은 조건으로 자신의 동일 장소 배치용 기반 체계에 올릴 수도 있을 것이다. 일부 큰 회사는 그들 자체 데이터 센터를 구축할 것이다. 비용 대비 유연성은 중요한 트레이드 오프가 된다. 물리 하드웨어를 가지고 있을 때 확장은 필연적으로 느릴 수 밖에 없으며 피크의 부하를 처리하기 위해 데이터 센터를 구축해야할 때 설비는 대부분 충분히 활용되지 못할 것이다. 보다 작은 회사는 AWS나 랙스페이스와 같은 공용 클라우드에 일부 기반 체계를 가져가고 일부는 내부 기반 체계를 활용하는 하이브리드 전략을 개발할 것이다. 이런 설비 사이에서 일을 어떻게 최적화시켜 분배할지는 간단한 문제가 아니다. 이것이 운영 조직의 분야이다. 하이브리드 환경에서 효율적으로 돌아가는 애플리케이션 개발은 운영 팀과의 건강한 협력 하에 가져야할 개발자의 책임이다. 

시스템 성능을 모니터하기 위해 사용하는 메트릭은 시스템 관리자가 개발해야하는 또 다른 관점이다. 80년대나 90년대 초에는 전화벨이 울려대기 시작하면 기계가 고장났다는 것을 알 수 있었다. HP의 OpenView와 같은 초기 시스템 모니터링 도구는 시스템과 네트워크 동작에 대한 제한된 정보를 제공하기는 했지만 단순한 심박 테스트나 도달 가능여부 테스트 이상의 정보는 주지 못했다. DTrace와 같은 요즘의 도구는 거의 모든 시스템 행위에 대한 정보를 줄 수 있다. 현대 운영 조직의 가장 큰 도전 과제는 장애가 발생하기 전에 문제를 예측할 수 있는 데이터를 취할 수 있는 분석도구나 메트릭을 개발하는 것이다. 우리는 이제 필요한 데이터에 접근할 수 있지만 이를 어떻게 사용해야할지 모를 뿐이다. 그리고 우리가 분산 시스템에 더 의존하게 될수록, 모니터링은 보다 더 중요해진다. 다른 그 무엇보다도, 모니터링은 애플리케이션 그 자체의 일부가 되어야한다. 운영은 성공에 결정적이지만, 개발과 협업하고 애플리케이션의 개발에 참여해서 스스로를 모니터하고 복구할 수 있도록 확장하는 것만이 운영이 성공할 수 있는 방법이다. 

성공은 운영을 온전히 개발에 통합하는데 기반하지 않는다. 고성능의 도전 목표, 분산 애플리케이션을 잘 알고 있는 최고의 개발 그룹이라면 절대로 망가지지 않는 소프트웨어를 만들 수 있으리라 생각한다면 순진한 생각이다. 이런 상부상조적인 관계에서, 개발자나 IT 스텝이 호출기를 착용해야 할까? 올스포가 지적했듯, 개발자를 그들의 결과물에서 분리시키지 않는 것이 중요한데, 왜냐하면 그들이 작성한 코드에서 종종 불이 나기도 하기 때문이다. 그래서 개발자와 운영자 둘 다 호출기를 가지고 다녀야 한다. 책임을 공유하는 것은 또 다른 이익을 가져온다. 장애가 나쁜 코드에 의한 것인지, 운영 실수에 의한 것인지 밝해내려 애쓰는, 서로를 손가락질 하는 사후 분석보다는 운영과 개발팀이 함께 장애를 해결하는 사후 분석이 누군가를 탓하는데 집중하기 보다 앞으로 시스템을 보다 견고하게 만드는데 집중할 수 있도록 해준다. 비록 장애가 발생하면 "근본 원인 분석"은 해야하지만, 단일 원인을 찾아내는 것은 도움이 되지 않는다는 것을 우리는 잘 알고 있다. 대부분의 장애는 평범한 일상의 작은 사고가 "엎친데 덮친, 복합적인 상황"의 결과이다. 무엇이 잘못되었는지 알아내고 재발 방지를 보장하는 절차(대부분 항상 비효율적이고 예상밖의 취약점을 수반하는 프로세스)를 수립하는 대신, 요즘의 운영은 일상의 에러에 직면해서 심지어 예상하지 못한 조합으로 에러가 발생해도 회복이 가능한 시스템을 설계한다. 

10년 전에는, 소프트웨어 개발 실천법에 있어 주로 변화를 보아왔다. 지루한 선행 투자식 계획을 수반한 "폭포수" 방법론의 다양한 버전에서부터 "최소의 실행 가능한 제품", 지속적인 통합, 지속적인 배포에 이르기까지 변화했다. 폭포수와 80년대의 방법론이 "나쁜 생각"이거나 실수가 아니라는 것을 이해하는 것이 중요하다. 비닐 포장된 소프트웨어의 시대에는 이런 것들이 완벽하게 적용되었다. "골드 디스크"를 만들고 수천 장(또는 수백만장)의 사본을 양산하고 나서 무언가가 잘못되었음을 알게 되었을때 그에 따른 불이익은 어마어마했다. 버그가 있다면, 다음 출시까지 고칠 수 없다. 이런 환경에서, 소프트웨어 출시는 대단한 이벤트였다. 그러나 요즘같은 웹과 모바일 애플리케이션의 시대에서, 배포는 그렇게 대단한 일이 아니다. 조기에, 자주 출시할 수 있게 되었다. 지속적인 통합에서 지속적인 배치에까지 이르렀다. 새로운 릴리즈가 심각한 문제를 가진 경우 빠르게 해결할 수 있는 기술을 개발했으며, 사용자의 작은 일부분을 기반으로 테스트 릴리즈를 하는 A/B 테스팅 기법을 터득했다. 

이런 모든 변화에는 개발자와 운영 스탭간의 협력과 협업이 필요하다. 운영 그룹은 이런 변화를 채택해야하며, 많은 경우, 이런 변화를 구현하기 위해 주도적으로 애써야한다. 이들은 복구와 모니터링, 변경 배포와 원상 복구의 전문가들이다. 오라일리의 벨로시티 컨퍼런스의 많은 참석자들, 강당에서의 논의, 대화와 기조연설에서 이들이 적응하고 있음을 볼 수 있었다. 이들은 소프트웨어 공학에서 완전히 새로운 회복력을 위한 접근을 채택하는 방법을 배우고 있다. 이들은 모니터하고 진단하며, 대규모의 자동화를 하고, 압박 속에서 디버깅하는 방법을 배우고 있다. 최근의 미팅에서, 제시 로빈스(Jesse Robbins)는 운영 스탭을 위한 응급 의료진 훈련 세션을 계획해서 그들이 응급 상황에서 어떻게 그들 자신을 제어하고 다른 이들과 의사소통하는지 이해할 수 있도록 하고자 한다는 이야기를 했다. 이는 흥미롭고 자극적인 발상이며, 현대 운영 스텝이 개발자와 같이 일할 때 염두에 두어야할 많은 것들 중 하나이다. 

장차 운영에는 무슨 일이 일어날까? 시스템과 네트워크 모니터링은 이질적이고 최첨단 기술만 같았다. 지금은, 이런 것들이 요구된다. 하지만 아직 충분히 해보지는 못했다. 아직 어떻게 시스템을 모니터해야하는지, 현대 모니터링 도구에서 생성되는 데이터를 어떻게 분석하는지, 그리고 결과를 효과적으로 보여주도록 대시보드를 어떻게 구축해야 하는지를 배우고 있다. "하둡 클러스터를 모니터하기 위해 하둡 클러스터를 사용한다"고 농담하곤 했는데, 이게 현실과 그리 동떨어져 있지는 않다. 수집할 수 있는 정보의 양이 기하급수적이어서, 기계학습과 같은 기술을 사용하지 않고는 사람이 분석할 수 있는 정도를 넘어서기도 한다. 

마찬가지로, 운영 조직은 SPDY와 같이 새롭고 보다 효율적인 웹을 위한 프로토콜을 배치하는데 대단한 역할을 하고 있다. 운영은 전보다 더 운영 체계와 서버(우리의 물리적 제어 밖의 것이라 하더라도)의 성능을 조율하는데 관여하고 있다. ISDN과 56Kbps 아날로그 모뎀 시대의 많은 TCP 튜닝의 "모범 사례"는 기가비트 이더넷, OC48* 파이버 및 그 파생 기술의 현실에 적용될 수 없다. 운영 조직은 이런 기술을 (그리고 그 계승 기술을) 어떻게 효율적으로 사용할 수 있을지 알아낼 책임이 있다. 이제 겨우 IPv6를 이 변화가 네트워크 기반 체계에 가져올 변화를 이제 이해하기 시작했을 뿐이다. 그리고 회복성을 애플리케이션에 구축하는 주제에 대해 많은 기고를 했지만, 아직 걸음마 단계에 있을 뿐이다. 우리가 아직 알지 못하는 많은 것들이 있을 것이다. 운영 그룹은 오래된 규율(제어시스템 이론, 제조, 의학)로부터 모범 사례를 채택하는데에 있어 리더가 되어 왔으며 이를 소프트웨어 개발에 통합시키고 있다. 

무운영에 대해서는 어떤가? 궁극적으로, 이는 나쁜 이름이지만, 이름은 그리 중요치 않다. "무운영"을 성공적으로 실천하는 조직은 운영을 몰아낸 것이 아니다. 단지 운영이 다른 곳으로 옮겨져서 다른 이름으로 불리는 것 뿐이다. 잘못 지어진 이름이 앞으로 도움이 될지 방해가 될지는 지켜볼 일이지만, 운영은 사라지지 않는다. 운영은 효율적이고 신뢰할 만한 소프트웨어를 고객에게 인도하기 위한 도전에 맞서 발전할 것이다. 구식의 시스템 운영자는 아마도 정말 사라질 것이다. 그렇다 하더라도, 이들은 개발팀과 밀접하게 협업하고 지속적인 배치를 제대로 하는, 대규모로 분산된 시스템을 구축하는 보다 복잡한 운영 전문가로 대체될 것이다. 그리고 물론, EBS가 망가졌을 때 한밤 중에 호출기에 응답할 사람은 바로 개발운영이다.



http://www.hanbit.co.kr/network/view.html?bi_id=1831 링크 

1편 언어에서 강력함 과 대중성  그리고 스칼라 

이글은 스칼라에 대한 전문적인글이 아니며 ,  스칼라의 모든부분을 말하는글은 아닙니다.
스칼라의 함수자(Functor) , 컬렉션 및 유틸리티 에 대해서 한정되있으며,  
먼저 스칼라말고 다른 여러가지 언어들에 대해서 말하고도 있습니다. 제목에 일반적이라고 붙힌이유는 , 스칼라의 다른 기능들 , 소위 Active Object &  Actor Pattern 기반의 동시성 구현라이브러리로 알려진 아카라든지,콤비네이터를 이용한 인터프리터 개발같은것들을 대중적이라고 보기엔 무리라고 판단하였기에 그런것들을 제외한 극소수의 내용들로 이루어져있기때문입니다. 그리고 아래 글에서 예시로 보여지는 (슈도)코드는 문법적으로 정확치 않은 예 이며, 글에 쓰여진 모든 지식은 저의 것이아니라 다른 사람으로부터 나온것입니다. (저도 틈틈히 공부중이며,  이번 사태를 계기로 수준높은 글들이 앞으로 많이 나올것을 기대합니다.)

강력함 과 대중적이라는것은 전혀 별개의 말입니다. 
강력하다고 해서 대중적으로 선택받지 못함은 역사를 통해 다들 잘 알고있을터입니다. 


본글에서는 단지 간단한 예제들을 나열함으로서  글을 읽는 여러분들이 알아서 생각하게끔 구성하였습니다.  
자 아래에는 어떤 목록에서 원하는 값을 찾는 과정에 대한  코드입니다.

예제1 in C++ ) 


string guy = null;

for(  Groups::iterator i = Groups.iterator();  i != Groups.end() ; ++i){
	
	player p = *i;
	
	if(p.id == 1){
		guy =  p.name;
	}
}

return guy;

위의 코드는 그룹에서 ID 1 번 선수의 이름을 찾아서 리턴해주는 로직입니다. 보는바와 같이 길게 늘어써져 있습니다.   코드를 읽어야지만 멀하자는건지 알수있습니다.

아래의 코드를 보면 지원되는 함수 util 을 이용하여 조금 짧게 이루어져있습니다. 

예제2 in JAVA )


player = Groups.findbyId(1);
if( player ){
	return player.name;
}
elsle{
	return null;
}

findbyId 함수를 이용하여  보다 간략하게 코딩하였음을 알수있습니다. 멀하자는건지 금방 알수있습니다.
 저 코드를 짜려면 findbyId 라는 함수를 알고있어야겠지요. 

에제3  in Haskell )   * 헤스켈 ( 순수 함수형 언어)  


fmap (getName)  (findId 1) 

( findId 1  <-- 이 부분을 functor 라고 합니다.)  findId 1 에서  값을 찾으면  getName 이 호출되어 , 원하는 선수 이름을 얻을수있으며 값을 못찾으면 Nothing 이 되면서, 아무것도 하질 않습니다. 
1줄로 줄어들었으며, Null 참조 및  잘못된 배열참조에 대한 불안감으로부터 해방되었습니다.
가독성 좋아졌는지는 잘모르겠고 , 코드가 강건해졌다고 볼수있습니다. 

스칼라를 사용하면, 추상정도를 높히는 코드를 C++,자바보다 더 자유롭게 표현할수 있게됩니다. 
추상정도를 높히면 강력합니다! 더 정확히 말하면 강력하게 변신하기 쉬워집니다. 

자 다들 저렇게 쓰도록 바꾸자!!! 고요 ?  저는 잘모르겠습니다. 독자분들마다 생각이 다를 것입니다.
대중들은 단순히 람다표현식 혹은 함수자 유틸의 사용으로 라인수 줄어든것만으로 굳이 공부하거나 바꿀 필요성을 느끼지 못할거 같습니다. 대중이 판단할때  현재 '진짜'  강력해야 선택받습니다.


제가 그렇게 생각하는  이유를 전에 쓴 글에서 가져오고 제 경험을 말씀드리면 
 c++ 은 10년전부터 다른언어의 특성들을  차용하며 소위 Modern C++ 이라는 이름으로 나름 발전을 이루어왔습니다. 주로 c++ 언어특성으로 발전을 하여온것은 아니고 STL 이름의 라이브러리등을  통하여 발전하였습니다. 언어 창조자의 철학이, 먼가가 필요하면 굳이 언어를 변경하는것보다는 라이브러리 차원으로 제공하는게 낫다라는 입장입니다. 아마도 자바 또한 그러한 보수적인 관점에서 신기능의 추가 (제너릭,병렬, 함수형등) 에 조심성을 가지고 발전한거겠지요. 

c++ 에 다양한 함수자유틸들, 템플릿특화,  일반화함수자, 타입추론 , 템플릿메타프로그래밍등 많은 개념들이 10년도 훨씬 전에 생겨났지만, 대중은 그닥 관심이 없습니다. (물론 몇몇 게임등 쓰이는곳도 있습니다)
알필요도 없지요. 안쓰고 개발해도 충분했으니깐요.  라이브러리 제작자한테나 필수일까...

실제 사례로, 제가 이전프로젝트에서의 c++ 코드라인이 300백만줄이 넘고, 10년동안  8백만라인이상의 코드를 접해왔는데  저런것을 쓴 사례가 거의 없습니다.
너와 니네팀이 무식해서 그렇다고요? 네 반은 맞습니다. 대부분은 비판하는 분과 다르게 저 수준입니다.
(반은 C++ 의 함수형스타일이 그닥 어렵게 공부해서 적용할 필요성이 없었습니다.)
 
정말 간단한  C++ 예를 이전 글에서 가져와서 보여 들어드리자면 

bool is_cool (const Thing& x) { ... }
find_if(begin, end, not1(ptr_fun(is_cool)));

위의 예제는  어떤 컬렉션을 순회하며 , 쿨하지 않은것을 찾아서 리턴해주는 표준함수,부정자 및 바인더라고불리는 것들입니다.  굉장히 단순해 보여서 많이 쓰일거 같지요?  저는 적극적으로 사용했습니다만, 대부분 저런식의 코딩을 하지 않습니다. 그냥 대부분 for 문으로 순회하면서 내용에 비지니스 로직 적습니다. not1 이 부담스러워요, ptr_fun 이 부담스럽습니다. 대중화에 실패했습니다.  
먼가 부족했습니다.
덜 강력했습니다. 먼가 조금 아쉽습니다..
저거에 openMP나 PPL  같은 병렬을 쉽게 해주는 수준까지 지원됬다면 , 더 나아가 GPGPU 까지 흠흠...

스칼라 혹은 함수형패러다임언어가  대중화에 성공 할수있을까요?  
단순히 케이스클래스, 람다, 함수형들을 써서 간단,간편,쉽게하자라는 이유라면  No 라고 생각합니다.
대신 높은 추상화를 가진 표현을 통해, 응용개발자들이 신경쓰지 않아도 라이브러리혹은 언어내부에서의 업그레이드로 손쉽게 강력함을 얻을수있다는 보장이 되는것에 공감대를 얻게된다면  Yes 일거라고 봅니다. (자바,C++ 도 저런측면에서 강력함을 어필하기 위해 노력하고있습니다.  치열한 싸움이 예상됩니다. )


자 이제 다음 예제로 넘어가 볼까요? 

예제1 in SQL )

select * from player  where id = 1  

우와 엄청 간단합니다. 강력합니다. 실수할 여지가 별로 없습니다. 추상의 끝판왕입니다. 
SQL 은 대중적으로 성공했습니다. ^^

저것을 풀어쓰면 

예제 in JAVA)


public Table select (Selector where){

	Table resultTable = new ConcreteTable(null, columnNames.clone()};
	Results currentRow = (Results) rows();
	Cursor[] envelop = new Cursor[] { currentRow };

	while(currentRow.advance(){
		if(where.approve(envelope))
			resultTable.insert( currentRow.cloneRow());
	}
	
	return new UnmodifiableTable(resultTable);
}

더 풀어쓰면

예제 in C )

struct table {
	....
}

table t [][];
              ....


for(int i = 0 ; i <  rows ; i++){
	for( int j = 0 ; j < cols; j++){
		...
	}
}

한 몇백라인되겠지요. 
SQL 은 성공했습니다. !!!


자 이제 JQuery  를 보겠습니다.

$("div")     모든 div 태그를 가져오는것을 한방에 합니다.
document.getElementByTag("div")      조금 길어졌네요.

$("div.reply")   모든  div 중에서 reply 클래스들만 가져옵니다.
JQuery 안쓰면 ?  헬입니다.

$("div.reply:hidden").show()    우와 함수까지 한방에 해결입니다.

다른 값을 감싸고 한방에 해결해줍니다. (모나드라는 특성의 일부분을  가지고 있습니다)
JQuery 성공했습니다.!!!


자 이제 마지막으로 스칼라 (위에 썼듯이 스칼라의 일부분) 를 보겠습니다. 다양한 스칼라 예제를 살펴보고
과연 이것도 성공할수있을지 실제 코딩에서 사용할지...여러분이 알아서 생각하세요.
여러분이 선택하는게 정답입니다.  대중이 선택한것이 대부분 정답이니깐요. ( 물론 이부분은 상황에 따라서 시각이 다를수있겠습니다) 


+ Recent posts