Оригинальная статья о технологии конских сот, пожалуйста, найдите общедоступный номер для получения дополнительной галантереи: mfwtech
Функция обмена мгновенными сообщениями (IM) очень важна для платформ электронной коммерции, особенно электронной коммерции для путешествий.
С точки зрения сложности товара туристический товар может включать в себя одежду, еду, жилье, транспорт и т. д. пользователя в течение определенного периода времени в будущем, с точки зрения количества потребления, единовременное количество потребления часто бывает большим, непривычным для назначения, в Возможные проблемы на маршруте, эти факторы заставляют пользователей остро нуждаться в общении с продавцами до, во время и после покупки. Можно сказать, что простой в использовании IM может в определенной степени повысить GMV бизнеса электронной коммерции предприятия.
В этой статье мы объединим процесс разработки службы обмена мгновенными сообщениями для электронной коммерции Mafengwo и сосредоточимся на реконструкции системы обмена мгновенными сообщениями на основе Go, надеясь дать ссылку на друзей, у которых есть похожие проблемы.
Часть 1 Технические предпосылки и проблемы
В отличие от обмена мгновенными сообщениями в широком смысле каждое направление электронной коммерции имеет свою собственную бизнес-логику, такую как логика распределения гостей в системе чата обслуживания клиентов, логика обнаружения конфиденциальных слов и т. д., которые часто объединяются в процесс общения. По мере доступа ко все большему количеству бизнес-направлений избыточность служб обмена мгновенными сообщениями будет становиться все выше и выше. В то же время отслеживание всей ссылки на сообщение является сложной задачей, а на стабильность службы сильно влияет бизнес-логика.
Раньше отправка сообщений в нашем IM-приложении в основном основывалась на технологии опроса, длинный запрос на соединение модуля опроса сообщений был реализован в очереди блокировки через php-fpm. Когда объем запроса велик, если процесс php-fpm не может быть запущен вовремя, он потребляет много ресурсов сервера.
Чтобы решить эту проблему, мы использовали метод OpenResty + Lua для преобразования, используя метод сопрограммы Lua для переноса общей способности опроса из PHP в обработку Lua, сняв давление PHP. Хотя этот метод может несколько улучшить производительность, смешанный гетерогенный режим PHP-Lua делает систему очень сложной в использовании, обновлении, отладке и обслуживании, а ее универсальность также оставляет желать лучшего.Во многих бизнес-сценариях по-прежнему необходимо полагаться на Интерфейс PHP.Эффект оптимизации не очевиден.
Чтобы решить вышеуказанные проблемы, мы решили реконструировать службу обмена мгновенными сообщениями на основе специфического фона электронной коммерции IM.Ядро состоит в том, чтобы реализовать разделение бизнес-логики и службы обмена мгновенными сообщениями.
Часть 2 Двухуровневая распределенная архитектура обмена мгновенными сообщениями на основе Go
2.1 Достижение целей
1. Разделение бизнеса
Отделение бизнес-логики от коммуникационного процесса делает архитектуру службы обмена мгновенными сообщениями более понятной, реализует полное отделение от бизнес-логики обмена мгновенными сообщениями электронной коммерции и обеспечивает стабильность службы.
2. Гибкие методы доступа
Прежде чем получить доступ к новым службам, среду OpenResty и код сопрограммы Lua нужно было настроить на сервере службы, что было очень неудобно, а универсальность службы обмена мгновенными сообщениями также была низкой. Учитывая реальную ситуацию в существующем бизнесе, мы надеемся, что система обмена мгновенными сообщениями может обеспечить два метода доступа, HTTP и WebSocket, для гибкого использования бизнес-сторонами в соответствии с различными сценариями.
Например, система задач группы настройки электронной коммерции, которая была подключена и работала хорошо, индивидуальная система сбора заказов на поездки, система жалоб и другие связанные с ней системы и т. д., эти службы не имеют очевидного высокого уровня параллелизма. требования, и к ним можно быстро получить доступ через HTTP.Нет необходимости быть знакомым с несколько сложным протоколом WebSocket, что снижает ненужные затраты на исследования и разработки.
3. Стойка расширяемая
Чтобы справиться с проблемами, вызванными непрерывным ростом бизнеса для производительности системы, мы рассматриваем возможность использования распределенной архитектуры для разработки службы обмена мгновенными сообщениями, чтобы система могла постоянно расширяться и улучшаться.
2.2 Выбор языка
В настоящее время технологическая система Ma Honeycomb в основном включает PHP, Java и Golang, Стек технологий относительно богат, поэтому при выборе бизнеса можно выбрать более подходящие инструменты и языки в соответствии со сценарием проблемы.
В сочетании с конкретными сценариями приложений IM, причины, по которым мы выбираем Go, включают в себя:
1. Производительность
С точки зрения производительности, особенно для сценариев приложений с интенсивным вводом-выводом, таких как сетевое взаимодействие. Производительность системы Go ближе к производительности C/C++.
2. Эффективность разработки
Go прост в использовании, эффективен при написании кода и быстр в использовании, особенно для разработчиков с определенной базой C++, которые могут написать код за неделю.
2.3 Архитектурный дизайн
Общая схема архитектуры выглядит следующим образом:
Глоссарий:
Клиент: обычно относится к пользователю, который покупает продукт.
Продавец: поставщик, который предоставляет услугу, продавец будет иметь персонал службы поддержки клиентов, чтобы предоставить клиенту функцию онлайн-консультации.
Модуль распространения: Диспетчер, который обеспечивает связующую роль для распространения сообщений в указанные рабочие модули.
Рабочий модуль: то есть рабочий сервер, который используется для предоставления услуг WebSocket и является модулем, который действительно работает.
Слои архитектуры:
-
отображать слой: Предоставляет два метода доступа: HTTP и WebSocket.
-
Бизнес-уровень: отвечает за инициализацию строк сообщений и обработку бизнес-логики. Если клиент получает доступ через HTTP, сообщение будет отправлено на бизнес-сервер в формате JSON для декодирования сообщения, распределения обслуживания клиентов, фильтрации конфиденциальных слов, а затем отправлено в модуль рассылки сообщений для следующего шага преобразования; бизнес-доступ через WebSocket Нет необходимости в рассылке сообщений, и они напрямую отправляются в модуль обработки сообщений в виде WebSocket.
-
сервисный уровень: он состоит из двух уровней распространения и обработки сообщений и развертывает несколько узлов Dispatcher и Worker распределенным образом. Диспетчер отвечает за получение местоположения получателя на сервере, отправку сообщения соответствующему работнику с помощью RPC, а затем модуль обработки сообщений отправляет сообщение клиенту через WebSocket.
-
слой данных: Кластер Redis, который записывает уникальный ключ, состоящий из идентификатора пользователя, информации о подключении, клиентской платформы (мобильный терминал, веб-терминал, настольный терминал) и т. д.
2.4 Сервисный процесс
шаг первый
Как показано в правой части рисунка выше, пользовательский клиент устанавливает длинное соединение WebSocket с модулем обработки сообщений. Через алгоритм балансировки нагрузки клиент подключается к соответствующему серверу (воркеру модуля обработки сообщений). После успешного подключения запишите информацию о подключении пользователя, включая роль пользователя (гость или продавец), клиентскую платформу (мобильный терминал, веб-терминал, настольный терминал) и т. д., чтобы сформировать уникальный ключ и записать его в кластер Redis. .
Шаг 2
Как показано в левой части рисунка, когда пользователь, покупающий продукт, хочет отправить сообщение домработнице, он сначала отправляет сообщение на бизнес-сервер через HTTP-запрос, а бизнес-сервер обрабатывает сообщение с помощью бизнес-логики. .
(1) Сам этот шаг представляет собой HTTP-запрос, поэтому могут быть доступны клиенты с различными языками разработки. Отправьте сообщение на бизнес-сервер в формате JSON.Бизнес-сервер сначала расшифрует сообщение, а затем получит обслуживание клиентов продавца, которому пользователь хочет его отправить.
(2) Если покупатель ранее не общался в чате, в логике бизнес-сервера должен быть процесс назначения службы поддержки клиентов, то есть установление связи между покупателем и службой поддержки клиентов продавца. Получите идентификатор этой службы поддержки клиентов и используйте его для доставки деловых сообщений; если вы уже общались в чате, пропустите эту ссылку.
(3) На бизнес-сервере сообщение будет асинхронно поступать в базу данных. Гарантия того, что сообщение не потеряется.
Шаг 3
Бизнес-сервер отправляет сообщение в модуль рассылки сообщений по HTTP-запросу. Роль модуля распространения здесь заключается в передаче и, наконец, отправке сообщения с сервера назначенному продавцу.
Шаг 4
На основе информации о подключении пользователя в кластере Redis модуль рассылки сообщений пересылает сообщение на сервер WebSocket (рабочий процесс в модуле обработки сообщений), подключенный к целевому пользователю.
(1) Модуль распределения пересылает сообщение рабочему, подключенному целевым пользователем через RPC.Метод RPC имеет более высокую производительность и передает меньше данных, что снижает стоимость сервера.
(2) Когда сообщение прозрачно передается в Worker, различные стратегии гарантируют, что сообщение будет доставлено в Worker.
Шаг пять
Модуль обработки сообщений отправляет сообщение клиенту через протокол WebSocket:
(1) Во время доставки получатель должен иметь сообщение ACK (ответ) для обратной связи с рабочим сервером, сообщая рабочему серверу, что получатель отправленного сообщения получил его.
(2) Если получатель не отправит этот ACK, чтобы сообщить об этом рабочему серверу, рабочий сервер повторно отправит эту информацию получателю сообщения в течение определенного периода времени.
(3) Если доставленная информация была отправлена клиенту, и клиент ее получил, но информация ACK не была отправлена на сервер из-за нестабильности сети, сервер будет повторно доставлять ее клиенту.Идентификатор сообщения: отображаются вперед и назад.
Поток данных вышеперечисленных шагов примерно такой, как показано на рисунке:
2.5 Проект целостности системы
2.5.1 Надежность
(1) Сообщение не потеряно
Во избежание потери сообщений мы устанавливаем механизм повторной передачи по таймауту. Сервер будет ждать ACK от клиента после отправки сообщения клиенту.Если клиент не вернет ACK, сервер попытается отправить несколько раз.
В настоящее время тайм-аут по умолчанию составляет 18 с. Если повторная передача будет неудачной 3 раза, соединение будет разорвано, а сервер будет снова подключен. После повторного подключения для обеспечения целостности сообщения используется механизм извлечения исторических сообщений.
(2) Синхронизация сообщений с несколькими терминалами
У клиента есть браузер ПК, клиент Windows, H5, iOS/Android.Система позволяет нескольким пользователям быть в сети одновременно, и один и тот же конец может находиться в нескольких состояниях, что должно гарантировать, что мультитерминал, мульти- пользователя, и сообщения с несколькими состояниями синхронизируются.
Мы используем хеш-хранилище Redis для записи информации о пользователе, уникального соответствующего значения соединения, идентификатора соединения, IP-адреса клиента, идентификатора сервера, роли, канала и т. д., чтобы можно было найти соединения пользователя на нескольких концах с помощью ключа (uid), соединения. можно найти через ключ+поле.
2.5.2 Доступность
Как мы уже говорили выше, поскольку это двухуровневый дизайн, он включает связь между двумя серверами, используя канал для внутрипроцессной связи и используя очередь сообщений или RPC для непроцессных. Комплексная производительность и использование ресурсов сервера, мы, наконец, выбираем метод RPC для связи между серверами. При выборе линейки RPC на базе Go мы сравнивали следующие основные технические решения:
-
Go STDRPC: RPC для стандартной библиотеки Go с оптимальной производительностью, но без управления
-
RPCX: преимущество в производительности 2*GRPC + управление услугами
-
GRPC: кросс-язычный, но не такой производительный, как RPCX.
-
TarsGo: кросс-языковая, производительность 5*GRPC, недостаток в том, что фреймворк большой и его сложно интегрировать
-
Dubbo-Go: немного уступает по производительности, больше подходит для сценариев общения между Go и Java
В конце концов, мы выбрали RPCX, потому что производительность тоже очень хорошая, а также управление сервисом.
Связь также требуется между двумя процессами.Здесь ETCD реализует механизм обнаружения регистрации службы.
Когда мы добавляем Worker, если нет реестра, нам нужно использовать файлы конфигурации для управления этой информацией о конфигурации, что очень проблематично. И после того как вы добавите новый, модуль дистрибутива нужно обнаружить сразу без промедления.
Если есть новая услуга, модуль распространения надеется быстро воспринять новую услугу. При использовании механизма продления аренды ключа, если действие продления аренды ключа не отслеживается в течение определенного периода времени, считается, что услуга была приостановлена, и услуга будет удалена.
При выборе регистрационного центра мы в основном исследовали ETCD, ZK, Consul, и результаты опрессовки трех из них следующие:
Результаты показывают, что производительность ETCD является лучшей. Кроме того, ETCD поддерживается Alibaba и принадлежит к экосистеме Go.Также используется внутренний кластер нашей компании K8S.
После всестороннего рассмотрения мы решили использовать ETCD в качестве компонента регистрации и обнаружения службы. И мы используем режим кластера ETCD.Если один сервер выходит из строя, другие серверы в кластере могут по-прежнему нормально предоставлять услуги.
За счет обеспечения нормальной связи между службами и процессами, а также проектирования режима кластера ETCD гарантируется общая высокая доступность службы обмена мгновенными сообщениями.
2.5.3 Расширяемость
Как модуль распределения сообщений, так и модуль обработки сообщений могут быть расширены горизонтально. Когда общая нагрузка службы высока, нагрузку можно разделить, добавив узлы, чтобы обеспечить оперативность сообщений и стабильность службы.
2.5.4 Безопасность
Из соображений безопасности мы создали механизм черного списка для ограничения одного uid или ip. Например, в рамках одного и того же uid, если количество подключений, установленных за период времени, превышает установленный порог, считается, что uid может быть под угрозой, и услуга приостанавливается. Если uid продолжает отправлять запросы во время приостановки услуги, время ограничения услуги будет соответственно увеличено.
2.6 Оптимизация производительности и наступившие ямы
2.6.1 Оптимизация производительности
(1) Кодирование и декодирование JSON
Сначала мы использовали официальный инструмент кодирования и декодирования JSON, но из-за стремления к производительности мы переключились на использование Json-итератора Didi с открытым исходным кодом, который совместим с собственными инструментами кодирования и декодирования JSON Golang и значительно повысил эффективность. Ниже приведена справочная диаграмма для сравнения измерений давления:
(2) time.After
Во время стресс-теста мы обнаружили, что использование памяти было очень высоким, поэтому мы использовали Go Tool PProf для анализа использования памяти функциями Golang и обнаружили проблему постоянного создания таймеров time.After, которые находились в сопрограмма сердцебиения.
Исходный код выглядит следующим образом:
Оптимизированный код:
Точка оптимизации заключается в том, чтобы не использовать комбинацию select + time.After в цикле for.
(3) Использование карты
Карта используется при сохранении информации о соединении. Потому что когда я работал над проектом TCP Socket, я столкнулся с ямой, то есть Map не безопасен под сопрограммой. Когда несколько сопрограмм читают и записывают карту одновременно, будет выдана фатальная ошибка: Ошибка плода: одновременное чтение карты и запись карты, после этого опыта мы используем sync.Map здесь
2.6.2 Опыт хождения по ямам
(1) Исключение сопрограммы
Основываясь на стоимости разработки и стабильности сервиса, наш сервис WebSocket разработан на основе инфраструктуры Gorilla/WebSocket. Одна из проблем заключается в том, что при аварийном завершении сопрограммы чтения сопрограмма записи этого не воспринимает, в результате сопрограмма чтения завершилась, но сопрограмма записи продолжает работать и не завершается до тех пор, пока не сработает исключение. Хотя это не влияет на бизнес-логику на поверхности, это пустая трата внутренних ресурсов. При кодировании вы должны обратить внимание на активное уведомление сопрограммы записи после выхода из сопрограммы чтения.Такая небольшая оптимизация может сэкономить много ресурсов при высоком параллелизме.
(2) Дизайн сердцебиения
Например, ранее мы пошли по пути разработки функции пульсации в режиме ожидания. Первоначально контрольный сигнал, отправляемый на стороне сервера, был синхронизированным контрольным сигналом, но когда он использовался в реальных бизнес-сценариях, было обнаружено, что лучше планировать контрольный сигнал, когда сервер простаивает. Поскольку все пользователи общаются в чате, отправка кадра сердцебиения — пустая трата эмоций и ресурсов полосы пропускания.
В настоящее время рекомендуется пока не писать код, если вы не можете написать код в процессе развития бизнеса.Во-первых, объедините бизнес-требования и используйте слова, чтобы разобраться в логике, и вы может обнаружить, что это будет более гладко, чтобы продолжить позже.
(3) Делите журнал каждый день
Исходя из соображений производительности, модуль журнала решил использовать библиотеку ZAP с открытым исходным кодом Uber во время первоначального расследования и соответствовать требованиям ведения бизнес-журнала. Выбор библиотеки журналов очень важен, и неправильный выбор также повлияет на производительность и стабильность системы. К преимуществам ЗАП относятся:
-
Требование отображать номер строки кода поддерживается ZAP, но не Логрусом, это улучшение производительности. Представление номера строки важно для задач позиционирования.
-
По сравнению с Логрусом, ZAP более эффективен: он не использует рефлексию при записи логов в формате JSON, а использует встроенный кодировщик json для прямой конкатенации строк через явные вызовы типов для минимизации накладных расходов на производительность.
Малая яма:
Функция записи лог-файла каждый день в настоящее время не поддерживается ZAP, для ее поддержки необходимо написать собственный код или обратиться за поддержкой в системный отдел.
Часть 3 Производительность
Стресс-тест 1:
Запустили производственную среду и соединили с бизнес стороной и проверили давление.В настоящее время кастомизированный бизнес подключен ко всему процессу и написан Клиент. Периодически имитируйте отправку фреймов пульса, а затем используйте среду Docker. Было открыто 50 контейнеров, и каждый контейнер имитировал и инициировал 20 000 подключений. Таким образом, к одному серверу выполняются миллионы подключений. Память одной машины составляет около 30G.
Стресс-тест 2:
При этом одновременно подключается 3000, 4000, 5000 подключений, и регулируется частота отправки, соответствующая аплинку: 600 000, 800 000, 1 000 000, 2 миллиона, структура лога около 6к.
Половина из них — пакеты пульса, а другая половина — структуры журнала. Данные о задержке нисходящей линии связи при различных давлениях следующие:
**Вывод.**По мере того, как параллелизм восходящего потока становится больше, задержка контролируется в пределах 24–66 мс. Следовательно, это небольшая задержка для нисходящих услуг. Кроме того, для 600 000 восходящих каналов по 5 000 используется другой скрипт для имитации одновременного открытия 50 сопрограмм и тел данных по 1 000. Задержка улучшена по сравнению с отсутствием параллельного нисходящего канала и увеличена примерно на 40 мс.
Часть 4 Резюме
На основе WebSocket служба обмена мгновенными сообщениями, реорганизованная на основе Go, проектирует бизнес-уровень как двухуровневую архитектурную модель, оснащенную модулем распределения сообщений и модулем обработки сообщений, так что обработка бизнес-логики предварительно обрабатывается для обеспечения чистота и эффективность служб обмена мгновенными сообщениями Стабильность, в то же время служба HTTP модуля рассылки сообщений облегчает быстрое подключение нескольких языков программирования, так что каждое направление бизнеса может быстро получить доступ к службе мгновенных сообщений.
Наконец, я хочу поблагодарить Go. Многие знают, что технологическая система с конскими сотами в основном основана на PHP, и некоторые основные направления деятельности также переходят на Java. В то же время Go также играет роль во все большем количестве проектов. Сейчас облачная концепция постепенно становится одной из основных тенденций.Мы видим, что Go является основным языком разработки во многих основных проектах, необходимых для создания облачных приложений, таких как Kubernetes, Docker, Istio, ETCD, Prometheus и т. д. Включая распределенную базу данных с открытым исходным кодом третьего поколения TiDB.
Таким образом, мы можем назвать Go родным языком эпохи облачных технологий. «Эра облачных технологий — лучшая эра для разработчиков.» В рамках этой волны, чем раньше мы войдем в Go, тем раньше мы сможем захватить ключевые направления в этой новой эре. Мы надеемся, что больше друзей присоединится к нам в лагере разработчиков и изучения Go, расширит свою карту навыков и примет облачные решения.
Автор статьи: Анти Уокер, инженер по исследованиям и разработкам базовой платформы электронной коммерции Mafengwo Travel Network.