Статьи

Сравнение платформ сообщений чата

Если вы пишете приложение для чата, первый большой вопрос, который нужно рассмотреть, заключается в том, собираетесь ли вы написать настоящий бэкэнд чата. Почему ты хочешь? Вы получите полный контроль над функциональностью, надежностью и интерфейсом. Почему бы вам не захотеть? Ну, постоянные сокетные системы, которые масштабируются до тысяч или сотен тысяч пользователей, — это не малое дело. Плюс, куча хороших платформ уже существует. Возможно, все зависит от того, является ли чат частью вашего основного бизнеса. Если вы Snapchat, вы, вероятно, хотите написать свою собственную платформу чата. Если чат — это просто функция, которую вы используете для достижения своей реальной ценности, то, вероятно, не имеет смысла тратить на это инженерное время.

Какие существуют платформы, на которых можно написать приложение для чата, и как они складываются? Вот краткое изложение на конец 2015 года. Мои критерии зависят от того, что я пытаюсь построить:

  • Отличная документация обязательна.
  • iOS, Node и Python SDK предпочтительнее.
  • iOS-уведомления.
  • Веб-хуки (поэтому мы можем использовать веб-серверы без сохранения состояния).
  • Чаты с более чем двумя участниками.
  • Необходимо иметь возможность расширять сообщения с помощью пользовательских метаданных.
  • Сквозное шифрование сообщений было бы идеальным.
  • Доказанная способность обрабатывать большое количество сеансов.

Я оценил восемь платформ и серьезно рассмотрел три варианта, прежде чем принять окончательное решение.

Также Rans

Эти платформы можно было использовать для создания приложения чата, но у всех были различные вопиющие проблемы.

  • Telegram — никаких SDK, только API-интерфейсы REST и куча кода iOS, который вы можете реорганизовать в соответствии со своими потребностями. Эй, ребята, я не хочу писать код HTTP-запроса вручную для трех платформ!
  • Intercom.io — их основной задачей является создание приложения для чата в колл-центре, а не создание собственного приложения. Насколько я могу судить, чаты всегда были один на один.
  • Пушер — Они делают гораздо больше, чем просто общаются, не сосредоточившись на этом. Нет поддержки уведомлений iOS.
  • Firebase — они делают гораздо больше, чем просто чат, вы можете создать все свое приложение и развернуть его на своей платформе. Нет родной семантики чата, вам нужно все реализовать самостоятельно.
  • Iron.io — это очередь сообщений. Нет iOS SDK. API является REST, а не на основе сокетов.

# 3: Quickblox

Quickblox ориентирован на чат и опыт разработчика (у них нет приложения для чата). Они были некоторое время (2013), и они основаны на XMPP, который является открытым стандартом. У них есть отличное справочное приложение для iOS.

Нет Python SDK и нет веб-хуков. Но убийца была трудностью нашей прототипной работы. Вызвать их REST API из Python было очень больно. Запрос на аутентификацию имеет метку времени, одноразовый номер и подпись HMAC, что является очень хрупким. Если порядок параметров не алфавитный или есть какие-то проблемы с настройкой HMAC, вы просто получаете общую ошибку подписи без информации об устранении неполадок. Затем у вас есть ряд необходимых пользовательских заголовков HTTP.

Все эти проблемы можно решить, написав собственный Python SDK. Но SDK для iOS и Node.js также было трудно заставить работать. Их библиотека Node, по общему признанию в бета-версии, была сломана. В целом, их документация была плохой — было трудно заставить вещи работать. Посмотрите, насколько подробен следующий код:

import random
import time
import hashlib
import hmac

import requests


APP_ID = 'XXX'
AUTH_KEY = 'XXX'
AUTH_SECRET = 'XXX'
USER_ID = 'XXX'
PASSWORD = 'XXX'
DIALOG_ID = 'XXX'


def create_session():
    nonce = str(random.randint(1, 10000))
    timestamp = str(int(time.time()))
    signature_raw_body = (
        'application_id=' + APP_ID +
        '&auth_key=' + AUTH_KEY +
        '&nonce=' + nonce +
        '&timestamp=' + timestamp +
        '&user[login]=admin' +
        '&user[password]=password')
    signature = hmac.new(AUTH_SECRET, signature_raw_body, hashlib.sha1).hexdigest()
    response = requests.post(
        'https://api.quickblox.com/session.json',
        headers={
            'Content-Type': 'application/json',
            'QuickBlox-REST-API-Version': '0.1.0',
        },
        json={
            'application_id': APP_ID,
            'auth_key': AUTH_KEY,
            'timestamp': timestamp,
            'nonce': nonce,
            'signature': signature,
            'user': {
                'login': 'admin',
                'password': 'password',
                }
        })
    json_data = response.json()
    return json_data['session']['token']


def get_messages(qb_token):
    return requests.get(
        'https://api.quickblox.com/chat/Message.json?chat_dialog_id=' + DIALOG_ID,
        headers={
            'QB-Token': qb_token,
        }
    )


if __name__ == '__main__':
    qb_token = create_session()
    messages = get_messages(qb_token)
    print messages, messages.content

№ 2: PubNub

PubNub не ориентирован исключительно на чат, но чат является первоклассной темой в их документации. Действительно отличные SDK практически для каждого языка, который вас волнует. Иногда существует несколько SDK для одного и того же языка, которые специализируются для конкретных сред — например, Django, Twisted и Tornado. Они имеют большой кругозор в сообществе разработчиков; Я постоянно слышу о других инженерах, использующих PubNub для хакатон-проектов. Они часто появляются в моем твиттере.

