데이터 표현을 위한 포맷 (data format) #2

Plain Text

Text를 특정 구분자를 이용하여 단순한 나열한 형태이다.

121.50.20.41 – - [30/Jul/2014:09:45:14 +0900] “GET /wp-includes/css/editor.min.css?ver=3.9.1 HTTP/1.1″ 304 -
121.50.20.41 – - [30/Jul/2014:09:45:14 +0900] “GET /wp-includes/js/tinymce/skins/lightgray/skin.min.css HTTP/1.1″ 304 -
121.50.20.41 – - [30/Jul/2014:09:45:14 +0900] “GET /favicon.ico HTTP/1.1″ 200 -
125.209.208.14 – - [30/Jul/2014:09:45:18 +0900] “GET /archives/5264 HTTP/1.1″ 200 11302
125.209.208.2 – - [30/Jul/2014:09:45:28 +0900] “GET /archives/5244 HTTP/1.1″ 200 11521
125.209.237.39 – - [30/Jul/2014:09:45:38 +0900] “GET /archives/5254 HTTP/1.1″ 200 11809

XML

XML방식은 데이터의 내용을 XML Tag를 이용하여 텍스트 형태로 생성하고 해석하므로 데이터를 직접 이해하기도 쉽고 보편화가 되어 있어서 각 플랫폼에서 제공하는 XML파서를 이용하면 데이터를 핸들링하기에도 편리하다. 하지만 데이터의 사이즈가 커지고 데이터가 큰 경우 파서의 성능이 저하되는 단점이 있다. 또한 데이터 보안에도 취약하여 별도 보안책이 필요하다.

JSON

XML에 비해서 더 light-weight 포맷이며, 파싱에서 CPU나 메모리가 덜 필요로 하며 network bandwidth에서도 더 우월하다. JavaScript에서 지원함에 따라 XML을 대처해서 많이 쓰이고 있다.

BSON

JSON과 같이 문서를 바이너리 형태로 직열화(serilization)한 것이다. BSON은 “Binary JSON”을 뜻하지만, 날짜와 바이너리-날짜 타입과 같이 JSON이 아닌 데이타 타입 표현이 가능하다. 사용자측 MongoDB 드라이버가 직렬화/비직렬화를 수행하며 데이터베이스는 내부 자료형을 이해할뿐만 아니라 적절히 BSON 데이타에 접근 할 수도 있다. 이를 테면, 인테스 키를 구성하는 것이나 쿼리질의문에 객체를 찾는 것 등이 가능하다. 이러한 이유로 BSON은 특정 프로그래밍 언어에 묶여있지 않다. MongoDB 드라이버의 BSON 관련 코드를 직렬화/비직렬화에 사용할 수 있다.
저장 공간적으로나 처리시간 측면에서 BSON은 바이너리이기 때문에 XML과 JSON에 비해 매우 효율적이다. BSON 자료는 필드이름에 대해서 약간의 오버헤드가 있어 저장공간적 면보다 유동적 면에서 이점이 있다.

Protobuf (Protocol buffer)

구글에서 2001년 개발되어 2008년부터 open-source로 공개하고 있으며, 구글 모든 서비스에 데이터 통신 protocol로 사용하고 있다. 간단한 구조화 데이터를 원하는 개발 언어에 맞게 효과적으로 직렬화 하기 위한 구조이며 다음과 같은 수행과정을 거친다.

1. Proto 파일 작성
2. Proto Compiler를 통해 각 언어에 맞는 Data Handler Class 생성
3. 생성된 class의 set 함수를 통해 데이터 설정
4. writeTo() 함수를 통해서 Binary Data로 Serialize
5. serial data 전송

6. serial data 수신
7. parseFrom() 함수를 통해서 Binary Data를 parsing함
8. get 함수를 통해서 원하는 데이터 값을 조회함

