Java for Beginner
870 subscribers
1.01K photos
275 videos
14 files
1.69K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Что такое BlockingQueue и где он применяется? 🤓

Ответ:

BlockingQueue
из java.util.concurrent — это интерфейс для потокобезопасных очередей с блокирующими операциями.

При попытке взять элемент из пустой очереди поток блокируется до появления элемента.

При попытке добавить элемент в переполненную очередь (если есть ограничение) поток блокируется до освобождения места.

Классическая реализация — паттерн Producer-Consumer. Реализации: ArrayBlockingQueue (ограниченная), LinkedBlockingQueue (опционально ограниченная), PriorityBlockingQueue и др.



#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История технологии сегодня — 10 апреля

ℹ️ Кто родился в этот день

Прийт Касесалу (родился 10 апреля 1972 г.) — эстонский программист и разработчик программного обеспечения, наиболее известный своим участием в разработке Kazaa, Skype и, совсем недавно, Joost. В настоящее время он работает в Ambient Sound Investments и живет в Таллинне, Эстония .


🌐 Знаковые события

1957 — в Дубне введён в действие синхрофазотрон Объединённого института ядерных исследований.

2019 — миру представлена первая фотография чёрной дыры.


#Biography #Birth_Date #Events #10апреля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Раздел 10. Исключения, логирование, отладка

Глава 1. Иерархия исключений (Exceptions)

Checked vs Unchecked исключения: когда что использовать.


Разделение исключений на checked и unchecked в Java основано на фундаментальном различии между двумя категориями проблем. Проверяемые исключения (checked) предназначены для ситуаций, которые клиентский код может разумно ожидать и от которых может восстановиться. Непроверяемые исключения (unchecked) сигнализируют о программных ошибках, которые должны быть исправлены в коде, а не обработаны во время выполнения.

Классическое правило, сформулированное в документации Oracle, гласит: если клиент может разумно ожидать восстановления от исключения, сделайте его проверяемым. Если клиент не может ничего сделать для восстановления, сделайте его непроверяемым. Это различие отражает два разных мира: внешние, непредсказуемые условия (отсутствие файла, недоступность сети) против внутренних нарушений контрактов (передача null вместо валидного аргумента, выход за границы массива).

Однако за четверть века существования Java эта модель подверглась серьезной критике и эволюции. Современные фреймворки и языки на JVM демонстрируют явный сдвиг в сторону unchecked-исключений даже для традиционно checked-сценариев.


Механика checked-исключений: контракты и обязательства

Checked-исключения представляют собой часть сигнатуры метода, формируя контракт между вызывающим и вызываемым кодом. Когда метод объявляет throws IOException, он явно заявляет о возможности сбоя ввода-вывода, обязывая вызывающий код либо обработать эту ситуацию через try-catch, либо передать ответственность дальше по стеку вызовов. Этот механизм создает цепочку ответственности.

Рассмотрим шесть уровней вызовов между точкой возникновения исключения и его обработкой: каждый промежуточный метод должен либо поймать исключение, либо добавить его в свою сигнатуру throws. Это приводит к эффекту загрязнения сигнатур (signature pollution), когда изменение в низкоуровневом методе вынуждает обновлять десятки сигнатур выше по стеку.

Практический пример демонстрирует эту проблему:
// Низкоуровневый метод работы с базой данных
public User findById(String id) throws SQLException {
Connection conn = dataSource.getConnection();
// ... выполнение запроса
}

// Сервисный слой вынужден пробрасывать checked-исключение
public User getUser(String id) throws SQLException {
return userRepository.findById(id);
}

// Контроллер также заражен checked-исключением
public UserDto getUserEndpoint(String id) throws SQLException {
User user = userService.getUser(id);
return userMapper.toDto(user);
}


В этой иерархии SQLException пронизывает все слои приложения, нарушая принцип разделения ответственности. Контроллер, отвечающий за HTTP-протокол, вынужден знать о деталях работы с базой данных, что создает нежелательную связанность (coupling) между модулями.


Unchecked-исключения: свобода и ответственность

