이 프로젝트는 Elixir와 Phoenix를 사용한 웹소켓 채팅 서버로, Nginx를 로드밸런서로 활용합니다. 여러 서버 인스턴스 간 메시지 동기화를 지원합니다.
이 시스템은 다음과 같은 구성 요소로 이루어져 있습니다:
-
Nginx 로드밸런서
- 클라이언트 웹소켓 연결을 여러 채팅 서버 인스턴스에 분산
- 정적 파일(HTML, JavaScript, CSS) 제공
- 웹소켓 연결 프록시 처리
-
Elixir/Phoenix 채팅 서버 인스턴스
- Phoenix 채널을 사용한 웹소켓 통신
- PubSub을 통한 분산 메시지 브로드캐스팅
- Presence를 통한 사용자 상태 추적
- 실시간 웹소켓 기반 채팅
- 여러 채팅방 지원
- 사용자 온라인 상태 추적
- 타이핑 상태 표시
- 서로 다른 서버 인스턴스 간 메시지 동기화
- Nginx를 통한 로드밸런싱
여러 인스턴스에 접속한 사용자 간 메시지 동기화는 다음과 같이 이루어집니다:
- 사용자가 메시지를 특정 채널(예: "room:general")에 전송
- Phoenix 채널의
broadcast!
함수가 메시지를 PubSub 시스템에 발행 - PubSub 시스템은 모든 연결된 노드에 메시지를 전파
- 각 노드는 해당 채널을 구독하는 클라이언트에게 메시지 전달
이 방식으로 사용자들은 서로 다른 서버 인스턴스에 연결되어 있어도 모든 메시지를 수신할 수 있습니다.
이 프로젝트는 Docker Compose를 통해 쉽게 실행할 수 있습니다:
docker-compose up --build
이 명령은 다음 컨테이너들을 시작합니다:
- Nginx (로드밸런서)
- chat_node1 (채팅 서버 인스턴스 1)
- chat_node2 (채팅 서버 인스턴스 2)
개별 컴포넌트를 로컬에서 실행하려면:
- Elixir 의존성 설치:
mix deps.get
- 채팅 서버 노드 시작:
PORT=4001 NODE_NAME=chat_node1@127.0.0.1 iex --name chat_node1@127.0.0.1 --cookie chat_app_cookie -S mix phx.server
다른 터미널에서 두 번째 노드 시작:
PORT=4002 NODE_NAME=chat_node2@127.0.0.1 iex --name chat_node2@127.0.0.1 --cookie chat_app_cookie -S mix phx.server
- Nginx 실행:
nginx -c $(pwd)/nginx/nginx.conf
웹 브라우저에서 http://localhost
로 접속하면 채팅 UI가 표시됩니다.
프로그래밍 방식으로 연결하려면 Phoenix 소켓 클라이언트를 사용:
const socket = new Phoenix.Socket("/socket", {
params: { user_id: "user123" }
});
socket.connect();
const channel = socket.channel("room:general", {});
channel.join()
.receive("ok", resp => console.log("Joined", resp))
.receive("error", resp => console.error("Join failed", resp));
Nginx 로드밸런싱 전략은 nginx/nginx.conf
파일에서 설정할 수 있습니다:
upstream websocket_nodes {
# 기본값: 라운드 로빈
# least_conn; # 최소 연결 수 기반 로드밸런싱
# ip_hash; # 클라이언트 IP 기반 로드밸런싱
server chat_node1:4001;
server chat_node2:4002;
# 필요에 따라 더 많은 서버 추가
}
- ChatApp.Application: 애플리케이션 진입점 및 슈퍼바이저 트리
- ChatApp.Endpoint: Phoenix 엔드포인트 및 웹소켓 설정
- ChatApp.UserSocket: 웹소켓 연결 관리
- ChatApp.RoomChannel: 채팅방 채널 및 메시지 처리
- ChatApp.Presence: 사용자 온라인 상태 관리
-
클라이언트 연결:
- 클라이언트 → Nginx → 적절한 채팅 서버 인스턴스
-
메시지 전송:
- 클라이언트 → 서버 인스턴스 → PubSub → 모든 노드 → 해당 채널 구독 클라이언트
NODE_NAME
: Erlang 노드 이름 (예: chat_node1@chat_node1)PORT
: 웹소켓 서버 리스닝 포트 (기본값: 4000)NODE_COOKIE
: Erlang 분산 노드 간 인증 쿠키 (기본값: chat_app_cookie)
- 메시지 영구 저장을 위한 데이터베이스 통합
- 사용자 인증 시스템
- 채팅방 생성 및 관리 기능
- 미디어 공유 기능