<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0">

<channel>

<title>Блог об аналитике, визуализации данных, data science и BI, заметки с тегом: troubleshooting</title>
<link>http://test.leftjoin.ru/tags/troubleshooting/</link>
<description></description>
<generator>E2 (v3365; Aegea)</generator>

<item>
<title>Проблема Roistat как готового решения для аналитики</title>
<guid isPermaLink="false">149</guid>
<link>http://test.leftjoin.ru/all/problema-roistat-kak-gotovogo-resheniya-dlya-analitiki/</link>
<comments>http://test.leftjoin.ru/all/problema-roistat-kak-gotovogo-resheniya-dlya-analitiki/</comments>
<description>
&lt;p&gt;&lt;img src=http://test.leftjoin.ru/pictures/unnamed-1.png  border=“0” width=100% height=100%&gt;&lt;/p&gt;
&lt;p&gt;Нередко компании, которые обращаются к нам за системой сквозной аналитики, уже используют какие-то инструменты для работы с данными. Иногда это «коробочные» решения. Сразу отказываться от них в пользу кастома не обязательно — на старте их можно интегрировать в новую систему, чтобы сэкономить время и деньги на разработку. Но в этом случае надо быть готовым к тому, что готовые решения не гарантируют точность данных на том же уровне, что специально построенная под вас система аналитики. Именно с этой проблемой мы столкнулись, когда начали строить аналитику для стартапа Refocus: до нас они использовали Roistat, но ошибки в его работе вынудили нас отказаться от него раньше, чем мы планировали.&lt;/p&gt;
&lt;p&gt;Способов построить сквозную аналитику, не обращаясь к готовым решениям, немало: можно собрать in-house отдел аналитики или найти специалистов на аутсорсе.&lt;/p&gt;
&lt;p&gt;При этом нужно учитывать, что работа целой команды аналитики может стоить порядка 5-7 тысяч евро или 500-700 тысяч рублей в месяц. И это не предел — все зависит от размеров компании-заказчика, состояния аналитики до начала работ и других факторов.&lt;/p&gt;
&lt;p&gt;На начальном этапе для многих компаний, у которых объем данных небольшой, такие расходы неоправданы. Из-за этого они часто предпочитают «коробочные» продукты — например, тот же &lt;b&gt;Roistat.&lt;/b&gt; Это самое популярное решение на рынке, которое предлагает премиум-тариф с максимумом функционала и полной поддержкой от 1500 евро / 150 тысяч рублей в месяц. Базовые тарифы у них и того дешевле. У этого инструмента больше 200 встроенных интеграций, и не только с популярными Google Analytics или Яндекс Директ, но и с целым списком нишевых кабинетов и систем, о которых вы даже не слышали.&lt;/p&gt;
&lt;p&gt;После настройки интеграций с вашими источниками Roistat позволит создать дашборды за пару кликов, а сверху предложит автоматизацию маркетинга и лидогенерации. Конкуренты Roistat вроде &lt;b&gt;OWOX BI&lt;/b&gt; или &lt;b&gt;Alytics&lt;/b&gt; также предоставляют базовые возможности для аналитики и визуализации. В общем, недорогих решений под любые потребности много.&lt;/p&gt;
&lt;p&gt;Хотя “коробка” гораздо лучше бесконечных таблиц и поначалу хорошо оптимизирует работу с данными, она далеко не идеальна. С точки зрения эксперта все минусы универсальных решений видны как на ладони:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;не заглянешь под капот, если что-то идет не так;&lt;/li&gt;
&lt;li&gt;их система интеграций может не включать нужных источников;&lt;/li&gt;
&lt;li&gt;интеграции могут устаревать, создавая ошибки в выгрузке данных.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Кроме того, у вендоров таких готовых решений почти никогда нет ресурсов и возможностей, чтобы подогнать его под специфические требования каждого клиента. Более того, чаще такие “кастомные” требования многократно превосходят стоимость самой коробки (в этом суть готового решения). Если что-то не так на бэке у юзера, если меняется воронка в CRM, если нужно настроить кастомные интеграции — велики шансы, что за это придется доплатить немаленькие деньги, если вендор вообще согласится реализовать эти “специфичные требования”.&lt;/p&gt;
&lt;p&gt;На одном из наших проектов нам довелось поработать именно с такой ситуацией: Roistat не смог обеспечить точность данных и интеграций со всеми нужными источниками, и с этим пришлось разбираться самой компании — и нам.&lt;/p&gt;
&lt;h2&gt;Как Refocus работал с аналитикой&lt;/h2&gt;
&lt;p&gt;Один из наших заказчиков,  который строил аналитику на самых ранних этапах — EdTech-стартап Refocus. Неудивительно, что когда мы пришли создавать для них кастомную систему, они уже работали с Roistat.&lt;/p&gt;
&lt;p&gt;Почему Refocus выбрали именно его? Во-первых, он доступен на русском языке, и это было удобно для команды, в которой много русскоязычных сотрудников. Во-вторых, им нужна была именно система сквозной аналитики, а не CRM с функцией автоматического построения графиков, поэтому альтернативы вроде HubSpot не подходили. В-третьих, все базовые потребности компании он удовлетворял за относительно небольшую сумму. Ну и в-четвертых — многие сотрудники из команды Refocus уже имели опыт работы с Roistat, так что для них это был удобный и знакомый инструмент.&lt;/p&gt;
&lt;p&gt;Roistat тянул данные из двух основных рекламных кабинетов — &lt;b&gt;Google и Facebook&lt;/b&gt;*. В нем трекались все базовые метрики — бюджеты, показы, клики, конверсия — и отражались на автоматически созданных внутри программы дашбордах.&lt;/p&gt;
&lt;p&gt;&lt;img src=http://test.leftjoin.ru/pictures/unnamed-(2).png border=“0” width=100% height=100%&gt;&lt;/p&gt;
&lt;p&gt;К сожалению, скрины дашбордов из Roistat у нас не сохранились, но мы нашли для вас пример в интернете — выглядели они примерно так.&lt;/p&gt;
&lt;p&gt;Там даже присутствовал свой пользовательский идентификатор — когда мы вводили общий айди студентов, мы думали попробовать использовать его для мэтчинга персональных данных. На практике оказалось, что сопоставление по нему проходит очень неточно, и эту идею оставили.&lt;/p&gt;
&lt;p&gt;Стартап рос, число требований и людей увеличилось, поэтому нас позвали  строить для ребят кастомную аналитику и более подробные дашборды, и подключились мы.&lt;/p&gt;
&lt;h2&gt;Как обнаружилась проблема&lt;/h2&gt;
&lt;p&gt;Мы провели аудит существующей системы, получили доступы к нужным ресурсам и начали выстраивать инфраструктуру и готовить первый дашборд — маркетинговый, самый актуальный для компании на тот момент.&lt;/p&gt;
&lt;p&gt;Откуда мы тянули данные для этого дашборда? Конечно, из Roistat, ведь именно там уже лежали готовые маркетинговые показатели. Настройка хранилища и выгрузка данных из источников, которые Roistat не трекал, еще не была закончена, а смотреть на классные, качественные дашборды хотелось уже сейчас.&lt;/p&gt;
&lt;p&gt;Например, так выглядел код для выгрузки данных о затратах на рекламу через API:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;def get_costs(channel, channel_level, date_from, date_to, source):
         
     data = '{&amp;quot;dimensions&amp;quot;: [&amp;quot;marker_level_2&amp;quot;, &amp;quot;marker_level_3&amp;quot;, &amp;quot;marker_level_4&amp;quot;],\
     &amp;quot;metrics&amp;quot;:[&amp;quot;visitsCost&amp;quot;, &amp;quot;impressions&amp;quot;, &amp;quot;visits&amp;quot;],\
     &amp;quot;period&amp;quot;:{&amp;quot;from&amp;quot;:&amp;quot;' + date_from + '&amp;quot;,&amp;quot;to&amp;quot;:&amp;quot;' + date_to + '&amp;quot;},\
     &amp;quot;filters&amp;quot;:[{&amp;quot;field&amp;quot;:&amp;quot;' + channel_level + '&amp;quot;, &amp;quot;operation&amp;quot;:&amp;quot;=&amp;quot;, &amp;quot;value&amp;quot;:&amp;quot;' + channel + '&amp;quot;}],\
     &amp;quot;interval&amp;quot;: &amp;quot;1d&amp;quot;}'
     columns=['dimensions.marker_level_2.value',
             'dimensions.marker_level_2.title',
             'dimensions.marker_level_3.value',
             'dimensions.marker_level_3.title',
             'dimensions.marker_level_4.value',
             'dimensions.marker_level_4.title',
             'dateFrom'
             ]
     headers = {'content-type': 'application/json'}

     response = requests.request(&amp;quot;POST&amp;quot;, url, data=data, headers=headers)
     df = response.json()['data']

     l = []
     for i in range(len(df)):
         if df[i]['items'] != []:
             l.append(df[i])
     if len(l) &amp;gt; 0:
         res1 = pd.json_normalize(l, 'items', ['dateFrom'])[columns]
         res2 = pd.DataFrame()
         q = pd.json_normalize(l, ['items', 'metrics'])
         for x in [&amp;quot;visitsCost&amp;quot;, &amp;quot;impressions&amp;quot;, &amp;quot;visits&amp;quot;]:
             res2[x] = q.query('metric_name == @x')['value'].values
         res = pd.concat([res1, res2], axis=1)
         res['source'] = source
         return res
     else:
         return pd.DataFrame()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Подробнее о &lt;tt&gt;marker_levels&lt;/tt&gt; можно прочитать &lt;a href="https://help-en.roistat.com/API/methods/analytics/#sample-report"&gt;здесь&lt;/a&gt; в документации API Roistat.&lt;/p&gt;