Unchecked-исключения, наследующие RuntimeException, не требуют объявления в сигнатурах методов. Они могут возникнуть в любом месте и распространяться по стеку вызовов до тех пор, пока не будут перехвачены или не приведут к завершению потока. Эта модель освобождает разработчика от обязательной обработки каждого возможного сбоя, но требует дисциплины в проектировании границ транзакций и обработки ошибок.

Ключевое преимущество unchecked-исключений — сохранение чистоты сигнатур методов. Метод может сосредоточиться на своей основной ответственности, не загромождая сигнатуру перечнем возможных сбоев. Обработка ошибок откладывается на уровень, обладающий достаточным контекстом для принятия решений: retry, fallback, уведомление пользователя или аварийное завершение операции.


#Java #для_новичков #beginner #exception #checked #unchecked
👍3
Пример рефакторинга предыдущего кода в unchecked-стиле:
// Кастомное unchecked-исключение для домена
public class UserNotFoundException extends RuntimeException {
private final String userId;

public UserNotFoundException(String userId) {
super("User not found: " + userId);
this.userId = userId;
}
}

// Репозиторий скрывает детали SQL, транслирует в доменное исключение
public User findById(String id) {
try {
Connection conn = dataSource.getConnection();
// ... выполнение запроса
} catch (SQLException e) {
throw new DataAccessException("Database error while loading user", e);
}
}

// Сервисный слой чист, без throws
public User getUser(String id) {
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
}

// Контроллер также чист, фокусируется на HTTP-логике
public UserDto getUserEndpoint(String id) {
User user = userService.getUser(id);
return userMapper.toDto(user);
}


В этом варианте SQLException перехватывается на границе инфраструктурного слоя и транслируется в DataAccessException — unchecked-исключение из Spring Framework. Доменное исключение UserNotFoundException также unchecked, так как отсутствие пользователя — это валидный бизнес-сценарий, который контроллер обрабатывает через глобальный обработчик исключений, преобразуя в HTTP 404.


Эволюция Spring Framework: от checked к unchecked

Spring Framework стал одним из первых major-фреймворков, систематически отказавшихся от checked-исключений. Философия Spring заключается в том, что большинство исключений в enterprise-приложениях — это фатальные сбои текущей операции, от которых невозможно восстановиться на месте.

Центральным элементом этой стратегии является иерархия DataAccessException. Spring перехватывает низкоуровневые checked-исключения (SQLException, HibernateException, JPAException) и транслирует их в структурированную иерархию unchecked-исключений: DataRetrievalFailureException, DataIntegrityViolationException, QueryTimeoutException и другие. Это достигается двумя целями: абстрагирование от конкретной технологии доступа к данным и признание того, что большинство ошибок базы данных фатальны для текущей транзакции.

Пример интеграции с Spring Data:
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
// Метод объявлен без throws, но может выбросить unchecked исключения
Optional<Order> findByOrderNumber(String orderNumber);
}

@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;

@Transactional
public Order processOrder(String orderNumber) {
// Никаких try-catch для checked-исключений
Order order = orderRepository.findByOrderNumber(orderNumber)
.orElseThrow(() -> new OrderNotFoundException(orderNumber));

// При ошибке базы данных будет выброшено DataAccessException
// Транзакция откатится автоматически
return executeBusinessLogic(order);
}
}


В этом коде отсутствуют объявления throws, несмотря на потенциальные сбои базы данных. Spring управляет транзакциями через @Transactional, и любое unchecked-исключение приводит к автоматическому откату.

Обработка ошибок централизована через @ControllerAdvice:
@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(DataAccessException.class)
public ResponseEntity<ErrorResponse> handleDatabaseError(DataAccessException e) {
// Логирование, метрики, уведомление
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
.body(new ErrorResponse("Database temporarily unavailable"));
}

@ExceptionHandler(OrderNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(OrderNotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse(e.getMessage()));
}
}



#Java #для_новичков #beginner #exception #checked #unchecked
👍3
Этот подход разделяет бизнес-логику и обработку ошибок. Бизнес-код остается чистым и сфокусированным, в то время как обработка ошибок концентрируется в специализированных компонентах с полным контекстом HTTP-запроса.


Kotlin: полный отказ от checked-исключений

Kotlin пошел дальше, полностью устранив концепцию checked-исключений из языка. Все исключения в Kotlin по умолчанию unchecked, что соответствует философии минимизации шаблонного кода (boilerplate) и прагматичной разработки.

