Эволюция архитектуры торговой системы (2): Версия 2.0

Архитектура

Обзор версии 1.0

Давайте рассмотримВерсия 1.0После анализа требований окончательная версия 1.0 — это всего лишьMVP — минимально жизнеспособный продукт, который завершает только самый упрощенный основной процесс, а именно:注册 ——> 登录 ——> 入金 ——> 交易 ——> 出金. С точки зрения проектирования архитектуры, от проектирования API до проектирования ключевых процессов, проектирования баз данных и, наконец, проектирования сервера, в основном принимается во внимание схема проектирования с наименьшими затратами для снижения затрат на разработку.

В целом общий дизайн версии MVP разделен на переднюю и заднюю часть. Внедрение унифицированного APIПротокол связи HTTP + протокол передачи JSON, и добавитьTLSШифровать передаваемые данные. Аутентификация пользователя использует схему JWT, которая может быть без сохранения состояния. Всего определено 19 интерфейсов API, которые разделены наПользователи, Аккаунты, Транзакции, Котировки4 модуля. Запросы на чтение класса запроса используют метод GET единообразно, а запросы класса, не относящиеся к запросу, используют метод POST. В процессе реализации нет ни кеша, ни промежуточного программного обеспечения, такого как MQ, для базы данных используется только MySQL, а для сопоставления транзакций также используется простая схема сопоставления с базой данных. Подключены две сторонние платформы, одна из которых — это пуш-платформа почтовых ящиков Alibaba Cloud, которая используется для отправки электронных писем, а другая используется для подключения к блокчейну Ethereum.infuraПлатформа. С точки зрения дизайна базы данных также предлагаются некоторые спецификации и принципы проектирования.Согласно идее дизайна DDD, окончательно разработаны 9 таблиц. Сервер представляет собой единое приложение, внутри которого используется простая трехуровневая архитектура.Уровень API, уровень обслуживания, уровень DAO.

Общая схема архитектуры выглядит примерно так:

В этой версии 1.0 профессиональные люди должны видеть, что есть еще несколько важных проблем, которые необходимо решить:

  • Отсутствуют некоторые бизнес-функции, в том числе извлечение пароля, изменение пароля, выход из системы, аутентификация KYC (аутентификация по реальному имени) и фон управления.

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

  • Интерфейс опрашивает рыночные данные и считывает их из базы данных, которая имеет высокую задержку, низкую эффективность и низкую производительность.

  • Производительность сопоставления с базой данных слишком низкая.

Далее мы изучим проектные решения для решения этих задач.

Итерация по бизнес-требованиям

Для отсутствующих бизнес-функций, извлеките пароли, измените пароли, выходные логины, это просто добавляют интерфейсы API и реализуют бизнес-логику, очень просто. Сертификация KYC требует загрузки документов, вы можете получить доступ к сторонней службе хранения файлов. Управление в настоящее время более важные особенности фона должны включать: управление пользователями, обзор KYC, восстановление, управление заказом, управление транзакциями и т. Д.

Бизнес-функции, которые необходимо улучшить, и поддержка большего количества пар транзакций в основном связаны с несколькими основными API-интерфейсами блокчейна.В настоящее время основные блокчейны в основном имеют относительно зрелые сторонние API-интерфейсы, и доступ к ним относительно прост. Также просто добавить больше периодов данных графика K-line.Достаточно накопить данные и записать их в соответствии с формулой расчета разных периодов.

Ниже приведены некоторые другие более важные моменты дизайна, которые необходимо дополнить.

Сначала дополните дизайн, связанный с паролем, от пользователя, вводящего пароль на клиенте, до передачи по сети, до хранения данных на стороне сервера, во всем процессе, чтобы обеспечить безопасность пароля, что должно быть лучшее практическое решение? Вот несколько ключевых моментов, которые я придумал:

  1. TLS — это основа;

  2. Затем пароль шифруется отдельно, а алгоритм шифрования должен быть асимметричным, напримерЮАР, ЕСК;

  3. Если пароль пользователя неверный при входе в систему, сообщение об ошибке не должно напрямую указывать «ошибка пароля», а должно давать только общее приглашение, например «ошибка имени пользователя или пароля»;

  4. Если количество неправильных паролей превышает N раз подряд, например, 6 раз, пользователь будет заблокирован на определенный период времени;

  5. для базы данныхМедленный хэш + сольРазные пользователи используют разные значения Salt для хранения.Основные алгоритмы медленного хеширования:Аргон2, Скрипт, Bcrypt, PBKDF2;

  6. Добавьте несколько проверок, таких как обнаружение устройства входа в систему, распознавание отпечатков пальцев, распознавание лиц, код подтверждения мобильного телефона и т. д.