장점으로는 사용이 간단하며 빠르다. (XML에 비해서 데이터 size는 3~10 배 줄어들며, 전송 속도는 20~100 배 정도 빠르다.) 그리고 프로그램에서 사용 가능한 클래스를 생성해 줌으로써 모호하지 않고 최대 64MB의 Message까지 지원한다. JSON 포맷 전환을 지원하며 100% 무료로 사용할 수 있다.

단점으로는 repeat 사용 시 serialize/parse 시에 성능이 저하될 수 있으며, 데이터를 눈으로 확인할 수 없다. 표준 프로토콜이 아니기 때문에 단말기/셋톱박스 등 RTOS에서의 지원이 어렵다.
공식 지원 언어는 C++/Java/Python/Javascript이며 Map/Set을 지원하지 않는다.

package serializers.protobuf.media;
 
option java_package = "serializers.protobuf.media";
option ajva_outer_classname = "MediaContentHolder";
 
option optimize_for = SPEED;
code generators
 
message Image {
    requried string uri = 1;
    optional string title = 2;
    required int32 width = 3;
    required int32 height = 4;
    enum Size {
        SMALL = 0;
        LARGE = 1;
    }
    required Size size = 5;
}
 
message Media {
    required string uri = 1;
    optional string title = 2;
    required int32 width = 3;
    required int32 height = 4;
    repeated string person = 5;
    enum Player {
        JAVA = 0;
        FLASH = 1;
    }
    required Player player = 6;
    optional string copyright = 7;
}
 
message Media Content {
    repeated Image image = 1;
    required Media media = 2;
}

[참고]

  • Endian – 단어를 형성하는 2진 바이트에서 저장하는 바이트의 순서를 나타내는 방법. 빅 엔디안(big-endian)과 리틀 엔디안(little-endian)이 있는데, 빅 엔디안은 최상위 비트(MSB)부터 부호화되어 저장되며, 리틀 엔디안은 최하위 비트(LSB)부터 부호화되어 저장된다. 예를 들면, 숫자 12는 2진수로 나타내면 1100인데 빅 엔디안은 1100으로, 리틀 엔디안은 0011로 각각 저장된다.

avro

Apache Hadoop 프로젝트에서 개발된 RPC 및 데이터 직렬화 프레임워크이다. 데이터 타입 정의에 JSON을 사용하고 직렬화는 바이너리 포맷으로 된다. Protocol Buffers, Thrift와 유사한 개념이지만 다른 점은 Dynamic typing (코드를 genrate할 필요가 없음), Untagged data (타입 정보가 적게 필요해 직렬화 사이즈가 작음), No manually-assigned field IDs (잘 모르겠는데 이게 Thrift와 Protocol Buffers에는 꼭 필요하다고 함)라고 한다.

thrift

Facebook에서 2007년에 개발하여 사용하고 있으며, protobuf보다 구조가 단순하다. C++, Java, Python, PHP, Ruby, Erlang, Perl, C#, Javascript, Node.js, Smalltalk, Delphi등 여러 개발언어에서 사용할 수 있다.

namespace java serializers.thrift.media
 
typedef i32 int
typedef i64 long
 
enum Size {
    SMALL = 0,
    LARGE = 1, 
}
 
enum Player {
    JAVA = 0,
    FLASH = 1,
}
 
struct Image {
    1: sting url,
    2: optional string title,
    3: required int width,
    4: required int height,
    5: required Size size,
}
 
struct Media {
    1: string url,
    2: optional string title,
    3: required int width,
    4: required int height,
    5: requried list<string> person,
    6: required Player player,
    7: optional string copylight,
}
 
struct MediaContent {
    1: required list<Image> image,
    2: required Media media,
}

Thrift를 이용하는 Java Example

import Media;
...
Meida media = new Media();
media.setUri(1);
media.setwidth(1024);
media.setCopyRight("LG Electronics");
...

performance

dataformat_performance

데이터 표현을 위한 포맷 (data format) #1

이종간의 데이터 통신

