맞춤형 XMPP 솔루션으로의 성공적인 마이그레이션
게시 됨: 2019-04-08제3자 채팅에서 영국 기반 메시징 의료 솔루션인 Forward Health를 위한 맞춤형 XMPP 기반 메시징 솔루션으로 마이그레이션하는 데 직면한 문제에 대해 이야기하겠습니다. 이 기사에서는 마이그레이션의 이유, 구현의 현실과 기대치, 추가 기능 구축의 과제를 다룹니다.
우리가 시작한 곳
우리 고객인 Forward Health는 채팅 기능을 포함하여 영국의 의료 종사자를 위한 모바일 커뮤니케이션 애플리케이션을 구축하기를 원했습니다. 스타트업으로서 그들은 작동하는 제품을 빨리 보여주고 싶었습니다. 동시에 메시징은 안정적이고 강력해야 하며 민감한 환자 데이터를 안전하게 보낼 수 있어야 했습니다. 이를 달성 하기 위해 채팅 기능에 사용 가능한 타사 솔루션 중 하나를 사용하기로 결정했습니다 .
채팅 기능은 특히 의료 산업 지원을 목표로 할 때 사소한 일이 아닙니다. 앱이 성장함에 따라 라이브러리 측에서 제3자가 작업하기를 꺼리는 일부 버그와 더 많은 엣지 케이스가 발생했습니다. 또한 Forward Health는 타사 라이브러리에서 지원하지 않는 새로운 기능을 추가하기를 원했습니다. 다음 단계는 맞춤형 솔루션으로 전환하는 것이었습니다.
그때부터 우리는 MongooseIM 으로 작업을 시작했습니다. MIM은 잘 정립된 XMPP 프로토콜을 기반으로 하는 오픈 소스 솔루션입니다. 우리는 백엔드를 설정하고 맞춤형 솔루션 구현을 지원하기 위해 외부 회사인 Erlang Solutions Limited와 협력했습니다.
처음에는 메시징에 대한 모든 것이 달라 보였습니다. 이전에는 SDK와 해당 REST API로 모든 요구 사항을 충족했습니다. 이제 MongooseIM을 사용하여 XMPP의 특성을 이해하고 자체 SDK를 구현하는 데 시간이 필요했습니다 . "기본" XMPP 서버는 실시간으로 클라이언트 간에 스탠자(XML 메시지)만 전달하는 것으로 나타났습니다. 스탠자는 일반 채팅 메시지, 현재 상태, 요청 및 응답과 같이 다양한 유형이 될 수 있습니다. 예를 들어 메시지를 저장하고 클라이언트가 메시지를 쿼리할 수 있도록 다양한 모듈을 서버에 추가할 수 있습니다.
클라이언트 측(Android, iOS)에는 일부 저수준 SDK가 있었습니다. 불행히도 그들은 MongooseIM 및 XEP(모든 메시지에 대한 푸시 알림 전송을 담당하는 XMPP 확장 프로토콜)라고 하는 일부 플러그형 모듈과의 통신을 가능하게 하는 레이어로만 작동했습니다. 메시지 처리, 저장 및 쿼리를 위한 전체 아키텍처는 우리 팀에서 구현해야 했습니다.
우리를 구한 것은 이전에 사용했던 타사 라이브러리 였습니다. 그것은 매우 잘 생각한 API를 가지고 있었기 때문에 우리의 솔루션이 비슷한 방식으로 작동하도록 만들었습니다. 우리는 이전 솔루션의 인터페이스에 해당하는 인터페이스를 사용하여 XMPP 특정 코드를 내부 SDK로 분리했습니다. 그 결과 마이그레이션 후 애플리케이션 코드가 약간만 변경되었습니다.
MongooseIM을 구현하는 동안 우리는 표준이라고 생각했지만 XEP에서도 사용할 수 없는 요소에 여러 번 놀랐습니다.
XMPP 기반 채팅의 주요 기능 구현
타임스탬프
우리가 했던 것처럼 타임스탬프는 "메시지를 받았습니다. 이것을 타임스탬프와 함께 UI에 표시합니다."와 같이 간단하다고 생각할 수도 있습니다. 아니, 그렇게 쉬운 일이 아닙니다. 기본적으로 메시지 스탠자에는 타임스탬프 필드가 없습니다. 우리 팀에 다행스럽게도 XMPP는 쉽게 확장할 수 있는 프로토콜입니다. 백엔드에서 사용자 지정 기능을 구현하여 MongooseIM 서버를 통과한 모든 메시지에 타임스탬프를 추가했습니다. 그러면 수신자는 메시지에 첨부된 타임스탬프를 갖게 됩니다.
발신자가 타임스탬프를 추가할 수 없는 이유는 무엇입니까? 글쎄, 우리는 그들이 그들의 전화에 정확한 시간을 설정했는지 모릅니다.
왜 XEP가 없습니까? XMPP는 실시간 프로토콜이기 때문에 이론적으로 전송된 모든 메시지가 즉시 수신됩니다.
편집: Florian Schmaus가 지적한 대로: "실제로 하나가 있지만 이름이 헷갈리기 때문에 쉽게 놓칠 수 있지만 XEP-0203: Delayed Delivery." 배달이 지연되는 경우에만 메시지에 타임스탬프를 추가합니다. 그렇지 않으면 메시지가 방금 전송되었습니다.
오프라인 메시지
두 사용자 모두 애플리케이션에 로그인하면 실시간으로 서로에게 메시지를 보낼 수 있습니다. 그러나 그들 중 하나가 오프라인이라면 어떻게 될까요? 빠른 대답은 메시지가 백엔드에서 버퍼링되어야 한다는 것 입니다. 오프라인 메시지 기능은 이 작업을 처리하고 사용자가 다시 로그인하면 버퍼링된 모든 스탠자를 사용자에게 보냅니다.
그러나 다음과 같은 몇 가지 질문이 생깁니다.
- 이러한 메시지는 얼마나 오래 버퍼링되어야 합니까?
- 그 중 몇 개?
- 다시 로그인한 직후에 다시 보내야 합니까? 하지만 클라이언트에 메시지가 넘쳐흐를 것입니다. 그렇죠?
- 사용자가 로그인만 하고 새 메시지로 채팅에 입장하지 않으면 어떻게 될까요? 그들은 모두 사라질 것인가?
- 사용자가 여러 장치에서 로그인하면 어떻게 됩니까?
오프라인 메시지 기능은 온라인으로 다시 돌아올 첫 번째 장치에만 메시지를 보낼 수 있었고 그 메시지는 다른 모든 장치에 대해 손실된다는 것이 분명해졌습니다. 우리는 이 기능을 버리고 XMPP 백엔드에 다른 영구적인 방식으로 메시지를 저장하기로 결정했습니다.