Это решение было обусловлено накопившимися проблемами checked-исключений в Java: многословностью, разрушением композиции в функциональном стиле, принуждением к плохим практикам (пустые catch-блоки, оборачивание в RuntimeException) и несовместимостью с лямбда-выражениями. Как отмечают разработчики Kotlin, за 25 лет checked-исключения в Java продемонстрировали столько же проблем, сколько решений, и ни один другой язык не принял эту модель .

В Kotlin код обработки ошибок выглядит естественно:
fun updateOrderQuantity(orderId: OrderId, quantity: Int) {
require(quantity > 0) { "Quantity must be positive" }
val order = loadOrder(orderId) // Может выбросить исключение, но не требует try-catch
order.quantity = quantity
storeOrder(order) // То же самое
}


Ключевая концепция Kotlin — централизованная обработка I/O-ошибок. Вместо разбросанных по коду try-catch блоков для каждой сетевой операции, Kotlin предлагает выносить обработку на границу между бизнес-логикой и пользовательским интерфейсом. Это соответствует реальности enterprise-приложений, где сетевые сбои обрабатываются единообразно: retry с экспоненциальным backoff, fallback к кэшу или уведомление пользователя о временной недоступности сервиса.

Для взаимодействия с Java-кодом, использующим checked-исключения, Kotlin предоставляет аннотацию @Throws, которая генерирует соответствующие сигнатуры в байткоде для Java-вызывающих сторон. Однако внутри Kotlin- codebase эта информация не используется для статической проверки.


Фатальный удар: checked-исключения и Java 8 Streams

Введение лямбда-выражений и Stream API в Java 8 выявило фундаментальную несовместимость checked-исключений с функциональным программированием. Функциональные интерфейсы (Function<T,R>, Consumer<T>, Supplier<T>) не объявляют checked-исключений в своих абстрактных методах, что делает невозможным прямое использование методов, бросающих checked-исключения, в лямбдах.

Рассмотрим попытку использовать метод, объявляющий throws IOException, внутри Stream.map():
// Метод с checked-исключением
public String fetchDataFromUrl(String url) throws IOException {
return httpClient.fetch(url);
}

// Невозможно напрямую использовать в Stream
List<String> urls = List.of("http://api1.com", "http://api2.com");
List<String> data = urls.stream()
.map(this::fetchDataFromUrl) // Ошибка компиляции: IOException не обработано
.collect(Collectors.toList());


Разработчики вынуждены прибегать к обходным маневрам: оборачиванию исключений в RuntimeException внутри лямбды, созданию специальных функциональных интерфейсов с throws, или использованию библиотек вроде Vavr. Все эти решения уродливы и разрушают элегантность функционального стиля.

Пример оборачивания, который демонстрирует проблему:
List<String> data = urls.stream()
.map(url -> {
try {
return fetchDataFromUrl(url);
} catch (IOException e) {
throw new RuntimeException(e); // Уничтожение семантики checked-исключения
}
})
.collect(Collectors.toList());


Эта ситуация привела к тому, что комитет по развитию Java фактически признал checked-исключения тупиковой ветвью эволюции языка. Отсутствие поддержки checked-исключений в Stream API — это не oversight, а осознанное решение, демонстрирующее, что checked-исключения и современный Java несовместимы.


#Java #для_новичков #beginner #exception #checked #unchecked
👍4
Современные best practices: доминирование unchecked

Современные рекомендации по проектированию исключений в Java существенно эволюционировали. Если ранее считалось нормой делать все восстановимые сбои checked, то теперь преобладает подход "unchecked by default" с несколькими четкими критериями для исключений.

Используйте checked-исключения только когда:
- Восстановление возможно и вероятно на месте вызова. Пример: повторная попытка при временном сбое сети с backoff, запрос альтернативного ввода от пользователя.
- Исключение является частью доменной модели и требует явного внимания клиента. Пример: бизнес-исключение InsufficientFundsException в финансовой системе, где вызывающий код должен явно обработать сценарий недостаточности средств.
- API предназначен для широкого использования внешними клиентами, и пропуск исключения приведет к катастрофическим последствиям.

