<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Карим Абушаев</title><generator>teletype.in</generator><description><![CDATA[Карим Абушаев]]></description><image><url>https://img3.teletype.in/files/eb/16/eb16c151-f664-463f-9966-2e96d01bd8e4.png</url><title>Карим Абушаев</title><link>https://teletype.in/@karim_abushaev</link></image><link>https://teletype.in/@karim_abushaev?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=karim_abushaev</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/karim_abushaev?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/karim_abushaev?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Wed, 15 Apr 2026 15:06:23 GMT</pubDate><lastBuildDate>Wed, 15 Apr 2026 15:06:23 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@karim_abushaev/reYDxlWAZEa</guid><link>https://teletype.in/@karim_abushaev/reYDxlWAZEa?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=karim_abushaev</link><comments>https://teletype.in/@karim_abushaev/reYDxlWAZEa?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=karim_abushaev#comments</comments><dc:creator>karim_abushaev</dc:creator><title>Stream API. Реактивное программирование FLUX</title><pubDate>Tue, 14 Apr 2026 09:53:46 GMT</pubDate><description><![CDATA[Когда я выбираю тему для исследования, я думаю о пользе для специалиста, особенно для тех, кто недавно в профессии.]]></description><content:encoded><![CDATA[
  <p id="c97e"><strong>Когда я выбираю тему для исследования, я думаю о пользе для специалиста, особенно для тех, кто недавно в профессии.</strong></p>
  <p id="H5d5">Однако, если ты опытный специалист и постоянно используешь стримы в своей работе, возможно даже для тебя будет изюминка, ради которой тебе стоит прочитать статью. Я подпишу блок для тебя как *ИЗЮМИНКА* </p>
  <p id="XX3E">Захотелось рассмотреть важную тему Stream API, но чтобы сделать статью интереснее, я решил сравнить его с инструментами реактивного программирования — <strong>Flux и Mono</strong> из Project Reactor.</p>
  <p id="afMP"><strong>Начнём с того, что такое Stream и в чём его схожесть с Flux.</strong></p>
  <hr />
  <figure id="LZTY" class="m_column">
    <img src="https://img1.teletype.in/files/8f/78/8f78bde0-f9cc-4fcd-a8fb-ea8456201055.png" width="2400" />
  </figure>
  <hr />
  <blockquote id="FXqM">Stream — это тип данных в Java, который представляет собой <strong>поток элементов</strong>. Когда я объясняю ученикам, что такое поток данных, я предлагаю визуализацию: представьте реку, по которой плывут лодки. Река — это поток, лодки — это данные.</blockquote>
  <p id="XDlN">Технический момент: Stream <strong>не создаёт отдельный поток операционной системы</strong> (thread). Он выполняется в том потоке, который его вызвал (например, main). Данные идут друг за другом, последовательно. Обычно стримы конечны, хотя и есть возможность сделать их бесконечными с помощью <code>Stream.iterate()</code> или <code>generate()</code>. Тогда потоки нужно ограничивать оператором <code>.limit()</code>, иначе они будет работать вечно -&gt; это приведет к сингулярности.</p>
  <p id="8vGh">Мы можем преобразовать коллекции в  поток данных  (вызвать <code>.stream()</code> у коллекции) и с помощью стандартных команд обработать каждый элемент: отфильтровать (<code>filter</code>), изменить/преобразовать (<code>map</code>), ограничить количество (<code>limit</code>).</p>
  <blockquote id="y7i7">Небольшое отступление. Исходя из методов, можно заметить сходство Stream c Flux. Именно поэтому я и рассматриваю их в связке. Вы можете проработать 2+ года разработчикам и не разу не встретить такой тип данных как Flux и как раз чтобы у вас не было удивления, когда вы все таки встретитесь, я сравниваю именно эти инструменты. У Flux тоже есть <code>filter</code>, <code>map</code>, <code>flatMap</code>, <code>take</code> (аналог <code>limit</code>). И на первы взгляд вам может показаться, что это одно и тоже, но это не так, сегодня мы разберем разницу, чуть дальше по тексту.</blockquote>
  <p id="H1pe">Помним аналогию: </p>
  <blockquote id="1V4a">У нас есть река (Stream), по которой плывут лодки (данные).</blockquote>
  <p id="DZuM"> А как работать с таким потоком? </p>
  <p id="LWX0">О Стримах важно знать разделение между доступными операциями - это промежуточные(такие операции запускается сразу после вызова стрима у коллекции) и терминальные(финальные операции, обычно их используют чтобы собрать данные в новую коллекцию). </p>
  <blockquote id="mk4F"><strong>Промежуточные </strong>— это <code>filter</code>, <code>map</code>, <code>flatMap</code>, <code>limit</code>, <code>skip</code>. Они не запускают обработку, а только описывают, что нужно сделать. Их можно навешивать сколько угодно. Результат такой операции — новый стрим или новый Flux.</blockquote>
  <blockquote id="EWnk"><strong>Терминальные</strong> — это <code>collect</code>, <code>forEach</code>, <code>reduce</code>, <code>subscribe</code>. Они запускают всю цепочку. После них поток данных закрывается. Повторно использовать тот же стрим или Flux нельзя.</blockquote>
  <p id="7W88">В Stream терминальная операция одна — без неё ничего не выполнится. Во Flux терминальная операция — это <code>subscribe()</code>. Без неё Flux просто ничего не делает.</p>
  <p id="BVVN">Ниже разберем основные методы у стримов:</p>
  <p id="5qsH"><strong>filter</strong> — метод фильтрует данные по условиям, которые вы указываете.</p>
  <p id="O1oV">java</p>
  <pre id="bONt">List&lt;Integer&gt; numbers = List.of(1, 2, 3, 4, 5);
numbers.stream()
    .filter(n -&gt; n % 2 == 0)   // оставляем чётные
    .forEach(System.out::println); // 2, 4</pre>
  <p id="dO03"><strong>map</strong> — преобразует каждый элемент. Например, мы можем пройтись стримом по списку строковых значений, взять каждый элемент, привести к верхнему регистру, а потом распечатать каждый элемент или собрать в новый список.</p>
  <p id="8hjw">java</p>
  <pre id="H21L">List&lt;String&gt; words = List.of(&quot;cat&quot;, &quot;dog&quot;, &quot;mouse&quot;);
words.stream()
    .map(String::toUpperCase)
    .forEach(System.out::println); // CAT, DOG, MOUSE</pre>
  <p id="N5gW"><strong>flatMap</strong> — разворачивает вложенные потоки. Сильный инструмент. Иногда вам представится необходимость пройтись по списку, в котором каждый отдельный элемент тоже будет являться списком. И вам нужно будет эту вложенность развернуть наизнанку, взять каждый элемент и обработать его. Например, собрать в лист.</p>
  <p id="UH5y">java</p>
  <pre id="ffDx">List&lt;List&lt;String&gt;&gt; nested = List.of(List.of(&quot;dog&quot;, &quot;cat&quot;), List.of(&quot;lion&quot;, &quot;bird&quot;));
List&lt;String&gt; result = nested.stream()
    .flatMap(list -&gt; list.stream())
    .map(String::toUpperCase)
    .toList();</pre>
  <p id="elOg"><strong>limit</strong> — берём только первые N элементов. Как если бы мы сказали: «Возьми только первые пять элементов, остальные не интересуют».</p>
  <p id="IXEe">java</p>
  <pre id="SqnV">Stream.iterate(0, i -&gt; i + 1)
    .limit(5)
    .forEach(System.out::println); // 0,1,2,3,4</pre>
  <p id="SaXN"><strong>skip</strong> — пропускаем первые N элементов. Смотрим на поток, но первые три не трогаем.</p>
  <p id="GFy1">java</p>
  <pre id="GYkU">Stream.of(1,2,3,4,5)
    .skip(2)
    .forEach(System.out::println); // 3,4,5</pre>
  <p id="SmJr"><strong>collect</strong> — собираем все элементы в коллекцию (в List, Set, Map). Самый частый терминальный оператор.</p>
  <p id="ywFg">java</p>
  <pre id="200E">List&lt;String&gt; result = words.stream()
    .filter(w -&gt; w.length() &gt; 3)
    .collect(Collectors.toList());</pre>
  <p id="8YW0"><strong>forEach</strong> — работает как цикл фор, перебирает элементы и можно описать любую логику для обработки элемента внутри forEach()</p>
  <p id="vFYM"><strong>reduce</strong> — сворачивает весь поток в один результат. Например, считаем сумму всех элементов.</p>
  <p id="JxM6">java</p>
  <pre id="i2o9">int sum = Stream.of(1,2,3,4)
    .reduce(0, (a, b) -&gt; a + b); // 10</pre>
  <blockquote id="tZSL"><strong>Важное правило:</strong> стрим можно использовать только один раз. После того как ты применил терминальную операцию (<code>collect</code>, <code>forEach</code>, <code>reduce</code>), река пересыхает. Попытка снова вызвать операцию упадёт с ошибкой.</blockquote>
  <p id="qdua"><em>Правило о том, что стрим нельзя использовать второй раз, предлагаю вам запомнить. У меня был собес в Сбере, и собеседующий задал мне вопрос: можем ли мы обратиться к стриму снова? Я недолго думая сказал: «Да». Однако терминальная операция закрывает стрим, и мы больше не имеем доступа к этому стриму. Но вы можете заново запустить стрим у той же коллекции, если необходимо, или у новой коллекции, которую отфильтровали.</em></p>
  <p id="BVuF">Предлагаю на пальцах рассмотреть пример работы стрима с преобразованием данных. Чтобы точно получилось понять всем как это работает я буду максимально подробен, чуть не забыл!</p>
  <p id="Igdt">*ИЗЮМИНКА* - разбирая код мы рассмотрим что такое -&gt; лямбда и как она создает анонимная метод. Почему именно это я считаю изюминкой, просто потому что сам долго пользовался стримами, но совсем недавно понял до конца что такое анонимный метод.</p>
  <p id="DzaR">Что нам понадобится для понимания</p>
  <p id="XXhb">Прежде чем писать код, разберём термины, которые часто пугают.</p>
  <blockquote id="yZPo"><strong>Анонимный метод</strong> — это метод без имени. Ты не объявляешь его отдельно, а пишешь прям на месте, где он нужен. Как если бы тебе сказали: «дай сюда действие, которое я выполню для каждого элемента». Ты берёшь и даёшь это действие тут же, не создавая отдельную функцию.</blockquote>
  <blockquote id="bhuk"><strong>Лямбда</strong> — это способ записать анонимный метод коротко. Вместо того чтобы писать:</blockquote>
  <p id="RFhM">java</p>
  <pre id="xKTQ">public void doSomething(String s) { // вот так выглядит обычный метод
    System.out.println(s);
}</pre>
  <p id="7WqC">А анонимный метод пишется чере лямбду -&gt; ты просто оборачиваешь логику в скрытый от глаз метод, хотя это буквально реализация метода:</p>
  <p id="OvN9">java</p>
  <pre id="GVB0">s -&gt; System.out.println(s) // если логики много обособляешь блок кода {}</pre>
  <p id="Fv1j">Стрелочка <code>-&gt;</code> разделяет параметры и тело метода. Слева то, что приходит на вход (аргументы вашего анонимного метода), справа — что делаем.</p>
  <blockquote id="ejXe"><strong>Функциональный интерфейс</strong> — это интерфейс с одним методом. Например, <code>Function&lt;T, R&gt;</code>(принимает T, возвращает R), <code>Consumer&lt;T&gt;</code> (принимает T, ничего не возвращает), <code>Predicate&lt;T&gt;</code>(принимает T, возвращает boolean). Именно такие интерфейсы можно заменить лямбдой.</blockquote>
  <hr />
  <p id="PSEP">Предлагаю решить простую задачу, чтобы закрепить знания по Stream</p>
  <p id="sorl">У нас есть карта (<code>HashMap</code>), где ключ — имя сотрудника, значение — его грейд (Junior, Middle, Senior). Мы хотим:</p>
  <ol id="A9cH">
    <li id="Q0QP">Пройти по всем записям</li>
    <li id="fgYh">Для каждого сотрудника с грейдом Junior изменить значение на Middle</li>
    <li id="zJdZ">Добавить приписку «(повышен)»</li>
    <li id="eTT8">Вывести результат</li>
  </ol>
  <hr />
  <blockquote id="xWIB">Кстати я всегда для себя комментариями пишу пошагавшую инструкцию, как я сделал выше, для того чтобы мне самому было легче разрабатывать. Так что берите на заметку и не бойтесь писать черновые наброски для себя.</blockquote>
  <hr />
  <h3 id="n7AS">java</h3>
  <pre id="wMHP">// 1. Создаём HashMap и заполняем её