Прототипирование не могло быть проще. Посмотрите код ниже. Единственным недостатком является то, что они не имеют реальной поддержки веб-хуков. Их веб-хуки ограничены тем, когда разговоры начинаются и заканчиваются, а не включаются в каждое сообщение чата.

Почему веб-хуки имеют большое значение? Они, вероятно, не для многих случаев использования. Но одна вещь, которую мы хотим сделать, это иметь бота Python, который слушает все разговоры и выполняет различные действия в режиме реального времени. Насколько я могу судить, модель PubNub для этого должна использовать что-то вроде Twisted, который является асинхронным сетевым механизмом на основе событий в Python. Вы постоянно оставляете сокет открытым для PubNub и получаете уведомления о новых сообщениях по этому активному соединению.

Это действительно хорошо, поэтому они это делают. Но это значительно увеличивает сложность в отношении высокой доступности производства по сравнению с обычным веб-сервером. С веб-сервером, если вы потеряете узел (и вы будете думать, что экземпляр EC2 выйдет из строя), это не проблема. У вас просто есть несколько узлов позади балансировщика нагрузки. Поскольку все они не сохраняют состояние, восстановление выполняется автоматически, поскольку новые запросы продолжают поступать на остальные узлы.

С активным демоном сокета вы должны решить проблему высокой доступности самостоятельно. Если вы создаете архитектуру главного / горячего резервного копирования, вам необходимо понимать, что основной узел не работает, и дополнительный сервер должен установить соединение и возобновить любые разговоры в процессе . Это отдельный критический путь кода, который будет выполняться очень редко — рецепт ошибок. Если вы активны / активны, то у вас возникают все те же проблемы с возобновлением части соединений в случае сбоя узла, плюс вам нужно выяснить, что такое соединение.

Ни одна из них не является непреодолимой проблемой. Но они значительно усложняют ситуацию, чего я и пытаюсь избежать, используя существующую платформу. PubNub имеет супер простой SDK, но затем они заставляют вас решать кучу архитектурных сложностей самостоятельно.

Вот наш прототип кода. Обратите внимание на парадигму; Циклы событий. Этот код блокируется, пока вы не убьете процесс.

from pubnub import Pubnub


def callback(message, channel):
    print(message)


def error(message):
    print("ERROR : " + str(message))


def connect(message):
    print("CONNECTED")
    print pubnub.publish(channel='test', message='Hello from the PubNub Python SDK')


def reconnect(message):
    print("RECONNECTED")


def disconnect(message):
    print("DISCONNECTED")


pubnub = Pubnub(publish_key='XXX', subscribe_key='XXX', secret_key='XXX')

pubnub.subscribe(
    channels='dit',
    callback=callback,
    error=callback,
    connect=connect,
    reconnect=reconnect,
    disconnect=disconnect,
)

# 1: слой

Слой новый игрок. Они ориентированы исключительно на чат. У них есть хорошие iOS и Node SDK (но не Python). Их семантика REST API предназначена для чата. Беседы и сообщения являются понятиями первого класса. Их документация хороша, если не так исчерпывающа, как PubNub. Важно, что у них есть веб-хуки для каждого сообщения (в настоящее время в закрытой бета-версии)

Недостатком является то, что они не поддерживают сквозное шифрование per se. Конечно, вы можете вручную зашифровать свои тела сообщений. Будучи более новыми, они также не имеют послужного списка, который есть у PubNub.

Несмотря на то, что они не имеют Python SDK, наш прототип не мог бы быть более Pythonic. Это дает мне уверенность, что мы сможем написать чистый код и использовать простую архитектуру веб-сервера.

Наконец, есть существенный плюс в том, чтобы быть платформой для чата. SDK поддерживают такие понятия, как чтение / чтение, указание индикаторов и т. Д. Вы можете сделать это самостоятельно через PubNub, но вам придется писать больше кода. PubNub не имеет схемы — вы пишете всю схему плюс сериализацию самостоятельно, отдельно для каждого языка. Код враг!

import requests


APP_ID = 'XXX'
PLATFORM_API_TOKEN = 'XXX'
HOST = 'https://api.layer.com'
BASE_URL = HOST + '/apps/%s' % APP_ID
HEADERS = {
    'Accept': 'application/vnd.layer+json; version=1.0',
    'Authorization': 'Bearer %s' % PLATFORM_API_TOKEN,
    'Content-Type': 'application/json',
}


def _request(method, relative_url, data=None):
    data = data or {}
    callable_ = getattr(requests, method)
    return callable_(
        BASE_URL + relative_url,
        headers=HEADERS,
        json=data,
    )


def get_conversation():
    return _request('post', '/conversations', data={
        'participants': ['John', 'Jane'],
        'distinct': True,
    })


def get_messages(conversation_id):
    return _request('get', '/conversations/%s/messages' % conversation_id)


def post_message(conversation_id, text):
    return _request('post', '/conversations/%s/messages' % conversation_id, data={
        'sender': {
            'name': 'Chase',
        },
        'parts': [
            {
                'body': text,
                'mime_type': 'text/plain',
            }
        ]
    })


conversation = get_conversation()
conversation_id = conversation.json().get('id')
conversation_id = conversation_id.replace('layer:///conversations/', '')
print conversation_id

messages = get_messages(conversation_id)
print messages, messages.content

post_response = post_message(conversation_id, 'this is a test')
print post_response, post_response.content

Вывод

Я надеюсь, что это поможет вам, если вам нужно принять аналогичное решение. Порази меня в Твиттере и дай мне знать, как это происходит!

В настоящее время я работаю в NerdWallet , стартапе в Сан-Франциско, пытаясь внести ясность во все финансовые решения жизни. Мы нанимаем как сумасшедшие . Напишите мне в твиттере, я бы с удовольствием пообщался