Во всех остальных случаях используйте unchecked-исключения:
- Программные ошибки (null-аргументы, выход за границы, нарушение инвариантов).
- Фатальные сбои внешних систем (база данных недоступна, сетевой таймаут), где восстановление требует транзакционной логики на более высоком уровне.
- Сценарии, где исключение транслируется через несколько слоев абстракции перед обработкой.
- Код, интегрирующийся с функциональными API (Streams, CompletableFuture, Optional).

Пример проектирования современного API:
// Checked-исключение для редкого, но критичного сценария восстановления
public class PaymentReversalRequiredException extends Exception {
private final Payment originalPayment;
private final String reasonCode;

public PaymentReversalRequiredException(Payment payment, String reasonCode, String message) {
super(message);
this.originalPayment = payment;
this.reasonCode = reasonCode;
}

// Методы доступа для логики восстановления
public Payment getOriginalPayment() { return originalPayment; }
public String getReasonCode() { return reasonCode; }
}

// Unchecked-исключения для всех остальных сценариев
public class PaymentProcessingException extends RuntimeException {
private final String transactionId;

public PaymentProcessingException(String transactionId, String message, Throwable cause) {
super(message, cause);
this.transactionId = transactionId;
}
}

public class InvalidPaymentRequestException extends IllegalArgumentException {
private final String fieldName;

public InvalidPaymentRequestException(String fieldName, Object value) {
super(String.format("Invalid value for field %s: %s", fieldName, value));
this.fieldName = fieldName;
}
}


PaymentReversalRequiredException — checked, потому что требует немедленного действия: отката платежа, уведомления бухгалтерии, возможно, ручного вмешательства. Это редкий, но критичный бизнес-сценарий.
PaymentProcessingException — unchecked, так как ошибки процессинга (таймаут, недоступность шлюза) фатальны для текущей операции и обрабатываются централизованно через retry-механизм или circuit breaker.
InvalidPaymentRequestException — unchecked, так как передача невалидных данных — это ошибка вызывающего кода, которая должна быть исправлена до деплоя.


#Java #для_новичков #beginner #exception #checked #unchecked
👍3
Паттерн "Railway Oriented Programming" и альтернативы исключениям

В функциональном стиле, особенно при работе с Kotlin или современными Java-библиотеками, исключения часто заменяются на типы-результаты (Result types). Это позволяет явно моделировать ошибки в типовой системе без checked-исключений.

Пример с использованием библиотеки Vavr:
import io.vavr.control.Either;
import io.vavr.control.Try;

public Either<PaymentError, PaymentReceipt> processPayment(PaymentRequest request) {
return validateRequest(request)
.flatMap(this::authorizePayment)
.flatMap(this::executeTransfer)
.map(this::generateReceipt);
}

private Either<PaymentError, ValidatedRequest> validateRequest(PaymentRequest request) {
if (request.getAmount() <= 0) {
return Either.left(new PaymentError("INVALID_AMOUNT", "Amount must be positive"));
}
if (request.getCurrency() == null) {
return Either.left(new PaymentError("MISSING_CURRENCY", "Currency is required"));
}
return Either.right(new ValidatedRequest(request));
}


В этом подходе ошибки — это значения (values), а не исключительные ситуации. Клиентский код вынужден обрабатывать оба случая (success и failure) через pattern matching или методы fold, getOrElse, orElseThrow. Это устраняет проблему "забытого catch-блока", присущую unchecked-исключениям, без возврата к verbosity checked-исключений.


#Java #для_новичков #beginner #exception #checked #unchecked
👍4
Что выведет код?

import java.io.IOException;

public class Task100426 {
public static void main(String[] args) {
try {
method();
} catch (Exception e) {
System.out.println("Catch: " + e.getClass().getSimpleName());
}
}

static void method() {
try {
throw new IOException("IO");
} catch (IOException e) {
throw new RuntimeException("Runtime");
} finally {
return;
}
}
}


#Tasks
👍1
Что такое ReentrantLock и чем он отличается от synchronized? 🤓

Ответ:

ReentrantLock
— это гибкая альтернатива блоку synchronized из пакета java.util.concurrent.locks.

Преимущества: возможность прервать ожидание (lockInterruptibly()), попытаться захватить lock без блокировки (tryLock()), создание честных блокировок (fairness).