&lt;p&gt;Когда Refocus посмотрели на первую версию дашборда, они заметили, что цифры не сходятся с их реальными бюджетами, кликами, конверсией в рекламных кабинетах.&lt;/p&gt;
&lt;p&gt;Мы начали искать ошибку с нашей стороны, но убедились, что наш код работает исправно и данные в нашем дашборде один в один совпадают с цифрами в Roistat. Никаких сбоев между источником и дашбордом не происходило.&lt;/p&gt;
&lt;p&gt;Дело в том, что пока компания смотрела только на визуализации Roistat, расхождения в данных не бросались в глаза. А наш дашборд содержал несколько новых графиков, более подробных, чем мог предложить Roistat. Когда Refocus стали сравнивать детали по разным срезам, на которые Roistat не позволял посмотреть, расхождения стали очевидны.&lt;/p&gt;
&lt;p&gt;&lt;img src=http://test.leftjoin.ru/pictures/unnamed-(1).png border=“0” width=100% height=100%&gt;&lt;/p&gt;
&lt;p&gt;У нас дашборд был более гибкий — можно было отфильтровать данные по разным важным срезам, более тонко настроить гранулярность. И благодаря этому ошибки в метриках стали заметны.&lt;/p&gt;
&lt;p&gt;Об этом мы и сообщили коллегам, продемонстрировав совпадение. Как раз в этот момент сыграл роль недостаток закрытой системы — посмотреть внутрь Roistat и найти, где именно происходит ошибка, не могли ни мы, ни бэкенд-специалисты Refocus.&lt;/p&gt;
&lt;p&gt;Разумеется, кастомная система уже предполагала отключение этого источника в будущем — зачем платный посредник, если мы все равно будем грузить данные напрямую и из рекламных кабинетов, и из кучи других источников?&lt;/p&gt;
&lt;p&gt;После обнаружения ошибки стало ясно, что затягивать с этим не стоит, если мы хотим предоставить клиенту чистые данные.&lt;/p&gt;
&lt;p&gt;Полностью отказаться от Roistat сразу не получилось, поэтому мы добавили на дашборд фильтр &lt;tt&gt;“match with Roistat”.&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;На нем мы отмечали количество рекламных кампаний, данные по которым расходятся с показателями на нашем дашборде и на Roistat’овском.&lt;/p&gt;
&lt;h2&gt;Как мы наконец заменили Roistat на кастомную систему и уточнили данные о воронках&lt;/h2&gt;
&lt;p&gt;Мы сразу написали несколько скриптов для выгрузки тех же самых данных из GA4 и Facebook Ads* через API.&lt;br /&gt;
Например, так данные о рекламных показах, затратах и кликах  за день в разрезе {campaign, adset, adname} выгружались из кабинета GA4:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;def get_google_costs(date_start, customer_id):

    request = RunReportRequest(
        property = f&amp;quot;properties/{property_id}&amp;quot;,
        dimensions = [Dimension(name = &amp;quot;date&amp;quot;),
                     Dimension(name = &amp;quot;sessionGoogleAdsCustomerId&amp;quot;),
                     Dimension(name = &amp;quot;sessionGoogleAdsCampaignId&amp;quot;),
                     Dimension(name = &amp;quot;sessionGoogleAdsCampaignName&amp;quot;),
                     Dimension(name = &amp;quot;sessionGoogleAdsAdGroupId&amp;quot;),
                     Dimension(name = &amp;quot;sessionGoogleAdsAdGroupName&amp;quot;),
                     Dimension(name = &amp;quot;sessionGoogleAdsCreativeId&amp;quot;)],
        metrics = [Metric(name=&amp;quot;advertiserAdCost&amp;quot;),
                   Metric(name=&amp;quot;advertiserAdImpressions&amp;quot;),
                   Metric(name='advertiserAdClicks')],
        date_ranges = [DateRange(start_date = date_start, end_date = date_start)],
        dimension_filter = FilterExpression(
            filter = Filter(
                field_name = &amp;quot;sessionGoogleAdsCustomerId&amp;quot;,
                string_filter = Filter.StringFilter(value = customer_id),
            )
        ),
    )
    response = client.run_report(request)

    date = map(lambda x: x.dimension_values[0].value, response.rows)
    customer_id = map(lambda x: int(x.dimension_values[1].value), response.rows)
    campname_id = map(lambda x: int(x.dimension_values[2].value), response.rows)
    campname = map(lambda x: x.dimension_values[3].value, response.rows)
    groupname_id = map(lambda x: int(x.dimension_values[4].value), response.rows)
    groupname = map(lambda x: x.dimension_values[5].value, response.rows)
    adnam_id = map(lambda x: int(x.dimension_values[6].value), response.rows)
    visitsCost = map(lambda x: float(x.metric_values[0].value), response.rows)
    impressions = map(lambda x: int(x.metric_values[1].value), response.rows)
    visits = map(lambda x: int(x.metric_values[2].value), response.rows)

    df = pd.DataFrame({'region': customer_id, 'date': date, 'campname': campname, 'groupname': groupname, 'campname_id': campname_id, 'groupname_id': groupname_id, 'adnam_id': adnam_id, 'visitsCost': visitsCost, 'impressions': impressions, 'visits': visits})
    return df

