Sell Side platform
Добрый день, дорогой читатель! Продолжая цикл статей о реализации стека RTB нашей компанией, предлагаю вам ознакомиться с реализацией нашей SSP — VOX Ad Exchange.
Нагрузка здесь не такая большая, как, например, в нашей DSP, а основное время обработки запроса тратится на ожидание ответов от подключенных DSP. Написана она в виде асинхронного ASP.NET MVC-приложения.
Итак, давайте для начала разберёмся, как же происходит обработка запросов.
Запрос на получение рекламы начинает формироваться ещё на стороне клиента. Здесь посредством javascript мы стараемся собрать как можно больше информации о посетителе сайта, на котором установлен наш скрипт. В основном это техническая информация: включен ли в браузере flash player, размеры окна браузера, размеры экрана, получаем адрес страницы и реферер на страницу, с которой пришёл пользователь и прочее. Далее информация собирается в JSON строку, кодируется и отправляется запрос на получение рекламы. Теперь за дело берётся back-end.
Получив запрос на показ рекламы, так же получаем из базы всю известную нам информацию о данном посетителе (историю его интересов, просмотров рекламы и кликов). По user-agent’у мы получаем данные об используемом браузере, его версии и операционной системе посетителя, а по ip-адресу определяем его текущее местоположение. Всю собранную о пользователе информацию мы отправляем в нашу DMP для её дальнейшего анализа.
Собрав данные о пользователе, мы переходим к площадке. Получаем информацию о стоимости рекламного места, CTR площадки, категориях контента, наличию запрещённого контента и т.д., на основе этих данных будет сформирована минимальная пороговая цена аукциона. Закончив с площадками, переходим к самой длительной задачи SSP, это опрос подключенных DSP. На основе полученных выше описанным способом данных формируется запрос к DSP. Далее происходит паралельный опрос DSP, на ответ каждой из них отводиться 120 ms, в которые входит и время на доставку запроса, и получение ответа (ping до серверов, на которых расположена DSP). Так что месторасположение DSP тоже играет важную роль. Получив ответ от всех DSP, остаётся лишь отсортировать их по размеру ставки и взять ответ с самой большой. Ответ в виде JSON’а, для показа рекламы мы возвращаемся на front-end.
Фронтенду остаётся самая малость — вывести код полученного объявления. Так как уследить за качеством кода DSP и его совместимостью с площадками нет никакой возможности, наш код для вывода объявления на страницу создаёт отдельный iframe, загружает в него код, и вот его-то мы и выводим на страницу. На этом и заканчивается обработка запроса на рекламу.
Давайте теперь разберёмся с какими трудностями пришлось столкнуться в ходе реализации SSP, и как мы с ними справились. Одной из основных проблем в ходе реализации SSP стало забивание пула IIS. Причиной этому было ожидание ответа от DSP в течении 100 и более миллисекунд. В то время, когда SSP ждала ответа от DSP, остальные запросы ждали, пока SSP обработает текущий. А решением данной проблемы стало использование асинхронных контроллеров, вот здесь нам и пригодились модификаторы async/await. Также не лишним будет упомянуть, что информация о площадке и DSP кешируется в памяти приложения и ежеминутно обновляется. Следующим основным местом, на которое стоит обратить внимание, является опрос DSP. Первоначально встал вопрос, как распараллелить опрос DSP. В ходе разработки SSP было опробовано несколько вариантов решения этой задачи, таких как: Parallel.ForEach, Task.WaitAll и Task.WhenAll. Особых различий в производительности обнаружено не было, каждый способ исправно выполнял свою задачу, но в связи с переходом на асинхронную модель выбор пал на последний вариант. Сам запрос к DSP осуществляется посредством HttpWebRequest. Самое главное, что следует сделать при работе с HttpWebRequest, это включить поддержку KeepAlive, потому что запросы к DSP идут постоянно друг за другом, и постоянные обрывы и установка соединения начнут заметно тормозить получение ответа. Приведу пример получившегося класса для опроса DSP:
public class DspRequests
{
private const string _method = "POST";
private const string _contentType = "application/json";
private static List _headers = new List { "x-openrtb-version: 2.0" };
private List _dspUrls { get; set; }
public List DspUrls {
get
{
returns _dspUrls;
}
set
{
_dspUrls = value;
}
}
public async Task> PollDspAsync(string request)
{
List responses = null;
var tasks = _dspUrls.DspRecords.Select(el => PollAsync(el, request));
responses = (await Task.WhenAll(tasks)).Where(el => el != null).ToList();
return responses;
}
private async Task PollAsync(string url, string bidRequest)
{
string response = null;
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.Method = _method;
httpWebRequest.ContentType = _contentType;
httpWebRequest.KeepAlive = true;
foreach (string header in _headers)
httpWebRequest.Headers.Add(header);
httpWebRequest.Timeout = 120;
using (StreamWriter streamWriter = new StreamWriter(await httpWebRequest.GetRequestStreamAsync()))
{
streamWriter.Write(bidRequest);
}
using (HttpWebResponse webResponse = (HttpWebResponse) await httpWebRequest.GetResponseAsync())
{
if (webResponse.StatusCode == HttpStatusCode.OK)
{
using (StreamReader streamReader = new StreamReader(webResponse.GetResponseStream()))
{
response = streamReader.ReadToEnd();
}
}
}
return response;
}
}
Клиентская часть нашей SSP. Это личный кабинет веб-издателя (площадки), который содержит статистику по всем его рекламным местам. VOX — это self-service платформа, позволяющая создавать как баннерные рекламные места (5 самых популярных в RTB размеров), так и Rich Media баннеры (fullscreen). На каждое рекламное место можно повесить заглушку (HTML или Javascript), а также добавить счетчик показов сторонней системы.
Вот на этом, пожалуй, мы и закончим обзор работы нашей SSP.