채팅과 같은 통신을 구현하기 위해 양방향 RPC 프로토콜을 사용하는 룸 메시징 서버 구현. 신뢰할 수있는 전달, 단일 사용자의 여러 연결, 실시간 권한 및 존재와 같은 일반적인 공개 네트워크 메시징 문제를 처리하도록 설계되었습니다. RPC 요청 처리 및 객실 메시지 형식은 후크를 통해 사용자 정의 할 수 있으므로 채팅 룸 서버에서 복잡한 충돌 해상도가있는 공동 작업 애플리케이션에 이르기까지 모든 것을 구현할 수 있습니다. 룸 메시지는 또한 공개 API를 만들거나 IoT 장치의 M2M 통신을 터널에 사용하는 데 사용될 수 있습니다.
서버 측 기록 저장소 및 동기화 API를 사용한 안정적인 룸 메시징.
임의의 메시지는 유효성 검사 함수 (hook)를 통해 형식화되어 사용자 정의/이종 메시지 형식 (메시지 내부의 이진 데이터 포함)을 허용합니다.
객실 당 사용자 존재 API 알림이 있습니다.
실시간 객실 생성 및 방당 사용자 권한 관리 API. 블랙리스트 또는 화이트리스트 기반 액세스 모드 및 선택적 관리자 그룹을 지원합니다.
다양한 사용자의 연결에 대한 다양한 지원이 서비스 인스턴스에 이르렀습니다.
상태가없는 마이크로 서비스로 작성되며 State Store로 Redis (클러스터 구성을 지원 함)를 사용하여 수평으로 수평으로 스케일링 할 수 있습니다.
광범위한 사용자 정의 지원. 클라이언트 요청 처리를 위해서는 후크를 통해 사용자 정의 기능을 추가 할 수 있습니다. 요청 (명령) 핸들러는 API를 통해 서버 측에서 호출 할 수 있습니다.
플러그인 가능한 네트워킹 전송. 클라이언트-서버 통신은 양방향 RPC 프로토콜을 통해 수행됩니다. Socket.io 전송 구현이 포함되어 있습니다.
플러그인 가능한 상태 저장소. 메모리 및 Redis 상점이 포함되어 있습니다.
가벼운 온라인 사용자를 온라인 사용자 메시지를 지원합니다.
더 많은 배경 정보는이 기사를 읽으십시오.
이 프로젝트는 NPM을 통해 사용할 수있는 노드 모듈입니다. 로컬로 설치되지 않은 경우 확인하십시오.
$ npm i chat-service먼저 서버 구성을 정의합니다. 서버 측에서 서비스는 외부 인증 구현에 의존하므로 소켓 연결 고리를 정의합니다. 사용자는 인증 확인 만 통과하면 명시적인 사용자 추가 단계가 필요하지 않습니다.
const ChatService = require ( 'chat-service' )
const port = 8000
function onConnect ( service , id ) {
// Assuming that auth data is passed in a query string.
let { query } = service . transport . getHandshakeData ( id )
let { userName } = query
// Actually check auth data.
// ...
// Return a promise that resolves with a login string.
return Promise . resolve ( userName )
} 서버를 만드는 것은 간단한 객체 인스턴스화입니다. 참고 : 서비스 인스턴스를 올바르게 종료하려면 close 방법을 호출 해야합니다 (실패 복구 참조).
const chatService = new ChatService ( { port } , { onConnect } )
process . on ( 'SIGINT' , ( ) => chatService . close ( ) . finally ( ( ) => process . exit ( ) ) ) 서버는 이제 memory 상태를 사용하여 포트 8000 에서 실행 중입니다. 기본적으로 '/chat-service' 네임 스페이스가 사용됩니다. 객실 소유자로 admin 가있는 방을 추가하십시오. 모든 객실은 명시 적으로 만들어야합니다 (클라이언트 측에서 객실 생성을 허용하는 옵션도 제공됩니다).
// The room configuration and messages will persist if redis state is
// used. addRoom will reject a promise if the room is already created.
chatService . hasRoom ( 'default' ) . then ( hasRoom => {
if ( ! hasRoom ) {
return chatService . addRoom ( 'default' , { owner : 'admin' } )
}
} ) 클라이언트에서는 socket.io-client 구현이 필요합니다. 요청 (명령)을 보내려면 emit 메소드를 사용하면 결과 (또는 오류)가 socket.io ACK 콜백에 반환됩니다. 서버 메시지가 메소드 on 사용됩니다.
const io = require ( 'socket.io-client' )
// Use https or wss in production.
let url = 'ws://localhost:8000/chat-service'
let userName = 'user' // for example and debug
let token = 'token' // auth token
let query = `userName= ${ userName } &token= ${ token } `
let opts = { query }
// Connect to a server.
let socket = io . connect ( url , opts )
// Rooms messages handler (own messages are here too).
socket . on ( 'roomMessage' , ( room , msg ) => {
console . log ( ` ${ msg . author } : ${ msg . textMessage } ` )
} )
// Auth success handler.
socket . on ( 'loginConfirmed' , userName => {
// Join room named 'default'.
socket . emit ( 'roomJoin' , 'default' , ( error , data ) => {
// Check for a command error.
if ( error ) { return }
// Now we will receive 'default' room messages in 'roomMessage' handler.
// Now we can also send a message to 'default' room:
socket . emit ( 'roomMessage' , 'default' , { textMessage : 'Hello!' } )
} )
} )
// Auth error handler.
socket . on ( 'loginRejected' , error => {
console . error ( error )
} ) 실행 가능한 코드이며 파일은 example 디렉토리에 있습니다.
socket.io 이외의 다른 전송을 사용할 수 있습니다. 컨셉 전송 증명이 있으며, 일부 최소 API 추상화 레이어 WS 메시징과 백엔드 메시징 팬 아웃 추상화와 같은 간단한 이미 터 퍼비브 브로커와 함께 WebSocket 연결을 사용하는 개념 전송이 있습니다.
운송이 허용해야 할 주요 사항은 다음과 같습니다.
서버에서 클라이언트 그룹으로 메시지를 보냅니다 (단일 문자열 전체 일치 기준, 일명 룸 메시징 기반).
클라이언트에서 서버로 요청 반영 통신을 구현하십시오.
어떤 종류의 지속적인 연결 (또는 의미 적으로 동등한)을 구현하면 존재 추적에 필요합니다.
채팅 서비스는 Redis를 끈기있는 공유 매장으로 사용하고 있습니다. 실제 응용 프로그램 에서이 정보 중 일부는 다른 서비스에서 필요할 수 있지만 State Store를 완전히 상환하는 것은 실용적이지 않습니다. 더 나은 대안 접근법은 후크를 사용하는 것입니다. 예를 들어, 다른 데이터베이스 내에 모든 객실 메시지를 저장하려면 roomMessageAfter 사용할 수 있습니다. 또한 ServiceAPI 백엔드 메시징 버스를 통해 다른 내부 서버로 노출 될 수 있습니다.
정상적인 상황에서는 서비스 사용자에게 반환되는 모든 오류 (요청 응답을 통해 loginConfirmed 또는 loginRejected 메시지)가 ChatServiceError 의 인스턴스입니다. 다른 모든 오류는 서비스 인프라의 프로그램 버그 또는 실패를 나타냅니다. 이러한 오류의 디버그 로깅을 활성화하려면 export NODE_DEBUG=ChatService 사용하십시오. 라이브러리는 Bluebird ^3.0.0 약속 구현을 사용하고 있으므로 긴 스택 트레이스를 사용할 수 있도록 export BLUEBIRD_DEBUG=1 . 후크 및 ChatServiceError 서브 클래스에 API의 약속 버전을 사용하는 것이 좋습니다.
서버 측 API 및 RPC 문서는 온라인으로 제공됩니다.
서비스는 사용자 개념에서 연결 개념을 완전히 추상화하므로 단일 사용자는 둘 이상의 연결 (다른 노드 간 연결 포함)을 가질 수 있습니다. 사용자의 존재의 경우 결합 된 소켓의 수는 0보다 크기가 높아야합니다. 모든 API는 사용자 수준에서 작동하도록 설계되어 사용자의 여러 연결을 완벽하게 처리합니다.
연결은 완전히 독립적이며 추가 클라이언트 측면 지원이 필요하지 않습니다. 그러나 다른 사용자의 연결에 대한 정보를 얻는 데 사용할 수있는 정보 메시지와 명령이 있습니다. 모든 연결을 동일한 객실과 결합하도록 유지하는 등 클라이언트 측 동기 패턴을 실현할 수 있습니다.
각 객실에는 권한 시스템이 있습니다. 모든 관리자 권한이 있으며 사용자를 관리자 그룹에 할당 할 수있는 단일 소유자 사용자가 있습니다. 관리자는 다른 사용자의 액세스 권한을 관리 할 수 있습니다. 블랙리스트와 화이트리스트의 두 가지 모드가 지원됩니다. 액세스 목록/모드 수정 후 서비스는 액세스 권한을 잃은 사용자를 자동으로 제거합니다.
enableRoomsManagement 옵션이 활성화되면 사용자는 roomCreate 명령을 통해 객실을 생성 할 수 있습니다. 객실의 제작자는 소유자이며 roomDelete 명령을 통해 삭제할 수도 있습니다.
후크를 사용하여 추가 권한 시스템을 구현할 수 있습니다.
사용자가 방 메시지를 보내면 RPC에서 회신하면 메시지 id 반환됩니다. 이는 메시지가 상점에 저장되었음을 의미합니다 (구조와 같은 원형 버퍼 만 추가). 룸 메시지 ID는 1 에서 시작하는 시퀀스이며, 방에서 성공적으로 전송 된 메시지에 대해 하나씩 증가합니다. 클라이언트는 항상 roomHistoryInfo 명령을 통해 마지막 방 메시지 ID를 확인하고 roomHistoryGet 명령을 사용하여 누락 된 메시지를받을 수 있습니다. 이러한 접근 방식은 회전으로 인해 삭제되지 않는 한 메시지를 수신 할 수 있도록합니다.
기본적으로 클라이언트는 {textMessage: 'Some string'} 으로 제한되는 메시지를 보낼 수 있습니다. 사용자 정의 메시지 형식을 활성화하려면 directMessagesChecker 또는 roomMessagesChecker 후크를 제공합니다. 후크가 해결되면 메시지 형식이 수락됩니다. 메시지는 몇 가지 제한이있는 임의의 데이터 일 수 있습니다. 최상위 레벨은 timestamp , author 또는 id 필드가없는 Object 여야합니다 (서비스는 메시지를 보내기 전에이 필드를 채 웁니다). 중첩 레벨에는 임의의 데이터 유형 (바이너리)이 포함될 수 있지만 필드 type 'Buffer' 로 설정된 중첩 객체는 포함되지 않습니다 (이진 데이터 조작에 사용).
각 사용자 명령은 후크 추가 전후에 지원되며 클라이언트 연결/분리 후크도 지원됩니다. 명령 및 후크는 순차적으로 실행됩니다 : 전 후크 - 명령 - 후크 후 (명령 오류에서도 호출됨). 후크가 가능하기 전에 시퀀스 종료가 가능합니다. 클라이언트는 추가 명령 인수를 보낼 수 있고, 후크는 읽을 수 있으며 추가 인수로 답장 할 수 있습니다.
사용자 명령 서버를 실행하려면 execUserCommand 제공됩니다. 또한 ServiceAPI 및 TransportInterface 에서 제공하는 더 서버 측만 제공됩니다. 사용자 정의 예제에서 몇 가지 사용자 정의 사례를 찾으십시오.
서비스는 스토어에 사용자의 존재 및 연결 데이터를 유지하여 지속적이거나 공유 할 수 있습니다. 따라서 인스턴스가 잘못 종료되면 (전화를 걸거나 close 방법을 기다리지 않고) 매장에 완전히 네트워크 연결을 잃어 버리면 PRESENTED 데이터가 잘못됩니다. 이 경우 수정하려면 instanceRecovery 방법이 제공됩니다.
또한 연결 의존적 데이터 일관성과 관련하여 더 미묘한 사례가 있습니다. 전송 통신 인스턴스 및 저장 인스턴스는 다양한 종류의 네트워크, 소프트웨어 또는 하드웨어 고장을 경험할 수 있습니다. 일부 엣지 케이스 (여러 사용자의 작업과 같은)의 경우 이러한 실패로 인해 불일치가 발생할 수 있습니다 (대부분의 경우 명령의 발행자에게 반환됩니다). 이러한 이벤트는 인스턴스 이미 터 ( storeConsistencyFailure 이벤트와 같은)를 통해보고되며, 데이터는 RecoveryAPI 메소드를 통해 동기화 될 수 있습니다.
기본적으로 모든 사용자는 고유 한 로그인 (사용자 이름)이 있다고 가정합니다. 이름 생성을 관리하는 대신 별도의 전송과의 통합 (또는 다중 연결, 예를 들어 다른 Socket.io 네임 스페이스)을 사용할 수 있습니다. 객실 메시지는 후크 후 운송 후 roomMessage 에서 전달할 수 있으며 로그인없이 액세스 할 수 있습니다. 그 반대의 경우, 우회 권한 옵션을 사용하여 execUserCommand 통해 익명 사용자가 일부 서비스 명령을 실행할 수 있습니다.
후크 후 roomMessage 한 방에서 다른 방으로 메시지를 전달하는 데 사용될 수도 있습니다. 따라서 객실은 다른 방의 메시지 집계에 사용될 수 있습니다. 후크는 단지 기능이며 메시지 컨텐츠에 완전히 액세스 할 수 있으므로 임의의 컨텐츠 기반 전달 규칙을 구현할 수 있습니다. 고도로 개인화 된 사용자 (클라이언트) 특정 피드를 갖춘 시스템 구현을 포함합니다.
기본적으로 다른 사용자가 방에 연결된 사용자 연결 번호와 유형을 알 수있는 방법은 없습니다. 이러한 정보는 예를 들어 쿼리 문자열로 전달 된 다음 연결 후크를 통해 저장할 수 있습니다. 발표는 직접 전송 된 sendToChannel 메서드를 사용하여 onJoin 및 onLeave Hooks로 발표 할 수 있습니다. 또한 가입 된 장치 유형에 대한 추가 정보는 후크 후 roomGetAccessList 에서 보내야합니다 (목록 이름이 'userlist' 와 같을 때).
객실 기록 내에서 불일치를 만들기 때문에 삭제 또는 편집 작업이 없습니다. 삭제 및 편집을위한 일반적인 대안은 클라이언트가 메시지를 숨기거나 변경하는 데 사용할 특별한 의미와 함께 룸 메시지를 사용하는 것입니다.
이 패키지에 버그가 발생하면 Github Repo 문제에 버그 보고서를 제출하십시오.
PR도 받아 들여집니다.
MIT