История IT-технологий сегодня — 06 декабря
ℹ️ Кто родился в этот день
Джеффри Эверест Хинтон, CC FRS FRSC (родился 6 декабря 1947 года в Уимблдоне, Великобритания) — британско-канадский учёный, один из «крёстных отцов» современного глубокого обучения; фундаментальные работы по нейронным сетям заложили основу современных систем ИИ.
🌐 Знаковые события
1946 — выдан патент на первую в мире микроволновую печь.
#Biography #Birth_Date #Events #06Декабря
Джеффри Эверест Хинтон, CC FRS FRSC (родился 6 декабря 1947 года в Уимблдоне, Великобритания) — британско-канадский учёный, один из «крёстных отцов» современного глубокого обучения; фундаментальные работы по нейронным сетям заложили основу современных систем ИИ.
1946 — выдан патент на первую в мире микроволновую печь.
#Biography #Birth_Date #Events #06Декабря
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
С 29.11 по 05.12
Предыдущий пост(с 22.11 по 28.11)
Воскресный мотивационный пост:
Не было мотивации
Запись встреч/видео:
не было
Обучающие статьи:
Технический разбор: Spring WebFlux Gateway с JWT аутентификацией
Java:
Коллекции в Java
Глава 2. List — списки в Java
Метод set
Методы remove, contains
GraphQL
Advanced GraphQL: реактивность и Federation
Spring Cloud Gateway
Архитектурный страж микросервисов
Внутренние компоненты и жизненный цикл запроса
Полезные статьи и видео:
Kafka без боли: моя шпаргалка для собесов в Java
Вредные советы Java: просто используй Parallel Stream
Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
Предыдущий пост(с 22.11 по 28.11)
Воскресный мотивационный пост:
Не было мотивации
Запись встреч/видео:
не было
Обучающие статьи:
Технический разбор: Spring WebFlux Gateway с JWT аутентификацией
Java:
Коллекции в Java
Глава 2. List — списки в Java
Метод set
Методы remove, contains
GraphQL
Advanced GraphQL: реактивность и Federation
Spring Cloud Gateway
Архитектурный страж микросервисов
Внутренние компоненты и жизненный цикл запроса
Полезные статьи и видео:
Kafka без боли: моя шпаргалка для собесов в Java
Вредные советы Java: просто используй Parallel Stream
Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
👍3
История IT-технологий сегодня — 07 декабря
ℹ️ Кто родился в этот день
Франческа Росси (родилась 7 декабря 1962 года) — итальянский учёный в области искусственного интеллекта, IBM Fellow, эксперт по этике ИИ и многопараметрическим методам оптимизации для интеллектуальных агентов.
Авраам Ноам Хомский (родился 7 декабря 1928 года) — лингвист и философ, чьи формальные работы по грамматикам (иерархия Хомского) фундаментально повлияли на теорию формальных языков и компиляторы — важная теоретическая база для информатики.
🌐 Знаковые события
1972 — запуск космического корабля «Аполлон-17», совершившего последнюю в программе «Аполлон» посадку на Луну.
#Biography #Birth_Date #Events #07Декабря
Франческа Росси (родилась 7 декабря 1962 года) — итальянский учёный в области искусственного интеллекта, IBM Fellow, эксперт по этике ИИ и многопараметрическим методам оптимизации для интеллектуальных агентов.
Авраам Ноам Хомский (родился 7 декабря 1928 года) — лингвист и философ, чьи формальные работы по грамматикам (иерархия Хомского) фундаментально повлияли на теорию формальных языков и компиляторы — важная теоретическая база для информатики.
1972 — запуск космического корабля «Аполлон-17», совершившего последнюю в программе «Аполлон» посадку на Луну.
#Biography #Birth_Date #Events #07Декабря
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Привет! 👋
Сегодня я обращаюсь к тебе - подписчик этого канала.
Мы, возможно, никогда не встречались. Но раз ты здесь, раз ты видел мои посты или видео — значит, у нас точно есть что-то общее.
Скорее всего, Java, я угадал?😉
Ранее я неоднократно спрашивал мнение подписчиков о контенте и канале, но откликались единицы (и я им благодарен❤️ ).
Поэтому сегодня я вынужден спросить лично у тебя - зачем ты тут?
Не в глобальном смысле, а буквально - зачем ты остаёшься на этом канале?
- Ведь подобных каналов много тысяч и всегда есть из чего выбрать.
- Ведь судя по просмотрам, только 10% из всех подписчиков, вообще смотрят, что за очередную фигню я там написал.
- Ведь иногда кажется, что даже лайк поставить бывает сложнее, чем выучить Stream API.😄
Но я никого не выгоняю.
Те кому неинтересно или сложно, или слишком легко, или слишком смешно, уйдут сами.
И меня снедает любопытство, почему ты еще здесь?
Почему остаешься в виде безмолвного наблюдателя? Когда-то подписался на канал, поставил на беззвучку и забыл выйти?😄
Почему не хочешь высказать свое мнение? Или предложить свою тему?
(Хотя знаю, что и на это сообщение большинство ничего не ответит🤷♀️ )
Как я писал раньше - канал для меня уже давно не хобби.
Это способ прокачки, способ найти друзей, способ доказать что любой может стать достойным программистом и получить свой оффер.
Поэтому у меня лично к тебе просьба - помоги мне сделать канал лучше?
Скажи, почему ты здесь.
Что хочешь от Java.
Что хочешь от контента.
А я - если это в моих силах - попробую реализовать☺️
Для меня любой отклик, комментарий, предложение или вопрос - важны.
Заранее спасибо.🙂
#мои_мысли
Сегодня я обращаюсь к тебе - подписчик этого канала.
Мы, возможно, никогда не встречались. Но раз ты здесь, раз ты видел мои посты или видео — значит, у нас точно есть что-то общее.
Скорее всего, Java, я угадал?
Ранее я неоднократно спрашивал мнение подписчиков о контенте и канале, но откликались единицы (и я им благодарен
Поэтому сегодня я вынужден спросить лично у тебя - зачем ты тут?
Не в глобальном смысле, а буквально - зачем ты остаёшься на этом канале?
- Ведь подобных каналов много тысяч и всегда есть из чего выбрать.
- Ведь судя по просмотрам, только 10% из всех подписчиков, вообще смотрят, что за очередную фигню я там написал.
- Ведь иногда кажется, что даже лайк поставить бывает сложнее, чем выучить Stream API.
Но я никого не выгоняю.
Те кому неинтересно или сложно, или слишком легко, или слишком смешно, уйдут сами.
И меня снедает любопытство, почему ты еще здесь?
Почему остаешься в виде безмолвного наблюдателя? Когда-то подписался на канал, поставил на беззвучку и забыл выйти?
Почему не хочешь высказать свое мнение? Или предложить свою тему?
(Хотя знаю, что и на это сообщение большинство ничего не ответит
Как я писал раньше - канал для меня уже давно не хобби.
Это способ прокачки, способ найти друзей, способ доказать что любой может стать достойным программистом и получить свой оффер.
Поэтому у меня лично к тебе просьба - помоги мне сделать канал лучше?
Скажи, почему ты здесь.
Что хочешь от Java.
Что хочешь от контента.
А я - если это в моих силах - попробую реализовать
Для меня любой отклик, комментарий, предложение или вопрос - важны.
Заранее спасибо.
#мои_мысли
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥3🆒1 1
История IT-технологий сегодня — 08 декабря
ℹ️ Кто родился в этот день
Илья Суцкевер (иврит : איליה סוצקבר ; родился в 1986 году в СССР (Горький) — один из сооснователей OpenAI; ключевой исследователь глубокого обучения и соавтор знаменитой сети AlexNet и множества современных архитектур.
Уильям Аллан Вульф (8 декабря 1939 — 10 марта 2023) — американский специалист по языкам программирования и компиляторам; внёс вклад в разработку BLISS и системные исследования в области ПО.
🌐 Знаковые события
1947 — учреждена компания Eckert–Mauchly Computer Corporation (EMCC) — одна из первых компьютерных компаний. Эта компания стала предтечей массового производства компьютеров (в частности, участвовала в создании компьютеров семейства UNIVAC).
#Biography #Birth_Date #Events #08Декабря
Илья Суцкевер (иврит : איליה סוצקבר ; родился в 1986 году в СССР (Горький) — один из сооснователей OpenAI; ключевой исследователь глубокого обучения и соавтор знаменитой сети AlexNet и множества современных архитектур.
Уильям Аллан Вульф (8 декабря 1939 — 10 марта 2023) — американский специалист по языкам программирования и компиляторам; внёс вклад в разработку BLISS и системные исследования в области ПО.
1947 — учреждена компания Eckert–Mauchly Computer Corporation (EMCC) — одна из первых компьютерных компаний. Эта компания стала предтечей массового производства компьютеров (в частности, участвовала в создании компьютеров семейства UNIVAC).
#Biography #Birth_Date #Events #08Декабря
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Раздел 6. Коллекции в Java
Глава 2. List — списки
Практика В проекте «Библиотека» заменить массив книг на ArrayList. Реализовать методы: добавить книгу, найти по названию, удалить по индексу
Убедитесь, что ваш проект готов, и вспомните ключевые возможности ArrayList:
Динамический размер (растёт автоматически)
Доступ по индексу O(1)
Добавление в конец O(1) в среднем
Удаление O(n) (сдвиг элементов)
Поиск indexOf O(n)
Откройте проект «Библиотека»: Запустите IntelliJ IDEA и откройте проект LibraryProject.
Проверьте структуру: У вас должен быть класс Book с полями title, author, year, конструктором и методом printDetails(). Класс Library с массивом книг и методом addBook.
Замена массива на ArrayList
Замените поле массива:
Откройте файл Library.java.
Удалите объявление массива Book[] books и переменную bookCount.
Вместо этого объявите приватное поле:
private List<Book> books = new ArrayList<>();
Это будет наш основной список книг.
Инициализация:
Убедитесь, что books инициализируется в конструкторе (если не сделали при объявлении).
Можно также создать отдельный конструктор Library(), где books = new ArrayList<>(); — на ваш выбор.
Реализация метода добавления книги
Создайте или обновите метод addBook(Book book):
Метод должен быть публичным и принимать объект Book.
Используйте метод add() из ArrayList для добавления книги в конец списка.
Добавьте вывод сообщения: "Книга [title] успешно добавлена".
(Опционально) Проверьте, что книга не null перед добавлением.
Реализация метода поиска книги по названию
Создайте метод findBookByTitle(String title):
Метод должен возвращать объект Book или null, если не найден.
Переберите весь список с помощью цикла for или for-each.
Для каждой книги сравните её title с искомым (используйте equals(), учитывая регистр или используйте equalsIgnoreCase() для нечувствительности к регистру).
Если совпадение найдено — верните эту книгу.
Если цикл завершился — верните null.
Добавьте вывод: "Книга найдена: [title]" или "Книга не найдена".
Альтернативный способ (по желанию):
Используйте метод indexOf() с временным объектом-заглушкой, у которого title совпадает с искомым (переопределите equals() в Book, если нужно).
Реализация метода удаления книги по индексу
Создайте метод removeBookByIndex(int index):
Метод должен возвращать boolean: true — если удаление прошло успешно, false — если индекс неверный.
Проверьте, что индекс в допустимом диапазоне: >= 0 и < books.size().
Если индекс неверный — выведите сообщение "Неверный индекс" и верните false.
Если индекс корректный — используйте метод remove(index) из ArrayList.
Сохраните удаленную книгу (Book removedBook = books.remove(index);) и выведите сообщение "Книга удалена: [title удаленной книги]".
Верните true.
#Java #для_новичков #beginner #List #ArrayList #LinkedList #Практика
Глава 2. List — списки
Практика В проекте «Библиотека» заменить массив книг на ArrayList. Реализовать методы: добавить книгу, найти по названию, удалить по индексу
Убедитесь, что ваш проект готов, и вспомните ключевые возможности ArrayList:
Динамический размер (растёт автоматически)
Доступ по индексу O(1)
Добавление в конец O(1) в среднем
Удаление O(n) (сдвиг элементов)
Поиск indexOf O(n)
Откройте проект «Библиотека»: Запустите IntelliJ IDEA и откройте проект LibraryProject.
Проверьте структуру: У вас должен быть класс Book с полями title, author, year, конструктором и методом printDetails(). Класс Library с массивом книг и методом addBook.
Замена массива на ArrayList
Замените поле массива:
Откройте файл Library.java.
Удалите объявление массива Book[] books и переменную bookCount.
Вместо этого объявите приватное поле:
private List<Book> books = new ArrayList<>();
Это будет наш основной список книг.
Инициализация:
Убедитесь, что books инициализируется в конструкторе (если не сделали при объявлении).
Можно также создать отдельный конструктор Library(), где books = new ArrayList<>(); — на ваш выбор.
Реализация метода добавления книги
Создайте или обновите метод addBook(Book book):
Метод должен быть публичным и принимать объект Book.
Используйте метод add() из ArrayList для добавления книги в конец списка.
Добавьте вывод сообщения: "Книга [title] успешно добавлена".
(Опционально) Проверьте, что книга не null перед добавлением.
Реализация метода поиска книги по названию
Создайте метод findBookByTitle(String title):
Метод должен возвращать объект Book или null, если не найден.
Переберите весь список с помощью цикла for или for-each.
Для каждой книги сравните её title с искомым (используйте equals(), учитывая регистр или используйте equalsIgnoreCase() для нечувствительности к регистру).
Если совпадение найдено — верните эту книгу.
Если цикл завершился — верните null.
Добавьте вывод: "Книга найдена: [title]" или "Книга не найдена".
Альтернативный способ (по желанию):
Используйте метод indexOf() с временным объектом-заглушкой, у которого title совпадает с искомым (переопределите equals() в Book, если нужно).
Реализация метода удаления книги по индексу
Создайте метод removeBookByIndex(int index):
Метод должен возвращать boolean: true — если удаление прошло успешно, false — если индекс неверный.
Проверьте, что индекс в допустимом диапазоне: >= 0 и < books.size().
Если индекс неверный — выведите сообщение "Неверный индекс" и верните false.
Если индекс корректный — используйте метод remove(index) из ArrayList.
Сохраните удаленную книгу (Book removedBook = books.remove(index);) и выведите сообщение "Книга удалена: [title удаленной книги]".
Верните true.
#Java #для_новичков #beginner #List #ArrayList #LinkedList #Практика
👍3
Обновление Main для тестирования
Теперь протестируем новые методы в классе Main.
Создайте объект Library и добавьте книги:
Создайте несколько объектов Book (минимум 4-5).
Добавьте их в библиотеку через addBook().
Протестируйте поиск:
Вызовите findBookByTitle() с существующим и несуществующим названием.
Если книга найдена — вызовите printDetails() на возвращенном объекте.
Протестируйте удаление:
Выведите текущий размер списка (books.size()).
Удалите книгу по валидному индексу (например, 1).
Удалите по невалидному индексу (например, -1 или больше size).
Выведите размер после удаления.
Дополнительно: Добавьте метод printAllBooks() в Library, который перебирает весь список и вызывает printDetails() для каждой книги — используйте его для проверки состояния библиотеки.
Тестирование и отладка
Запустите проект:
Run 'Main.main()' — наблюдайте за сообщениями в консоли.
Проверьте поведение:
Добавление: Список растёт, нет ограничений по размеру.
Поиск: Находит по точному совпадению.
Удаление: Корректно удаляет и сдвигает элементы.
Отладка:
Установите breakpoint в методе removeBookByIndex и findBookByTitle.
Шагайте по коду (F8) и смотрите, как меняется размер списка и содержимое.
Полезные советы для новичков
Импорты: Не забудьте import java.util.ArrayList; и import java.util.List;
Generics: List<Book> books — всегда используйте generics.
Проверка на null: В addBook: if (book == null) return;
Регистр в поиске: equalsIgnoreCase() для нечувствительности к регистру.
Вывод размера: books.size() вместо books.length (это массив!).
Удаление: remove(index) возвращает удаленный элемент — удобно для сообщений.
Практическое задание
Задача 1: Добавьте в Library метод getBookCount(), возвращающий books.size().
Задача 2: Реализуйте метод removeBookByTitle(String title), который находит книгу по названию и удаляет её (используйте цикл или indexOf + remove).
Задача 3: Добавьте метод printAllBooks(), который выводит все книги с номерами (индекс + 1).
#Java #для_новичков #beginner #List #ArrayList #LinkedList #Практика
Теперь протестируем новые методы в классе Main.
Создайте объект Library и добавьте книги:
Создайте несколько объектов Book (минимум 4-5).
Добавьте их в библиотеку через addBook().
Протестируйте поиск:
Вызовите findBookByTitle() с существующим и несуществующим названием.
Если книга найдена — вызовите printDetails() на возвращенном объекте.
Протестируйте удаление:
Выведите текущий размер списка (books.size()).
Удалите книгу по валидному индексу (например, 1).
Удалите по невалидному индексу (например, -1 или больше size).
Выведите размер после удаления.
Дополнительно: Добавьте метод printAllBooks() в Library, который перебирает весь список и вызывает printDetails() для каждой книги — используйте его для проверки состояния библиотеки.
Тестирование и отладка
Запустите проект:
Run 'Main.main()' — наблюдайте за сообщениями в консоли.
Проверьте поведение:
Добавление: Список растёт, нет ограничений по размеру.
Поиск: Находит по точному совпадению.
Удаление: Корректно удаляет и сдвигает элементы.
Отладка:
Установите breakpoint в методе removeBookByIndex и findBookByTitle.
Шагайте по коду (F8) и смотрите, как меняется размер списка и содержимое.
Полезные советы для новичков
Импорты: Не забудьте import java.util.ArrayList; и import java.util.List;
Generics: List<Book> books — всегда используйте generics.
Проверка на null: В addBook: if (book == null) return;
Регистр в поиске: equalsIgnoreCase() для нечувствительности к регистру.
Вывод размера: books.size() вместо books.length (это массив!).
Удаление: remove(index) возвращает удаленный элемент — удобно для сообщений.
Практическое задание
Задача 1: Добавьте в Library метод getBookCount(), возвращающий books.size().
Задача 2: Реализуйте метод removeBookByTitle(String title), который находит книгу по названию и удаляет её (используйте цикл или indexOf + remove).
Задача 3: Добавьте метод printAllBooks(), который выводит все книги с номерами (индекс + 1).
#Java #для_новичков #beginner #List #ArrayList #LinkedList #Практика
👍2
Что выведет код?
#Tasks
import java.util.*;
public class Task081225 {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("a");
list1.add("b");
List<String> list2 = new LinkedList<>();
list2.add("a");
list2.add("b");
System.out.println(list1.equals(list2));
System.out.println(list1.get(0) == list2.get(0));
System.out.println(list1.get(0).equals(list2.get(0)));
System.out.println(list1.iterator().getClass() == list2.iterator().getClass());
}
}
#Tasks
👍1
Варианты ответа:
Anonymous Quiz
21%
true true true false
0%
true false true true
29%
true true true true
50%
false true true false
👍1
Вопрос с собеседований
Разница между Callable и Runnable?🤓
Ответ:
Runnable не возвращает результат и не бросает проверяемых исключений.
Callable возвращает значение и может выбрасывать исключения, что делает его удобным для асинхронных задач.
Обычно используется вместе с Future и ExecutorService для получения результатов.
#собеседование
Разница между Callable и Runnable?
Ответ:
Callable возвращает значение и может выбрасывать исключения, что делает его удобным для асинхронных задач.
Обычно используется вместе с Future и ExecutorService для получения результатов.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
История IT-технологий сегодня — 09 декабря
ℹ️ Кто родился в этот день
Грейс Хоппер (англ. Grace Hopper, урождённая Grace Brewster Murray — Грейс Брюстер Мюррей; 9 декабря 1906 — 1 января 1992) — пионер программирования, создатель компиляторов и одного из предшественников COBOL; её работа по машинно-независимым языкам стала краеугольным камнем прикладного ПО.
Дэвид Стифлер Джонсон (9 декабря 1945, Вашингтон — 8 марта 2016) — ведущий исследователь в области алгоритмов и теории сложности, автор важной монографии по NP-полноте и многолетний руководитель исследований по оптимизации в AT&T Labs.
Шветак Пател (Патель) (Shwetak N. Patel; род. 9 декабря 1981, Сельма, Алабама) — современный исследователь в области сенсоров и энергосберегающих систем (убер-сенсоры для умного дома, мобильные сенсоры), лауреат премий за прикладные решения в мобильном и экологическом сенсировании.
🌐 Знаковые события
1968 — Дуглас Энгельбарт впервые публично продемонстрировал изобретённые им или в его лаборатории компьютерную мышь и гипертекст.
#Biography #Birth_Date #Events #09Декабря
Грейс Хоппер (англ. Grace Hopper, урождённая Grace Brewster Murray — Грейс Брюстер Мюррей; 9 декабря 1906 — 1 января 1992) — пионер программирования, создатель компиляторов и одного из предшественников COBOL; её работа по машинно-независимым языкам стала краеугольным камнем прикладного ПО.
Дэвид Стифлер Джонсон (9 декабря 1945, Вашингтон — 8 марта 2016) — ведущий исследователь в области алгоритмов и теории сложности, автор важной монографии по NP-полноте и многолетний руководитель исследований по оптимизации в AT&T Labs.
Шветак Пател (Патель) (Shwetak N. Patel; род. 9 декабря 1981, Сельма, Алабама) — современный исследователь в области сенсоров и энергосберегающих систем (убер-сенсоры для умного дома, мобильные сенсоры), лауреат премий за прикладные решения в мобильном и экологическом сенсировании.
1968 — Дуглас Энгельбарт впервые публично продемонстрировал изобретённые им или в его лаборатории компьютерную мышь и гипертекст.
#Biography #Birth_Date #Events #09Декабря
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Конфигурация маршрутов в Spring Cloud Gateway
Декларативная конфигурация через application.yml
Конфигурация через YAML-файлы является декларативным подходом, где маршруты определяются статически на этапе компиляции или до запуска приложения. Этот метод обеспечивает простоту чтения, версионирование через системы контроля версий и простоту аудита изменений.
Базовая структура конфигурации маршрута
В YAML маршруты конфигурируются через иерархию spring.cloud.gateway.routes. Каждый маршрут должен иметь уникальный идентификатор, URI назначения, список предикатов и опциональный список фильтров.
Детализация предикатов
Предикаты в YAML конфигурируются как список строк, где каждая строка соответствует фабрике предикатов и её параметрам. Формат: Name=arg1,arg2,...,key1=value1,key2=value2.
Предикат Path:
Составные предикаты с помощью After и Before:
Предикат Query для проверки параметров:
Предикат RemoteAddr для контроля доступа по IP:
Кастомный предикат с использованием SpEL:
Фильтры в YAML конфигурации
Фильтры определяют преобразования, применяемые к запросу или ответу. Они выполняются в порядке объявления.
Базовые фильтры преобразования:
Фильтры для работы с телом запроса:
#Java #middle #Spring_Cloud_Gateway
Декларативная конфигурация через application.yml
Конфигурация через YAML-файлы является декларативным подходом, где маршруты определяются статически на этапе компиляции или до запуска приложения. Этот метод обеспечивает простоту чтения, версионирование через системы контроля версий и простоту аудита изменений.
Базовая структура конфигурации маршрута
В YAML маршруты конфигурируются через иерархию spring.cloud.gateway.routes. Каждый маршрут должен иметь уникальный идентификатор, URI назначения, список предикатов и опциональный список фильтров.
spring:
cloud:
gateway:
routes:
- id: user_service_route
uri: http://localhost:8081
predicates:
- Path=/api/users/**
- Method=GET,POST
- Header=X-Requested-With, XMLHttpRequest
filters:
- StripPrefix=1
- AddRequestHeader=X-Gateway-Version, v2.0
- RewritePath=/api/users/(?<segment>.*), /$\{segment}
metadata:
response-timeout: 5000
connect-timeout: 2000
max-auto-retries: 3
Детализация предикатов
Предикаты в YAML конфигурируются как список строк, где каждая строка соответствует фабрике предикатов и её параметрам. Формат: Name=arg1,arg2,...,key1=value1,key2=value2.
Предикат Path:
predicates:
- Path=/api/v1/.*
- Path=/api/orders/{segment}, /api/products/{segment}
При использовании фигурных скобок {segment} происходит извлечение переменных пути, которые затем доступны в фильтрах через шаблоны типа ${segment}.
Составные предикаты с помощью After и Before:
predicates:
- After=2023-01-20T17:42:47.789-07:00[America/Denver]
- Between=2023-01-20T17:42:47.789-07:00[America/Denver],2023-01-21T17:42:47.789-07:00[America/Denver]
Временные предикаты используют формат даты-времени ISO-8601 и учитывают временные зоны. Они полезны для канарей-развёртываний и управления версиями API.
Предикат Query для проверки параметров:
predicates:
- Query=version, ^v[0-9]+$
- Query=token
Первый вариант проверяет, что параметр version существует и соответствует регулярному выражению. Второй вариант проверяет только наличие параметра token без проверки значения.
Предикат RemoteAddr для контроля доступа по IP:
predicates:
- RemoteAddr=192.168.1.1/24, 10.0.0.1/8
Поддерживается CIDR-нотация. Этот предикат часто комбинируется с фильтрами аутентификации для создания многоуровневой безопасности.
Кастомный предикат с использованием SpEL:
predicates:
- name: Custom
args:
name: myPredicate
spel: "#{@tokenValidator.validate(request)}"
Для кастомных предикатов используется полная форма конфигурации с указанием имени и аргументов. SpEL (Spring Expression Language) позволяет вызывать Spring Beans и выполнять сложную логику.
Фильтры в YAML конфигурации
Фильтры определяют преобразования, применяемые к запросу или ответу. Они выполняются в порядке объявления.
Базовые фильтры преобразования:
filters:
# Удаление частей пути
- StripPrefix=2
# Добавление заголовков
- AddRequestHeader=X-Client-Version, 1.0.0
- AddResponseHeader=X-Response-Time, "`new java.text.SimpleDateFormat('yyyy-MM-dd HH:mm:ss.SSS').format(new java.util.Date())`"
# Установка пути
- SetPath=/api/v2/{segment}
# Изменение порта и хоста
- SetRequestHost=api.example.com
# Перенаправление
- RedirectTo=302, https://secure.example.com
Фильтры для работы с телом запроса:
filters:
# Модификация тела запроса (требует чтения всего тела в память)
- ModifyRequestBody=com.example.JsonTransformer, application/json, application/json, 102400
# Модификация тела ответа
- ModifyResponseBody=com.example.ResponseNormalizer, application/json, application/json
# Кэширование тела запроса для многократного чтения
- CacheRequestBody=""
Фильтры модификации тела используют регистрируемые через Spring Beans трансформеры. Важно понимать, что они требуют чтения всего тела запроса в память, что может быть проблематично для больших payload.
#Java #middle #Spring_Cloud_Gateway
👍1
Фильтры устойчивости и ограничения:
URI и метаданные
URI назначения поддерживает несколько схем:
Метаданные маршрута предоставляют дополнительную конфигурацию, специфичную для маршрута:
#Java #middle #Spring_Cloud_Gateway
filters:
# Circuit Breaker с Resilience4J
- name: CircuitBreaker
args:
name: userServiceBreaker
fallbackUri: forward:/fallback/user
statusCodes: 500,502,503
# Ограничение частоты запросов с Redis
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1
key-resolver: "#{@userKeyResolver}"
# Retry с экспоненциальной отсрочкой
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY,INTERNAL_SERVER_ERROR
methods: GET,POST
series: SERVER_ERROR
backoff:
firstBackoff: 50ms
maxBackoff: 1000ms
factor: 2
basedOnPreviousValue: false
URI и метаданные
URI назначения поддерживает несколько схем:
uri: http://localhost:8081 # Прямой URL
uri: https://api.example.com:8443 # HTTPS
uri: lb://USER-SERVICE # Балансировка нагрузки через Service Discovery
uri: ws://echo.example.com # WebSocket
uri: forward:/fallback # Внутреннее перенаправление
Схема lb:// активирует клиентскую балансировку нагрузки. При использовании с Service Discovery (Eureka, Consul) имя сервиса автоматически разрешается в список доступных экземпляров.
Метаданные маршрута предоставляют дополнительную конфигурацию, специфичную для маршрута:
metadata:
# Таймауты для Netty
response-timeout: 5000 # Таймаут ответа в миллисекундах
connect-timeout: 2000 # Таймаут соединения
max-auto-retries: 2 # Автоматические повторы для сетевых ошибок
# Конфигурация для конкретных интеграций
hystrix.commandName: userCommand
metrics.tags: "service=user,version=v2"
# Пользовательские метаданные
feature-flags: "canary=true,experimental=false"
sla: "p99<200ms"
Метаданные доступны в фильтрах через exchange.getAttribute(ROUTE_ATTRIBUTE).getMetadata() и могут использоваться для реализации динамического поведения.
#Java #middle #Spring_Cloud_Gateway
👍1
Java DSL: Fluent API для программируемой конфигурации
Java DSL через RouteLocatorBuilder предоставляет императивный способ определения маршрутов, который позволяет использовать всю мощь Java: условия, циклы, вызовы методов, dependency injection.
Базовый fluent-синтаксис
Преимущества Java DSL перед YAML
1. Использование Spring Beans в конфигурации:
#Java #middle #Spring_Cloud_Gateway
Java DSL через RouteLocatorBuilder предоставляет императивный способ определения маршрутов, который позволяет использовать всю мощь Java: условия, циклы, вызовы методов, dependency injection.
Базовый fluent-синтаксис
@Configuration
public class GatewayConfiguration {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("user_route", r -> r
.path("/api/users/**")
.and()
.method(HttpMethod.GET, HttpMethod.POST)
.and()
.header("X-API-Version", "v2")
.filters(f -> f
.stripPrefix(1)
.addRequestHeader("X-Gateway-Route", "user")
.circuitBreaker(config -> config
.setName("userCircuitBreaker")
.setFallbackUri("forward:/fallback/user"))
.retry(config -> config
.setRetries(3)
.setStatuses(HttpStatus.INTERNAL_SERVER_ERROR,
HttpStatus.BAD_GATEWAY)
.setMethods(HttpMethod.GET)
.setBackoff(50, 200, 2, true))
)
.uri("lb://user-service")
.metadata("response-timeout", 3000)
.metadata("connect-timeout", 1000)
)
.route("product_route", r -> r
.path("/api/products/**")
.filters(f -> f
.rewritePath("/api/products/(?<segment>.*)",
"/v2/products/${segment}")
.addResponseHeader("X-Cache-Control", "max-age=3600")
.requestRateLimiter(config -> config
.setRateLimiter(redisRateLimiter())
.setKeyResolver(userKeyResolver()))
)
.uri("lb://product-service")
)
.build();
}
@Bean
public RedisRateLimiter redisRateLimiter() {
return new RedisRateLimiter(10, 20, 1);
}
@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getHeaders()
.getFirst("X-User-Id")
);
}
}
Преимущества Java DSL перед YAML
1. Использование Spring Beans в конфигурации:
@Bean
public RouteLocator dynamicRouteLocator(
RouteLocatorBuilder builder,
FeatureFlagService featureService,
CircuitBreakerRegistry breakerRegistry) {
return builder.routes()
.route("feature_route", r -> r
.path("/experimental/**")
.predicate(exchange -> {
// Динамическая проверка feature flags
boolean enabled = featureService.isEnabled(
"experimental_api",
exchange.getRequest().getHeaders()
.getFirst("X-User-Id")
);
return enabled;
})
.filters(f -> f
.circuitBreaker(config -> config
.setCircuitBreakerFactory(
new ReactiveResilience4JCircuitBreakerFactory(
breakerRegistry
)
)
.setName("experimentalBreaker")
)
)
.uri("lb://experimental-service")
)
.build();
}
#Java #middle #Spring_Cloud_Gateway
👍1
2. Генерация маршрутов на основе данных из внешних источников:
3. Создание сложных составных предикатов:
Что можно делать только в DSL и нельзя в YAML
1. Использование произвольных Predicate<ServerWebExchange>:
2. Интеграция с внешними системами конфигурации:
#Java #middle #Spring_Cloud_Gateway
@Bean
public RouteLocator generatedRoutes(
RouteLocatorBuilder builder,
RouteTemplateService templateService) {
RouteLocatorBuilder.Builder routesBuilder = builder.routes();
// Загрузка шаблонов маршрутов из базы данных
List<RouteTemplate> templates = templateService.loadTemplates();
for (RouteTemplate template : templates) {
routesBuilder.route(template.getId(), r -> {
RouteLocatorBuilder.Builder spec = r
.path(template.getPathPattern())
.uri(template.getTargetUri());
// Динамическое добавление фильтров
for (FilterConfig filterConfig : template.getFilters()) {
spec.filters(f -> addFilterDynamically(f, filterConfig));
}
return spec;
});
}
return routesBuilder.build();
}
3. Создание сложных составных предикатов:
@Bean
public RouteLocator complexPredicateRoute(RouteLocatorBuilder builder) {
return builder.routes()
.route("business_rule_route", r -> r
// Комбинация предикатов с кастомной логикой
.asyncPredicate(exchange -> {
return businessRuleEngine()
.evaluate(exchange.getRequest())
.map(BusinessDecision::isAllowed);
})
.and()
.weight("group_a", 80) // 80% трафика
.and()
.cookie("session_id", ".*")
.filters(f -> f
.modifyRequestBody(String.class, String.class,
(exchange, body) -> {
// Модификация тела на основе бизнес-правил
return Mono.just(
transformPayload(body, exchange)
);
}
)
)
.uri("lb://primary-service")
)
.build();
}
Что можно делать только в DSL и нельзя в YAML
1. Использование произвольных Predicate<ServerWebExchange>:
.route("custom_predicate", r -> r
.predicate(exchange -> {
// Любая Java-логика
String clientIp = exchange.getRequest()
.getRemoteAddress()
.getAddress()
.getHostAddress();
return !ipBlacklist.contains(clientIp) &&
rateLimiter.tryAcquire(clientIp);
})
.uri("lb://service")
)2. Интеграция с внешними системами конфигурации:
.route("external_config", r -> r
.asyncPredicate(exchange -> {
// Загрузка правил из внешнего сервиса
return configClient.getRoutingRules()
.map(rules -> rules.matches(exchange.getRequest()));
})
.filters((exchange, chain) -> {
// Динамическая модификация на основе конфигурации
ServerHttpRequest request = exchange.getRequest();
Map<String, String> headers = externalConfig
.getHeadersForRoute(request.getPath().toString());
ServerHttpRequest mutated = request.mutate()
.headers(h -> headers.forEach(h::add))
.build();
return chain.filter(
exchange.mutate().request(mutated).build()
);
})
.uri("lb://target")
)#Java #middle #Spring_Cloud_Gateway
👍1
3. Генерация маршрутов в runtime:
Динамическая маршрутизация
DiscoveryClientRouteDefinitionLocator
DiscoveryClientRouteDefinitionLocator обеспечивает автоматическое создание маршрутов на основе сервисов, зарегистрированных в Service Discovery. При активации, для каждого зарегистрированного сервиса создаётся маршрут, который перенаправляет трафик на этот сервис.
Эта конфигурация создаст маршруты вида:
http://gateway/services/user-service/** → lb://user-service
http://gateway/services/order-service/** → lb://order-service
Интеграция с Eureka
Для интеграции с Eureka необходимо добавить зависимость и соответствующую конфигурацию:
Eureka-специфичные метаданные могут использоваться для управления маршрутизацией:
#Java #middle #Spring_Cloud_Gateway
@Bean
public RouteLocator runtimeGeneratedRoutes(
RouteLocatorBuilder builder,
ApplicationContext context) {
RouteLocatorBuilder.Builder routes = builder.routes();
// Динамическое создание маршрутов на основе Bean-ов
Map<String, RouteProvider> providers = context
.getBeansOfType(RouteProvider.class);
providers.forEach((name, provider) -> {
provider.getRoutes().forEach(routeDef -> {
routes.route(routeDef.getId(), r -> {
AbstractRouteSpec spec = r
.path(routeDef.getPath())
.uri(routeDef.getUri());
routeDef.getFilters().forEach(filter -> {
spec.filters(f -> configureFilter(f, filter));
});
return spec;
});
});
});
return routes.build();
}
Динамическая маршрутизация
DiscoveryClientRouteDefinitionLocator
DiscoveryClientRouteDefinitionLocator обеспечивает автоматическое создание маршрутов на основе сервисов, зарегистрированных в Service Discovery. При активации, для каждого зарегистрированного сервиса создаётся маршрут, который перенаправляет трафик на этот сервис.
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
predicates:
- name: Path
args:
pattern: "'/services/' + serviceId.toLowerCase() + '/**'"
filters:
- name: RewritePath
args:
regexp: "'/services/' + serviceId.toLowerCase() + '/(?<remaining>.*)'"
replacement: "'/${remaining}'"
Эта конфигурация создаст маршруты вида:
http://gateway/services/user-service/** → lb://user-service
http://gateway/services/order-service/** → lb://order-service
Интеграция с Eureka
Для интеграции с Eureka необходимо добавить зависимость и соответствующую конфигурацию:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
eureka:
client:
service-url:
defaultZone: http://eureka-server:8761/eureka
registry-fetch-interval-seconds: 5
instance:
lease-renewal-interval-in-seconds: 10
lease-expiration-duration-in-seconds: 30
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
# Добавление метаданных из Eureka в маршруты
include-expression: metadata['gateway.enabled'] == 'true'
url-expression: "'lb://' + serviceId"
Eureka-специфичные метаданные могут использоваться для управления маршрутизацией:
@Bean
public RouteLocator eurekaAwareRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route("zone_aware", r -> r
.path("/zone/**")
.predicate(exchange -> {
// Выбор экземпляра в той же зоне доступности
ServiceInstance instance = loadBalancer.choose(
"target-service",
new ZonePreferenceServiceInstanceListSupplier()
);
return instance != null;
})
.uri("lb://target-service")
)
.build();
}
#Java #middle #Spring_Cloud_Gateway
👍1
Интеграция с Consul
Consul предоставляет более богатые возможности для service discovery и health checking:
Consul теги могут использоваться для реализации сложной логики маршрутизации:
#Java #middle #Spring_Cloud_Gateway
Consul предоставляет более богатые возможности для service discovery и health checking:
spring:
cloud:
consul:
host: localhost
port: 8500
discovery:
health-check-path: /actuator/health
health-check-interval: 10s
tags:
- gateway-enabled
- version=v2
query-passing: true # Использовать только здоровые инстансы
gateway:
discovery:
locator:
enabled: true
predicates:
- name: Path
args:
pattern: "'/consul/' + serviceId + '/**'"
filters:
- name: RewritePath
args:
regexp: "'/consul/' + serviceId + '/(?<remaining>.*)'"
replacement: "'/${remaining}'"
# Фильтрация по Consul-тегам
include-expression: tags.contains('gateway-enabled')
Consul теги могут использоваться для реализации сложной логики маршрутизации:
@Bean
public RouteLocator consulTagBasedRoutes(
RouteLocatorBuilder builder,
ConsulDiscoveryClient discoveryClient) {
return builder.routes()
.route("canary_route", r -> r
.path("/api/canary/**")
.predicate(exchange -> {
// Динамическая канареечная маршрутизация
List<ServiceInstance> instances = discoveryClient
.getInstances("product-service");
// Выбор инстансов с тегом canary
ServiceInstance canaryInstance = instances.stream()
.filter(inst -> inst.getMetadata()
.getOrDefault("canary", "false")
.equals("true"))
.findFirst()
.orElse(null);
// 10% трафика на canary
return canaryInstance != null &&
Math.random() < 0.1;
})
.uri("lb://product-service")
)
.build();
}
#Java #middle #Spring_Cloud_Gateway
👍1
Кастомный RouteDefinitionRepository
Для полностью динамической маршрутизации можно реализовать собственный RouteDefinitionRepository, который будет загружать маршруты из произвольного источника (БД, файловая система, внешний сервис).
Базовая реализация с поддержкой обновлений:
Реализация с поддержкой веб-интерфейса для управления маршрутами:
#Java #middle #Spring_Cloud_Gateway
Для полностью динамической маршрутизации можно реализовать собственный RouteDefinitionRepository, который будет загружать маршруты из произвольного источника (БД, файловая система, внешний сервис).
Базовая реализация с поддержкой обновлений:
@Component
public class DatabaseRouteDefinitionRepository
implements RouteDefinitionRepository, ApplicationEventPublisherAware {
private final RouteDefinitionDAO routeDefinitionDAO;
private final Map<String, RouteDefinition> cache =
new ConcurrentHashMap<>();
private ApplicationEventPublisher publisher;
public DatabaseRouteDefinitionRepository(
RouteDefinitionDAO routeDefinitionDAO) {
this.routeDefinitionDAO = routeDefinitionDAO;
loadRoutes();
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(cache.values());
}
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap(routeDef -> {
return routeDefinitionDAO.save(routeDef)
.doOnSuccess(saved -> {
cache.put(saved.getId(), saved);
publishRefreshEvent();
})
.then();
});
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap(id -> {
return routeDefinitionDAO.delete(id)
.doOnSuccess(deleted -> {
cache.remove(id);
publishRefreshEvent();
})
.then();
});
}
private void loadRoutes() {
routeDefinitionDAO.findAll()
.doOnNext(routeDef -> cache.put(routeDef.getId(), routeDef))
.subscribe();
}
private void publishRefreshEvent() {
if (publisher != null) {
publisher.publishEvent(
new RefreshRoutesEvent(this)
);
}
}
@Override
public void setApplicationEventPublisher(
ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
// Метод для внешних триггеров обновления
@Scheduled(fixedDelay = 30000)
public void refreshRoutes() {
loadRoutes();
publishRefreshEvent();
}
}
Реализация с поддержкой веб-интерфейса для управления маршрутами:
@RestController
@RequestMapping("/api/gateway/routes")
public class RouteManagementController {
private final RouteDefinitionWriter routeDefinitionWriter;
private final RouteDefinitionLocator routeDefinitionLocator;
@PostMapping
public Mono<ResponseEntity<Void>> createRoute(
@RequestBody RouteDefinition routeDefinition) {
return routeDefinitionWriter
.save(Mono.just(routeDefinition))
.then(Mono.just(ResponseEntity.ok().build()));
}
@GetMapping
public Flux<RouteDefinition> getAllRoutes() {
return routeDefinitionLocator.getRouteDefinitions();
}
@PutMapping("/{id}")
public Mono<ResponseEntity<Void>> updateRoute(
@PathVariable String id,
@RequestBody RouteDefinition routeDefinition) {
return routeDefinitionWriter
.delete(Mono.just(id))
.then(routeDefinitionWriter.save(Mono.just(routeDefinition)))
.then(Mono.just(ResponseEntity.ok().build()));
}
@DeleteMapping("/{id}")
public Mono<ResponseEntity<Void>> deleteRoute(@PathVariable String id) {
return routeDefinitionWriter
.delete(Mono.just(id))
.then(Mono.just(ResponseEntity.noContent().build()));
}
}
#Java #middle #Spring_Cloud_Gateway
👍1
Интеграция с внешними системами конфигурации:
Обработка событий обновления маршрутов:
#Java #middle #Spring_Cloud_Gateway
@Component
public class ExternalConfigRouteDefinitionRepository
implements RouteDefinitionRepository {
private final ConfigClient configClient;
private final AtomicReference<List<RouteDefinition>> cachedRoutes =
new AtomicReference<>(Collections.emptyList());
public ExternalConfigRouteDefinitionRepository(
ConfigClient configClient) {
this.configClient = configClient;
configClient.watchRoutes()
.doOnNext(this::updateRoutes)
.subscribe();
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(cachedRoutes.get());
}
private void updateRoutes(List<RouteConfig> routeConfigs) {
List<RouteDefinition> routeDefinitions = routeConfigs.stream()
.map(this::convertToRouteDefinition)
.collect(Collectors.toList());
cachedRoutes.set(routeDefinitions);
// Генерация события обновления маршрутов
SpringApplication.publishEvent(
new RefreshRoutesEvent(this)
);
}
private RouteDefinition convertToRouteDefinition(
RouteConfig config) {
RouteDefinition definition = new RouteDefinition();
definition.setId(config.getId());
definition.setUri(URI.create(config.getUri()));
// Конвертация предикатов
config.getPredicates().forEach((name, args) -> {
PredicateDefinition predicate = new PredicateDefinition();
predicate.setName(name);
predicate.setArgs(args);
definition.getPredicates().add(predicate);
});
// Конвертация фильтров
config.getFilters().forEach((name, args) -> {
FilterDefinition filter = new FilterDefinition();
filter.setName(name);
filter.setArgs(args);
definition.getFilters().add(filter);
});
definition.setMetadata(config.getMetadata());
return definition;
}
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
// Делегирование сохранения во внешнюю систему
return route.flatMap(routeDef ->
configClient.saveRoute(convertToRouteConfig(routeDef))
).then();
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap(id ->
configClient.deleteRoute(id)
).then();
}
}
Обработка событий обновления маршрутов:
@Component
public class RouteUpdateListener {
private final RouteRefreshListener routeRefreshListener;
@EventListener
public void handleRefreshRoutesEvent(RefreshRoutesEvent event) {
// Логирование обновления маршрутов
logger.info("Routes refreshed by {}", event.getSource());
// Метрики
meterRegistry.counter("gateway.routes.refresh").increment();
// Уведомление подписчиков
routeRefreshListener.notifyRefresh();
}
@EventListener
public void handleApplicationEvent(ApplicationEvent event) {
if (event instanceof InstanceRegisteredEvent) {
// Обработка регистрации нового инстанса
updateRoutesForNewInstance(
((InstanceRegisteredEvent) event).getInstance()
);
}
}
}
#Java #middle #Spring_Cloud_Gateway
👍1
Что выведет код?
#Tasks
import java.util.LinkedList;
import java.util.Queue;
public class Task091225 {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
queue.offer("first");
queue.offer("second");
queue.offer(null);
queue.offer("third");
System.out.println(queue.poll());
System.out.println(queue.peek());
System.out.println(queue.element());
System.out.println(queue.size());
}
}
#Tasks
Варианты ответа:
Anonymous Quiz
0%
first second second 2
25%
first second second 3
0%
first null исключение
75%
first null null 3