Map&lt;String, String&gt; employees = new HashMap&lt;&gt;();
employees.put(&quot;Анна&quot;, &quot;Junior&quot;);
employees.put(&quot;Борис&quot;, &quot;Middle&quot;);
employees.put(&quot;Виктор&quot;, &quot;Junior&quot;);
employees.put(&quot;Карим&quot;, &quot;Senior&quot;);

// 2. Используем стрим, чтобы пройти по всем записям
Map&lt;String, String&gt; updatedEmployees = employees.entrySet().stream()
    // .entrySet() — получаем набор пар (ключ + значение)
    // .stream() — открываем стрим для этих пар
    
    .map(entry -&gt; {
        // 3. Лямбда для преобразования каждой пары, помним,
        // мы создали анонимный метод в который передаем по очереди каждый элемент
        // entry — это один элемент стрима (пара &quot;ключ=значение&quot;)
        
        String name = entry.getKey();      // достаём имя (ключ)
        String grade = entry.getValue();   // достаём грейд (значение)
        
        // 4. Если грейд Junior, то повышаем до Middle
        if (&quot;Junior&quot;.equals(grade)) {
            grade = &quot;Middle (повышен)&quot;;
        }
        
        // 5. Возвращаем новую пару (такое же имя, обновлённый грейд)
        return Map.entry(name, grade);
    })
    // 6. Собираем результат обратно в HashMap
    .collect(Collectors.toMap(
        Map.Entry::getKey,   // ключ оставляем тот же
        Map.Entry::getValue  // значение — возможно, изменённое
    ));
</pre>
  <hr />
  <p id="W6f2">Остановимся на части кода, где вы могли заметить нетривиальную запись <code>Map.Entry::getKey</code></p>
  <p id="OxqQ">Это ссылка на метод. Короткая запись вместо <code>entry -&gt; entry.getKey()</code>. Тоже лямбда, просто ещё короче. То есть указываем объект и через :: указываем метод который будет применен.</p>
  <hr />
  <blockquote id="x45I">А теперь просто представьте, как выглядел бы код, если бы мы вместо лямбд каждый раз писали обычный метод. Мы захламили бы весь код и разбираться в нем было бы значительно сложнее.</blockquote>
  <p id="XokK">Финально закрепим как это выглядело бы на примере одного метода. Вернёмся к лямбде:</p>
  <p id="PiMD">java</p>
  <pre id="F5wE">entry -&gt; {
    String name = entry.getKey();
    String grade = entry.getValue();
    if (&quot;Junior&quot;.equals(grade)) {
        grade = &quot;Middle (повышен)&quot;;
    }
    return Map.entry(name, grade);
}</pre>
  <p id="TdJq">Под капотом Java превращает это в анонимный класс, примерно такой:</p>
  <p id="erIF">java</p>
  <pre id="IzHV">new Function&lt;Map.Entry&lt;String, String&gt;, Map.Entry&lt;String, String&gt;&gt;() {
    @Override
    public Map.Entry&lt;String, String&gt; apply(Map.Entry&lt;String, String&gt; entry) {
        String name = entry.getKey();
        String grade = entry.getValue();
        if (&quot;Junior&quot;.equals(grade)) {
            grade = &quot;Middle (повышен)&quot;;
        }
        return Map.entry(name, grade);
    }
}</pre>
  <p id="i9CI">В первом случае 7 строк кода, во втором — 11, плюс аннотации и модификаторы (<code>new</code>, <code>Function</code>, <code>@Override</code>, <code>public</code>, <code>apply</code>). Лямбда просто скрывает этот boilerplate. И оставляет синтетический сахар. </p>
  <hr />
  <p id="nUlI">Внимательный читатель и разработчик, который первый раз встретился со стримами, подумает, почему бы не использовать просто цикл, чтобы перебирать элементы.</p>
  <p id="Q51w">Мы могли бы написать:</p>
  <p id="HePM">java</p>
  <pre id="VIAP">for (Map.Entry&lt;String, String&gt; entry : employees.entrySet()) {
    // та же логика
}</pre>
  <p id="i5F7">И это было бы нормально. Но стрим даёт нам:</p>
  <ol id="coDR">
    <li id="UF6c"><strong>Декларативность</strong> — мы говорим «что сделать», а не «как сделать»</li>
    <li id="tY5m"><strong>Цепочки</strong> — можно добавить <code>filter</code>, потом <code>map</code>, потом <code>collect</code>, и это читается как предложение</li>
    <li id="2RI2"><strong>Параллельную обработку</strong> — достаточно добавить <code>.parallelStream()</code> вместо <code>.stream()</code>, и Java сама разложит задачу по ядрам</li>
    <li id="shXZ"><strong>Ленивость</strong> — если мы напишем <code>limit(10)</code>, стрим не будет обрабатывать все элементы, а остановится после десятого</li>
  </ol>
  <p id="ZZ6o">В нашем примере с мапой из 4 элементов разница незаметна. Однако со временем, когда с постоянным использованием стримов, конструкции работы с таким инструментом сформируются у вас. Вы поймете, что это удобно и быстро. Особенно стрим ценятся именно за декларативность, как я уже сказал выше.<br /><br />Теперь мы готовы рассмотреть Flux и разобраться в его ключевых отличиях от Stream</p>
  <hr />
  <figure id="mVzp" class="m_column">
    <img src="https://img1.teletype.in/files/8c/3f/8c3fa636-f289-481b-9d57-8686d2b94927.png" width="2400" />
  </figure>
  <hr />
  <blockquote id="AXPc"><strong>Flux</strong> — это интструмент из Project Reactor. Это неограниченная последовательность от 0 до N элементов. Работает по системе подписки, сначала нужно описать действия, что сделать посредством методов, а потом подписаться на последовательность Flux.</blockquote>
  <p id="svfs">Главное отличие от Stream: Stream — это данные, которые уже есть в памяти. Flux — это данные, которые <strong>ещё только придут</strong>. Может, через секунду. Может, через час. Мы заранее не знаем, когда и сколько мы всего лишь открываем соединение.</p>
  <p id="C2Vr">java</p>
  <pre id="67cJ">Flux&lt;Integer&gt; flux = Flux.just(1, 2, 3, 4, 5);
flux.map(i -&gt; i * 2);
// описали действия

flux.subscribe(System.out::println);
// Подписались, после чего запустился процесс обработки</pre>
  <hr />
  <p id="c7P0">Mono — брат-близнец Flux, только для одного элемента, что в принципе следует из его названия.</p>
  <blockquote id="Er5P">Если Flux — это последовательность от 0 до N элементов, то Mono — это 0 или 1 элемент. Всё остальное то же самое: подписка, ленивость, операторы.</blockquote>
  <p id="AHve">java</p>
  <pre id="DOZk">Mono&lt;String&gt; mono = Mono.just(&quot;Привет&quot;);