Многие думают, что если TLS уже есть, то пароль нельзя зашифровать отдельно, но на самом деле это неправильно. Что TLS может гарантировать, так это только зашифрованный текст, видимый сторонним захватом пакетов в процессе передачи, но он не может предотвратить хакеров, которые перехватывают данные на клиенте и сервере.Перехватить данные на сервере сложно, но легче перехват на клиенте. Самый простой способ — посетить Zhihu, JD.com и другие известные веб-сайты в своем браузере и получить запрос с помощью инструмента захвата пакетов. Вы обнаружите, что хотя это запрос HTTPS, данные, которые вы видите, не являются зашифрованным текстом, а простой текст. Существует также множество атак на HTTPS, таких как атаки с понижением версии и атаки «человек посередине». Таким образом, просто использовать HTTPS для защиты недостаточно.

Почему для шифрования паролей рекомендуется асимметричное шифрование, а не одностороннее хеширование или симметричное шифрование? Если используется односторонний хеш, такой как MD5/SHA, для сервера фактическим паролем является хэшированное значение, а не исходный пароль пользователя, и обновлять алгоритм шифрования в будущем очень проблематично; для хакеров , Нет необходимости взламывать исходный пароль пользователя, просто используйте хешированный пароль, чтобы запросить сервер для прохождения проверки. Если используется симметричное шифрование, такое как AES/DES, клиенту необходимо безопасно хранить ключ шифрования, что очень сложно. При асимметричном шифровании не имеет значения, хранится ли открытый ключ на стороне клиента, даже если произошла утечка.

С точки зрения хранения базы данных в прошлом основное внимание уделялось тому, как предотвратить утечку данных, но теперь больше внимания уделяется тому, как предотвратить утечку данных.Запретить восстановление данных. То есть мы должны это сделать, даже если все данные и коды будут украдены, все равно сложно взломать исходный пароль. Для достижения этой цели основная идея состоит в том, чтобы увеличить стоимость взлома, чтобы стоимость взлома была намного больше, чем выгода, чтобы во взломе не было смысла. Схема медленного хеширования + Slat может достичь этой цели, поскольку значение Salt у каждого пользователя разное, его нельзя использовать.радужный столВыполняйте пакетный взлом; при медленном хешировании временные затраты на взлом методом грубой силы увеличиваются в геометрической прогрессии.

Добавление нескольких проверок поднимает безопасность на новый уровень.Теперь WeChat, Alipay и многие финансовые приложения обычно используют только 6-значные платежные пароли.Причиной безопасности также является механизм многократной проверки. Предполагая, что вероятность взлома каждого уровня проверки отдельно составляет 30 %, после добавления трех уровней проверки вероятность взлома становится равной:30% * 30% * 30% = 2.7%, безопасность значительно улучшена.

Вернемся к нашей торговой системе, в этой итеративной версии эти функции должны быть добавлены, и стоимость разработки не будет очень высокой. Что касается многократной проверки, то во время входа в систему и оплаты можно добавить функцию вторичной проверки мобильного телефона.Это решение является самым простым в реализации и в определенной степени повышает безопасность.

После разговора о дизайне защиты паролем я объясню дизайн фона управления.

Управление и клиент имеет совершенно разные функции, многие бизнес-логики не одинаковы, по сравнению с конечным пользователем пользовательского клиента пользователя по-прежнему имеет больше привилегий данных, поэтому управление и клиенты обычно разделены на разные услуги, но это тот же набор базовой базы данных. После управления предпосылкой в ​​сочетании с общей структурой всей торговой системы заключается в следующем:

Оптимизация рыночных вопросов

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

Клиент получает рыночные данные

Клиент получает рыночные данные, что по сути является проблемой мгновенной связи между сетью и сервером.На самом деле существует четыре способа реализовать мгновенную связь в сети:Опрос, Долгий опрос, Длинное соединение, WebSocket.

