실습 로그 / 산학 실시간 심전도 처리 서버

산학 6단계 · 생성기 MQTT 송신

생성기가 TCP 말고 MQTT로도 보낼 수 있게 해서, 같은 데이터를 두 방식으로 보내 지연·유실·재연결을 비교할 준비를 한다.

진행 과정은 실습 내용을 AI와 함께 정리했고, 배운 점은 그걸 보고 제가 이해한 대로 직접 썼습니다. 배운 점 바로가기 ↓

MQTT네트워크트러블슈팅

목표

생성기가 TCP 말고 MQTT로도 보낼 수 있게 한다. 같은 데이터를 두 방식으로 보내서 지연·유실·재연결을 비교하기 위해서다. (10.28)

A. TCP와 MQTT 차이

TCP  : 생성기 ───────────────▶ 수신부           (직접 연결)
MQTT : 생성기 ──▶ 브로커 ──▶ 수신부              (중간에 브로커)
TCP MQTT
연결 생성기가 수신부에 직접 둘 다 브로커에 붙음
메시지 경계 줄바꿈(\n)으로 직접 나눔 브로커가 메시지 단위로 지켜줌
받는 쪽 수신부 하나 같은 토픽을 여러 곳이 구독 가능
필요한 것 없음 브로커(mosquitto)
  • 토픽 : 메시지에 붙이는 주소. 환자마다 ecg/0, ecg/1 … 받는 쪽은 ecg/+ 하나로 전부 구독 (+ = 그 자리 아무거나)
  • QoS 0 : 한 번 보내고 끝 (TCP 지금 방식과 비슷) / QoS 1 : 브로커가 받았다고 답할 때까지 다시 보냄

B. 코드 구조 (server/stream/stream_tcp.py)

class TcpSender:
    def send(self, msgs):
        lines = [json.dumps(m) + "\n" for m in msgs]
        self.sock.sendall("".join(lines).encode())

class MqttSender:
    def send(self, msgs):
        for m in msgs:
            info = self.client.publish(f"{self.topic}/{m['channel_id']}", json.dumps(m), qos=self.qos)
            if info.rc != 0:
                self.failed += 1
  • 보내는 부분만 두 클래스로 나눔. 메시지를 만드는 코드는 그대로 → 두 방식이 같은 내용을 보내야 비교가 공정함
  • TcpSender : 0.1초마다 환자 전원 메시지를 줄바꿈으로 이어서 한 번에 보냄 (기존 방식)
  • MqttSender : 환자마다 자기 토픽으로 하나씩 publish. 줄바꿈은 안 붙임
  • info.rc != 0 : 브로커 연결 끊김·큐 가득 참이면 실패로 셈
  • 옵션 : --transport tcp|mqtt(기본 tcp, 기존 스크립트 그대로), --topic, --qos, 포트 기본값 TCP 8000 / MQTT 1883
  • 확인용 구독기 mqtt_check.py : 받은 건수, seq 빈칸(유실), 지연 p50·p95 출력

C. 결과

환경 조건 보냄 받음 유실 지연 p50 / p95
로컬 (파이썬 테스트 브로커) 20명 × 5초 QoS 0 1,000 1,000 0 4.5 / 6.0ms
로컬 (파이썬 테스트 브로커) 100명 × 3초 QoS 0 3,000 3,000 0 19.7 / 30.2ms
테스트 서버 (mosquitto) 10명 × 10초 QoS 0 1,000 1,000 0 1.2 / 1.3ms
  • 로컬에서 지연이 높았던 건 테스트용 파이썬 브로커가 느려서. 실제 브로커(mosquitto)에서는 1ms대
  • 다음 : 수신부(MQTT 받는 부분)까지 붙여서 20·50·100명으로 TCP와 비교

트러블슈팅

MQTT 테스트에서 1,000건 중 900건만 받음

  • 증상 : 받음 900건, 그런데 seq 빈칸(유실)은 0
  • 원인 : 구독기를 20초만 켜뒀는데 생성기가 처음 실행이라 PhysioNet 기록을 받느라 앞에서 약 11초를 씀 → 마지막 1초(100건)가 오기 전에 구독기가 먼저 꺼짐
  • 확인 : 유실이면 중간에 seq 빈칸이 생겨야 하는데 0 → 끝부분이 잘린 것
  • 해결 : 기록이 캐시된 상태에서 다시 실행 → 1,000건 전부

남은 작업

  • 생성기 MQTT 송신 + 서버에서 송신 확인
  • MQTT 수신부까지 붙여 TCP와 비교 (20·50·100명, 지연·유실·재연결)

배운 점

직접 작성

E. 생성기 MQTT 송신

  • 목적 : 기업 요청대로 TCP 말고 MQTT로도 보낼 수 있게 해서 비교 준비
    • 기업이 실제 제품에서 어느 방식을 사용할지 정하지 않아서 MQTT도 검토 중
    • 둘 다 만들어서 비교
  • 설명 : --transport mqtt 옵션 추가. 환자별 토픽 ecg/{환자번호}로 보내고 받는 쪽은 ecg/+로 전부 구독. 서버에서 10명 × 10초 유실 0, 지연 p95 1.3ms
    • MQTT : 브로커가 중간에서 메시지를 받아 구독자에게 나눠주는 방식.
      • IoT 기기에서 많이 사용
    • 토픽 : MQTT 메시지에 붙이는 주소
      • 받는 쪽은 토픽을 구독해서 해당 메시지만 받는다
      • ecg/+ 의 +는 ecg/0 , ecg/1, ecg/2 …. 모든 환자 메시지를 한 번에 받음
    • 브로커 : 메시지를 받아서 나눠주는 중계 서버 (Mosquitto)
    • TCP와 차이 :
      • TCP : 생성기와 수신부가 직접 연결
      • MQTT : 브로커를 거침
        • 메시지 끝 줄바꿈 같은 경계 표시 필요 없음