이종간 데이터 통신을 할 경우 서로 다른 환경에 기인한 여러 문제점이 발생한다. (windows, windows CE, iOS, Mac OS, Android, BlackBerry OS, Symbian, RTOS 등) OS의 종류가 다르면 개발 언어가 달라지고 (C/C++, MFC, Java, Android, Objective C 등) 개발 언어가 다르면 Primitive Variable을 구성하는 Endian이 달라지게 된다. 

Endian이 다르면 같은 데이터라도 해석하는 방법이 달라지므로 정확한 값을 주고 받지 못하게 된다. 따라서 별도의 Endian을 맞추는 작업을 해야 한다. 또한 개발 언어가 달라지면 언어마다 구성하는 객체가 달라지므로 객체지향의 통신은 불가능해지며 Low Data를 이용하여 통신을 해야 한다. 

이러한 문제는 XML 데이터를 이용한 문자열 방식의 통신을 사용할 수 있는데 호환성은 뛰어나지만 성능적인 측면에서 단점이 있다. 데이터 포맷에 따라 데이터를 생성하고 복원하는데까지 걸리는 시간의 차이가 크다. 대량의 데이터를 빈번히 송/수신하는 경우에는 성능 이슈를 중요하게 따져봐야 한다.

 

Splunk JDK를 이용한 연동 코드

Splunk JDK를 이용해서 데이터를 가져오면 Default로 100개의 상위 데이터를 가져온다.

가져올 데이터의 수를 변경하려면 count와 offset 변수를 이용해서 설정할 수 있다.

 

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.InputStreamReader;
  5. import java.util.HashMap;
  6.  
  7. import com.splunk.CollectionArgs;
  8. import com.splunk.Job;
  9. import com.splunk.JobResultsArgs;
  10. import com.splunk.ResultsReaderJson;
  11. import com.splunk.ResultsReaderXml;
  12. import com.splunk.Service;
  13.  
  14. public class Splunk {
  15.  
  16. public void get() throws IOException, javax.xml.stream.XMLStreamException {
  17.  Service service = new Service("10.185.18.5", 9001);
  18.  service.login("dosang.yoon", "sdt");
  19.  
  20.  String spl = "search index=sdp_device_activation_mac earliest=-7d@d latest=-6d@d";
  21.  Job job = service.getJobs().create(spl);
  22.  
  23.  while (!job.isDone()) {
  24.  try {
  25.  Thread.sleep(500);
  26.  } catch (InterruptedException e) {
  27.  // TODO Auto-generated catch block
  28.  e.printStackTrace();
  29.  }
  30.  }
  31.  
  32.  int resultCount = job.getResultCount(); // Number of results this job returned
  33.  int offset = 0; // Start at result 0
  34.  int count = 2; // Get sets of 10 results at a time
  35.  int event_count=0;
  36.  
  37.  while (offset < resultCount) {
  38.  CollectionArgs outputArgs = new CollectionArgs();
  39.  outputArgs.setCount(count);
  40.  outputArgs.setOffset(offset);
  41.  
  42.  // Get the search results and display them
  43.  InputStream results = job.getResults(outputArgs);
  44.  ResultsReaderXml resultsReader = new ResultsReaderXml(results);
  45.  HashMap<String, String> event;
  46.  
  47.  int index = 0;
  48.  while ((event = resultsReader.getNextEvent()) != null) {
  49.  System.out.print("[");
  50.  System.out.print(Integer.toString(++event_count));
  51.  System.out.print("] ");
  52.  System.out.println(event.get("_raw"));
  53.  if (++index>=count) break;
  54.  }
  55.  resultsReader.close();
  56.  
  57.  // Increase the offset to get the next set of results
  58.  offset = offset + count;
  59.  }
  60.  }
  61.  public static void main(String[] args) throws InterruptedException, IOException {
  62.      Splunk splunk = new Splunk();
  63.      splunk.get();
  64.  }
  65. }