Опрос,Также известен каккороткий опрос, но клиент периодически отправляет запросы на сервер, а сервер возвращает данные ответа, поэтому полученные данные не в реальном времени, и есть задержка. Кроме того, поскольку сервер постоянно запрашивается, новые данные во многих случаях не обновляются, поэтому большинство запросов являются недействительными, что является серьезной тратой ресурсов сервера. Преимущество опроса в том, что он прост, понятен и прост в реализации, поэтому мы решили использовать опрос в первой версии.

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

Длинное соединение (SSE)Существенно отличаясь от коротких и длинных опросов, он позволяет серверу передавать данные клиенту. Процесс выглядит следующим образом: клиент отправляет запрос, сервер блокируется после получения запроса и сохраняет соединение, когда у сервера есть данные для ответа, он отвечает сохраненным соединением и сохраняет соединение. Постоянное соединение подходит для сценария одностороннего проталкивания от сервера к клиенту. Однако недостатком длительного подключения является то, что оно применимо только к продвинутым браузерам, IE не входит.

WebSocketЭто совершенно другое, за исключением того, что протокол HTTP используется при первоначальной установке соединения, в других случаях связь напрямую основана на протоколе TCP, который может реализовать полнодуплексную связь между клиентом и сервером с высокой производительностью и низкой скоростью. накладные расходы.Лучший выбор для связи. Причем не только на веб-странице, но и на стороне приложения. Таким образом, схема оптимизации нашего клиента для получения рыночных данных также является наиболее подходящей для использования WebSocket.

Однако приложение WebSocket намного сложнее реализовать, чем HTTP, и дизайн также отличается от HTTP API.Если дизайн не хорош, ресурсы все равно будут потрачены впустую, поэтому необходимо объяснить.

Во-первых, после того, как клиент устанавливает соединение с сервером, сервер обычно уведомляется о том, какие данные ему нужны, путем отправки сообщения о подписке, такого как:

{ "subscribe": "market.ethusdt.kline.1min" }

Это сообщение о подписке на 1-минутные данные K-линии торговой пары ETH/USDT.После получения сервером подписки, пока обновляются 1-минутные данные K-линии торговой пары, обновленные данные будут постоянно передаваться клиенту, пока пользователь не отменит подписку или не отключится. Отписаться тоже просто, клиент отправляет еще одно сообщение вот такого вида:

{ "unsubscribe": "market.ethusdt.kline.1min" }

Однако сообщения подписки используются только для получения данных для последующих обновлений, то есть добавочных данных. Однако иногда клиенту необходимо получить полный объем данных, особенно во время инициализации, в это время может быть отправлено одно сообщение-запрос, аналогичное HTTP-запросу, например, мы хотим получить 1-минутную свечу график торговой пары ETH/USDT Исходные полные данные, можем отправить на сервер такое сообщение:

{ "request": "market.ethusdt.kline.1min" }

После того, как сервер получает эту информацию, он возвращает весь объем данных за один раз.

В идеале соединение между клиентом и сервером будет сохраняться до тех пор, пока обе стороны не отключатся активно. Но реальная ситуация приведет к тому, что один конец будет отключен ненормально по разным причинам, в то время как другой конец не знает. В большинстве случаев клиент аварийно отключается, но сервер об этом не знает и будет продолжать передавать данные клиенту, а данные будут потеряны. Чтобы справиться с этой ситуацией, необходим механизм для определения того, соединены ли два конца, и этот механизмОбнаружение сердцебиения. Обнаружение пульса на самом деле заключается в том, что один конец отправляет пакеты данных (также называемые пакетами пульса) на другой конец через равные промежутки времени (например, каждые 5 секунд), а другой конец отправляет обратно пакет данных сразу после получения пакета данных, чтобы чтобы определить, является ли соединение между двумя концами нормальным. Реализация пакета сердцебиения также проста, инициатору нужно только инициироватьpingсообщение и еще один ответ на другом концеpongсообщение, подобное этому:

{ "ping": 1606972817326 }
{ "pong": 1606972817326 }

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

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

Чтение рыночных данных базы данных

Каковы основные проблемы при чтении рыночных данных непосредственно из текущей базы данных MySQL?

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

Чтобы решить проблему узкого места производительности чтения базы данных, первое решение, о котором думает большинство людей, эторазделение чтения-записи. Разделение чтения-записи фактически делит базу данных наосновная библиотекаа такжеиз библиотеки, запрос на чтение считывается из ведомой библиотеки, главная библиотека обрабатывает запрос на запись, и после записи данных они копируются в ведомую библиотеку. Таким образом, нагрузка большого количества операций чтения переносится на подчиненную библиотеку.Если одна подчиненная библиотека не может поддерживать большое количество запросов на чтение, можно развернуть несколько подчиненных библиотек для достижения балансировки нагрузки. Как правило, MyCat используется для разделения чтения и записи.

