Эту статью опубликовали специалисты по инфраструктурному хранению данных Xiaohongshu Конг Конг и Лю Бэй. Первоначальное название: «Как Xiaohongshu справляется с проблемами взаимоотношений в социальных сетях на уровне триллиона? Система хранения графов REDtao уже здесь!» измененный.
Xiaohongshu — это продукт, ориентированный на атрибуты сообщества. Он охватывает жизненные сообщества в различных областях и сохраняет обширные связи в социальных сетях.
Чтобы решить проблему обновления и связанного с этим чтения сверхкрупномасштабных данных в социальных сценариях, а также снизить нагрузку на базу данных и затраты, мы разработали собственную систему хранения графов REDtao для сверхкрупномасштабных социальных сетей, которая значительно повышает стабильность системы. Эта система основана на конструкции системы хранения графов Facebook, инкапсулирует кеш и базовую базу данных, а также предоставляет унифицированный API запросов к графам для внешнего мира, обеспечивая конвергенцию доступа и эффективное агрегирование границ в кеше.
В этой статье мы поделимся с вами процессом архитектурного проектирования и технической практики REDtao, системы хранения графов Xiaohongshu для сверхбольших социальных сетей. Надеюсь, она сможет вас вдохновить.
Пустой:Группа хранения инфраструктуры Xiaohongshu,Отвечает за систему хранения изображений REDtao и разработку распределенного кэша.
Лю Бэй:Группа хранения инфраструктуры XiaohongshuОтветственныйлюди,Ответственный за REDkv / REDtao / REDtable / REDgraph общая архитектура и технологическая эволюция.
Группа инфраструктуры хранения данных предоставляет бизнес-подразделениям Xiaohongshu стабильные и надежные услуги хранения и баз данных для удовлетворения бизнес-требований к функциональности, производительности, стоимости и стабильности продуктов хранения. В настоящее время отвечает за собственную разработку распределенного KV, распределенного кэша, системы хранения графов, базы данных графов и хранилища таблиц.
Продукты онлайн-хранилища включают в себя:
Xiaohongshu — это платформа для записи и обмена информацией, предназначенная главным образом для молодых людей. Пользователи могут записывать свою повседневную жизнь и делиться своим образом жизни с помощью коротких видеороликов, изображений и текстов.
В социальном поле Xiaohongshu есть такие сущности, как пользователи, заметки и продукты, и между этими сущностями существуют различные отношения.
Например:Между пользователем и заметкой может быть разрывсуществовать“иметь”(выпускать)、"Нравиться"、“собирать”Три вида отношений,В то же время существует и существование, соответствующее обратной зависимости «нравился».,«Собрал» и т. д.
Данные социальных графов Xiaohongshu достигли масштаба в один триллион ребер и растут очень быстро. Когда пользователи входят в Xiaohongshu, каждый пользователь видит друзей, поклонников, лайки, коллекции и другой контент, специально предназначенный для него.
Эта информация в высшей степени персонализирована и требует считывания информации, связанной с пользователем, из этих огромных данных о социальных отношениях в режиме реального времени. Это процесс, ориентированный на чтение, и давление чтения очень велико.
Раньше мы хранили эти данные социальных графов в базе данных MySQL, которая хорошо функционировала и обслуживалась.
Однако даже если мы масштабируемся только до миллиона запросов в секунду, загрузка ЦП MySQL все равно достигнет 55%. В связи с взрывным ростом числа пользователей и количества ежедневных активных пользователей базу данных MySQL необходимо постоянно расширять, что приводит к огромным затратам и снижению стабильности.
Чтобы решить эти проблемы и учитывая, что подходящего решения с открытым исходным кодом нет, в начале 2021 года мы начали процесс самостоятельной разработки REDtao от 0 до 1.
Мы полностью изучили внедрение других производителей в отрасли и обнаружили, что компании с сильными социальными характеристиками в основном имеют самостоятельно разработанную систему хранения графов (как показано на рисунке ниже).
например:
Учитывая, что данные нашего социального графа уже хранились в MySQL База данных огромна, а служба данных социальных графов — очень важная служба с очень высокими требованиями к стабильности. обратная трассировка Facebook Проблемы, с которыми мы столкнулись тогда, были похожи на наши. Данные хранились в. Memcache и MySQL середина。поэтому,Ссылка Facebook из системы хранения изображений Tao больше соответствует нашей реальной ситуации и существующей технической архитектуре.,Менее рискованно.
социальный графиз Посетите главнуюдасторонаизреляционный запрос。
В нашей графовой модели отношение Воля выражается как <key, value> Да, среди них key да ( FromId, AssocType, ToId ) из тройки, значение дасвойствоиз JSON Формат.
Например, «Пользователь A «Следовать» за пользователями B », сопоставленный с REDtao из Структура хранения данных такая.:
1<FromId:UserAизID, AssocType: внимание, ToId:пользовательBизID> -> Value (поле атрибута json)
Мы проанализировали потребности бизнеса и инкапсулировали 25 Семантика графа из API Для делового использования,Удовлетворяет потребности в добавлениях, удалениях, модификациях и поиске.,И объединить бизнес-методы и методы использования.
по сравнению с Facebook из Tao,Мы также дополнили семантику графа, необходимую для социального графа.,Дополнительные параметры фильтрации предусмотрены для сценариев борьбы с мошенничеством.
в то же время,иметь уровень кэширования,Мы поддерживаем разныеиз Полесуществоватькэшсередина Настроить локально Уровень 2индекс。
Вот несколько типичных сценариев использования.
1) Сценарий 1:получатьсосредоточиться на A из Всех обычных пользователей (и исключая читерских пользователей):
1getAssocs("Сфокусированный тип", UserAизID, смещение пейджинга, максимальная возвращаемая стоимость, Возвращать только обычных пользователей, да менять ли с нового на старого по времени)
2) Сценарий 2:получать A из Количество фанатов (без учета читерских пользователей):
1getAssocCount("Сфокусированный тип", UserAизID, Возвращаются только обычные пользователи)
При проектировании архитектуры REDtao учитываются следующие ключевые элементы:
Общая архитектура разделена на три уровня:
Бизнес-стороны получают доступ к услугам через REDtao SDK.
Как показано ниже:
В этой архитектуре:и Facebook Tao В отличие от изда, наш уровень кэширования да представляет собой независимый распределенный кластер, а уровень персистентности да отделен от него. Ниже уровня кэша находится уровень персистентности, который можно расширять и уменьшать независимо, а кэш сегментируется. MySQL Шардинг не требует однозначного соответствия, что обеспечивает большую гибкость, MySQL Кластер также стал подключаемым и заменяемым постоянным хранилищем.
1) Процесс чтения:клиент Воля Запросы на чтение отправляются на router,router полученный RPC После запроса выберите соответствующее из по типу кромки. REDtao Кластеры, основанные на тройках ( FromId, AssocType, ToId ) через последовательность Hash Посчитать количество осколков Follower узел, перенаправить запрос на этот узел. Последователь Для этого запроса полученный узел сначала запрашивает локальный кеш графа, и если происходит совпадение, результат возвращается напрямую. если нет попаданий, то Воля просит вперед к Leader узел. То же самое, Лидер Если узел попал, он будет возвращен. Если он не попал, будет запрошен нижний уровень. MySQL база данных.
2) Процесс написания:клиент Воля Заявки на запись отправляются на маршрутизатор, процесс чтения тот же, он пересылает прибытие, соответствующее из Follower на узле. Последователь Узел перенаправит запрос на запись на Leader Узел, Лидер узел пересылает на MySQL, когда MySQL Вернув успешное письмо, Лидер Очистит локальный кеш карты, соответствующий Ключ и синхронизируется со всеми остальными Follower очисти это Ключ обеспечивает конечную согласованность данных.
REDtao разделен на два независимых уровня: уровень кэша и уровень персистентности. Каждый уровень гарантирует высокую доступность.
1) самоисследование распределенного кэша:
Мы реализовали семантику графов и кластеры распределенного кэша, поддерживая автоматическое обнаружение и восстановление ошибок, а также горизонтальное расширение и сжатие.
Это двойной слой кэш, каждый осколок имеет один Leader и несколько Последователь. Все запросы сначала отправляются на внешний уровень. Последователь, тогда Follower вперед к Лидер. Преимущество этого в том, что когда чтение вызывает стресс, вам нужно расширяться только по горизонтали. Последователь, одна точка Leader Метод записи также менее сложен и облегчает достижение согласованности данных.
Если одна реплика выходит из строя, система переключается в течение нескольких секунд. При сбое уровня персистентности уровень распределенного кэша все равно может предоставлять услуги внешнего чтения.
2) Высокодоступный кластер MySQL:
MySQL Кластер использует промежуточное программное обеспечение самоисследование, реализующее схему подбазы данных и подтаблицы и поддерживающую MySQL из Горизонтальное расширение. каждый MySQL База данных имеет несколько подчиненных библиотек и связана с другими базами данных внутри компании. MySQL План эксплуатации и технического обслуживания является последовательным, что обеспечивает высокую доступность.
3) Функция защиты от ограничения тока:
Чтобы предотвратить проникновение в кэш, вызванное MySQL Внезапное большое количество запросов, приводящее к MySQL Время простоя, мы ограничиваем каждый мастер-узел до максимума MySQL Количество одновременных запросов на реализацию защиты по ограничению тока MySQL. После достижения максимального предела одновременных запросов запрос будет приостановлен до тех пор, пока запрос не будет обработан и возвращен, или пока запрос по истечении времени ожидания не будет отклонен и не будет продолжен. MySQL . Порог ограничения тока регулируется онлайн в соответствии с MySQL Существуют соответствующие ограничения на настройку размера кластера.
Чтобы предотвратить частую обработку одних и тех же данных сканерами или мошенническими пользователями, мы используем REDtaoQueue При последовательном выполнении запросов на запись или проверку одного и того же ребра длина очереди будет ограничена для контроля одновременного выполнения большого количества одинаковых запросов. по сравнению сединый глобальныйиз Очередь контролирует все запросыиз Способ,Очередь для каждого запроса хорошо работает, чтобы ограничить один и тот же запрос.,не затрагивая другие обычные запросы.
Структура данных, разработанная RED, обеспечивает высокую производительность и является важной гарантией.
Мы используем три уровня вложенности HashTable из Дизайн, основываясь на отправной точке from_id с первого уровня HashTable найден в REDtaoGraph, записывает все type Внизпереписыватьсяизвсеизвнесторонаинформация。
Затем на втором уровне HashTable , по мнению определенного type_id найденный AssocType соответствующий type Внизсторонавсевнесторонаизсчитать、индекси другие метаданные。
наконец-то на последнем уровне HashTable ,проходить AssocType из кого-то to_id найденныйфинальныйсторонаинформация。
Мы записываем время создания, время обновления, версию, данные и REDtaoQueue, а time_index соответствует сортировке списка по времени создания.
последний уровень HashTable И предел индекса хранит последние 1000 дополнительную информацию, чтобы ограничить использование суперточек слишком большого количества памяти, одновременно сосредоточив внимание на повышении скорости и эффективности последних запросов к горячим данным. REDtaoОчередь Используется для постановки в очередь текущего чтения и записи определенного отношения и записывает только метаданные последнего текущего запроса.
Каждый раз, когда вы запрашиваете или пишете, сначала запрашивается REDtaoAssoc:
Благодаря этому многослойному хешу+таблице переходов мы можем эффективно организовывать точки.、сторона、индекс、Связь между связанными списками временных рядов. Приложение памяти、Релиз существует осуществляется в том же потоке.
существовать В онлайн-среде наша система может существовать 16 Запустите приезжать на виртуальной машине основного поставщика облака. 150w Запрос(ы) одновременно CPU Использование только 22.5% . Ниже приведено изображение мониторинга онлайн-кластера, автономной машины. QPS приезжать 3w , каждый RPC Запросы агрегируются 50 запрос.
1) Богатый семантический API графа:
мы REDtao инкапсулированный в 25 Семантика графа из API Он используется бизнес-стороной для удовлетворения потребностей бизнес-стороны в добавлении, удалении, изменении и запросе. Деловым сторонам не нужно писать свои собственные SQL Соответствующие операции можно реализовать с помощью операторов, их использование проще и более сходится.
2) URL-адрес единого доступа:
потому Серверные данные сообщества слишком велики, поэтому мы разбили их на несколько в соответствии с разными сервисами и приоритетами. REDtao кластер.
чтобы позволить Компания не знает о логике разделения внутреннего кластера, и мы реализовали единый уровень доступа.
Разным деловым сторонам нужно использовать только одну и ту же услугу. URL ,проходить SDK Запрос «Воля» отправляется на уровень доступа «приехать». Уровень доступа будет обрабатывать запросы от различных деловых сторон и семантики графов, а также маршрутизировать запросы на основе типов ребер. REDtao кластер. Он использует центр конфигурации подписки, который может воспринимать границу маршрутизации в режиме реального времени, тем самым обеспечивая унифицированный доступ. URL, удобный для деловых людей.
как данные социального графа,Согласованность данных имеет решающее значение. Нам необходимо строго обеспечить максимальную согласованность данных и строгую согласованность в определенных сценариях. с этой целью,Мы приняли следующие меры:
1) Конфликт обновления кэша из-за разрешения:
REDtao Генерируйте глобально увеличивающийся и уникальный номер версии для каждого запроса на запись. существуют использование MySQL Когда данные обновляют локальный кеш,Нужно сравнить номера версий,если номер версии меньше версии кэшированных данных,этот запрос на обновление будет отклонен,чтобы избежать конфликтов.
2) Согласованность чтения после записи:
Proxy будет то же самое fromId изPoint или Edge маршрутизация запроса приезжать читать то же самое cache узлы для обеспечения согласованности считываемых данных.
3) Сценарий неисправности главного узла:
Leader После того, как узел получит запрос на обновление, он изменит запрос на обновление на invalidate cache Запросы отправляются асинхронно другим последователь, чтобы обеспечить follower Приведенные выше данные в конечном итоге будут согласованными. существовать необычные обстоятельства, если Leader Очередь отправки заполнена. invalidate cache Запрос потерян, потом будет еще один follower cache Очистите их все.
если Leader Провал, новые выборы из Leader Остальные также будут уведомлены follower Воля cache Прозрачный.
Кроме того, Лидер посещу MySQL Запросы ограничены по потоку, чтобы гарантировать, что даже если кэш отдельных шардов будет очищен, он не будет заблокирован. MySQL Авария.
4) Небольшое количество строгих запросов на согласованность:
потому что MySQL Подчиненная библиотека также предоставляет услуги чтения. Для небольшого количества запросов на чтение, требующих строгой согласованности, клиент может запросить специальный флаг REDtao. Флаг будет прозрачно передан в базу данных. Proxy Уровень перенаправит запрос на чтение в соответствии с флагом «Воля приезжать». MySQL в основной базе данных, чтобы обеспечить строгую согласованность данных.
Межоблачная мультиактивность — это важная стратегия компании, а также важная функция, поддерживаемая REDtao.
Общая кросс-облачная мультиактивная архитектура REDtao выглядит следующим образом:
Это отличается от Facebook Tao из Кросс-облачная реализация мультиактивности, Facebook Tao Реализация кросс-облачной мультиактивности показана на рисунке ниже.
Facebook схема зависит от лежащего в основе MySQL изMaster-slave копирует оба процесса DTS Replication Приходите и сделайте это. и MySQL Нативная копия master-slave с собственной функцией, DTS В услугу не входит MySQL изMaster-slave репликации. Этот план требует MySQL и DTS Внесите определенные изменения. Ранее говорилось, что приезжать,Мы из кэша и уровня персистентности отделяем,существование архитектурно отличается.
Поэтому REDtao из Кросс-облачная мультиактивная архитектура Да Мы разработали ее на основе собственных сценариев, и она не меняет существующие MySQL Межоблачная мультиактивная функция реализуется на основе нескольких функций.
1)слой сохранениянаспроходить MySQL Родной из хозяина-раба binlog Синхронизировать копию данных Воля, прибыть в другое облако из подчиненной базы данных. Запросы на запись в другие облака и небольшое количество запросов на строгое согласованное чтение перенаправляются в основную базу данных. Обычный запрос на чтение Воля читает эту область из MySQL База данных соответствует требованиям к задержке запросов на чтение.
2)слой кэшированияизсогласованность данныхдапроходить MySQL DTS Реализация абонентского обслуживания из, Воля binlog Преобразовать в invalidate cache Просьба очистить эту территорию REDtao cache слойиз stale данные。потому чтоRead запрос будет случайным образом читать любое сообщение в этой области MySQL база данных, следовательно DTS В подписке используется функция отложенной подписки, чтобы гарантировать, что от binlog Синхронизируйте самый медленный узел, чтобы читать журналы, чтобы избежать DTS из invalidate cache Запросить эту область read cache miss из-за конфликтов запросов, приводящих к несогласованности данных.
REDtao Встроенные функции изCloud ориентированы на эластичную масштабируемость и поддержку нескольких AZ и Region Распределение данных и продукты можно переносить между разными поставщиками облачных услуг. REDтао С самого начала проектирования учитывалась поддержка упругого расширения и сжатия, автоматическое обнаружение и устранение неисправностей.
вместе с Kubernetes Облачные технологии становятся все более зрелыми, и мы также думаем о том, как их использовать. k8s изспособность Воляразвертыватьи Отвязать виртуальную машину,Дальнейшая облачная нативизация,Удобное развертывание и миграция между различными поставщиками облачных услуг.
REDtao Реализована программа, работающая на Kubernetes На кластере Оператор для более быстрого развертывания, расширения и замены вышедших из строя машин.
чтобы позволить k8s Способен определять распределение сегментов кластера и контролировать один и тот же сегмент. Pods Запланировано на разных хостах, группировка и шардинг кластера распределяются по k8s Operator Рендеринг и контроль создания DuplicateSet (Самоисследования Сяохуншу k8s ресурсный объект).
REDtao Затем будут созданы мастер и слейв на основе Operator Отображение информации о сегментировании для создания кластера, единого Pod Перезапуск при сбое приведет к повторному присоединению к кластеру без повторного создания всего кластера. При обновлении кластера Оператор. Этот определяет распределение «главный-подчиненный» и контролирует порядок «ведомый-затем-главный», а также выполняет обновление в порядке распределения сегментов, чтобы уменьшить влияние онлайн во время обновления.
Любые изменения даются нелегко. Внедрение нового REDtao завершило лишь относительно легкую часть работы.
Запущен сервис данных социальных графов Xiaohongshu MySQL В течение многих лет на нем работает множество различных предприятий, любые небольшие проблемы повлияют на возможность проживания онлайн-пользователей. Поэтому как обеспечить возможность незаметного переноса существующего бизнеса без остановки сервиса? REDtao стало очень большим испытанием.
В нашей миграционной работе есть два ключевых момента:
1)Волястарыйизбольшой MySQL Кластер разбит на четыре в соответствии с приоритетом. REDtao Таким образом, мы можем сначала перенести службу с самым низким приоритетом. REDtao Кластеры должны быть полностью выделены серым цветом перед миграцией кластеров с высоким приоритетом;
2)Специально разработанный Tao Proxy SDK, поддержка оригинальных MySQL Кластер и REDtao Кластер выполняет двойную запись и двойное чтение, а также проверку и сравнение данных.
При миграции:Мы сначала Волянизкий приоритетизданныеот MySQL проходить DTS Сервис был перенесен на REDtao Кластеризация и обновление бизнес-метода SDK 。DTS Служба всегда синхронизирует дополнительные данные. деловая сторона SDK Подпишемся на центр конфигурации из изменений конфигурации, мы изменяем конфигурацию, чтобы позволить Tao Proxy SDK читать и писать одновременно MySQL Кластер и REDtao кластер и выключить DTS Служить. будет использоваться в это время MySQL Кластер из Результат возвращается впользователь。
Остановка DTS При подаче:Могут быть новыеиз MySQL данныепроходить DTS Синхронизировано, что приводит к REDtao Вновь записанные данные в кластере синхронизируются и перезаписываются старыми данными. Поэтому существование закрыто DTS После обслуживания мы предоставим инструмент для чтения и двойной записи. DTS Обслуживание в этот период времени из binlog Проверьте и исправьте данные.
После завершения ремонта:Tao Proxy SDK Двойное чтение будет отображать несогласованный объем данных с обеих сторон и отфильтровывать запросы на несогласованность данных, вызванную несогласованными задержками двойной записи. Наблюдайте за оттенками серого через некоторое время. diff Число из в основном 0,Воля Tao Proxy SDK конфигурация изменена на только чтение и запись новая REDtao кластер.
финал:мы 22 Заполнил Красную Книжку в начале года.всеосновнойсоциальный графтриллионысторонауровеньданныеизмигрироватьи Проверка правильности,И приезжать всей миграционной службе без ведома,Процесс миграции прошел без единого сбоя.
90% наших обращений к данным социальных графов Все вышеперечисленные запросы являются запросами на чтение, а данные социальных графов имеют очень сильную временную локальность (то есть к самым последним обновленным данным проще всего получить доступ). REDтао После выхода в Интернет получите 90% Выше cache процент попаданий, в MySQL из QPS уменьшенный 70%+ ,большойбольшойуменьшенный MySQL из CPU Скорость использования. Сокращение штата MySQL После сложения количества копий общая стоимость составит 21,3%.
Все методы доступа к бизнесу конвергентныприезжать REDtao Предоставить из API На интерфейсе в процессе миграции мы также справились с каким-то старым из-за необоснованного доступа MySQL База данных из метода, а также настройка определенных полей для придания особого значения из необоснованных практик. REDtao Доступ к данным стандартизирован.
контраст 2022 Начало года 2023 В начале года вместе с DAU из вырос, социальный граф из запросов вырос 250% выше, перед еслида MySQL из старой архитектуры, ресурсы расширения в основном пропорциональны скорости роста запросов, требуется как минимум расширение 1 В разы превышает стоимость ресурсов (десятки тысяч ядер).
И получить выгоду от REDtao Система сохраняет существование благодаря своему 90% изкэшпроцент попаданий, на самом деле общая стоимость только увеличилась 14,7% (тысячи ядер) справятся с этим 2.5 раз из роста запросов. существующие Стоимость и стабильность были значительно улучшены.
существоватькорочеизвремя,У нас есть система хранения фотографий REDtao., решая проблему быстрого роста данных о взаимоотношениях социальных графов.
REDtao Узнал от FaceBook Tao Согласно диссертации, было внесено множество улучшений в общую архитектуру и межоблачную мультиактивность, а также реализован новый высокопроизводительный кэш распределенных графов, который больше соответствует нашим собственным бизнес-характеристикам и обеспечивает большую гибкость. При этом используйте k8s возможности дальнейшей реализации облачной нативизации.
вместе с DAU продолжает расти,Масштаб триллионов данных продолжает расти,Мы также сталкиваемся с более техническими проблемами.
В настоящее время в компаниииз OLTP Сцена в основном разделена на три части:
1)социальный графданные Служить:проходитьсамоисследованиесистема хранения графов REDtao Он решает проблемы сверхкрупномасштабного обновления данных и связанного с ними чтения в социальных сценариях. В настоящее время сохранены триллионы масштабов отношений;
2)Сценарий контроля рисков:проходитьсамоисследованиекартинаданные Библиотека REDgraph, удовлетворяет многоскачковому запросу в реальном времени существующей линии. В настоящее время хранятся сотни миллиардов отношений точек и ребер, что удовлетворяет 2 тоже прыгай 2 Прыжок Выше Запрос;
3)социальная рекомендация:Это в основномдадва прыжкаиз Запрос。каждый деньпроходить Hive Импортируйте все данные в пакетном режиме, перенесите DTS Служба записывает обновленные данные практически в реальном времени. Из-за онлайн-сценария требования к задержке в настоящее время очень высоки. REDgraph Он до сих пор не в состоянии удовлетворить столь высокие требования, поэтому бизнес-сторона в основном использует REDkv хранить.
Для приведенного выше сценария:Для быстрого удовлетворения потребностей бизнеса,Мы используем три разные системы хранения: REDtao, REDgraph и REDkv.
Очевидно, что при наличии 3 наборов систем хранения более целесообразно использовать систему с единой архитектурой для решения этих сценариев, связанных с графами.
будущее:мы будем Воля REDgraph и REDtao слиться в одно целоеизданные Библиотека产品,Создайте лучшую в отрасли технологию обработки изображений,Расширьте возможности реализации большего количества сценариев внутри компании.
[3] Как добиться сжатия без потерь огромных изображений пользователей на основе социальной сети Yelpda?
[7] Fading away из Ренрен: Десять лет опыта из Обзор социальных продуктов Интернета и размышления
Технический обмен:
- Мобильный терминалIMВводные статьи о разработке:《Новичкам достаточно одной статьи: разработка мобильного IM с нуля》
- Открытый исходный кодIMИсходный код платформы:https://github.com/JackJiang2011/MobileIMSDK(Альтернативный адрес нажмите здесь)
(Эта статья синхронизирована с Опубликовано в:http://www.52im.net/thread-4495-1-1.html)