df = pd.DataFrame()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;После этого обнаружилось еще одно несовпадение: метрики в самом кабинете были чуть меньше, чем у нас на дашбордах. Отклонение не было критичным, но клиент снова не был уверен в точности наших данных.&lt;/p&gt;
&lt;p&gt;Оказалось, что в GA4 по умолчанию включается настройка &lt;b&gt;Thresholding applied.&lt;/b&gt; Ее цель — предотвратить возможную идентификацию отдельных пользователей по данным о возрасте, гендере и интересах. В результате ее применения терялись данные о кампаниях с небольшим (меньше 50) количеством кликов. Чтобы этого избежать, нужно было или не трекать эти показатели, или не использовать в дашбордах метрики, касающиеся пользователей напрямую. Roistat тоже не выгружал эти “пограничные” кампании через свою дефолтную интеграцию с GA4, поэтому для Refocus расхождение стало неожиданностью. Зато при прямой выгрузке через API этого ограничения не было, и ускользнувшие поначалу кампании считались, так что данные были более полными без дополнительных ухищрений.&lt;/p&gt;
&lt;p&gt;С добавлением все новых и новых источников, конечно, пришлось еще и мэтчить данные рекламных кабинетов о затратах, кликах и показах с записями на вебинары и их посещением, со сделками в CRM — но Roistat этого вовсе не делал, и поэтому не мог дать полной картины о воронках.&lt;/p&gt;
&lt;p&gt;После введения прямой выгрузки из рекламных кабинетов данные наконец-то стали полностью совпадать с реальностью, и Roistat отключили, заменив другими источниками. Теперь Refocus могли не платить дважды и получить куда более точный анализ.&lt;/p&gt;
&lt;h2&gt;Выводы&lt;/h2&gt;
&lt;p&gt;Кастомная сквозная аналитика — конечно, не панацея, и есть много причин и возможностей обойтись без услуг агентств, которые ее строят. Тем не менее, даже самое популярное на рынке коробочное решение, очевидно, не всегда корректно работает. Обрубать его сразу не обязательно, и ничего не мешает сочетать Roistat и похожие продукты с кастомом. В конце концов, иногда удобно просто достроить интеграции, которые не поддерживает Roistat, и держать дашборды в нем. Но в этом случае важно понимать, что данные там могут быть неполными или некорректными, а сообщение с бэком компании — ненадежным. В результате обнаружить или исправить ошибки может быть сложнее, чем организовать прямую выгрузку данных в хранилище — и здесь кастомная система отлично справится.&lt;/p&gt;
&lt;p&gt;&lt;i&gt;*Facebook принадлежит компании Meta, которая признана экстремистской организацией в России.&lt;/i&gt;&lt;/p&gt;
</description>
<pubDate>Wed, 02 Oct 2024 15:25:45 +0300</pubDate>
</item>