메시지 아카이브 관리(MAM)
MAM은 메시지를 위한 온-서버 저장소입니다. 클라이언트가 로그인하면 서버에 메시지를 쿼리할 수 있습니다. 페이지별로 쿼리할 수 있고 날짜별로 쿼리할 수 있습니다. 유연합니다. 특정 ID가 있는 메시지 앞이나 뒤에 페이지를 쿼리하여 정확한 대화의 메시지에 대한 필터를 추가할 수도 있습니다.
그러나 여기에 캐치가 있습니다. 일반 채팅 메시지는 고유한 ID가 있는 MAM 메시지 안에 래핑되어 저장됩니다. 사용자가 스트림에서 채팅 메시지를 수신하면 MAM ID가 포함되지 않습니다. 그들은 그것을 얻기 위해 MAM을 쿼리해야 합니다.
MAM에서 검색하는 것은 네트워크 요청이므로 비교적 오랜 시간이 걸릴 수 있습니다. 사용자가 채팅에 들어가면 즉시 메시지를 보고 싶어합니다. 따라서 로컬 데이터베이스 도 필요합니다.
사용자가 스트림(온라인 메시지)에서 메시지를 받으면 로컬 데이터베이스에 저장하고 사용자에게 보여줍니다. 이를 통해 실시간으로 빠르게 도착하는 메시지를 사용자에게 표시합니다.
또한 채팅 화면에 들어갈 때마다 지금부터 해당 대화에 대해 로컬 DB에 저장된 최신 MAM 메시지로 모든 메시지를 다운로드하고 중복을 무시하고 데이터베이스에 넣습니다.
이것이 우리가 오래된 메시지를 저장하는 방법입니다. 또한 데이터베이스에 MAM의 첫 번째 메시지와 마지막 메시지 사이의 특정 대화에 대한 완전한 메시지 세트가 있다고 확신합니다.
MAM에서 다운로드한 메시지를 추적하기 위해 대화 엔터티에 두 가지 속성을 추가했습니다.
- 데이터베이스에 있는 최신 MAM 메시지의 MAM ID
- 데이터베이스에서 가장 오래된 MAM 메시지의 MAM ID
로컬 데이터베이스에서 부서진 MAM 메시지 집합을 처리하는 것은 매우 문제가 될 것입니다.
또한 모든 대화에 대해 이 두 속성을 사용하면 래퍼(MAM 메시지)를 무시하면서 일반 채팅 메시지를 데이터베이스에 저장할 수 있습니다. 그리고 사용자가 채팅에 들어가면 데이터베이스의 최신 메시지를 표시하고 백그라운드에서 MAM에서 누락된 메시지를 가져올 수 있습니다.
받은 편지함
모든 채팅 기반 앱에는 이름, 마지막 메시지 및 읽지 않은 메시지 수를 볼 수 있는 채팅 목록이 있는 화면이 필요합니다. 그것에 대한 해결책이 있어야합니다!
실제로는 없습니다... Roster라는 것이 있습니다. "친구"로 태그가 지정된 사용자 목록을 보유할 수 있습니다. 유감스럽게도 마지막 메시지도 없고 첨부된 읽지 않은 메시지 수도 없습니다. 물론 백엔드에서 필요한 정보를 조각으로 얻을 수 있습니다. 처음에는 그렇게 하고 싶었지만 천천히 하고 하기가 복잡했습니다. 그 때 우리는 받은 편지함 기능에서 Erlang 솔루션 과 함께 작업하기 시작했으며 오픈 소스로 나아가고 있습니다.
사용자가 XMPP 백엔드에 연결하면 앱은 해당 사용자의 모든 대화(일대일 및 팀 채팅 모두)가 포함된 받은 편지함을 가져옵니다. 그들 각각에는 첨부된 마지막 메시지와 읽지 않은 메시지 수가 있습니다. 응용 프로그램은 전체 받은 편지함을 로컬 데이터베이스에 저장합니다. 사용자가 앱에 있고 새 메시지가 도착하면 받은 편지함 상태를 로컬로 업데이트합니다. 그렇게 하면 앱이 모든 새 메시지에 대해 받은 편지함을 가져올 필요가 없습니다.
요약
일부 타사 채팅 솔루션은 높은 수준의 추상화를 제공합니다. 간단한 채팅 응용 프로그램을 만들려는 경우 괜찮습니다. Forward 앱에서 자체 XMPP 기반 솔루션을 구현하여 훨씬 더 나은 하위 수준 액세스를 얻을 수 있었고 문제를 훨씬 쉽게 해결할 수 있었습니다. 물론 시간이 걸렸지만 이제 영국의 의사들이 NHS에서 승인한 안전하고 쉬운 방식으로 의사 소통할 수 있도록 모든 맞춤 기능을 제공할 수 있다는 것을 알고 있습니다.
메시징은 고성능 실시간 통신에 관한 것입니다. MIM으로 전환함으로써 솔루션의 모든 부분을 최적화하여 속도, 안정성 및 궁극적으로 신뢰를 개선할 수 있었습니다. 현재 전체 코드가 있으므로 쉽게 추적할 수 있습니다. 또한 안정화 단계를 거치고 있으며 메시징과 관련된 보고서 수가 급격히 감소했습니다. 사용자는 플랫폼을 신뢰할 수 있다는 것에 만족합니다.
자체 SDK를 설계하고 작성하는 것은 어려운 작업이었고 우리는 그것을 좋아했습니다. 서버에서 데이터를 가져와서 화면에 보여줘야 하는 단순한 애플리케이션과는 차원이 다른 것이었다. 구현하는 동안 이전에 사용한 타사 라이브러리 API의 많은 디자인 선택 사항을 이해했습니다. 왜요? 같은 문제를 겪었기 때문입니다.