메시지팩(MessagePack) C# <-> 자바 직렬화 데이터 전달시 주의할 점

메시지팩(MessagePack, http://msgpack.org/)은 이 기종의 데이터 통신을 원활하게 할 수 있게, 데이터 포맷을 투명하게 직렬화하는 라이브러리이다. 메시지팩은 종종 프로토콜 버퍼, 스리프트등과 같은 직렬화 프레임웍과 비교되는 오픈소스 라이브러리이다. 이 라이브러리의 특징은 다른 직렬화 프레임웍과는 다르게 메세지 포맷 파일을 정의하는 정적 형태가 아니라, 소스코드에서 바로 사용하도록 지원하는 동적 형태이다. 그래서 다른 프레임웍과 비교해서 약간의 성능상 단점은 있지만, 추가 작업(IDL 컴파일 등)이 필요하지 않아서 쉽게 사용할 수 있다.

* 이 기종 : 윈도, 리눅스 등에서 자바를 사용하는 형태가 아니다. 윈도 머신을 사용한다고 해도 C#, C++등과 자바를 사용해서 서로 통신하는 형태가 이 기종이라고 인식하면 된다.
* 투명하다 : 쉽게 이해하고, 인식할 수 있는 형태이다. 메시지팩은 여러 데이터 타입을 직렬화하는데 고유한 스펙을 사용한다. 스펙은 https://github.com/msgpack/msgpack/blob/master/spec.md 에서 확인할 수 있다.
* 직렬화/역직렬화 : 직렬화는 개별 플랫폼에서 사용하는 데이터를 바이트 배열로 변경하는 작업이다. 역직렬화는 직렬화된 데이터인 바이트 배열을 개별 플랫폼에서 사용하는 형태(객체)로 변경하는 작업이다.

테스트한 필자의 환경은 다음과 같다.
서버 : 자바
클라이언트 : 자바
에이전트 : C#, C++

위의 구성에서 자바 플랫폼에서 사용하는 라이브러리는 https://github.com/msgpack/msgpack-java 을 사용하고 있고 문제가 없다. 문제는 에이전트와 자바 서버간의 통신에서 발생했다. 프로젝트에서 사용하는 C# 라이브러리는 https://github.com/msgpack/msgpack-cli을 사용하고 있다.

C# 코드
– EventMessage 메시지는 에이전트가 서버와 통신하는데 사용하는 이벤트 모델의 최상위 클래스이다.

var stream = new MemoryStream();
var serializer = SerializationContext.Default.GetSerializer<EventMessage>();
serializer.Pack(stream, registerMessage);
byte[] messageBytes = stream.ToArray();

자바 코드
– 자바는 아래와 같이 MessageBufferPacker 클래스를 사용해서 직렬화 한다.

MessageBufferPacker msgPacker = MessagePack.newDefaultBufferPacker();
try {
msgPacker.packString("string")
 .packString("string")
 .packInt(eventValue)
 .packInt(resultValue);

return msgPacker.toByteArray();
} finally {
 msgPacker.close();
}

이제 위의 코드가 들어가 있는 소스를 실행하면 아래와 같은 에러 로그를 보게 된다. 로그의 내용으로 봐서는 읽어야 하는 바이트 배열에 특정 바이트 값이 잘 못 들어가 있다는 것을 알 수 있다.

ERROR 2016-04-06/10:23:32.503/PDT [MessageReader] com.xxx.xxx.xxx.MessageHandlerImpl:exceptionCaught(139): org.msgpack.core.MessageTypeException: Expected String, but got Array (93)

그래서 이것저것 확인 하다가 C# 라이브러리의 Packer 클래스를 사용하는 형태를 확인할 수 있고, 아래와 같은 형태로 소스 코드를 변경했다. 예상은 바이트 배열의 코덱정도로 볼 수 있는 PackerCompatibilityOptions 차이로 생각했는데, 에러를 내는 SerializationContext.Default.GetSerializer<EventMessage>(); 의 객체도 동일한 옵션을 사용하고 있다. 그래서 정확한 문제 파악은 시간도 없고 패스했다(능력부족 ㅠ.ㅠ).

아래는 Packer를 사용해서 직렬화 부분을 수정한 소스코드이다.

var stream = new MemoryStream();
var packer = Packer.Create(stream, PackerCompatibilityOptions.None, true);

packer.Pack("string");
packer.Pack("string");
packer.Pack(event);
packer.Pack(result);

stream.Position = 0;
byte[] messageBytes = stream.ToArray();

이제 메시지팩을 사용하는 C# 플랫폼에서 자바 플랫폼에 바이트 배열을 제대로 보내고 받을 수 있는 것을 알 수 있다. 어찌보면 간단하지만 처음 프로젝트에 도입해서 사용하는 경우에는 C#에서 Packer를 모른다면 난감할 수 있어서 포스팅 했다.

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.