Недостаток: нужно вручную освобождать в блоке finally. synchronized проще в использовании, менее подвержен ошибкам, но менее гибок.

Оба являются reentrant — поток может повторно захватить уже удерживаемый им же монитор/lock.



#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4😱1
История технологии сегодня — 11 апреля

ℹ️ Кто родился в этот день

Ю́рий Никола́евич Калачников (11 апреля 1928, Кунгур — 4 октября 1998, Пермь) — советский конструктор артиллерийских систем. Разработаны: миномёт (артиллерийская часть) 2Б8 для 240-мм самоходного миномёта 2С4 «Тюльпан» (1972), 152-мм буксируемая пушка 2А36 «Гиацинт-Б» (1979) и её вариант — артиллерийская часть 2А37 для самоходной пушки 2С5 «Гиацинт-С» (1976). Семейство 120-мм артиллерийских орудий «Нона»: орудие 2А51 (артиллерийская часть) для самоходного орудия 2С9 «Нона-С» (1981), 120-мм буксируемое орудие 2Б16 «Нона-К» (1986), 120-мм орудие 2А60 для самоходного орудия 2С23 «Нона-СВК» (совместно с ЦНИИ «Буревестник») (1990). Боевые машины и транспортно-заряжающие машины реактивных систем залпового огня 9К57 «Ураган» (1976) и 9К58 «Смерч» (1986). Артиллерийская часть боевой машины и транспортно-заряжающей машины тяжёлой огнеметной системы ТОС-1 «Буратино».

Алекса́ндр Алекса́ндрович Андро́нов (29 марта [11 апреля] 1901 год, Москва — 31 октября 1952, Горький) советский физик, механик и математик. Специалист в области электротехники, радиофизики и прикладной механики, создатель нового направления в теории колебаний и динамике систем, талантливый деятель высшей школы.

Никола́й Дми́триевич Девя́тков (29 марта [11 апреля] 1907 год, Вологда — 1 февраля 2001, Москва) советский и российский учёный и организатор науки в области военной и медицинской электроники. Специалист в области разработки газоразрядных и сверхвысокочастотных приборов.

Масару Ибука (яп. 井深大 Ибука Масару, 11 апреля 1908, Никко — 19 декабря 1997, Токио) — японский инженер и предприниматель, один из основателей корпорации Sony. В 1946 году Ибука и Акио Морита совместно основали корпорацию Sony, которая первоначально называлась «Токийской телекоммуникационной инженерной корпорацией».

Э́ндрю Джон Уа́йлс (англ. Andrew John Wiles; род. 11 апреля 1953, Кембридж)английский математик, профессор математики Принстонского университета, заведующий его кафедрой математики, член научного совета Института математики Клэя. Наиболее известен доказательством Великой теоремы Ферма.


🌐 Знаковые события

1970 — запущен Аполлон-13.

2017 — прекращение расширенной технической поддержки операционной системы Windows Vista.


#Biography #Birth_Date #Events #11апреля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
This media is not supported in your browser
VIEW IN TELEGRAM
https://devforge.ru/

Обновление залито, погнали тестить 🧑‍💻

☝️Из важного:
- почту указывайте реальную, письмо с кодом туда уйдет (пока синхронно не пугайтесь).
- Шибко не спамим, все может сломаться))
- Access jwt токен вроде на час, а логику refresh токена я так и не протестил, может не работать. Просто напишите, что не работает, не нада какахами кидаться, я ж не супермен)))))
- Для мобилок пока все плохо)) Лучше с компа

В остальном жду ваших оценок😜

Oleborn
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Я не понял🤨

То ли сайт плохой, то ли зайти не можете? 😭

Где Ваши отзывы камрады?🥸
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
С 04.04 по 10.04
Предыдущий пост(с 28.03 по 03.04)

Воскресный мотивационный пост:
Наверно эту рубрику пора в топку истории...

Запись встреч/видео:
10. Timeout и Bulkhead: как не дать сервису утонуть в запросах?

Обучающие статьи:
Раздел 8. Stream API и функциональный стиль

Глава 8: За пределами коллекций. Бесконечность и I/O
I/O операции как потоки данных