Однако, если используется разделение чтения-записи, также возникнет проблема непротиворечивости данных ведущий-ведомый. Главная библиотека и подчиненная библиотека должны обеспечить согласованность данных, чтобы подчиненная библиотека могла считывать правильные данные, поэтому существует механизм синхронизации данных (репликации) между главной и подчиненной библиотеками. Хотя существует три механизма репликации master-slave:Асинхронная репликация, полная синхронная репликация, полусинхронная репликация. Но независимо от того, какая схема репликации используется, поскольку база данных стала кластеризованной, трудно добиться одновременно высокой производительности и согласованности. Асинхронная репликация может поддерживать высокую производительность, но не может гарантировать согласованность данных. Полная синхронная репликация гарантирует согласованность, но сильно снижает производительность. Полусинхронная репликация — это компромисс, баланс между ними.

На самом деле, для решения нашей проблемы использование разделения чтения-записи — не единственное и не оптимальное решение. Помимо разделения чтения-записи, вы также можете использовать кэширование Redis, а также можете использовать MongoDB. Кэш Redis следует предпочесть, потому что его производительность чтения и записи самая высокая.

Затем, выбрав Redis, нужно подумать, какая структура данных больше подходит для хранения различных рыночных данных. Наши рыночные данные включают данные о глубине, записи транзакций, данные K-линии и данные тикера.

Данные о глубине в основном включают цену и количество ордеров на покупку и продажу, которые необходимо отсортировать по цене и которые будут часто меняться, поэтому их лучше использовать.sorted setспасти. Отдельные ордера на покупку и продажуkeyспасти,scoreустановить цену,valueустанавливается в два кортежа из цены и количества. Кроме того, особенность данных о глубине заключается в том, что каждая цена может иметь только один фрагмент данных, поэтому не может быть нескольких записей одного и того же показателя, поэтому, когда вам нужно обновить количество сделок по определенной цене, вам нужно сначала удалите запись оценки, соответствующую цене, а затем снова вставьте.

Записи транзакций со временем увеличиваются, данные тикера также будут обновляться в соответствии с последними записями транзакций, а данные K-линии также рассчитываются на основе совокупных записей транзакций. Тогда записи транзакций больше подходят дляОчередь сообщений (MQ)Чтобы сэкономить, потоки обработки Ticker и K-line могут отслеживать очередь, получать последние транзакции и обновлять свои соответствующие данные. Версия Redis 5.0+streamТип идеально подходит для этого сценария.

Данные K-линии включают в себя цену открытия, цену закрытия, самую высокую цену, самую низкую цену, объем торгов и т. д. Записи также увеличиваются со временем, но в самый последний период времени, то есть последний запись должна быть частой. Измените, более подходящая структура храненияlist.

Данные тикера в основном отображают цену открытия, самую высокую цену, самую низкую цену, последнюю цену, объем сделки и т. д. Для этого не требуется несколько записей, нужна только одна запись, и некоторые данные будут обновляться в соответствии с к результатам транзакционных записей.hashНаиболее подходящий тип для хранения.

Слияние для решения рыночных проблем

Затем необходимо объединить проектные решения двух независимых задач, которые могут быть соединены последовательно для формирования общего решения.

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

  1. Отправьте последнюю запись транзакции подписанному клиенту через WebSocket;

  2. Обновите последние данные K-линии и кэшируйте их, а затем отправьте последнюю запись K-линии клиентам, которые подписываются на соответствующие данные K-линии через WebSocket;

  3. Обновите данные тикера и кэшируйте их, а затем отправьте последнюю информацию тикера клиенту, подписавшемуся на данные тикера, через WebSocket;

  4. Обновите данные о глубине за вычетом объема, который был продан.

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

Кроме того, данные глубины отправки WebSocket не запускаются событием, а отправляются регулярно, обычно каждую 1 секунду.

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

Обновление технологии сопоставления