mono.map(String::toUpperCase)
    .subscribe(System.out::println);</pre>
  <p id="fp93">Где используется Mono? Там, где ты ожидаешь не более одного результата:</p>
  <ul id="dqmB">
    <li id="g1SI">Запрос в БД по ID</li>
    <li id="IoDn">GET запрос по одному ресурсу</li>
    <li id="5Ku2">Ответ от внешнего API, который возвращает один объект</li>
  </ul>
  <p id="WGC1">Вот основные методы Flux:</p>
  <p id="VfmP"><code>Flux.just(1, 2, 3)</code> — создаёт Flux из конкретных значений, которые ты перечисляешь.</p>
  <p id="aysu"><code>Flux.fromIterable(list)</code> — создаёт Flux из коллекции (List, Set). Всё, что есть в списке, станет элементами потока.</p>
  <p id="J2Sm"><code>Flux.range(1, 10)</code> — генерирует числа от 1 до 10. Первое число — старт, второе — сколько штук.</p>
  <p id="WhKQ"><code>map(x -&gt; x * 2)</code> — применяет функцию к каждому элементу и возвращает новый элемент. Из числа делает число, из строки — другую строку.</p>
  <p id="TLv4"><code>filter(x -&gt; x &gt; 5)</code> — пропускает дальше только те элементы, которые подходят под условие. Остальные отбрасываются.</p>
  <p id="14A3"><code>flatMap(x -&gt; anotherFlux(x))</code> — для каждого элемента вызывает асинхронную операцию (которая возвращает Flux), а потом все результаты склеивает в один общий поток. Самый мощный, но и самый сложный оператор. В целом тоже самое  что и у Stream.</p>
  <p id="s5DO"><code>take(10)</code> — берёт первые N элементов, остальные игнорирует. Если элементов меньше — возьмёт сколько есть.</p>
  <p id="Qs3Q"><code>collectList()</code> — ждёт, когда поток закончится, и собирает все элементы в один List. Превращает Flux в Mono&lt;List&lt;T&gt;&gt;.</p>
  <p id="BMcB"><code>subscribe(x -&gt; doSomething(x))</code> — самое главное. Подписывается на поток и запускает его. Без этого Flux ничего не делает.</p>
  <hr />
  <figure id="RF3h" class="m_column">
    <img src="https://img1.teletype.in/files/4d/e0/4de0828d-b912-47b4-ac1e-8fb0750df3a2.png" width="2400" />
  </figure>
  <hr />
  <p id="mixz">Итоговые вопросы для проверки себя:</p>
  <ul id="3rLP">
    <li id="5TsI">Чем метод map() отличается от flatMap()?</li>
    <li id="lUut">Почему Stream по умолчанию конечен, а Flux не имеет размера?</li>
    <li id="NgZT">Без чего Flux не начнет работать?</li>
    <li id="S58c">Расскажи своими словами, что такое промежуточные и терминальные операции?</li>
  </ul>
  <p id="VMO2">Теперь предлагаю тебе самостоятельно порешать задачки с помощью стримов, такие часто дают на собеседованиях. Мой личный опыт, каждая третья задача будет решаться с помощью Stream.</p>
  <p id="dYBb">Мой тг -@karim_product на связи родной/родная ;-)</p>
  <p id="MxMo">Если было полезно, поддержи подпиской. Мне будет мотивация продолжать в том же духе. Мой канал - <a href="https://t.me/+uH8Hm6kPWhU2OTc6" target="_blank">https://t.me/+uH8Hm6kPWhU2OTc6</a></p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@karim_abushaev/nx8OwnMj7Qr</guid><link>https://teletype.in/@karim_abushaev/nx8OwnMj7Qr?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=karim_abushaev</link><comments>https://teletype.in/@karim_abushaev/nx8OwnMj7Qr?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=karim_abushaev#comments</comments><dc:creator>karim_abushaev</dc:creator><title>Kafka. WebClient. Feign. WebSocket. Или как общаются микросервисы.</title><pubDate>Mon, 13 Apr 2026 14:20:48 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/6b/ad/6bad8adc-0c68-4e1a-8db1-af71e675f8b3.png"></media:content><description><![CDATA[<img src="https://img1.teletype.in/files/8d/91/8d918eb9-af5e-48d5-8f38-0b5780d18e48.png"></img>Начнем с того, как микросервисы могут общаться? На самом деле все просто, сложные приложения могут состоять из нескольких разных микросервисов.]]></description><content:encoded><![CDATA[
  <p id="B58a">Начнем с того, как микросервисы могут общаться. На самом деле всё просто: сложные приложения могут состоять из нескольких разных микросервисов.</p>
  <p id="fQHb">Каждый сервис будет иметь свою логику, свою ответственность. Сервисы одной системы могут быть написаны на разных языках программирования. Однако это не будет мешать им общаться. Так вот, общение — это буквально обмен информацией, обмен сообщениями определённого формата, который смогут понять все сервисы. Это похоже на общение между нами. Я говорю что-то, собеседник слушает информацию, дальше обрабатывает её неким образом своим мыслительным аппаратом, формирует ответное сообщение и проговаривает его вслух, адресуя голос в направлении оппонента. Для отправки сообщения нам, людям, нужно знать адресата или видеть его, чтобы обратиться к нему.</p>
  <p id="XdRu">Адресату нужно слышать и в идеале уметь понимать, на каком языке говорит другой человек. Если вы знаете несколько языков, то вы сможете принять сообщение на одном языке, обработать его, перевести в своей голове и выдать перевод другому человеку. Все эти модели общения похожим образом переносятся на взаимодействие между сервисами.</p>
  <hr />
  <figure id="tfQ3" class="m_column">
    <img src="https://img1.teletype.in/files/8d/91/8d918eb9-af5e-48d5-8f38-0b5780d18e48.png" width="2400" />
    <figcaption>Ох уж эти сервисы-экстраверты — ни минуты не могут провести молча.</figcaption>
  </figure>
  <hr />
  <p id="s3wh">Теперь давайте поговорим буквально о существующих инструментах общения. Можем разделить на две модели:</p>
  <p id="VU0D"><strong>Первый вариант.</strong> Общение посредством прямого обращения к существующим методам другого сервиса по URL. То есть мы знаем, что такой-то сервис имеет определённые методы, в которые мы можем обратиться. Посредством контроллеров по URL мы можем отправить понятный запрос и получить предполагаемый ответ.</p>
  <p id="YkwF"><strong>Второй вариант.</strong> Общение посредством брокера сообщений, например Kafka/Rabbit. Тут всё немного сложнее, так как нас как отправителя не заботит, как будут обработаны сообщения, нас не интересует ответ на наше отправленное сообщение. Нас интересует адрес, куда будем направлять сообщения, и адрес, откуда считываем. Простыми словами: есть хранилище сообщений, где всё разложено по полкам, каждая полка подписана (topic), и мы можем направлять сообщения на одну полку или считывать сообщения только с определённой полки. Прямого взаимодействия с сервисом у нас нет.</p>
  <p id="Mwhb">Итак, разберём, как общаться сервисам в первом варианте. Инструменты, которые вы можете использовать:</p>
  <blockquote id="LuNt"><strong>RestTemplate</strong> — блокирующий HTTP-клиент. Прост в использовании, но не рекомендуется для новых проектов.</blockquote>
  <blockquote id="7H7y"><strong>WebClient</strong> — реактивный. Неблокирующий асинхронный клиент на Project Reactor. Идеален для высоконагруженных систем. Обеспечивает асинхронное/реактивное общение через HTTP.</blockquote>
  <blockquote id="Xa2r"><strong>Feign Client</strong> — декларативный. Описываете интерфейс с аннотациями — Spring генерирует реализацию. Минимум кода. Обеспечивает синхронное общение через HTTP (REST).</blockquote>
  <blockquote id="tMbE"><strong>Декларативность</strong> — это парадигма программирования, когда мы фокусируемся на том, что нам нужно сделать, а не как. То есть декларативность обеспечивает вам лёгкую и понятную реализацию, где вы не лезете под капот, а пользуетесь доступными инструментами.</blockquote>
  <p id="XxBk">Выше вы могли обратить внимание на такое свойство WebClient, как <strong>реактивность</strong>, и давайте на этом моменте остановимся. В своей статье про многопоточность я касался темы синхронности и асинхронности. Поэтому останавливаться на этом я не буду, а про реактивность давайте поговорим.</p>
  <p id="St5q"><strong>Реактивность</strong> — это возможность системы реактивно реагировать на обновление данных. Соответственно, реактивная обработка потоков данных невозможна без асинхронной обработки. Однако не только параллельная обработка данных достигается реактивностью.</p>
  <ul id="Dows">
    <li id="eaDq">В отличие от обычной асинхронности, реактивность работает с <strong>потоками данных</strong> (множество значений во времени), а не с единичными результатами.</li>
    <li id="bhLY">Ключевая особенность реактивности — <strong>backpressure (обратное давление)</strong>: потребитель данных может замедлить работу поставщика данных при необходимости.</li>
    <li id="yE8t">Реактивность даёт декларативные <strong>операторы</strong> (map, filter, flatMap, buffer), позволяя красиво и лаконично преобразовывать потоки данных. Вы, скорее всего, подумали, что речь идёт о Stream API. Но нет: просто в реактивном программировании есть инструмент <strong>Flux&lt;T&gt;</strong>, который так же, как и Stream API, работает как поток данных, однако может обрабатывать поступающие данные через такие же методы асинхронно. То есть работает с поступающими данными асинхронно.</li>
  </ul>
  <p id="IMlw">java</p>
  <pre id="xK2s">Flux&lt;User&gt; users = userRepository.findAll();
Flux&lt;String&gt; names = users
    .filter(u -&gt; u.getAge() &gt; 18)
    .map(User::getName)
    .take(10);