Раздел 10. Исключения, логирование, отладка

Глава 1. Иерархия исключений (Exceptions)
Throwable — корень всех ошибок: Error, Exception, RuntimeException
Checked vs Unchecked исключения: когда что использовать.

[Совет по Java #023]
Тема: Тайм-ауты в параллельных стримах нельзя контролировать.

[Совет по Java #024]
Тема: ClassLoader не удаляет классы даже после сборки мусора.

Полезные статьи и видео:
Семь вещей, которые нельзя делать из-за стирания типов в Java

Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
👍2
🤣6🔥1
История технологии сегодня — 12 апреля

🚀 Международный день полёта человека в космос (на других официальных языках ООН: англ. International Day of Human Space Flight, исп. Día Internacional de los Vuelos Espaciales Tripulados, фр. Journée internationale du vol spatial habité) — памятная дата международного уровня в ознаменование начала космической эры для человечества. Ежегодно отмечается 12 апреля.

На корабле «Восток» 12 апреля 1961 года лётчик-космонавт СССР майор ВВС Юрий Алексеевич Гагарин совершил первый в мире пилотируемый полёт в космическое пространство. Старт корабля состоялся с советского космодрома Байконур в 9 часов 7 минут по московскому времени (06:07:00 UTC). Корабль выполнил один оборот вокруг Земли и совершил посадку в 10 часов 53 минуты (07:53:00 UTC) в районе деревни Смеловка Саратовской области. Длительность полёта составила 106 минут. Корабль стал и первым в мире управляемым космическим аппаратом, позволившим совершить полёт в космос.


ℹ️ Кто родился в этот день

Станисла́в Никола́евич Ко́нюхов (12 апреля 1937, с. Бекренево, Лежский район, Вологодская область, РСФСР — 3 апреля 2011) — учёный, инженер и конструктор в аэрокосмической области. Автор свыше 240 научных работ в области статики и динамики стойкости, рациональных способов обеспечения пространственной ориентации, механики взаимодействия твёрдых тел с препятствиями при гиперзвуковых скоростях.


🌐 Знаковые события

1903 — в Лондоне на маршрут вышел первый в мире городской автобус с двигателем внутреннего сгорания.

1961 — Гражданин СССР Юрий Гагарин на корабле «Восток-1» стал первым человеком, совершившим космический полёт.

1981 — в день 20-летия первого полёта человека в космос стартовал американский корабль «Колумбия». Первый пилотируемый полёт в космос по программе «Спейс шаттл».

1995 — запущен каталог «Yahoo!», быстро ставший одним из самых популярных в мире.


#Biography #Birth_Date #Events #12апреля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Что случилось и почему сайт не работал или хронология одного хренового утра

Сразу к делу: сайт лежал намертво.

Не просто не открывалась страница, а вообще никакого признака жизни извне.

При этом я зашел через VNC (прямую консоль провайдера) и увидел, что сервер внутри работает. Файлы на месте, база цела, контейнеры Docker крутятся. Но снаружи — бетонная стена.

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


Часть 1. Симптомы, которые сбивали с толку

Первый звоночек: SSH не подключается. Таймаут. Первая мысль может порт сменился или фаервол взбесился. Захожу через VNC и вижу странную картину. Фаервол чистый. SSH демон запущен. Но соединения извне нет вообще.

Делаю пинг до гугловской восьмерки и вижу заветные слова: Destination Host Unreachable. Это уже совсем плохой звоночек. Он означает, что сервер не просто не видит интернет, он не видит даже собственный шлюз провайдера. Он как будто в вакууме.

Дальше больше. Проверяю сетевые настройки: IP на месте, маска правильная, шлюз прописан. Сбрасываю интерфейс, перезагружаю сервер, чищу ARP-таблицу. Ноль реакции.


Часть 2. Техподдержка и стадия отрицания

Пишу в техподдержку. Объясняю ситуацию: консоль есть, сервер жив, но сети нет. В ответ получаю классику: "Переустановите операционную систему".

Переустановка ОС при такой проблеме это как менять двигатель у машины, у которой просто отвалилось колесо. Данные бы улетели, все настройки nginx и docker пришлось бы поднимать с нуля, но связь бы не появилась, потому что проблема была глубже.

Начинается битва аргументов. Привожу вывод команды ip neigh show, где напротив адреса шлюза красуется статус FAILED. Это железное доказательство того, что обрыв на канальном уровне, то есть на стороне виртуального коммутатора провайдера.


Часть 3. Смена IP и миграция

Чтобы отвязаться от настойчивых просьб переустановить систему, соглашаюсь на предложение сменить IP-адрес. Меняют. Прописываю новый адрес вручную в консоли. Результат тот же — Destination Host Unreachable. Это окончательно подтверждает: проблема не в софте, а в виртуальном железе, к которому прицеплен VPS.

Только после этого неожиданно сервер мигрируют на другую физическую ноду.

И о чудо. Пинг до восьмерки пошел. Сеть ожила.


Часть 4. Оживление пациента

Казалось бы, победа. Но это был только начало.

После миграции сайт все равно не открывался, а SSH валился с ошибкой Connection refused.

Выяснилось, что после миграции демон SSH просто отключился и не встал в автозагрузку.

Дальше пошла борьба с Nginx. Конфиги, которые прекрасно работали на старом месте, здесь встали колом. Оказалось, что запросы улетали в неправильный блок сервера и рвались с ошибкой Empty reply from server. Пришлось перелопатить конфиги, убрать лишние редиректы и явно прописать, что сайт должен слушаться не только по домену, но и просто по IP.

Затем всплыла история с сертификатами. Let's Encrypt честно выдал новые ключи, но браузер упорно не хотел показывать сайт по HTTPS.


Часть 5. Финальный босс — кэш браузера

Тут началась настоящая мистика. По IP через HTTP все грузилось идеально. А по домену devforge.ru — белый экран. Ни ошибок в консоли, ни проблем с сетью.

Оказалось, что это HSTS. Механизм безопасности браузера, который намертво запомнил старые настройки еще с прошлого IP. Браузер тупо блокировал загрузку ресурсов, считая, что сайт подменили.

Открываю то же самое в режиме инкогнито — и все работает как часы.

Выяснилось: сервер был полностью исправен уже несколько часов, а проблема сидела в кэше на стороне клиента (я ебалай).


Итог

Сервер перенесен на новую ноду. Связка Nginx, Docker и API работает стабильно. DNS обновлены. SSL сертификаты свежие.
Если у вас вдруг сейчас сайт не грузится или выглядит странно — просто почистите кэш браузера или откройте страницу в режиме инкогнито. Это уберет остатки старых редиректов.
Если кто пытался зайти на Devforge.ru, но не мог, спасибо за терпение.

Проект снова в строю и стал немного надежнее.

А я маленько вахуе 🤪
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🤯4
История технологии сегодня — 13 апреля

ℹ️ Кто родился в этот день

Джеремайя Пол (Джерри) Острайкер (англ. Jeremiah Paul «Jerry» Ostriker; 13 апреля 1937, Нью-Йорк — 6 апреля 2025, там же) — американский теоретик-астрофизик и космолог, основоположник теории тёмного вещества. Автор более 500 научных публикаций в сфере космологии, особо уделял внимание темам, в которых применяются сложные математические вычисления. Основными темами исследований Острайкера были тёмное вещество и тёмная энергия, тепло-горячая межгалактическая среда, формирование галактик, рост чёрных дыр и взаимодействие квазаров с окружающим пространством.

Ро́берт Алекса́ндр Уо́тсон-Уотт (англ. Robert Alexander Watson-Watt; 13 апреля 1892 — 5 декабря 1973) — шотландский физик, один из пионеров в области работ по радиолокации. Сконструировал одно из первых устройств, предназначенных для радиолокации воздушных объектов и получил первый патент на изобретение подобной системы в 1934 году, а 26 февраля 1935 года успешно продемонстрировал своё изобретение, которое могло обнаружить самолёт на расстоянии 64 км.

Антонио Санти Джузеппе Меуччи (итал. Antonio Santi Giuseppe Meucci; 13 апреля 1808 — 18 октября 1889) — итальянский учёный, являющийся изобретателем телефона. Именно он в 1860 году пришёл к выводу о возможности превращения звуковых колебаний в электрические импульсы, что позволяет передавать голос на расстояние с помощью проводов.


🌐 Знаковые события

1960 — Соединенные Штаты запускают Transit 1-B, первую в мире спутниковую навигационную систему.


#Biography #Birth_Date #Events #13апреля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
[Совет по Java #025]

Тема: ThreadLocal может вызвать утечку в серверах приложений.

Проблема: В серверах приложений (Tomcat, Jetty, WildFly) потоки из пула переиспользуются между запросами.

ThreadLocal хранит данные, привязанные к конкретному потоку. Если после обработки запроса не вызвать remove(), данные останутся в потоке навсегда. При следующем запросе, который получит тот же поток, код увидит "грязные" данные от предыдущего запроса, что приводит к непредсказуемому поведению — утечке контекста безопасности, некорректным настройкам локали, перемешиванию данных разных пользователей.

Более того, если загруженный класс (например, из веб-приложения) хранится в ThreadLocal, а поток продолжает жить, это создает классическую утечку памяти ClassLoader — приложение не может быть выгружено.

Решение: Всегда вызывайте ThreadLocal.remove() после использования, особенно в веб-среде.

Лучшая практика — оборачивать использование ThreadLocal в блок try-finally. Для фильтров и перехватчиков используйте один централизованный механизм очистки. Рассмотрите альтернативы: передача контекста явно через параметры методов, использование ScopedValue (Java 20+ incubator, Java 21+ preview), которое автоматически очищается.

public class ThreadLocalLeak {

//Антипаттерн: ThreadLocal без очистки
private static final ThreadLocal<UserContext> CURRENT_USER = new ThreadLocal<>();

public static void processRequestBad(User user) {
CURRENT_USER.set(new UserContext(user)); // Установили
// ... обработка запроса
// Забыли remove() — данные останутся в потоке навсегда
}

//Решение: try-finally с remove()
public static void processRequestGood(User user) {
try {
CURRENT_USER.set(new UserContext(user));
// ... обработка запроса
} finally {
CURRENT_USER.remove(); // Гарантированная очистка
}
}

// Демонстрация утечки
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(2);

// Плохой вариант
for (int i = 0; i < 10; i++) {
final int requestId = i;
executor.submit(() -> {
processRequestBad(new User("User" + requestId));
});
}

Thread.sleep(1000);

// Теперь в потоках пула остались старые данные
executor.submit(() -> {
UserContext context = CURRENT_USER.get(); // Может вернуть данные от старого запроса!
System.out.println("Остались данные: " + context);
});

executor.shutdown();
}

// Для веб-фильтра (Spring-стиль)
public static class WebFilter {
public void doFilter(Request request, FilterChain chain) {
try {
// Установка контекста из запроса
UserContext.setCurrent(request.getUser());
chain.proceed();
} finally {
UserContext.clear(); // Обязательная очистка после каждого запроса
}
}
}
}


Объяснение: Пул потоков в серверах приложений создает ограниченное количество потоков, которые обрабатывают тысячи запросов последовательно.

ThreadLocal хранит значение в массиве ThreadLocalMap внутри объекта Thread. Если не вызвать remove(), ссылка на значение остается в этом массиве. При следующем использовании того же потока get() вернет старое значение. Даже если ссылка на объект в ThreadLocal станет слабой (как у ThreadLocal по умолчанию), она все равно будет достижима через Thread.currentThread(), пока поток жив.

Это приводит к двум проблемам: логическая утечка (неправильные данные) и физическая утечка (классы из веб-приложения не выгружаются).

Правило простое: для каждого set() должен быть соответствующий remove() в том же потоке. Java 21 предлагает ScopedValue как иммутабельную и автоматически очищаемую альтернативу.


#Java #советы
👍5
Что выведет код?

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Task130426 {
static class UserContext130426 {
String userId;
UserContext130426(String userId) { this.userId = userId; }
}

static ThreadLocal<UserContext130426> currentUser = new ThreadLocal<>();

public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();

executor.submit(() -> {
currentUser.set(new UserContext130426("user1"));
System.out.print(currentUser.get().userId + " ");
});

executor.submit(() -> {
UserContext130426 ctx = currentUser.get();
System.out.println(ctx == null ? "null" : ctx.userId);
});

executor.shutdown();
}
}


#Tasks
👍1