<item>
<title>Clickhouse в качестве consumer для Amazon MSK</title>
<guid isPermaLink="false">34</guid>
<link>http://test.leftjoin.ru/all/clickhouse-as-a-consumer-to-amazon-msk/</link>
<comments>http://test.leftjoin.ru/all/clickhouse-as-a-consumer-to-amazon-msk/</comments>
<description>
&lt;p class="note"&gt;&lt;b&gt;Disclaimer&lt;/b&gt;: заметка носит технический характер, поэтому может быть интересна меньшему числу лиц с бизнес-бэкграундом.&lt;/p&gt;
&lt;p&gt;В этом блоге еще ни разу не затрагивалась тема &lt;a href="https://clickhouse.yandex/"&gt;Clickhouse&lt;/a&gt;, одной из самых быстрых баз данных от компании Яндекс. Краткая справка без деталей: Clickhouse — &lt;a href="https://habr.com/ru/company/otus/blog/463665/"&gt;наиболее эффективно написанная СУБД&lt;/a&gt; колоночного типа с точки зрения программного кода, информация о СУБД довольно &lt;a href="https://clickhouse.tech/docs/ru/"&gt;подробно описана в документации&lt;/a&gt; и во множестве видео на Youtube (&lt;a href="https://www.youtube.com/watch?v=bSyQahMVZ7w"&gt;раз&lt;/a&gt;, &lt;a href="https://www.youtube.com/watch?v=Ac2C2G2g8Cg"&gt;два&lt;/a&gt;, &lt;a href="https://www.youtube.com/watch?v=ltg8vstuHUU"&gt;три&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;В своей практике последние четыре года я использовал Clickhouse в качестве аналитика и эксперта по построению аналитической отчетности. В основном для решения задачи визуализации отчетности / отчетов с параметрами / дашбордов использовался &lt;a href="all/redash-polnocennaya-on-demand-analitika/"&gt;Redash&lt;/a&gt; как наиболее удобный интерфейс для доступа к данным Clickhouse.&lt;br /&gt;
Однако совсем недавно в Looker, &lt;a href="all/looker-overview/"&gt;о котором я рассказывал ранее&lt;/a&gt;, появилась возможность подключить Clickhouse в качестве источника данных. Следует заметить, что подключение к Clickhouse в Tableau существует уже довольно давно.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/setup-clickhouse-new-connection-624@2x.png" width="635" height="761" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Архитектура аналитического сервиса, в основе которого лежит Clickhouse, в основном облачная. И в рассматриваемой задаче было именно так. Предположим, у вас существует выделенный instance EC2 в Amazon (на который вы установили Clickhouse) и отдельный Kafka-кластер (решение &lt;a href="https://aws.amazon.com/ru/msk/"&gt;Amazon MSK&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Задача&lt;/b&gt;: подключить Clickhouse в качестве &lt;i&gt;consumer&lt;/i&gt; для получения информации с брокеров вашего кластера Kafka. На самом деле, в документации на сайте Amazon MSK довольно подробно описано &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/create-client-machine.html"&gt;как именно подключаться к кластеру Kafka&lt;/a&gt;, не буду дублировать эту информацию. В моем случае гайд помог: топики создавались продюсером с машины, на которой установлен Clickhouse и с неё читались консюмером.&lt;/p&gt;
&lt;p&gt;Но возникла проблема: при &lt;a href="https://clickhouse.tech/docs/ru/operations/table_engines/kafka/"&gt;подключении Clickhouse к Kafka в качестве консюмера&lt;/a&gt;, происходила следующая ошибка:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;020.02.02 18:01:56.209132 [ 46 ] {e7124cd5-2144-4b1d-bd49-8a410cdbd607} &amp;lt;Error&amp;gt; executeQuery: std::exception. Code: 1001, type: cppkafka::HandleException, e.what() = Local: Timed out, version = 20.1.2.4 (official build) (from 127.0.0.1:46586) (in query: SELECT * FROM events), Stack trace (when copying this message, always include the lines below):&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Продолжительное время я искал информацию в документации Clickhouse о том, что может вызывать эту ошибку, но не смог ничего найти. Следующей мыслью стала проверка работы локального брокера Kafka с той же машины. Установил клиент Kafka, подключил Clickhouse, отправил данные в топик и Clickhouse с легкостью их прочитал, т. е. консюмер Clickhouse работает с локальным брокером, а значит и вовсе работает.&lt;/p&gt;
&lt;p&gt;Поговорив со всеми своими знакомыми экспертами в области инфраструктуры и Clickhouse (&lt;a href="https://rebrainme.com/devops/"&gt;Вася&lt;/a&gt;, Макс, привет!), с ходу мы не смогли определить в чем проблема. Проверили firewall, настройки сети, все было открыто. Подтверждалось также тем, что с локальной машины можно было отправить в топик удаленного брокера Kafka сообщения командой &lt;i&gt;bin/kafka-console-producer.sh&lt;/i&gt; и прочитать оттуда же &lt;i&gt;bin/kafka-console-consumer.sh&lt;/i&gt;.&lt;/p&gt;
&lt;p&gt;Затем мне пришла в голову мысль обратиться к главному гуру и разработчику Clickhouse — Алексею Миловидову. Алексей с радостью постарался ответить на возникшие вопросы и предложил ряд гипотез, которые мы проверили (трассировку сетевых подключений и т. п.), однако и после более низкоуровневого аудита проблему локализовать не удалось. Тогда Алексей посоветовал обратиться к Михаилу Филимонову из компании &lt;a href="https://www.altinity.com/"&gt;Altinity&lt;/a&gt;. Михаил оказался очень отзывчивым экспертом и одну за другой предлагал гипотезы для проверки (параллельно подсказывая как именно будет лучше их проверить).&lt;/p&gt;
&lt;p&gt;В итоге в ходе совместных усилий мы обнаружили, что проблема возникает у библиотеки &lt;i&gt;librdkafka&lt;/i&gt;, так как другой пакет &lt;i&gt;kafkacat&lt;/i&gt;, который использует эту же библиотеку отваливается от подключения к брокеру с той же проблемой (&lt;i&gt;Local: timed out&lt;/i&gt;).&lt;/p&gt;
&lt;p&gt;После изучения подключения через &lt;i&gt;bin/kafka-console-consumer.sh&lt;/i&gt; и параметров подключения, Михаил посоветовал добавить такую строку в /&lt;i&gt;etc/clickhouse-server/config.xml&lt;/i&gt;:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;&amp;lt;kafka&amp;gt;&amp;lt;security_protocol&amp;gt;ssl&amp;lt;/security_protocol&amp;gt;&amp;lt;/kafka&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;И, о чудо! Clickhouse подключился к кластеру и вытянул необходимые данные с брокера.&lt;/p&gt;
&lt;p&gt;Надеюсь, этот рецепт и мой опыт позволит вам сэкономить время и силы на изучение похожей проблемы :)&lt;/p&gt;
</description>
<pubDate>Tue, 04 Feb 2020 11:55:18 +0300</pubDate>
</item>


</channel>
</rss>