Производительность сопоставления с базой данных обычно составляет около 100 TPS, а производительность очень низкая.Основная причина в том, что слишком много взаимодействия с базой данных, также много операций ввода-вывода, и это также ограничено логикой транзакций базы данных. . Система сопоставления баз данных, если вы хотите повысить производительность, вы можете полагаться только на обновление оборудования, которое требует очень высокой аппаратной конфигурации базы данных, а стоимость также очень высока. Более того, из-за правил сопоставления требуется, чтобы сопоставление могло выполняться только последовательно, а не параллельно, поэтому невозможно улучшить общую производительность за счет расширения сервера по горизонтали.

Логика сопоставления основана на "Приоритет цены, приоритет времени” для сопоставления транзакций, и ордера, которые не соответствуют транзакции немедленно, будут помещены вКнига заказов. Книга ордеров по сути представляет собой две очереди на покупку и продажу, обе очереди отсортированы по принципам приоритета цены и приоритета времени. Так называемый приоритет цены и приоритет времени означает, что заявки в очереди на продажу сортируются по цене от меньшей к высокой, а очередь заявок на покупку, наоборот, сортируется по цене от большей к меньшей; заявки с одинаковой ценой заказываются по времени заказа порядок в порядке.

img

Как показано на рисунке выше, каждый маленький квадрат представляет заказ, отмеченный значкомHпорядок наверху,NЭто ордер с той же ценой, что и H, но размещенный после H во время ордера.SЭто первый ордер по цене следующего транша. Из рисунка хорошо видно, что в горизонтальном направлении ордера отсортированы по времени, а в вертикальном – по цене.

При сопоставлении выньте его первымHЗаказ соответствует новому заказу. Если новый заказплатить, затем получитьОчередь ордеров на продажуПорядок H соответствует; если новый порядокЗаказы на продажу, затем получитьочередь на покупкуОрден Х. еслиHЕсли все заказы подобраны и выполнены, отметкаNзаказ становится новымHОдин. Если все заказы в первой строке совпадают, тоSсингл станет новымHОдин.

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

Поэтому сейчас индустрия в основном больше не использует сопоставление баз данных, а вместо этого используетСоответствие памяти. По производительности сопоставления памяти самый обычный сервер может легко достичь 10 000 TPS, а с высокой конфигурацией памяти достичь даже 100 000 TPS несложно.

Есть две ключевые причины, по которым сопоставление памяти может привести к такому значительному повышению производительности:

  1. Весь Orderbook кэшируется в памяти, и данные напрямую обрабатываются в памяти, что очень быстро.

  2. Книгу заказов легче сохранить в памяти со структурой данных, показанной на рисунке выше.Каждый раз, когда происходит совпадение, можно быстро получить главный заказ без необходимости полного запроса таблицы.

С точки зрения конкретного плана реализации, на самом деле есть два пути: один — использовать Redis для сохранения Книги заказов, другой — напрямую хранить Книгу заказов с объектом языка программирования. На самом деле сопоставление памяти в промышленности реализуется по второй схеме, но некоторые люди понимают сопоставление памяти как сопоставление базы данных в памяти, поэтому производится первая схема.

Однако недостатком сопоставления памяти является непостоянство памяти: когда сервер выходит из строя и останавливается, все данные транзакций будут потеряны, и, соответственно, надежность и согласованность системы будут значительно снижены. Для решения этой проблемы существуют сложные и простые решения. Комплексные программы в основном используются дляГорячее резервирование нескольких машинтехнологии для обеспечения надежности, повторного использованиякопировать конечный автоматтехнология, обеспечивающая согласованность. Простое решение — перезапустить сервер напрямую, запросить заказ из базы данных и перезагрузить его в память во время инициализации, что просто и грубо. В настоящее время нам удобнее сначала использовать простые решения, поскольку стоимость внедрения сложных решений слишком высока.Соотношение вход-выходневысокий.

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

Суммировать

На данный момент несколько важных проблем, оставшихся от версии 1.0, были решены одна за другой, после решения этих проблем можно сказать, что наша версия обновлена ​​до версии 2.0. Улучшайте бизнес-функции, оптимизируйте рыночные услуги и обновляйте технологию сопоставления. Вся система также делится наМаркетинговое обслуживание, клиентское обслуживание, сопоставление услуг, управление услугами, но на самом деле это не микросервисная архитектура, а просто четыре мономера. В следующей статье мы проанализируем и посмотрим, как должен развиваться следующий этап.

Прошлые статьи:

Эволюция архитектуры торговой системы (1): Версия 1.0