Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

node.js v0.8.0이 발표되었습니다.

v0.6.0이 작년 11월에 발표되었으니 7개월만에 새로운 안정버전인 v0.8.0이 발표되었습니다. v0.8.0에서 달라지는 부분에 대해서는 node.js v0.8의 API 변경사항에서 정리했었지만 이번 v0.8.0 발표내용에는 좀 더 설명이 잘 되어 있고 여러가지 관점에 대해 나와있는 부분이 있어서 공지글을 기반으로 정리를 합니다. (간만에 안정버전 릴리즈라 설레이군요... v0.7.x에서 많은 테스트를 거쳤다고 하더라도 정실 릴리즈가 되면 많은 이슈가 나올 가능성이 있으므로 실 서비스에 적용은 많은 테스트를 해본 후에 하기를 권장합니다.)



성능 개선
0.8.0에서는 0.6에 비해 성능이 크게 개선되었습니다. 다음은 Issacs가 자신의 맥북에서 벤치마크를 수행한 결과입니다. Node로 많은 트래픽을 사용하고 있다면 성능 개선을 느낄 수 있을 것입니다. 이번 성능개선은 V8이 개선된 덕이 아주 크로 V8 개발팀이 Node.js 프로젝트의 요청사항을 잘 받아주고 있다고 합니다.

# io.js

# 0.6.19, writes
Wrote 1024 byte buffers: 19.428793471925395 mB/s
Wrote 4096 byte buffers: 59.737156511350065 mB/s
Wrote 16384 byte buffers: 83.97010664203543 mB/s
Wrote 65536 byte buffers: 97.4184120798831 mB/s

# 0.8.0, writes
Wrote 1024 byte buffers: 61.236987140232706 mB/s
Wrote 4096 byte buffers: 109.05125408942203 mB/s
Wrote 16384 byte buffers: 182.18254691200585 mB/s
Wrote 65536 byte buffers: 181.91740949608877 mB/s

# v0.6.19, reads
Read 1024 byte buffers: 29.96883241428914 mB/s
Read 4096 byte buffers: 62.34413965087282 mB/s
Read 16384 byte buffers: 165.7550140891762 mB/s
Read 65536 byte buffers: 266.73779674579885 mB/s

# v0.8.0, reads
Read 1024 byte buffers: 57.63688760806916 mB/s
Read 4096 byte buffers: 136.7801942278758 mB/s
Read 16384 byte buffers: 244.8579823702253 mB/s
Read 65536 byte buffers: 302.2974607013301 mB/s

다음은 파일 읽기에 대한 밴치마크입니다.

# v0.6.19
read the file 110948 times (higher is better)
90141.32 ns per read (lower is better)
11093.69 reads per sec (higher is better)

# v0.8.0
read the file 158193 times (higher is better)
63217.16 ns per read (lower is better)
15818.48 reads per sec (higher is better)


다음은 간단한 "hello, world" HTTP 서버의 벤치마크입니다. 응답 메시지가 클수록 큰 성능향상이 있습니다.

