MsgPack 을 이용해서 직접 write 구현하기
MsgPack 를 이용해서 직접 byte 배열에다가 쓸 경우 다음 규칙을 지키자.
1. 객체 타입일 경우 멤버 변수의 갯수가 몇개인지 write 한다. - writeArrayBegin(N)
2. list(또는 array) 타입일 경우 list 의 size 를 write 한다. - writeArrayBeging(N)
2.1 list(또는 array) size 만큼 loop 를 돌면서 각 element 를 write 한다.
2.2 list(또는 array) 의 element 가 primitive 타입이 아닌 객체형일 경우에는 1 번에 해당하므로 객체 멤버 변수 갯수를 write 한다.
2.3 list(또는 array) element 의 size 가 0 일 경우에는 writeArrayBegin(0) 을 write 한다.
3. 숫자 타입일 경우, write(:integer) 을 사용하는데 반드시 인자값이 int(Integer) 여야 한다.
4. 그외 나머지는 write(:string) 를 쓴다.
5. null 값일 경우에는 writeNil() 을 쓴다.
ex)
- BoxData 데이터
idx |
item_id |
box_type |
rew_list |
rew_value |
rew_rate |
select_cnt |
select_list |
select_value |
1 |
9000 |
0 |
11|21|14|26|30010 |
1|2|1|3|1 |
5|4|3|2|1 |
0 |
0 |
0 |
2 |
9001 |
0 |
10|16|10006|19|25 |
3|3|1|1|1 |
5|4|3|2|1 |
0 |
0 |
0 |
3 |
9002 |
1 |
0 |
0 |
0 |
3 |
2|5|7|9|10001|10002|10005 |
3|3|3|3|1|1|1 |
4 |
9003 |
1 |
0 |
0 |
0 |
2 |
2|5|7|9|10001|10002|10005 |
3|3|3|3|1|1|1 |
- BoxData 클래스
@Data
@Message
public class BoxData {
private int idx;
private int itemId;
private int boxType;
private List<Integer> rewList;
private List<Integer> rewValue;
private List<Integer> rewRate;
private int selectCnt;
private List<Integer> selectList;
private List<Integer> selectValue;
}
- BoxDataWrapper 클래스
@Data
@Message
public class BoxDataWrapper {
private List<BoxData> elementList;
}
BoxDataWrapper 객체를 MsgPack 으로 serialize 하기 위해서는 다음과 같은 과정을 거쳐야한다.
1. BoxDataWrapper 의 멤버 갯수가 1 개(LIst<BoxData> elements)라서 array 가 1 이라고 write. packer.writeArrayBegin(1); 2. 멤버변수인 elements 가 List 라서 list size 를 write. packer.writeArrayBegin(elements.size()); 3. loop 를 돌면서 BoxData 를 write 한다. for (int i = 0; i < elements_size; i++) { ... } 3.1 loop 안에서 BoxData 이 객체이고 BoxData 의 멤버 갯수가 9 라서 array 를 9 라고 write. for (int i = 0; i < elements_size; i++) { packer.writeArrayBegin(9); ... } 3.2 이제 각각의 BoxData 객체들을 write 한다. packer.writeArrayBegin(9);
BoxData boxData = elements.get(0); write(boxData.getIdx); // idx write(boxData.getItemId); // itemId writeArrayBegin(5); // rewList list 시작 for (Integer data : boxData.getRewList() { // 11|21|14|26|30010 write write(data); } writeArrayEnd(); // 반드시 writeArrayEnd() 를 호출해야한다. packer 내부에 stack count 를 더하기/빼기를 하고 있음. for (Integer data : boxData.getRewValue() { // 1|2|1|3|1 write write(data); } writeArrayEnd(); ... writeArrayBegin(0); // selectList 는 리스트 요소가 아무것도 없는 경우라서(selectList = new ArrayList<>()) size 0 인 list 라고 표시. writeArrayEnd();
4. BoxData 객체 write 끝나면 end 표시를 한다. packer 내부의 count 를 stack 으로 관리하고 있다. writeArrayEnd() 를 안하면 loop 카운트가 128 이 넘는 순간 boundary index 오류를 보게된다. packer.writeArrayEnd(); |
- C# 에서 위의 msgpack 으로 serialize 한 파일(BoxData.file) 을 deserialize 하는 예제
using MessagePack; // MessagePack by neuecc v1.7.3.4
[MessagePackObject]
public class BoxDataWrapper
{
[Key(0)]
public List<BoxData> listElement { get; set; }
}
[MessagePackObject]
public class BoxData
{
[Key(0)]
public int idx { get; set; }
[Key(1)]
public int itemId { get; set; }
[Key(2)]
public int boxType { get; set; }
[Key(3)]
public List<int> rewList { get; set; }
[Key(4)]
public List<int> rewValue { get; set; }
[Key(5)]
public List<int> rewRate { get; set; }
[Key(6)]
public int selectCnt { get; set; }
[Key(7)]
public List<int> selectList { get; set; }
[Key(8)]
public List<int> selectValue { get; set; }
}
string filePath = "d:\\Data\\BoxData.file";
byte[] bytes = File.ReadAllBytes(filePath);
var wrapper = MessagePackSerializer.Deserialize<BoxDataWrapper>(bytes);
foreach (Model model in wrapper.elementList)
{
Console.WriteLine(model.idx + ":" + model.itemId + ":" + model.boxType);
}
- 시나리오
1. BoxData 데이터를 읽어서 BoxDataWrapper 객체 형태로 msgpack.write() 를 해서 byte 배열을 구한다.
2. C# 으로 BoxDataWrapper 로 unpack(deserialize) 해서 BoxData 데이터 값이 나오는지 확인한다.
※ java 에서 MsgPack 사용할때 주의할 점
객체를 byte 배열로 바꿀때, new 로 생성한 MessagePack 객체로 write 하는 방식은 thread safe 하지 않아서 한꺼번에 많은 객체를 serialize 할때, 특정 시점이 지나면 lock 이 발생한다. 프로젝트에서 jstack 으로 확인했다. write() 코드를 확인해서 trace 해보면 synchronized 를 사용한 메소드가 계속 보인다.
MessagePack msgpack = new MessagePack();
byte[] bytes = msgpack.write(object);
그래서 이 블로그 (참조한 글에 있는)에서는 Packer 를 사용하는 방식을 추천해서 적용해보았더니, CPU 사용량도 줄어들고 lock 도 발생을 안했다.
MessagePack msgpack = new MessagePack(); // singleton
Packer packer = msgpack.createPacker(outputStream); // createPacker every time
packer.write(object);
참조한 글
http://bugreporter.blogspot.com/2014/02/messagepack-java-permgen-memory-error.html
'Engineering > Etc' 카테고리의 다른 글
하룻밤에 읽는 Go 언어 이야기 (0) | 2019.10.04 |
---|---|
객체지향 개발 5대 원리 : SOLID (0) | 2019.09.27 |
Sublime Text 에서 ColorPicker 가 동작하지 않을 때 해결 방법 (0) | 2018.11.01 |
AWS S3 에 presigned url 로 파일 업로드할때 실패 해결 (0) | 2018.10.26 |
AWS S3 설정 (0) | 2018.10.19 |