[펌] 구글 프로토콜 버퍼 (Google Protocl Buffer) Enjoy/JAVA2013. 1. 3. 16:05
구글 프로토콜 버퍼 (Google Protocl Buffer)
PPT 작성 자료
* 배경
RPC와 쉽게 연동, Not RPC
하나의 개념으로 다양한 언어에서 쓸 수 있게 구글에서 다양한 언어별로 개발 하다 보니. 통신을 위한 단일 표준이 필요
proto 파일 -> proto 컴파일러 -> 여러 언어로 변환 (java, python, c++)
Not socket
구글 에서 2008년 7월 발표
이슈 제시
- XML 문제
- Parsing, serialization (debugging)
- Portable : IDL처럼 사용
- Heavy Optimization
- Language 지원
짧은 데이터의 송수신 용도/긴 데이터 송수신이 목표가 아님
* XML 보다 좋은 점
Simple
3~10배 작음
20~100배 속도 빠름
모호하지 않음
바로 프로그램에 사용하기 쉬움
* 레퍼런스
Protocol Buffers: A new open source release
http://www.youtube.com/watch?v=K-e8DDRwVUg
Home Page
http://code.google.com/p/protobuf/
개발자 가이드
http://code.google.com/intl/ko-KR/apis/protocolbuffers/docs/overview.html
Protocol Buffer Language Guide
http://code.google.com/intl/ko-KR/apis/protocolbuffers/docs/proto.html
API
http://code.google.com/intl/ko-KR/apis/protocolbuffers/docs/reference/java/index.html
PB 포맷
http://wiki.openstreetmap.org/wiki/PBF_Format
비슷한 솔루션과 비교
https://github.com/eishay/jvm-serializers/wiki
* 활용
구글
- 원래 index server request/response protocol로 사용했었음
- 48,162 different message types
- 12,183 .proto files
다양한 회사
국내/외 게임 회사의 통신
* 장점
쓰기 편함
Stub 코드 자동 생성
- 통신에서 가져야 할 보편적 특성을 다 추가
- Serializing, Parsing 지원
코드 일치
- 클라이언트/서버 코드 동일
IDL 형태로 정의가 단순
- Portable
- 클래스 또는 struct 디자인
언어 지원
java, c++, python
3rd party lib (많은 언어 지원.http://code.google.com/p/protobuf/wiki/ThirdPartyAddOns#RPC_Implementations)
배우기 쉬움
이클립스 플러그인 존재
Lite 버전 개발 가능
Good Document
BSD license
언어마다 특화되고 쓰기 편한 특징을 제공
* 단점
Output이 binary만 존재
- PB의 Reflection을 이용해서 json으로 전달 가능
Map, set 지원 없음
* 개발환경 구성
Download proto compiler in google code
- http://code.google.com/p/protobuf/downloads/list
(Option) eclipse plugin
- http://protoclipse.googlecode.com/svn/trunk/site/
* proto 파일
// text.txt
package test;
message Request {
required string command = 1;
optional string param = 2;
}
/cygdrive/c/protocolbuffer
$ ./protoc.exe test.txt --java_out=.
Test폴더 생기고 TestTxt.java 생성됨
(17KB, Protocol buffer 내부 클래스 사용)
// c++ 로 하면
test.txt.pb.cc (12K)
test.txt.pb.h (8K)
// python으로 하면,
test/txt_pb2.py (2K)
* 생성된 자바 코드
Descriptor 지원
- internal_static_test_Request_descriptor
Reflection 지원
- Message / Message.Builder interfaces.
- Json 처럼 프로토콜로 변경 가능 (ajax 가능)
메시지 수정시 하위 호환 보장, 새로운 메시지로 변경되면 기존 코드에 대한 필드만 처리
파일이름을 디폴트로 해서 소스를 생성하지만, 내가 원하는 클래지 이름과 클래스 이름의 개별 지정이 가능
- option java_package = "com.example.foo.bar";
- option java_outer_classname = “ProtocolData";
PB의 Enum은 java의 enum으로 변경
* 크기 제한
디폴트로 크기 제한 : 64 MB
속도를 최적화 또는 악의를 가진 사용자로부터 보호하기 위해서 크기를 제한할 수 있음
- CodedInputStream/CodedOutputStream (ZeroCopyInputStream/ZeroCopyOutputStream )
SetTotalBytesLimit 메소드
* proto 파일 생성시 유의할 점
package 선언
클래스 파일 이름
운영을 위한 파일명 .proto
message 등록
- Protocol buffer language guide
Protoc.exe(컴파일러)에 의해 만들어진 java, python, c++ 코드는 고치지 말아야 한다.(immutable)
protoc에서 컴파일 되면, 자동으로 accessor가 붙는다.
- get/set/has…
* message 설명
package
Type
- Bool, int32, uint32, float, double, string, bytes, ….
- Enum
Nested type
Default value
importing
Modifier
- required : 반드시 사용해야 할 필드. 미초기화된 상태 미초기화된 메시지를 빌드하면 RuntimeException, 초기화되지 않은 메시지를 파싱하면서 에러나면 IOException발생
- optional : option의 개념
hasXXX() 로 확인
- repeated : 0을 포함하는 개수를 계속 넣을 수 있음
자바에서는 List객체로 구현됨
* 필드에 번호를 반드시 주는 이유
번호를 주지 않으면 protoc 에러 발생
Write/Read 할 때, serialization 순서를 주기 위함
필드 정보가 set되었는지 쉽게 알기 위함(내부적으로 bit 연산함)
* Versioning 정보가 없는 이유
새로운 필드를 언제든지 추가 될 수 있음
모든 정보를 볼 필요 없이 필요한 정보만 파싱할 수 있도록 함
But, java는 기본으로 존재하지 않지만, c++ 은 존재한다. 링킹 이슈. (GOOGLE_PROTOBUF_VERIFY_VERSION 매크로)
Incompatible한 버전 때문에 문제가 없도록 해야 함
* 데모 1
package test;
message Request {
required string command = 1;
optional string param = 2;
}
generated 자바 코드 로 컴파일
package com.example.test;
import java.io.FileOutputStream;
import test.Test.Request;
public class Writer {
public static void main(String[] args) throws Exception {
Request request = Request.newBuilder()
.setCommand("commit")
.setParam("every files")
.build();
FileOutputStream output = new FileOutputStream("r.os");
request.writeTo(output);
output.close();
}
}
<Reader.java>
package com.example.test;
import java.io.FileInputStream;
import test.Test.Request;
public class Reader {
public static void main(String[] args) throws Exception {
Request request = Request.parseFrom(new FileInputStream("r.os"));
System.out.println("command : " + request.getCommand());
if (request.hasParam()) {
System.out.println("params : " + request.getParam());
}
}
}
command : commit
params : every files
* 데모 2
<writer.cpp>
#include <iostream>
#include <fstream>
#include <string>
#include "test.pb.h"
using namespace std;
int main(int argc, char* argv[]) {
Request request;
request.set_command("init");
request.set_param("0");
fstream out("streams", ios::out | ios::binary | ios::trunc);
request.SerializeToOstream(&out);
out.close();
return 1;
}
<reader.cpp>
#include <iostream>
#include <fstream>
#include <string>
#include "test.pb.h"
using namespace std;
int main(int argc, char* argv[]) {
Request request;
fstream in(“streams", ios::in | ios::binary);
if (!request.ParseFromIstream(&in)) {
cerr << "Failed to parse streams." << endl;
exit(1);
}
cout << “command: " << request.command() << endl;
if (request.has_param()) {
cout << “param: " << request.param() << endl;
}
}
* 데모 3
package tutorial;
option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
}
message AddressBook {
repeated Person person = 1;
}
Posted by My Story '김용환'