$ TYPE=bytes LENGTH=123 bash benchmark/http.sh  2>&1 | grep Req
# 0.6.19
Requests per second:    3317.24 [#/sec] (mean)
# 0.8.0
Requests per second:    3795.34 [#/sec] (mean)


$ TYPE=bytes LENGTH=1024 bash benchmark/http.sh  2>&1 | grep Req
# v0.6.19
Requests per second:    3258.42 [#/sec] (mean)
# 0.8.0
Requests per second:    3585.62 [#/sec] (mean)


$ TYPE=bytes LENGTH=123456 bash benchmark/http.sh  2>&1 | grep Req
# v0.6.19
Requests per second:    218.51 [#/sec] (mean)
# 0.8.0
Requests per second:    749.17 [#/sec] (mean)


유니코드 응답에 대한 벤치마크도 크게 차이가 납니다.

$ TYPE=unicode LENGTH=1024 bash benchmark/http.sh  2>&1 | grep Req
# v0.6.19
Requests per second:    3228.23 [#/sec] (mean)
# v0.8.0
Requests per second:    3317.60 [#/sec] (mean)

$ TYPE=unicode LENGTH=12345 bash benchmark/http.sh  2>&1 | grep Req
# v0.6.19
Requests per second:    1703.96 [#/sec] (mean)
# v0.8.0
Requests per second:    2431.61 [#/sec] (mean)

$ TYPE=unicode LENGTH=55555 bash benchmark/http.sh  2>&1 | grep Req
#v0.6.19
Requests per second:    161.65 [#/sec] (mean)
#v0.8.0
Requests per second:    980.38 [#/sec] (mean)

$ TYPE=unicode LENGTH=99999 bash benchmark/http.sh  2>&1 | grep Req
# v0.6.19
^C  # lost patience after a few hours
# v0.8.0
Requests per second:    252.69 [#/sec] (mean)




빌드 시스템
전에도 말했듯이 v0.8.0은 파이썬 기반의 WAF 빌드시스템 대신에 GYP를 사용합니다. 이는 크롬 프로젝트가 빌드시스템을 최근에 SCons에서 GYP 메타빌드 시스템으로 바꾼 것과 같습니다. GYP는 타겟 시스템에 따라 Makefile이나 Visual Studio 파일, XCode 파일을 생성합니다. GYP를 선택한 덕에 다음이 가능해 졌습니다.

  • 모든 플랫폼에서 최적의 빌드 시스템과 통합합니다.
  • 쉽게 V8의 빌드 과정을 자신만의 빌드과정과 통합합니다.
  • 관리하기 쉬운 선언적 컴파일을 정의합니다.
GYP에서는 Node 0.6에서도 Windows에서는 이미 사용하고 있었지만 이제는 모든 플랫폼에서 사요하게 되었습니다. 아직은 외부 애드온 모듈들이 GYP로 마이그레이션 되는 중에 있고 node-gyp는 npm처럼 node에 포함되었습니다. 추후의 버전에서는 node-waf를 공식적으로 폐기할 것이므로 wscript를 사용하고 있다면 최대한 빨리 gyp로 마이그레이션 해야 합니다.



안정화
0.6에서 libev와 libeio를 libuv로 교체한 것이 내부적으로 불안해지는 결과가 되었습니다. libuv는 크로스 플랫폼 비동기 I/O 라이브러리를 위한 명백한 선택이었고 이제는 WIndows와 Unix에서 모두 인상적인 성능을 보여주고 있습니다. 0.6에서의 변화는 사용자에게 불안정한 결과를 주었지만 이제 이 문제는 해결되었습니다. 아주 소수의 예외를 제외하고는 대부분 v0.6 프로그램이 0.8에서 돌아갈 것이고 혹시 돌아가지 않는다고 하더라도 고쳐야 하는 부분은 쉽고 명확할 것입니다. libuv는 오랫동안 개선되었고 그로 인해 Node 0.8은 더 간단해지고 효율적이 되었습니다.



파일 디스크립터의 귀환
Node v0.4에서는 소켓이나 포트에 이미 바인딩 된 파일 디스크립터에서 서버가 요청을 받을 수 있게 하는 listenFD 메서드가 있었습니다. 하지만 이는 Unix에 특화된 기능이었고 0.6의 크로스 플랫폼에서는 libuv로 작업하기가 어려웠기 때문에 v0.6에서 이 기능은 제거되었습니다. listenFD는 여러 Node 프로세스의 서버들이 의존 핸들을 공유하는 것이 일반적이 사용이므로 Cluster 모듈에 추가되었습니다. 얘기하지 않은 유즈케이스도 많이 있기 때문에 사용자들은 node 0.6을 사용하지 않았습니다.  이 기능은 v0.8에서 server.listen({ fd: number })로 교체되었습니다.

v0.4에는 있었지만 v0.6에서는 사라진 또 하나의 기능은 customFds 배열을 사용해서 자식 프로세스의 stdio로 임의의 파일 디스크립터를 전달하는 기능입니다. v0.6에서 customFds를 부모의 stdio 핸들을 상속하기 위해 사용할 수 있었지만 임의의 핸들이나 파일 디스크립터를 자식 프로세스의 stdio로 전달할 수는 없었고 표준 in, out, err 외에는 전달할 수 있는 방법이 없었습니다. v0.8에서는 child_process.spawn 옵션에 stdio 배열을 추가했으므로 다수의 파일 디스크립터, 핸들 등을 전달할 수 있습니다. 전달하면 자식 프로세스는 이미 열려 있는 파일 디스크립터를 사용할 것입니다.



강력해진 클러스터
클러스터 모듈은 0.8에서 많이 개선되었는데 실제로는 완전히 재작성되었습니다. 전체는 아니지만 대부분은 하위호환성을 가지고 있습니다. 그래서 마이너한 API 변경외에는 v0.6에서 클러스터를 사용한 코드가 v0.8에서도 동작하는데 훨씬 빠르고 잘 동작 할 것입니다. v0.8의 클러스터에 추가된 새로운 기능들도 꼭 사용해 보는 것을 추천합니다.



도메인
도메인에 대한 기본 생각은 오류가 발생했을 때 어떤 컨텍스트를 갖기 위해 여러 가지 다른 I/O 동작을 합치는 것이다. 이 기능은 작년에 Ryan이 NodeConf Summer Camp에서 노드 사용자들과 논의했기 때문에 그 뒤로 많은 리비전을 거쳐서 만들어졌다. 이 문제는 이해하기는 쉽지만 문제를 해결하려다 보니 심각한 성능저하가 발생하거나 어려운 엣지 케이스는 다루지 못하는 상황이 발생했습니다. 그래서 v0.8에서는 기본 아이디어에서 불필요한 것은 모두 제거한 버전을 적용했습니다. 그래서 도메인을 사용하면 최소한의 성능 영향만을 주고 사용하지 않을 경우에는 전혀 영향을 주지 않습니다. API 문서에 있는 많은 예제를 확인해 보기 바랍니다. 이 도메인 기능은 아직 실험적인 상태이므로 사용해 보고 피드백을 주기를 바랍니다.



REPL, READLINE, TTY
REPL, READLINE, TTY 세가지 모듈은 주요한 개선이 있었습니다. 세 모듈 사이의 인터페이스는 정리되었고 리팩토링했으며 많은 불편한 점을 제거해서 디버깅하기 쉽게 만들어졌습니다. 이는 중요하지 않아 보일 수 있지만 좋은 REPL은 전체적인 경험의 질을 크게 향상시킵니다. Issacs는 다음과 같은 기능을 좋아한답니다.

  • fs, net, path를 입력하면 자동으로 모듈을 로딩합니다.
  • npm install ...를 입력하면 유용한 메시지가 출력됩니다.




v0.9
v0.8의 API와 안정성은 생명주기 내내 계속 유지될 것입니다. 그리고 심각한 버그 수정이나 보안 이슈가 있을 경우 v0.6.x도 2012년 말까지는 계속 릴리즈 될 것이지만 Node 개발팀의 주 관심사는 아닙니다. v0.9 개발버전은 몇 주내에 시작할 것인데 다음과 같은 내용에 초점을 맞추고 있습니다.

  • HTTP 구현체 : 실제적으로 많은 사용이 되고 있지만 HTTP  모듈은 정리되고 리팩토링 될 필요가 있습니다. 특히 인터페이스를 더 일관성 있게 만들고 성능을 개선하고 엣지 케이스에서 제대로 동작하도록 해야 합니다.
  • Streams API : 스트림 API의 개념은 노드에서 핵심 개념입니다. 하지만 HTTP 모듈처럼 기능이 유기적으로 커왔기 때문에 이제 정리를 할 필요가 있습니다. 특히 오류 핸들링과 관련해서 제대로 다루기가 쉽지 않습니다.
  • libuv 스트림 : libuv의 핸들 인터페이스는 기반코드와 플랫폼에 걸쳐서 일관성을 높이기 위해서 리팩토링 할 예정입니다.
그 외에 아직 정리되진 않았지만 몇가지 고려하고 있는 부분들이 있습니다.

  • Buffer는 자주 사용하는 TypedArrays로 바꾸어야 합니다. Buffer도 계속 동작하겠지만 TypedArrays가 자바스크립트 네이티브 이므로 이렇게 변경하는 것이 맞습니다.
  • SSL 성능은 더 좋아져야 합니다. Node의 OpenSSL 인터페이스는 다소 나이브하고 최적화가 필요한 부분이 많이 있습니다.
  • VM 모듈은 많은 변경을 해야합니다. 특히 웹브라우저 자바스크립트 컨텍스트를 에뮤레이팅하는 필수 기능이 빠져있습니다.
  • Crypto 모듈은 아직도 아주 구식의 API를 사용합니다. v0.8에서는 많은 경우에 Buffer를 받아들일 수 있지만 스트리밍 인터페이스는 제공하지 않습니다.
여기서 설명했듯이 Node의 기능 범위는 고정되어 있습니다. 그래서 내부적으로 정리작업을 계속 할 것이지만 아직 새로운 기능은 계획하고 있지 않습니다. 우리는 Node의 경계을 정했고 현재는 안정성과 코어의 성능을 개선하는데 집중해야 할 때라고 판단하고 있습니다.
2012/06/26 04:00 2012/06/26 04:00