// ПОДПИСЫВАЕМСЯ, и только после этого начинаются обращения в БД
names.subscribe(name -&gt; {
    System.out.println(&quot;Получено имя: &quot; + name);
});</pre>
  <p id="x3g7">Посмотрите на код выше. Сразу обозначим разницу от Stream API: Flux — мы можем получать прямо из БД, это всего лишь возможность запустить поток, по которому пойдут данные. Далее мы применяем инструкции с помощью команд (map, filter, flatMap, buffer), но всё ещё в БД нет обращений. И только на этапе подписки <code>.subscribe(name -&gt;</code> мы начинаем вытаскивать данные и обрабатывать их. Вот мы слегка коснулись реактивного программирования.</p>
  <ul id="vpTP">
    <li id="1RvZ">В Java реактивность реализуется через <strong>Project Reactor</strong> (Mono/Flux) и Spring WebFlux, но она нужна только при высоких нагрузках или потоковой обработке — для обычного CRUD это избыточно.</li>
  </ul>
  <p id="BEQo">Теперь рассмотрим детальнее <strong>Feign Client</strong>. Это простой в использовании инструмент. Вы определяете Java-интерфейс с аннотациями Spring MVC, а Feign автоматически генерирует реализацию HTTP-клиента. Минимум кода, всё реализовано под капотом за счёт аннотаций, вручную не нужно писать HTTP-запросы. Есть возможность кэширования. Feign Client — синхронный.</p>
  <hr />
  <figure id="njKM" class="m_column">
    <img src="https://img1.teletype.in/files/80/cf/80cf369f-c2cc-4922-8558-fa5c482205d3.png" width="2400" />
    <figcaption>Feign Client </figcaption>
  </figure>
  <hr />
  <blockquote id="EYfW"><strong>Feign Client.</strong> Если сервис должен ждать ответ прямо сейчас — Feign проще. Если нужна максимальная пропускная способность — WebClient.</blockquote>
  <p id="0D7M">Также есть такой инструмент, как <strong>WebSocket</strong>. Это способ общения посредством событий, которые приходят в открытое соединение. Теперь нет никаких запросов. Есть канал для общения между сервисами, можно реализовать формат подписок. Один сервис бесконечно отправляет сообщения, другой считывает.</p>
  <p id="hVCD">Если Feign и WebClient — это когда ты звонишь другу, задаёшь вопрос, получаешь ответ и кладёшь трубку, то WebSocket — это когда вы встретились в кафе и болтаете без остановки. Оба могут говорить, когда хотят, не дожидаясь вопроса. WebSocket часто используют для чатов, игр, биржевых котировок — там, где данные должны появляться мгновенно, без постоянных «стуков» от клиента. Соединение одно, а сообщения летают туда-сюда. Но важный момент: если соединение оборвалось, данные могут потеряться. WebSocket не гарантирует доставку, как Kafka. Он про скорость, а не про надёжность.</p>
  <p id="e91g">И вот мы плавно перешли к мощнейшему инструменту — <strong>Kafka</strong>.</p>
  <hr />
  <figure id="rOJE" class="m_column">
    <img src="https://img2.teletype.in/files/96/38/9638b597-15e3-4572-99f0-c041a495ec5a.png" width="2400" />
    <figcaption>Kafka</figcaption>
  </figure>
  <hr />
  <blockquote id="gWJE"><strong>Kafka</strong> — это хранилище сообщений, распределённый брокер, который может хранить терабайты событий. И обещает не терять их, раздавая сотням потребителей. Если сообщение ушло в Кафку, можешь быть уверен — оно там и останется. В Кафке модель общения происходит через Consumer (потребителя) и Producer (отправителя). Сервис формирует сообщение и отправляет через продюсера сообщение в топик (topic — это подписанная полка, куда направляются сообщения; в Кафке может быть множество топиков, обычно их разделяют, чтобы разбивать информацию по блокам).</blockquote>
  <p id="dBTq"><strong>Почему Кафка — это мощно?</strong></p>
  <p id="z3dL">Потому что она даёт гарантии. <strong>At-least-once</strong> — сообщение не потеряется, но может прилететь два раза (если консьюмер упал после обработки, но до коммита). Для большинства сценариев — ок. А если нужно ровно один раз — есть <strong>exactly-once</strong> (через идемпотентность и транзакции). Кафка пишет на диск, реплицирует на несколько брокеров: один упал — другой подхватил. Ничего не теряется.</p>
  <blockquote id="rriu"><strong>Идемпотентность</strong> — это свойство операции отдавать один и тот же ответ на один и тот же запрос независимо от количества запросов. Например: вы запрашиваете запросом объект с id=9 — метод всегда будет возвращать один и тот же объект, ответ будет одинаковым. А есть метод POST: вы отправляете запрос на создание, и ответ всегда разный, вы создаёте сущность каждый раз с разными значениями, но значение id будет меняться, ведь вы создаёте новую запись.</blockquote>
  <p id="oBEK"><strong>Порядок внутри партиции — полный.</strong> Если тебе нужно, чтобы события одного пользователя шли строго по очереди — бросай его ID в ключ, и Кафка сама положит их в одну партицию. Также ты можешь настроить <strong>consumer group</strong> — запустил несколько консьюмеров, и они разберут партиции между собой, работая параллельно.</p>
  <hr />
  <figure id="aP80" class="m_column">
    <img src="https://img2.teletype.in/files/96/38/9638b597-15e3-4572-99f0-c041a495ec5a.png" width="2400" />
    <figcaption>Kafka</figcaption>
  </figure>
  <hr />
  <p id="suFl"><strong>Когда использовать Kafka?</strong></p>
  <p id="Wsxn">Спойлер: всегда, когда нужно обмениваться событийными сообщениями между сервисами.</p>
  <ul id="DO1l">
    <li id="zAaE">Когда у тебя события летят пачками, и сервисы не должны зависеть друг от друга (отправил email, начислил бонусы, записал в аналитику — всё асинхронно).</li>
    <li id="Y7nv">Когда нужно перечитывать историю (например, на лету пересчитать метрики за прошлую неделю).</li>
    <li id="Qnqt">Когда ты строишь потоковую обработку (Kafka Streams, ksqlDB) или ловишь изменения из БД (CDC).</li>
    <li id="HIYz">Когда надёжность важнее миллисекунды задержки.</li>
  </ul>
  <p id="QBVb"><strong>Минусы тоже есть:</strong></p>
  <ul id="PSiy">
    <li id="90IR">Сложная в настройке (кластер, партиции, репликация, ZooKeeper или KRaft — это боль, но оно того стоит). На самом деле базовая настройка не очень сложная, но гибкая настройка требует от тебя внимания к деталям.</li>
    <li id="X2Qf">Задержка выше, чем у прямого HTTP (не мгновенно, но для 99% асинхронных задач — незаметно).</li>
  </ul>
  <hr />
  <figure id="PUm6" class="m_column">
    <img src="https://img1.teletype.in/files/84/a7/84a72b35-9b7a-40fe-90a9-c0b8c8eb9f8a.png" width="2400" />
    <figcaption>Kafka</figcaption>
  </figure>
  <hr />
  <p id="HQMn"><em>Итак, мы разобрали основные способы общения микросервисов — от простых синхронных вызовов до реактивных потоков и брокеров сообщений.</em> Каждый инструмент закрывает свою зону ответственности. Feign — когда надо дёрнуть и получить ответ здесь и сейчас. WebClient — когда запросов много и жалко потоки. WebSocket — когда нужно живое общение туда-сюда без переподключения или когда стоит задача бесконечного потока временных рядов. Kafka — когда важна надёжность и асинхронность, и ты готов простить ей чуть бóльшую задержку ради гарантий и возможности переиграть историю.</p>
  <p id="lCdz">Оставайся со мной на связи, я исследую мир разработки и структурные данные в своих обучающих проектах. Хочешь двигаться со мной — жду тебя в ТГ: <strong>@karim_product</strong></p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@karim_abushaev/mZrFR5yBp8t</guid><link>https://teletype.in/@karim_abushaev/mZrFR5yBp8t?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=karim_abushaev</link><comments>https://teletype.in/@karim_abushaev/mZrFR5yBp8t?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=karim_abushaev#comments</comments><dc:creator>karim_abushaev</dc:creator><title>БАЗА, Победа будет...</title><pubDate>Thu, 09 Apr 2026 14:37:18 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/93/0b/930b4c5e-f58f-48c2-a8c3-343e8e0dd6da.png"></media:content><description><![CDATA[<img src="https://img1.teletype.in/files/0b/ac/0bac111e-a901-4a9e-9164-413acda180ad.jpeg"></img>Если серьезно, то сегодня мы поговорим про БАЗЫ данных. Как то один мой друг разработчик сказал, что программирование можно понимать как]]></description><content:encoded><![CDATA[
  <p id="yeAx">Если серьезно, то сегодня мы поговорим про БАЗЫ данных. Как-то один мой друг разработчик сказал, что программирование можно понимать как</p>
  <blockquote id="xKt3">&quot;настройка передачи данных из точки А в точку Б&quot;</blockquote>
  <figure id="M1Fx" class="m_column">
    <img src="https://img1.teletype.in/files/0b/ac/0bac111e-a901-4a9e-9164-413acda180ad.jpeg" width="1080" />
  </figure>
  <blockquote id="6Kw4">Повторюсь! Кажется при чем тут летящая машина. А это символика того, что данные летят из точки А в точку Б.</blockquote>
  <blockquote id="bMXH">Кажется, при чем тут летящая машина. А это символика того, что данные летят из точки А в точку Б.<br />Повторюсь! Кажется, при чем тут летящая машина. А это символика того, что данные летят из точки А в точку Б.<br />Так же осмелюсь сказать, что так ощущается отрыв от реальности, когда ты погружаешься в любую тему со мной =)<br />Поначалу это утверждение показалось мне недостаточно полным. Однако если убрать шелуху и оставить только суть, то программирование это буквально передача данных. Во время этого процесса мы можем изменять данные, но по итогу мы получаем буквально движение информации из точки А в точку Б.</blockquote>
  <blockquote id="ZM6g">И сегодня мы поговорим про точку Б — Базу данных.</blockquote>
  <p id="dPDJ">Обычно итоговые данные нужно хранить, передавать и обрабатывать. Хранение данных как раз обеспечивают базы данных. Обычно для хранения используют именно таблицы, хотя есть еще и хранилища, которые тоже внутри себя представляют таблицы, которые пользователям не показывают. И формат хранения определяется фундаментальной возможностью таблиц. Всего у нас два основных инструмента:</p>
  <blockquote id="h0OZ"><strong>Таблица</strong> — это удобный и структурированный подход к хранению данных, представляет собой колонки с названиями и строчки записей. Базы данных представляются в виде таблиц.</blockquote>
  <p id="6UDB"><strong>Реляционная БД (PostgreSQL, MySQL, Oracle)</strong> — из названия следует relation — связь. Связанные таблицы — это мощный инструмент для хранения данных и дальнейшего использования. Создание связи между таблицами позволит вам быстро находить информацию. Например, у вас есть таблица &quot;мировая таблица Бренды АВТО&quot; — внутри есть разные бренды: AUDI, Mercedes, BMW. В свою очередь, внутри, например, бренда BMW вы можете увидеть связанную таблицу автомобилей M5, 320i xDrive, M8, а внутри каждого автомобиля — таблицу с характеристиками. В такой таблице у вас появляется возможность быстро находить связанные данные и выгружать их.</p>
  <p id="MFEI"><strong>Нереляционная БД</strong> — очевидно, что тут речь идет уже о самостоятельных таблицах, которые не имеют никаких связей. Это менее удобный и ограниченный инструмент для хранения данных, потому что мы не сможем связывать данные из разных таблиц между собой. У вас есть одна таблица, которая ни с чем не связана. Грустно. Однако у нее есть свои плюсы: такие таблицы удобны, когда вам достаточно одной таблицы, но необходима скорость поиска.</p>
  <p id="dOoF">Вернемся к реляционным базам. Чаще всего в разработке приложений мы работаем с такими БД, потому что, как уже говорил выше, мы можем связать сущности и доставать необходимые данные в удобном формате. Вы, вероятно, обратили внимание на названия таких БД и заметили там приписку SQL.</p>
  <blockquote id="RNxA"><strong>SQL (Structured Query Language)</strong> — это структурный язык запросов, предназначенный для управления, хранения, изменения и извлечения данных в реляционных базах данных.</blockquote>
  <blockquote id="z7bQ"><strong>СУБД (система управления базами данных)</strong> — это комплекс программного обеспечения, предназначенный для создания, хранения, изменения, поиска и администрирования данных в электронном виде.</blockquote>
  <p id="lW61">Что такое язык? На нем кто-то говорит? Да, посредством такого языка мы общаемся с БД. Пример: у вас есть друг-библиотекарь, он работает в библиотеке. В данном примере библиотека — это наша база данных, например PostgreSQL, а библиотекарь, то есть ваш друг, — это и есть движок на базе SQL. Вы говорите ему: &quot;Принеси мне все книги по физике&quot;, и он это делает. Но так как мы общаемся посредством языка не с человеком, а с программой, то мы должны использовать строгий язык, понятный движку SQL. Правильно написанные запросы позволят доставать сгруппированные, отфильтрованные данные.</p>
  <p id="ITPg">Теперь коснемся той части, с которой мы работаем как Java-разработчики. Мы пишем код, то есть достаем данные из нашего хранилища БД. Получаем, изменяем, сохраняем и удаляем данные. Нас интересует, как мы в коде можем взаимодействовать с БД. То есть мы можем писать запросы на SQL вручную. Но благо есть инструменты, которые ускоряют написание запросов.</p>
  <p id="X6Rz">Самый низкоуровневый инструмент — это <strong>JDBC</strong>.</p>
  <p id="ZsnD"><strong>JDBC (Java Database Connectivity)</strong> — прямой API для отправки SQL в БД из Java. Ты пишешь строку запроса, выполняешь, получаешь ResultSet.<br />Такой инструмент дает нам возможность получать данные из БД и сохранять. Однако мы получаем данные из БД в определенном формате ResultSet — табличные данные в виде строк и колонок, а нам нужно получить объекты. Ты сам вручную пишешь <code>rs.getString(&quot;name&quot;)</code> и кладешь в поле объекта, мапя соответствующие поля, и получаешь удобный и понятный объект для дальнейшей работы в среде Java.</p>
  <p id="aYRZ"><strong>Термины:</strong></p>
  <blockquote id="KZGa"><strong>JPA (Java Persistence API)</strong> — это спецификация ORM (Object-Relational Mapping). Hibernate — самая популярная реализация. Ты работаешь с объектами Java, а JPA сама генерирует SQL, управляет кешем первого уровня, lazy-загрузкой.</blockquote>
  <blockquote id="qjrE"><strong>Hibernate</strong> — это реализация спецификации JPA. Но если JPA — это только интерфейс (набор аннотаций и правил), то Hibernate — это конкретный код, который:</blockquote>
  <ul id="ipCV">
    <li id="wz37">Читает твои Java-классы с аннотациями (@Entity, @Id, @OneToMany).</li>
    <li id="nVkS">По ним генерирует SQL-запросы (CREATE, INSERT, SELECT, JOIN...).</li>
    <li id="5zXS">Выполняет эти запросы через JDBC.</li>
    <li id="zHPQ">Забирает ResultSet и превращает строки обратно в Java-объекты.</li>
  </ul>
  <blockquote id="TegE"><strong>ORM (Object-Relational Mapping)</strong> — это инструмент для маппинга, то есть соединяет по названию колонки значения с полями в объекте и возвращает объект. То же самое делает в обратную сторону.</blockquote>
  <p id="1rm8">JPA — это спецификация, которая еще сильнее облегчает работу с базами данных за счет сильного инструмента Hibernate. Как видно, всю магию кибер берет на себя, генерирует запросы. Внутри JPA-спецификации есть набор базовых обращений в БД. Есть еще более удобная надстройка над JPA.</p>
  <p id="UMGO"><strong>JpaRepository</strong> — это часть Spring Data JPA (надстройка над JPA). Чистый JPA (без Spring) использует EntityManager и методы persist(), find(), merge(), remove(). Spring Data JPA добавляет удобные репозитории с готовыми методами.<br />Писать ничего не надо, всё уже есть. Если вы наследуете от <code>JpaRepository&lt;Type_of_Object, Type_for_ID&gt;</code> — внутрь дженерика передаете сначала объект, который берется за основу при работе с таблицей, к которой будете обращаться посредством Hibernate, а вторым аргументом — тип данных для вашего id:</p>
  <ul id="zHSf">
    <li id="YXJQ"><code>save(entity)</code> — сохраняет объект (INSERT, если id=null, иначе UPDATE)</li>
    <li id="yPXw"><code>saveAll(entities)</code> — сохраняет список объектов (пакетно)</li>
    <li id="XdZU"><code>findById(id)</code> — ищет по первичному ключу, возвращает Optional</li>
    <li id="d5q4"><code>existsById(id)</code> — проверяет, существует ли запись с таким id</li>
    <li id="Y6mP"><code>findAll()</code> — возвращает все записи</li>
    <li id="fR4l"><code>findAllById(ids)</code> — возвращает записи по списку id</li>
    <li id="h5PS"><code>count()</code> — возвращает количество записей</li>
    <li id="scZ2"><code>delete(entity)</code> — удаляет объект</li>
    <li id="CcBs"><code>deleteById(id)</code> — удаляет по id</li>
    <li id="ZEXG"><code>deleteAll()</code> — удаляет всё (осторожно!)</li>
    <li id="iiGc"><code>flush()</code> — принудительно синхронизирует изменения с БД</li>
    <li id="ljA2"><code>saveAndFlush(entity)</code> — сохраняет и сразу выполняет flush</li>
  </ul>
  <p id="7YQ5">Также будут доступны производные запросы типа поиска по одному или нескольким полям. Обычно они так и называются: <code>findByName()</code>. Внутри по ключевому слову пишется SQL-запрос WHERE name.</p>
  <p id="J5KO">После того как мы узнали про базы данных, про язык запросов SQL, теперь различаем реляционные и нереляционные. Разработчику становится важна скорость поиска и скорость вставки информации в данных.</p>
  <blockquote id="uYdX">Если вы водитель, то вам будет понятно как работает изучение сложных программ. </blockquote>
  <figure id="KNNm" class="m_column">
    <img src="https://img2.teletype.in/files/9a/a2/9aa22375-8acf-445e-a0d0-0842533ce0ad.jpeg" width="1199" />
    <figcaption>Только вперед!</figcaption>
  </figure>
  <p id="4v8R">Только вперед!<br />Вспомните: когда вы первые разы сидели за рулем, на чем был ваш фокус? На том, как не бросить сцепление, не перегазовать, поставить верную скорость и не заглохнуть. А тут еще и сверху внимание на пешеходов, другие машины. Ваш мозг был занят важнейшими процессами — переключение скоростей и контроль габаритов. Однако спустя три месяца вы внезапно для себя обнаруживаете, что способны построить маршрут сложнее, успеваете перестроиться, учитываете тормозной путь и в целом способны осваивать новые горизонты вождения. А спустя год-два вам становится интересно, как удержать машину в заносе. Это я о чем?</p>
  <p id="f4AX">О том, что программирование — то же самое. Сначала вы тратите ресурсы на умение читать код, не путаться в синтаксисе. Все сложные концепции проходят вас стороной. Однако, когда ресурсы освобождаются, вы легко читаете код, пишете сами из головы, можете позволить себе фривольность в реализации: тут протестировать такой подход, в другом месте объединить дублирующий код. Потом позволяете себе зайти на территорию, ранее не доступную — территорию скорости и распределения памяти между ресурсами. И вот как раз на этом этапе вам будет интересно узнать, что там по скорости записи.</p>
  <p id="Ytda">Помним, что любая операция чтения или записи данных — это физическое перемещение информации между носителем и оперативной памятью.<br />В зависимости от того, где хранятся физически данные, будет зависеть и скорость их поиска. Основные носители — это жесткий диск (HDD, SSD) и оперативная память RAM.</p>
  <blockquote id="OT33">RAM читает данные за наносекунды. SSD — за миллисекунды. Разница в 1000 раз даже на самом быстром диске.</blockquote>
  <p id="QfX3">Оперативная память — мощный инструмент, но она оперативная, потому что держит данные прямо сейчас, оперативно. Как только ты отрубаешься от сети, RAM теряет все данные. Поэтому скорость RAM следует использовать с умом. Иногда это незаменимый мощный инструмент: Redis как раз позволяет пользоваться таким инструментом, как RAM, для записи и хранения данных in-memory.</p>
  <p id="gcFr">Как мы можем ускорить поиск, если пользуемся памятью на жестком диске? Есть такой инструмент, как <strong>индексы</strong>. Они позволяют...</p>
  <p id="lj9V">Индекс — это отдельная структура данных, которая хранит значения и ссылки на физическое расположение строк в таблице. Пример поиска:</p>
  <ul id="o63e">
    <li id="mtw7">Таблица 10 млн строк, full scan = 500 000 чтений с диска → 2-5 секунд</li>
    <li id="RAxD">B-tree индекс = 4-5 чтений с диска → 1-2 миллисекунды</li>
  </ul>
  <p id="dO9y">Есть основные типы индексов.</p>
  <blockquote id="nB8M"><strong>B-tree</strong> работает за O(log N). Используется в PostgreSQL, MySQL, Oracle. Применяется для равенства, диапазонов и сортировки.</blockquote>
  <blockquote id="vgoi"><strong>Хеш</strong> работает за O(1). Живет в Redis и MEMORY-таблицах MySQL. Используй только для точного совпадения. Диапазоны и сортировку сделать будет сложно, поиск связанных сущностей — тоже, но есть мощный инструмент RediSearch, который позволит проиндексировать значения хэша, который передадим как значение.</blockquote>
  <blockquote id="tQZW"><strong>Инвертированный индекс</strong> имеет особую скорость поиска. Работает в Elasticsearch и Lucene. Можно использовать, когда нужно искать по словам и тексту.</blockquote>
  <p id="YTHe">Важную тему затронули сегодня. Мы поговорили про базы данных: про то, как они устроены, чем отличаются реляционные от нереляционных, как мы к ним обращаемся через JDBC, JPA и Hibernate, и почему Spring Data JPA с его JpaRepository делает нашу жизнь проще.</p>
  <p id="gwnu">А потом мы залезли чуть глубже. Поговорили про скорость. Понятно, что теория без практики ничего не значит. Но практика без теории — тоже, я вам скажу, ни о чем. Ты вроде делаешь, закрываешь задачки, но внутри пусто, ведь сам знаешь, что не сильно копаешь и тратишь много усилий впустую, особенно когда приходит бага. Поэтому, ребят, вникайте как можно глубже.</p>
  <p id="4Uau">Приятно понимать, что твои возможности безграничны, стоит лишь продолжать.</p>
  <blockquote id="70wd"><strong>Мой ник в ТГ: @karim_product — я говорю простыми словами о сложном.</strong></blockquote>
  <blockquote id="a3Vk">Если было полезно, поддержи подпиской. Мне будет мотивация продолжать в том же духе. Мой канал - <a href="https://t.me/+uH8Hm6kPWhU2OTc6" target="_blank">https://t.me/+uH8Hm6kPWhU2OTc6</a></blockquote>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@karim_abushaev/Vlj8ceOrtb_</guid><link>https://teletype.in/@karim_abushaev/Vlj8ceOrtb_?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=karim_abushaev</link><comments>https://teletype.in/@karim_abushaev/Vlj8ceOrtb_?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=karim_abushaev#comments</comments><dc:creator>karim_abushaev</dc:creator><title>Многопоточная интерпретация Эверетта</title><pubDate>Wed, 08 Apr 2026 08:05:44 GMT</pubDate><media:content medium="image" url="https://img1.teletype.in/files/46/cb/46cb3165-cd49-4619-b33f-8064b29bcdff.png"></media:content><description><![CDATA[<img src="https://img4.teletype.in/files/f8/55/f855fc60-223c-40b9-867f-69324b347b8a.png"></img>Если ты разработчик, то тебя это коснется. Спустя какое то время, кто-то раньше, кто-то позже, но каждый приходит к тому, что пора реально взять и разобраться в многопоточке. Я как то писал статью для себя, когда готовился к собесам и понял, что мне самому очень вкатывает такой формат обучения, когда ты пишешь статью на тему, которую исследуешь изучаешь. Так информация осваивается в разы лучше. Поэтому я собираюсь пропустить через себя огромный объем информации по многопоточности. Буду работать как супер компьютер обрабатывать сразу все возможные ветки в направлении освоения многопоточности.]]></description><content:encoded><![CDATA[
  <p id="Oz4n">Если ты разработчик, то тебя это коснется. Спустя какое то время, кто-то раньше, кто-то позже, но каждый приходит к тому, что пора реально взять и разобраться в многопоточке. Я как то писал статью для себя, когда готовился к собесам и понял, что мне самому очень вкатывает такой формат обучения, когда ты пишешь статью на тему, которую исследуешь изучаешь. Так информация осваивается в разы лучше. Поэтому я собираюсь пропустить через себя огромный объем информации по многопоточности. Буду работать как супер компьютер обрабатывать сразу все возможные ветки в направлении освоения многопоточности. </p>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <figure id="QcJw" class="m_column">
      <img src="https://img4.teletype.in/files/f8/55/f855fc60-223c-40b9-867f-69324b347b8a.png" width="1200" />
      <figcaption>Дороги, которые ведут тебя к истине</figcaption>
    </figure>
  </section>
  <blockquote id="6jzR">Дорогу осилит идущий</blockquote>
  <p id="hDJ6">Готовы погрузится настолько глубоко, насколько это возможно в рамках этой статьи? Делаем. Изучая тему многопоточки, мне стало интересно, что физически происходит в процессоре. Мы написали код на JAVA компилятор, скомпилировал наш код и передал его на уровень ниже, теперь JVM превращает скомпилированный код в набор инструкций понятных процессору. Сам процессор в свою очередь состоит из ядер, каждое ядро может выполнять единовременно только одну инструкцию (в нашем понимании это один поток), это связанно с тем, что для одного ядра выделяется один управляющий, который переключает датчики в соответсвии с инструкциями. Ядра могут работать одновременно каждый со своим набором инструкций. </p>
  <p id="RIkQ">Внутри ядра есть регистры, ранее я называл их датчиками. Правильно будет все таки понимать их как регистры, супер быстрые ячейки, которые хранят (числа, адреса, данные) без задержки в один такт</p>
  <blockquote id="Fech"><strong>Такт</strong> -  это минимальная единица времени, за которую процессор способен реализовать самую простую операцию например сдвинуть бит в регистре. Один набор инструкций на ядре может состоять из десятков тысяч тактов.</blockquote>
  <blockquote id="2GeD"><strong>Бит</strong> - это буквально два возможных состояния в транзисторе. Сдвиг бита, это переключение из двух возможных состояний, высокое напряжение или низкое, мы можем это понимать как 0 и 1.</blockquote>
  <p id="66r8">Еще один очень интересный момент. Одно ядро и правда может выполнять один поток, однако сейчас очень мощные процессоры и ядра, которые исполняют именно аппаратный поток. Благодаря суперсклярной архитектуре один аппаратный поток может за один такт выполнить 2-6 инструкций того же потока. Называется это Hyper-Threading, он же создаёт иллюзию двух потоков на одном ядре через быстрое переключение, но в каждый момент времени инструкции исполняются только одного из них.</p>
  <p id="udW0">Мой товарищ Паша Сорокин дал четкое руководство по которому мы и двинемся в изучении многопоточки, шаг первый <strong>понять как потоки устроены внутри</strong>.</p>
  <p id="O9Kq">Начнем с базы, мы знаем что когда запускается Java приложения то под каждый поток выделяется свой СТЕК памяти эта область памяти так и называется STACK и там хранятся все временные переменные. Однако напомню что все остальные объекты находятся в HEAP в стеках вообще нет объектов там есть только ссылки на объекты.</p>
  <p id="71nz">Получается мы имеем объекты в HEAP и в разных стеках под разные потоки данных могут находится ссылки на один и тот же объект. Вот так мы и получаем пространство вариантов. Где каждый поток по своему хочет и будет влиять на объект. Возникающие коллизии на основе такой структуры называют <strong>Race Condition</strong> в переводе - гонка потоков. Где потоки гонятся за объектами и пытаются на них воздействовать изменять по принципу кто успел тот и съел. Результат зависит от того, какой поток успел прочитать, изменить и записать значение — это и есть <strong>Race Condition</strong>. Как раз на этом фоне и возникают ошибки, которые иногда воспроизводятся, а иногда нет. Локально зачастую причем все будет работать корректно, потому что вы запустили Сервис и тестируете один метод. Этот метод отрабатывает и ни с кем не соревнуется. А на проде возможно к тем же самым объектам в этот момент будут пытаться обратиться другие потоки и у вас с некоторой периодичностью будут возникать коллизии и вы не будете знать от чего они, потому что просто не понимаете как устроены потоки внутри.</p>
  <p id="GV9k">Давайте разберем, что происходит на каждом уровне. Первый уровень -&gt; запуск программы, буквально файл который запускает приложение. Второй уровень -&gt; это запуск процесса в операционной системе и внутри процесса третий уровень  -&gt; где потоки делят между собой одну общую память.</p>
  <p id="YB6D">Получается, что процесс - это запущенный экземпляр программы, то есть Операционная Система ОС выделяет для этого процесса определенную область памяти, выделяет ресурсы для данного приложения. А поток это легковесная единица выполнения внутри процесса, таких потоков в процессе может быть множество. Физически один реальный поток занимает одно ядро процессора, значит количество реально одновременно работающих потоков ограниченно ядрами вашего процессора. Мы помним, что потоки делят между собой память и для каждого потока свой Стек памяти. </p>
  <section style="background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <figure id="MiEn" class="m_column">
      <img src="https://img2.teletype.in/files/d1/b1/d1b1d0d3-d1aa-4e34-8cf1-913f5b0073f8.png" width="1280" />
      <figcaption>Поймай дзен</figcaption>
    </figure>
  </section>
  <p id="7Wr1">Поговорим про методы синхронизации работы потоков между собой. КОгда мы работаем с потоками, иногда нам нужно чтобы они работали параллельно, иногда последовательно. Порой, нужно, чтобы потом проделал работу и ждал, когда он снова понадобиться. Для этого есть инструменты, давайте познакомимся с ними:</p>
  <p id="KL6M">wait/notify, notifyAll - это основные методы, которые позволят управлять потоками, из названий понятно, что wait() - заставляет поток ждать, то есть уснуть, пока какое-то условие не заставит его проснуться и дальше выполнять свою работу. А вот как раз методы notify() и notifyAll() - уведомляют либо один поток, либо сразу все, о том, что пора проснуться и продолжить свою работу.</p>
  <p id="9U1i">Познакомимся с понятием монитор, комната ожидания и захват монитора synchronized</p>
  <p id="ykee">Как это работает, представим, что у каждого объекта есть монитор, его можно понимать как охранник, распределитель. И есть две важные зоны:</p>
  <ol id="buUk">
    <li id="xm33"><strong>Entry Set</strong> — очередь потоков, которые хотят захватить <code>synchronized</code> блок.</li>
    <li id="Sbo9"><strong>Wait Set</strong> — комната ожидания для потоков, которые ожидают, пока не произойдет событие и тогда они активизируются.</li>
  </ol>
  <blockquote id="lxXO">ВАЖНОЕ ПРАВИЛО! <br />Команды<em> wait(), notify(), notifyAll()</em> могут быть вызванны только в <strong>блоке</strong> <code>synchronized</code> (или метода) того объекта, чей монитор используется. Иначе выбросится <code>IllegalMonitorStateException</code>. </blockquote>
  <p id="y40i"></p>
  <blockquote id="R6jz"><em><strong>На заметку!<br /></strong>notify() -  пробуждает любой поток из очереди потоков <strong>Wait Set, </strong> notifyAll() - будит все потоки, тако вариант лучше использовать чтобы не терять уснувшие потоки. Если будто один случайный поток, то если мы потеряем этот поток, то оставшиеся потоки будут спать вечно - так называется <strong>потерянное пробуждение (lost wakeup)</strong></em></blockquote>
  <p id="9H1x">За счет синхронизации захватывается монитор объекта, у каждого объекта, который наследуется от Object, а в джава это буквально каждый объект есть встроенная блокировка, которая и называется монитором. Блок синхронизации захватывает монитор у объекта и следующие потоки отправляются в Wait Set в комнату ожиданий. За счет <code>synchronized</code> достигаются два главных свойства многопоточности:</p>
  <p id="omqh">Взаимное исключение (Mutual Exclusion) - гарантирует, что только один поток захвативший монитор будет производить над ним изменения в один момент времени. Достигается видимость между всеми потоками с помощью переменных <code>volatile</code></p>
  <p id="H5pa">Видимость (Visibility) - гарантия того, что все другие объекты увидят изменения после выхода потока из блока синхронизации.</p>
  <p id="y7KM">Важно отметить, что мы говорили про блокировку у объектов, однако у класса тоже можно захватить монитор, с помощью статического метода. Помним, что статические методы и переменные относятся к классу, а не статические к объекту. Нужно так же понимать, что эти блокировки не конфликтуют друг с другом, так как одна блокирует монитор объекта this, а другая Utils.class.</p>
  <p id="tSJK">Когда я первый раз разбирал задачку по многопоточке, там в начале был создан новый объект и был назван как lock, я не понимал зачем был создан объект и почему назван как блокировка. Дальше этот объект выступал монитором, который блокировал остальные потоки. И понимание этой логики к вам придет, только после того, как вы поймете, что каждый объект имеет свою одну блокировку, которую можно использовать если не хотите блокировать тот объект в котором производите логику.</p>
  <p id="R0gl">Бывает, так что вы не хотите использовать монитор класса this (тот класс в котором вы находитесь), а хотите параллельно реализовывать логику в одном объекте в разных методах. Тогда вы можете передать в блок синхронизации любой другой объект, монитор которого, будет использоваться. Для этого просто создаете объект с помощью new Object(); и передаете его в блок синхронизации и теперь <code>synchronized</code> понимает монитор какого объекта он блокирует. </p>
  <p id="Ix0b">Вот тут еще хочется поговорить про концепцию<em> happens-before, </em>по факту это гарантия, на то, что события произошедшие до, будут считанны и известны другим потокам.</p>
  <p id="qGno">Если вы хотите управлять потоками более гибким способом, то вы можете передать не просто объект для захвата монитора, а объект new ReentrantLock(), тип данных Lock. Тогда у такого объекта вы сможете захватывать монитор и иметь дополнительные методы управления, такие как: </p>
  <ul id="Tw92">
    <li id="Wn0m"><code>tryLock()</code> — попытаться захватить блокировку без ожидания (вернуть false, если занята).</li>
    <li id="0shj"><code>tryLock(timeout, unit)</code> — ждать не бесконечно.</li>
    <li id="P1PV"><code>lockInterruptibly()</code> — позволить прервать ожидание блокировки.</li>
    <li id="jvdO">Справедливость (fairness) — можно создать <code>new ReentrantLock(true)</code>, тогда блокировка будет отдаваться самому долго ждущему потоку (но медленнее).</li>
  </ul>
  <blockquote id="Zs1N">Объект <strong><em>Semaphore sem = new Semaphore(3);</em></strong> позволит управлять количество потоков.</blockquote>
  <p id="Kxsw"><strong>Deadlock (взаимная блокировка):</strong> Поток А захватил монитор одного объекта и ждет освобождения монитора у другого объекта, а тот объект находится в захвате у другого блока синхронизации и ждет освобождения первого. В итоге оба стоят вечно — программа зависла, потоки не выполняются и не реагируют.</p>
  <p id="aboh"><strong>Livelock (живая блокировка):</strong> Потоки не заблокированы, но постоянно уступают друг другу — например, видят, что ресурс занят, отпускают свой и пробуют заново в том же порядке. Они активно крутятся, загружая процессор, но прогресса ноль.</p>
  <p id="EABx">Давайте притормозим тут, нам важно качественно осознать информацию выше, поэтому пушим в мастер.</p>
  <blockquote id="tGaT"><code>git commit -m &quot;Базовые знания по многопоточности: потоки, процессор, синхронизация, deadlock, livelock&quot;</code></blockquote>
  <p id="ukuy"></p>
  <p id="UFxg">Поговорим про синхронные и асинхронные методы, это не обязательно многопоточка, однако для понимания работы потоков нам необходимо понимание синхронности.</p>
  <p id="SC8s">Синхронный метод - это обычный метод, который работает по принципу, поток зашел в метод, начал выполнять свою работу и например обратился в базу данных с запросом, запрос обрабатывается на стороне БД, поэтому поток приложения в это время ничем не занят. И он ждет ответа. В этот момент поток не выполняет никакой работы -  простаивает. Сравнить можно с покупками на рынке, вы подходите в мясной отдел и запрашиваете курицу 2 килограмма, вам говорят сейчас упакуем, вы поток, ничего полезного в этот момент не делаете, просто ждете, когда вам отдадут курицу, вы пойдете дальше выполнять свою большую задачу по закупке.</p>
  <p id="QpLV">Асинхронный метод - это эффективно работающий метод с потоком, поток внутри метода встретившись в обращением к внешней системе не будет ждать выполнения, он тут же заключает контракт на обещание, что результаты будут позже. И поток идет дальше в комнату ожидания, если это многопоточная среда или выполнять другие асинхронные методы</p>
  <p id="YV9f">CompletableFuture - это контейнер для результата, которого ещё нет. Он может быть в трёх состояниях: ещё не завершён, завершён с результатом, завершён с ошибкой. Ты можешь навесить на него колбэки: что сделать, когда результат появится. Главная фишка — методы thenApply, thenCompose, thenAccept, которые позволяют строить цепочки асинхронных операций. </p>
  <blockquote id="nbTh"><strong>Пример:</strong><code>CompletableFuture.supplyAsync(() -&gt; userService.getUser(123L))<br />    .thenCompose(user -&gt; orderService.getOrdersAsync(user))<br />    .thenAccept(orders -&gt; System.out.println(orders));</code></blockquote>
  <p id="fdl8">Такой метод, будет выполнять все запросы асинхронно, то есть он пойдет в юзер сервис, дойдет до момента, когда обратиться в бд, получит обещание, что выполнится например через 1 секунду,  <code>CompletableFuture</code> - это значит, что выполнятся все метода последовательно. Это значит пока первый блок асинхронных методов не будет выполнен, поток не будет выполнять следующий шаг. Однако простаивать тоже не будет, он уйдет в пул потоков и будет там ждать новую работу.</p>
  <p id="cK79">Самое интересное, что начать исполнять <code>CompletableFuture</code> может один поток, а закончить другой, так как потоки не привязаны к методам, они как унифицированные работники, каждый может заменить другого. Они просто эффективно распределяют свое время.</p>
  <p id="Zclf">Ассинхронность это мощный инструмент, эффективного распределения ресурсов. Однако есть цена, которую придется заплатить, писать асинхронные методы довольно сложная работа, нетривиальная. Вы никогда не знаете какой поток, пойдет исполнять какой метод, ошибки будут неочевидными, поставив брейкпоинт в сервисе с синхронными методами вы будете читать код сверху вниз и ожил=дать остановку в вашем месте. Однако с асинхронными методами, вы не можете быть уверенны, что потоки пойдут исполнять именно этот метод. Вам нужно уметь прогнозировать предсказывать поведение асинхронных методов, связывать их корректно, чтобы не допускать не консистентности данных.</p>
  <blockquote id="sOp2">Вот вам и многомировая интерпретация - потоки выбирают свою вселенную, вопрос имеют ли потоки сознание и свободу воли, чтобы выбирать какую работу они пойдут выполнять? </blockquote>
  <figure id="JLQ8" class="m_original">
    <img src="https://img3.teletype.in/files/23/be/23be9710-edfa-4e68-a7f0-3ca6c47dde36.jpeg" width="736" />
    <figcaption>Определиолся?</figcaption>
  </figure>
  <p id="M25S"><u><em>Какой главный риск</em></u>, когда мы используем асинхронные методы Через CompletableFuture в многопоточке?</p>
  <p id="z7KY">Мы знаем, что количество возможных потоков в пуле потоков ForkJoinPool.commonPool(). Равно количество ядер минус один. Итого на 8 ядерном устройстве будет доступно в пуле потоков 7 потоков. А CompletableFuture буквально берет потоки из пула потоков. </p>
  <p id="pIpS">И все бы хорошо, поток получает обещание от запроса к бд внутри метода и уходит обратно в пул. Но бывает такое, что внутренние методы в рамках CompletableFuture на этапе обращения к бд встречаются с внутренними методами, которые не являются асинхронными и там поток засыпает, не переходя дальше, тогда и возникает риск захвата всех потоков, если все потоки от 7 одновременных запросов кнут на пути к бд на синхронном методе. Восьмой запрос просто не получит свободного потока и будет ждать. Девятый и десятый тоже. Очередь будет расти, приложение перестанет отвечать, а ты будешь гадать, почему всё работало на тесте, а под нагрузкой упало. Диагностировать такую проблему сложно, потому что внешне код выглядит как асинхронный, а внутри спрятана синхронная бомба замедленного действия.</p>
  <blockquote id="c0fe">Внутренние методы: <br />supplyAsync — запускает асинхронную операцию, которая возвращает результат<br />thenApply — преобразует результат (синхронно)<br />thenCompose — преобразует результат в новый CompletableFuture (асинхронно)<br />thenAccept — потребляет результат, ничего не возвращая<br />exceptionally — обрабатывает ошибку</blockquote>
  <p id="JNBj"><strong>Итог: проверь себя</strong></p>
  <p id="oNHi">Если ты честно осилил статью, пропустил через свой мыслительный аппарат, почувствовал сложность в осмыслении, если вопросы которые у тебя появлялись ты гуглил, чтобы закрыть пробелы, то я официально считаю тебя красавчиком. Респект тебе!  Можешь закрепить материал и ответить на эти вопросы:</p>
  <ol id="QarR">
    <li id="ufns">Почему потенциально может возникать гонка потоков Race Condition при создании потоков? <br /><em><u><code>Подсказка: STACK</code></u></em></li>
    <li id="xqyq">За счет чего достигается создание 20, 30 и более потоков на 8 ядерном процессоре? Ведь мы знаем, что количество потоков в пуле ограниченно количеством ядер минус один.<br /><em><u><code>Подсказка: Суперсклярность ядер процессора</code></u></em></li>
    <li id="Plx8">Что делает блок <code>synchronized</code> ? Чей монитор захватывается по умолчанию?<br />В чем особенности с блоком <code>synchronized</code> в рамках статического метода?<br /><em><u><code>Подсказка: Только ли у объекта можно захватить монитор?</code></u></em></li>
    <li id="vt79">Почему есть риск положить приложение из за нехватки потоков при использовании Асинхронных методов посредством <code>CompletableFuture</code>?<br /><em><u><code>Подсказка: Всегда ли гарантия Ассинхронности на метод распространяется на внутренние методы?</code></u></em></li>
  </ol>
  <hr />
  <p id="Pvhl">Ответил на все? Значит, тему ты честно преодолел большой шаг в понимании. На этом многопоточка только начинается, дальше предлагаю тебе порешать задачки, например задача <code>&quot;PING-PONG&quot;</code> Если на какие-то вопросы ответить не можешь, пиши мне, разберем вместе. Если в статье увидел не точности или противоречия, тоже пиши мне -разберемся почистим.</p>
  <p id="eWJ4">Мой тг -@karim_product на связи родной/родная ;)</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@karim_abushaev/Pu9-ghh6ej6</guid><link>https://teletype.in/@karim_abushaev/Pu9-ghh6ej6?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=karim_abushaev</link><comments>https://teletype.in/@karim_abushaev/Pu9-ghh6ej6?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=karim_abushaev#comments</comments><dc:creator>karim_abushaev</dc:creator><title>Карим Абушаев | Помогаю построить карьеру в IT</title><pubDate>Fri, 19 Dec 2025 14:18:35 GMT</pubDate><description><![CDATA[Почему я:  моя карьера в АйТи началась в 2019 году, я стартанул в IT компании, которая делала графику для TV и далее моя карьера:]]></description><content:encoded><![CDATA[
  <section style="background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="Qate"><strong>Почему я: </strong> моя карьера в АйТи началась в 2019 году, я стартанул в IT компании, которая делала графику для TV и далее моя карьера:</p>
    <p id="ck6d">Playrix Точка А  - 2д аниматор (зп 80 000) -&gt; VFX Lead (зп 230 000)</p>
    <p id="PddT"> Product Manager Точка А - Prisma PM (зп 120 000) -&gt; Pari Match Senior PM (зп 300 000)</p>
    <p id="I47r">ВТБ проект Точка А - Java Developer (зп 340 000) -&gt; Senior Java Developer (зп 380 000)</p>
    <p id="CXPM">Есть своя игра в Steam - собрал команду из 16 человек и под моим управлением создали и выпустили игру в Steam и на VK площадках. <br /><br />Разбираюсь в типовых профессиях Product/Project Manager и Java Developer. Управлял командами, побеседовал, провел более 100 CastDev для выявления болей клиентов. </p>
    <p id="D0zG">Под моим наставничеством, ты сможешь освоить передовую профессию в АйТи.</p>
    <p id="vmdp">Контакт для связи: <a href="https://t.me/karim_product" target="_blank">https://t.me/karim_product</a></p>
  </section>
  <section style="background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="q68f"><strong>Помогу: </strong></p>
    <ul id="3Fbp">
      <li id="cS6t">Выбрать подходящую профессию</li>
      <li id="OwmR">Разобрать твою личность и выстроить индивидуальный план обучения</li>
    </ul>
    <ul id="EfuP">
      <li id="pEji">Собрать качественное и актуальное резюме на 2026 год</li>
      <li id="S3ca">Поделюсь реальным опытом и собеседованиями моими и моих учеников</li>
      <li id="9eXM">Помогу начать карьеру в IT даже если у вас совсем нет опыта</li>
    </ul>
    <p id="Hhxk"><strong>Кому актуально:</strong></p>
    <ul id="7PSc">
      <li id="9Wgq">Тем кто хочет поменять профессию и начать зарабатывать в IT сфере</li>
    </ul>
    <ul id="Z1oQ">
      <li id="dEes">Тем кто уже на рынке труда, но хочет усилить свои навыки как специалиста и продать себя на рынке труда дороже</li>
    </ul>
  </section>
  <section style="background-color:hsl(hsl(34,  84%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="a7h4"><strong>Что входит в наставничество?</strong></p>
    <ul id="lPDi">
      <li id="AQqo">Теоретические материалы, PDF файлы и записанные видеоуроки</li>
      <li id="TMcI">Еженедельные онлайн встречи, на которых будем проверять освоенный материал и осваивать новый</li>
      <li id="DF29">Практические задания построение CJM, проведение CastDev для PM и написание микросевисов, приложений для Java Developer</li>
      <li id="pcXj">Когда мы освоим теорию и практику. Соберем вам резюме и отправимся в бой, будем откликаться, проходить собеседования. Я буду разбирать ваши ошибки и улучшать навык прохождения собеседований</li>
    </ul>
    <p id="oZN3"><strong>Результаты 2 - 3 месяцев работы:</strong></p>
    <ul id="RwyE">
      <li id="7W9X">Первая работа с доходом 100 000 - 120 000</li>
      <li id="X5VO">Если есть опыт, то сможем выбить вам оффер выше вашего на 30%</li>
    </ul>
    <p id="6Clo"><strong>Стоимость</strong></p>
    <ul id="S0dS">
      <li id="yXFJ">30 000 руб за все обучение + 30% от оффера с первой зарплаты</li>
    </ul>
    <p id="cSWI"><strong>Как начать:</strong></p>
    <p id="Sl0c">Первая консультация - бесплатная. На ней проанализирую ваш опыт и дам рекомендации как действовать, отвечу на любые вопросы</p>
    <p id="UxCG">Записаться на бесплатную консультацию: <a href="https://t.me/karim_product" target="_blank">https://t.me/karim_product</a></p>
  </section>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@karim_abushaev/M0vzE5CcnNJ</guid><link>https://teletype.in/@karim_abushaev/M0vzE5CcnNJ?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=karim_abushaev</link><comments>https://teletype.in/@karim_abushaev/M0vzE5CcnNJ?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=karim_abushaev#comments</comments><dc:creator>karim_abushaev</dc:creator><title>Путь Самурая</title><pubDate>Wed, 29 Oct 2025 22:58:49 GMT</pubDate><media:content medium="image" url="https://img1.teletype.in/files/c3/d4/c3d4cd66-0366-4b55-996e-9714ef4e60ea.png"></media:content><description><![CDATA[<img src="https://img2.teletype.in/files/94/37/94373ea6-1b52-4424-b27d-ff39b030ac69.png"></img>Путь в тысячу ли начинается с первого шага.]]></description><content:encoded><![CDATA[
  <blockquote id="kUM6"><em>Путь в тысячу ли начинается с первого шага.</em></blockquote>
  <p id="Zi1x">Продукт в котором я нуждаюсь. Всегда хотелось создать нечто полезное обществу и то что мне будет интересно. У меня есть множество идей, которые я не смогу реализовать если буду один. Именно поэтому я создаю этот продукт. </p>
  <p id="nvQb">Прокладывая путь самурая, я хочу открыть дорогу тем, кто тоже хочет учиться новому и исследовать.</p>
  <p id="XGKJ">Мне нравятся игры и я хочу в будущем создать свою многопользовательскую игру, с полным погружением, а сейчас я собираюсь прокладывать путь через создание своего сильного продукта.</p>
  <p id="HTEC">Я помогу тебе начать свой путь в IT сфере.</p>
  <figure id="JxVb" class="m_column">
    <img src="https://img2.teletype.in/files/94/37/94373ea6-1b52-4424-b27d-ff39b030ac69.png" width="1200" />
    <figcaption>Большая сила, большая ответсвенность. Мы постараемся поделиться частью силы, которая нам дана и имя той силы - знания.</figcaption>
  </figure>
  <p id="HRM5">Если ты самурай то выбери свой путь</p>
  <hr />
  <blockquote id="GvFV">Сёгун (Developer) - путь разработчика, ты сможешь развиваться как Java разработчик </blockquote>
  <blockquote id="s9zk">Сёхин (Project Manager)- путь управленца, человек который разбирается в процессах управления командой</blockquote>
  <blockquote id="G48C">Синрёку (Product Manager) - путь стратега, исследователь, тот кто ищет ответы на все вопросы и находит боли, которые нужно залечить</blockquote>
  <hr />
  <blockquote id="VUq1">Дзимей-кенси(Networking) - путь связующего звена в мире нетворкинга</blockquote>
  <blockquote id="IGbM">Кингаку(Investor) - путь финансиста <em>книга Теодора Драйзера &quot;Финансист&quot;</em></blockquote>
  <p id="TJu6">Для начала мы определим твои интересы и подберем наилучшую стратегию для твоего роста как специалиста. Твое трудоустройство будет не за горами.</p>
  <p id="Boe7">Записывайся на консультацию @karim_product - связь через телеграмм. Я подготовил подарок тем, кто напишет мне до 14.11.2025 года.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@karim_abushaev/132DjYUAaTD</guid><link>https://teletype.in/@karim_abushaev/132DjYUAaTD?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=karim_abushaev</link><comments>https://teletype.in/@karim_abushaev/132DjYUAaTD?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=karim_abushaev#comments</comments><dc:creator>karim_abushaev</dc:creator><title>Друзей не выбирают</title><pubDate>Thu, 05 Jun 2025 22:52:08 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/d5/b1/d5b1e512-a9bc-47b6-9e80-3de935a6d980.png"></media:content><description><![CDATA[<img src="https://img2.teletype.in/files/da/d1/dad159ee-d1ab-4c2c-9126-030f20a6e79f.jpeg"></img>Эта статья предназначена тем, кто хотел бы дружить, но не умеет.]]></description><content:encoded><![CDATA[
  <figure id="WRtJ" class="m_original">
    <img src="https://img2.teletype.in/files/da/d1/dad159ee-d1ab-4c2c-9126-030f20a6e79f.jpeg" width="1726" />
    <figcaption>Самый счастливый день фото 1</figcaption>
  </figure>
  <p id="yeLU"><br />Эта статья предназначена тем, кто хотел бы дружить, но не умеет.</p>
  <hr />
  <p id="MELQ"><br />Последнее время ловлю себя на мысли, что мой круг друзей сформировался. <br />а если быть точнее, круг закрылся. <br />Я всегда был очень открытым человеком, поэтому завести 100 друзей как в пословице для меня не составляло никакого труда. <br />И я всегда стремился к новым знакомствам, чтобы найти интересных людей, тех кто стремиться к чему то великому или духовному. <br />У меня сформировалось несколько кругов, друзья детства, моя компания с района (самые близкие друзья, были и будут), питерская молодежь, несколько сольных друзей. И еще куча хороший ребят, которых я тоже ни раз называл друзьями. Даже еще больше чем я перечислил. <br />И моей дикой энергии хватало на всех, увидеться со всеми, и в будни и на выходных, в баню сходить, в бильярд. В Москве, в Саранске, в Египте, на Камчатке. <br />Где только я в разных компаниях не оказывался. И все блин мои друзья, всех зову на различные мероприятия. Пытаюсь объединить. <br />Вернусь к началу мысли, по итогу я стал чувствовать свои ограничения. Уже внутренний я говорит &quot;Харош, ты куда разогнался?&quot; </p>
  <blockquote id="wbUR">Лучший друг может быть только один.</blockquote>
  <p id="TKv6">Все же слышали такую поговорку, а у меня градация, лучший друг, брат, близкий, родной человек. И все их больше и больше. <br />Но вот когда, начал погружаться в отношения с каждым человеком в отдельности, уже встретился с реальностью и увидел, что ко мне относятся далеко не так как я. <br />У одного времени нет, у другого дела какие-то непонятные, третий вообще звонит, только когда деньги занять нужно.  Четвертый дружит, потому что я крутой дядя для него. Пятый, потому что у меня можно чему то научится. <br />А настоящих друзей 2 - 3 человека. <br />И вот я поставил задачу себе на 2025 год и в целом ближайшие года, почистить свой круг, от тех, кто не является моим другом и не будет им никогда. <br />Решил для себя ввести определение дружбы и делюсь с вами дорогие читатели, может и вам будет полезно. <br />В общем есть несколько негласных правил, условий, как хотите, по которым стоит определять друга. <br />Просто обычно об этом не говорят, в вот я хочу чтобы уже всем понятно стало как дружить, зачем и почему. </p>
  <h2 id="M5LB">Кто такой настоящий друг? </h2>
  <figure id="ZpJj" class="m_original">
    <img src="https://img2.teletype.in/files/9d/96/9d96e0d7-ab42-4fe0-b163-d2595869ea9b.jpeg" width="1739" />
    <figcaption>Самый счастливый день фото 2</figcaption>
  </figure>
  <p id="eeXV">В моем понимании, это равноценно брату, если я дружу с человеком, то я скорее хочу чтобы он стал моей семьей. Поэтому и отношение у меня к нему соответствующее. </p>
  <ul id="pCEz">
    <li id="Kq6i">Проблема моего друга, это моя проблема</li>
    <li id="ZTWt">Радость моего друга, моя радость</li>
    <li id="tmJ3">Я отношусь к другу как к брату, переживаю за него, думаю о нем, желаю ему только добра </li>
  </ul>
  <p id="xdRb">Вот 3 простых вроде пункта, но если их реально осознать, то вы поймете, что для меня значит друг. Тут следует отметить, что это касается всего, то есть само собой разумеющиеся вещи выходят из этих пунктов: я буду честен с другом, если увижу, что он ступил не на ту дорогу. Я сам предложу помощь если увижу что она ему нужна. Я первый подойду извиниться, если зря быканул. Я вступлюсь за него, даже если он не прав, а потом мы уже решим что с этой ситуацией делать. <br />&quot;Острые козырьки&quot; смотрели? <br />Вот такого отношения я хочу к себе и сам для своих друзей хочу быть таким близким. <br />У некоторых людей, просто нет понимания дружбы и все тут. Говорить с ними бесполезно, они просто не понимают о чем речь. Они ничего не вкладывают в слово друг или просто не умеют дружить. Такие люди на самом деле всегда остаются одни и не доверяют никому. <br />Так во  к чему я, я подумал, что да у меня есть пару друзей, за которых я прям уверен. Но есть так же неопределенный круг, он уже закрыт, однако достаточно обширный, я вот хотел бы определиться с этим кругом в эти года и отсеять реально ненужных мне людей. А с оставшейся семьей только укреплять связи. <br />Конечно, чтобы у тебя были самые верные друзья, нужно самому быть хорошим другом и тогда люди к тебе потянутся. <br />Еще хочу выделить несколько правил для себя, по которым нужно исключать людей из круга: </p>
  <ul id="QbAS">
    <li id="pwWW">человеку без разницы как здоровье ваших родителей, жены, детей и он в целом не знает как обстоят дела у вашей семьи (это факт, такой человек тебе не друг) </li>
    <li id="Qi34">человек не звонит и не пишет вам, но если у него есть время, то может с вами встретиться по вашей инициативе (в этой категории находятся друзья детства, которых никак не получается отпустить, которые на самом деле в том детстве и остались и они вам не друзья)</li>
    <li id="Busl">человек который не находится с вами рядом в самые важные моменты вашей жизни (мы живем иногда на три города, но самые близкие люди, всегда найдут минуточку, чтобы пережить с вами ваши хорошие моменты) </li>
  </ul>
  <p id="xCaz">Вы в свою очередь задумайтесь, а к кому вы относитесь как к другу исходя из того текста, который я написал выше. <br />Думать о друге и быть другом, тоже разные вещи. <br />Кстати, я вообще не верю в женскую дружбу и дружбу между мужчиной и женщиной. Ладно, не то что не верю, я знаю такой дружбы нет. Могу написать статью об этом 😄</p>
  <hr />
  <p id="AxZT">Всем прочитавшим, желаю научиться дружить осознанно, глубоко, дружить годами, а если вам повезет, то дружить всю жизнь. Эта статья предназначена тем, кто хотел бы дружить, но не умеет. <br />хорошего дня 🫰</p>

]]></content:encoded></item></channel></rss>