NetMQTransport 상세 - planetarium/libplanet GitHub Wiki
이 문서에서는 Libplanet에서 제공하는 ITransport
인터페이스의 구현체인 NetMQTransport
에 대해 자세히 설명합니다.
메세지 인코딩
Libplanet의 NetMQTransport
에서는 NetMQ
에서 제공하는 multi-frames 메세지를 사용합니다. NetMQTransport
에서는 메세지의 각 프레임에 다음의 정보를 담아 통신합니다.
┌────────────┐
│ Identity │
│ (Optional) │
├────────────┤
│ Version │
├────────────┤
│ Type │
├────────────┤
│ Peer │
├────────────┤
│ Timestamp │
├────────────┤
│ Version │
├────────────┤
│ Sign │
├────────────┤
│ DataFrames │
├────────────┤
│ ... │
├────────────┤
│ ... │
└────────────┘
NetMQTransport는 NetMQ의 Router-Dealer 방식으로 통신을 하고 있습니다. Identity
는 Router
소켓에서 답장을 보낼 때 어떤 Dealer
소켓에 전달될 지 결정하는 값이므로, 처음 메세지를 보낼 때는 포함되지 않고 메세지를 받을 때 자동으로 추가되어 도착합니다. 답장을 보낼 때 해당 Identity
정보를 그대로 첫 번째 프레임에 포함하여 보내야 원래의 Dealer
소켓에 답장이 도착합니다.
Message
구현체를 이 NetMQMessage
형태로 바꾸고, 또 원래 Message
구현체로 돌리기 위해서 NetMQMessageCodec
을 활용합니다. 상세 구현은 해당 파일을 참고해주세요.
Kademlia DHT
초기 노드를 탐색과 노트 테이블 유지를 하기 위해 이더리움의 Kademlia DHT
알고리즘을 일부 채용하였습니다. 상세 구현은 Kademlia.cs 파일을 참고해주세요.
Send Message
NetMQTransport.SendMessageAsync() 함수를 통해 메세지를 다른 노드에 전달합니다. 병목을 막기 위해 _requests
Channel
에 리퀘스트를 담고, 별도로 계속해서 돌고 있는 ProcessRuntime
태스크에서 계속해서 리퀘스트들을 소비하여 처리합니다.
매 메세지마다 DealerSocket
을 새롭게 만들어 메세지를 인코딩하여 보내는 방식으로 구현되어 있으며, 사전에 정의된 예상된 답장을 모두 받을 때 까지 해당 DealerSocket
객체를 유지하는 방식으로 동작합니다.
Receive Message
NetMQTransport
객체를 생성할 때 RouterSocket
소켓을 하나 생성합니다. 이 소켓의 주소는 NetMQTransport
객체 생성자에 전달한 HostOption
정보 (IP Address 및 Port) 에 따라 생성됩니다. 다른 소켓에서 보낸 메세지는 이 RouterSocket
을 통해 받을 수 있으며, 메세지를 받을 때는 ReceiveMessage
함수를 통해 받게 됩니다. 해당 함수 내부적으로는 메세지를 디코딩해 Message
객체로 변환하여 외부에서 등록한 ProcessMessageHandler
를 인보크합니다.
외부에서도 이 이벤트 핸들러에 메서드를 등록한다면 받는 메세지를 구독할 수 있습니다.
Transport.ProcessMessageHandler.Register(ProcessMessageHandlerAsync);
Swarm
에서는 위처럼 해당 이벤트 핸들러에 Swarm.ProcessMessageHandlerAsync
함수를 등록해서 사용하고 있습니다.