Pull to refresh
838.44
Яндекс
Как мы делаем Яндекс

Яндекс выложил код счётчика Метрики в opensource

Reading time5 min
Views15K

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

Открытый нами код — это JS-бандл, который загружается вместе со страницей сайта и фиксирует различные события, происходящие на сайте. Теперь каждый разработчик может подробно изучить код счётчика и пересобрать его в той конфигурации, которая необходима для его проекта.

Немного истории

Первая версия счётчика Метрики вышла в далёком 2007 году как часть Директа и стала отдельным полноценным публичным сервисом 24 апреля 2009 года. В 2018 году произошло первое масштабное обновление кода счётчика. Тогда нам стало ясно, что пришло время серьёзного рефакторинга его архитектуры. В том числе это подразумевало переход на актуальные технологии, используемые в разработке.

Мы выбрали TypeScript как язык реализации для повышения качества кода. А для сборки мы начали использовать Rollup из-за компактного результата компиляции и широкой поддержки браузеров. 

За эти годы счётчик прошёл через множество оптимизаций и иногда нашей команде приходилось решать довольно нетривиальные задачи. Однажды мы столкнулись с аномальным количеством трафика сразу с нескольких сайтов: десятки мегабайт за минуты от каждого пользователя. Мы быстро поняли, что в попытке создать новогоднее настроение у посетителей, эти сайты реализовали снежинки, красиво падающие у пользователя на экране. Вот только технически это создавало сотни узлов в DOM, что невероятно тормозило сайт, вызывая полную переиндексацию кода страницы Вебвизором.

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

Что мы выкладываем

Сегодня мы выкладываем на гитхаб код счётчика Яндекс Метрики. Теперь собрать счётчик можно самостоятельно из исходников. Он существует параллельно обычному счётчику, при этом бэкенд, аналитика и выгрузка доступны через стандартные инструменты Метрики.

Мы предполагаем, что раскрытие кода позволит:

  • проаудировать, пересобрать и захостить у себя код, который выполняется на клиенте;

  • модифицировать счётчик под свои нужды, например, удалив какие-то нерелевантные для вас фичи;

  • использовать полезные фрагменты кода в своих проектах;

  • улучшить код счётчика, отправив pull request.

Как работает счётчик

Счётчик состоит из двух частей: JavaScript-кода, размещённого в элементе script, и HTML-кода, включённого в элемент noscript. При вставке также используется небольшой загадочный фрагмент кода. Он создаёт stackProxy, в который отправляются события, а дальше загружается сама Метрика. Так мы не теряем события, случившиеся до загрузки кода.

Если браузер поддерживает исполнение JavaScript-кода и оно не заблокировано настройками или сторонними расширениями, выполняется JavaScript-код, включённый в элемент script. В противном случае обрабатывается содержимое элемента noscript.

У JavaScript-кода есть доступ к информации о заголовке HTML-страницы, URL источника перехода на страницу, параметрах экрана и окна браузера, дополнительных расширениях браузера. Также он измеряет параметры загрузки страницы (например, время до отрисовки) и позволяет отправлять данные о достижениях целей.

У счётчика модульная архитектура, которая состоит из провайдеров, сендеров, middleware и транспортов. Провайдер собирает информацию или подписывается на будущие события, парсит данные и вызывает сендер. Сендер устанавливает базовую информацию о запросе, эндпоинт и общие параметры. Middleware вызываются до отправки запроса или после получения результата, обогащают его полезной информацией. Транспорт отправляет запрос в бекенд, используя XHR, картинку или beacon.

Как использовать код счётчика

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

Установка

Сборка проекта основана на Node.js, а сам счётчик работает нативно в браузере на JavaScript. Для установки зависимостей используйте следующую команду:

npm ci

Сборка

Проект содержит единственный вывод цели сборки в _build/public/watch.js. Чтобы создать его, запустите:

npm run build

Код собран в один файл, минифицирован и оптимизирован. При необходимости (например, для отладки) скрипт можно собрать в оригинальном виде:

npm run build:raw

Счётчик состоит из базового кода и набора функций. По умолчанию все функции включены за исключением тех, которые явно помечены как отключённые. Чтобы исключить объект из сборки, добавьте атрибут "disabled": true

Как добавить функцию

Модульная архитектура позволяет легко добавить собственную функцию в код счётчика. Все функции, перечисленные в features.json можно разделить на два типа:

  • Функции, добавляющие большое количество кода с изолированной функциональностью (например, провайдер, промежуточное программное обеспечение, транспорт). Это основной механизм добавления.

  • Функции, добавляющие небольшой блок кода в существующую функцию, чтобы как-то изменить её поведение. В основном такие функции мы использовали для небольших экспериментов.Например, это позволяет добавлять или исключать код для тестовых сборок.

Давайте добавим нового провайдера. Сначала создайте функцию в features.json в следующем формате:

{
    "code": "SOME_NEW_FEATURE", // Имя сгенерированной переменной, которая будет использоваться в коде
    "path": "someNewProvider", // Путь к папке в src/providers с модулем нового провайдера
    "desc": "Сюда запишите описание",
    "disabled": true, // Устанавливается только в том случае, если вы хотите исключить функцию из сборки
    "exp": true, // Семантический атрибут, указывающий на экспериментальную функцию. Не влияет на процесс сборки
    "weight": 1000, // По умолчанию равен 0, если не указано другое. Используется для сортировки объектов в коде инициализации объектов в порядке возрастания: чем меньше вес, тем раньше инициализируется провайдер
}

Затем создайте новый набор функций:

npm run features

Создайте папку внутри src/providers. Файл index.ts должен содержать только код инициализации и расширения для существующих типов. Он должен экспортировать функцию initProvider:

export const initProvider = () => {
    /*
        Код обёрнут фича-флагом.
        Если функция не включена в сборку, код обрезается при сборке.
        Результирующая пустая функция также отключается
    */

    if (flags[SOME_NEW_FEATURE]) {
        // Требуемый код
        providersSync.push(someNewProvider);

        // Необязательный код для любой дополнительной настройки
        addMiddlewareForProvider(SOME_NEW_PROVIDER, watchSyncFlags(), 1);
        providerMap[SOME_NEW_PROVIDER] = useSenderWatch;
        nameMap[SOME_NEW_PROVIDER] = fullList;
    }
};

Теперь напишите код провайдера и добавьте тест в папку __tests__. Используйте расширение .spec.ts. Готово!


Мы надеемся, что раскрытие кода счётчика Метрики поможет всем, кто хотел разобраться в его работе, настроить под свои задачи и переиспользовать опыт в своих проектах. Мы также будем рады вашим предложениям и пулл-реквестам. 

Tags:
Hubs:
+54
Comments34

Other news

Information

Website
www.ya.ru
Registered
Founded
Employees
over 10,000 employees
Location
Россия