Multiplexing Client Connections-1

반응형

이번 포스팅에서는 서버에 다수의 클라이언트가 접속했을 때의 패킷 처리 고려사항에 대해 알아보겠습니다.

 

다중 클라이언트 패킷 처리는 어떻게 해야 하나?

Multiplexing으로 검색하면 TCP/IP Transport Layer의 내용이 나오거나

다중 클라이언트 접속처리 예제 코드만 나오고. 응용프로그램 레벨에서의

다중 클라이언트 패킷 처리 관련 내용을 못 찾겠네요.

그래서 직접 설계해 보려고 합니다.

 

1:1 연결

서버와 클라이언트 관계가 1:1이면 앞선 글의 제목처럼 bridged 그 자체이므로 multiplexing에 대해 별다른 고려가 필요 없이 서버는 마치 내가 클라이언트인 것처럼 패킷을 주고받으면 그만입니다.

 

 

1:N 연결

그런데 위 그림처럼 다수의 클라이언트가 접속하게 되면 추가적인 고려사항은 무엇있까요?

일상 속의 휴대폰 먼저 생각해 보겠습니다.

콜, SMS, 이더넷 어플들은 프로토콜별로 각각의 채널을 할당받게 되고 이를 Channel MUX에서 Multiplexing 과정을 거쳐 단일구간을 통해 모뎀으로 전송하고, 모뎀으로부터의 응답은 다시 Channel MUX에서 Demultiplexing 과정을 거쳐 각각의 채널을 통해 상위 어플 레벨로 올려 보냅니다. 타임아웃도 고려해야 하고 채널별 ID를 관리해야 하고 메시지 타입별 ID도 관리해야 하고 Request에 대한 응답인지, Notification, Indication에 대한 응답인지도 고려해야 하고(전자는 송신자가 먼저 요청한 패킷으로서 Solicitated response라고 하고 후자는 송신자가 요청하지 않은 패킷으로 Unsolicitated Response라고 합니다. 보통 송신 패킷은 Request ID가 있어서 응답이 올 때까지 RequestID를 들고 있다가 관리를 하는데 Unsol이면 Request ID가 없죠. ) 클라이언트 모두에게 멀티캐스팅을 할 것인지 특정 클라이언트에게만 보낼 것인지 특정 정보를 C1에 구독신청 해놨는데 접속이 끊겼으면 C2로 전환해야 하는데 이를 요청자에게 노티를 보낼 건지 안 보내고 프레임워크레벨에서 처리할 건지 공유 자원에 대해 Lock으로 잠기는 구간이 최소화되도록 해야 하고 머리가 아픕니다.

 

Network Bridged 자동매매 프로그램에서 접속가능 클라이언트 수가 N이 된다고 가정하면, 서버는 각 클라이언트의 계좌 상황을 알고 있어야 하고 매매 체결신호 수신 시 클라이언트를 구분할 수 있어야 합니다. 장 운영정보 같은 패킷은 클라이언트에 특정되지 않으니 이건 공통으로 빼고요. 그림에서는 서버 스레드를 하나로 표현했지만 매매로직당 Worker thread를 할당할 것이므로 각각의 Thread가 각각의 Request를 보낼 건데 그 응답에 대한 Notificaion을 송신 thread에게 callback 해줘야 하므로 이 또한 고려해야 하고 차트데이터를 수신할 때 T1, T2, T3가 같은 종목을 요청했으면 이걸 각각의 Thread가 메모리 할당해서 들고 있을 필요는 없잖아요, 그래서 공용 데이터를 관리할 Database 객체도 만들어줘야 합니다. 각 클라이언트별로 소켓객체를 담고 있는 세션관리노드가 있는데 여기에 패킷 ack 관리까지 하는 정보를 넣으면 한 객체에 너무 많은 정보가 담겨 접근하는 스레드가 늘어나고 그로 인해 단순 데이터 송수신하는 속도가 생명인 스레드가 lock 진입 전 대기시간이 길어질 수 있어서 이 또한 고려해야 합니다.

 

TR 데이터 흐름

위 그림은 대략적인 TR 송수신 흐름의 스케치입니다.

서버의 매매전략 스레드 T1이 TR요청을 한다고 가정하면 송신부터 수신까지 대략 9단계의 단계로 이루어집니다.

1단계. TR Request 패킷 생성: TR의 Inblock을 생성하여 패킷 파라미터를 설정합니다.

2단계. 목적지 Client 선택: 가능한 클라이언트를 선택합니다. 클라이언트 모두에게 보낼 것인지, 특정 클라이언트에게 보낼 건지 정합니다. 매매신호는 모두에게 멀티캐스팅 해야 할 것이고 특정 종목의 차트는 가장 신뢰도가 높은 클라이언트를 선택하여 TR 패킷의 소켓정보를 설정합니다. 이 작업을 RIL_Tx 스레드에서 담당해도 되지만 다수의 매매 스레드에서 패킷을 요청할 때 RIL_Tx에 부하가 걸리므로 Caller가 목적지까지 정하여 패킷에 세팅합니다. 그리고 콜백을 받기 위해 자신의 TID와 MSG_ID, 그리고 LPARAM, RPARAM을 추가 셋팅합니다. 

3단계: Tx 스레드의 Tx Queue에 패킷을 Enqeue 합니다. 그리고 Tx 스레드에 Wakeup 이벤트를 발생시킵니다. 그럼 Tx 스레드가 깨어나서 Queue에서 패킷을 꺼냅니다.

4단계: 패킷을 보내기 전, 패킷에게 ID를 할당하고 이를 패킷 헤더에 붙입니다. 그리고 패킷 자체를 Tx/Rx 관리테이블에 붙여둡니다. 클라이언트는 응답을 보낼 때 서버가 전송한 RequestID를 그대로 회신해야 합니다.

8단계:RX 스레드가 응답 패킷의 RequestID를 통해 송신 패킷 객체를 조회하고 그로부터 T1의 TID와 콜백 메시지 정보를 얻습니다.

9단계: 수신 데이터를 DataBase 객체에 저장합니다.

10단계: 8단계에서 조회한 T1의 정보로 T1에 콜백메시지와 파라미터를 전송합니다.

11단계: T1은 DabaBase를 조회하여 데이터를 획득합니다.

별의별 예외상황이 있겠지만 송신단계에서 클라이언트 Disconnect는 송신 패킷을 Discard 하고 발신 스레드에는 콜백이벤트로 알립니다. 발신 스레드의 설정에 따라 Tx 스레드가 다른 클라이언트를 선택하게 할 수도 있습니다.

반응형