Socket.io 오버헤드 최적화하기 | Web IDE 구축하기 - 6

    반응형

    제가 개발한 Web IDE 서비스는 Socket을 통해 클라이언트의 코드를 받아 실시간으로 테스팅해 결괏값을 반환해주고 있습니다. 여기서 제가 발견한 한가지 문제점은 액션을 취하지 않아도 polling이 뜨면서 불필요한 요청이 많아지는 것입니다. 이런 불필요한 요청이 많아지면 오버헤드가 과도하게 발생될 수 있고, EC2 리소스 비용에 영향을 끼치는 것 같아 리팩토링을 해보려고 합니다 

    초기 소켓 연결 과정에서 뜨는 http 통신

     

     

     

    HTTP polling 요청이 많이 발생하는 이유


    • 최초 접속 시 http polling이 발생하는 이유
      • socket.io는 초기 핸드쉐이크 과정을 거치는데, 웹소켓을 사용하여 연결을 시도합니다. 클라이언트에서 웹소켓 사용이 가능하다면 socket.io가 웹소켓으로 업그레이드 하는 초기 연결 단계에서 long polling방식이 사용되여 폴링 요청이 발생됩니다. 그리고 웹소켓 연결에 실패하면 http polling을 사용하여 서버와 통신을 지속적으로 시도하는데 그 과정에서 페이지 렌더링이 완료되까지 컴포넌트 리렌더링 발생 + 지속적인 연결 시도 때문이지 않았을까 싶습니다.
      • 위에 캡쳐본을 보면 http get 요청이 많이 뜨는데 이 부분이 socket.io가 서버와 연결 시도할때 발생하는 부분들입니다
    •  input에 변화가 일어날때마다 http polling이 발생하는 이유
      • 이 부분은 onChange에 따른 state 변화 리렌더링 때문
      • 저같은 경우 클라이언트가 입력을 끝내고 코드를 제출할때만 업데이트된 입력값이 필요한거기 때문에 입력 변화에 따른 불필요한 통신을 줄이고자 useRef hook으로 변경했습니다
    // 변경전
    const [code, setCode] = useState<string>("");
    
    <CodeMirror
      value={code}
      onChange={(e) => setCode(e)}
      theme={copilot}
      height="100%"
      extensions={[loadLanguage(`${lang}`)!]}
    />
    
    // 변경후
    const codeRef = useRef<string>();
      
    <CodeMirror
      value={codeRef.current}
      onChange={(e) => {
        codeRef.current = e;
      }}
      theme={copilot}
      height="100%"
      extensions={[loadLanguage(`${lang}`)!]}
    />

     

     

    그럼 Websocket을 사용하면 되는것인가?


    맞습니다. scoket.io를 통해 websocket 설정을 해주면 됩니다. 기본적으로 scoket.io는 websocket을 쓴다고 했지만 옵션을 최우선 순위로 설정해서 polling을 최소화 시켜보겠습니다

    socket.io의 옵션중 전송 방법을 설정하는 transports 옵션이 있습니다. 클라이언트와 서버간의 연결을 설정할때 바로 websocket을 시도하라고 지시하고, 실패할 경우 polling 방식을 사용하도록 설정해줍니다

    // 변경전
    // 서버
    const io = new Server(server, {
      cors: {
        origin: "*",
        methods: ["GET", "POST"],
      },
    });
    
    // 클라이언트
    const socket = io(`${process.env.REACT_APP_API_ENDPOINT}/problem`, {
        query: {
          problem: problemId,
          path: "*",
        },
    });
    
    // 변경후
    // 서버
    const io = new Server(server, {
      transports: ["websocket", "polling"],
      cors: {
        origin: "*",
        methods: ["GET", "POST"],
      },
    });
    
    // 클라이언트
    const socket = io(`${process.env.REACT_APP_API_ENDPOINT}/problem`, {
        transports: ["websocket", "polling"],
        query: {
          problem: problemId,
          path: "*",
        },
    });

    이젠 페이지 접속 후 연결되었을때 HTTP polling이 확연히 출었습니다! 보면 http get 요청이 아예 사라졌습니다. 그 이유는 웹소켓 연결을 가장 먼저 시도하고, 이후의 통신은 웹소켓 프로토콜을 통해 이루어지고, 더이상 long polling 방식을 쓰지 않기 때문입니다. 또한 연결에 성공했으니 더이상 폴링 요청이 없어지는 것입니다.

    이렇게 설정 해놓고 나중에 비용이 얼마나 절감되었는지 비교해보려고 합니다

    반응형

    댓글