(java || kotlin) && devOps
366 subscribers
6 photos
1 video
6 files
307 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Всем привет!

В предыдущем посте я упомянул про защиту от DOS аттак в коде. Раскрою тему.
Для начала стоит различать DDOS и DOS - (Distribted) Denial of Service.
Первый - это когда злоумышленник долбит миллионами запросов в секунду. Такое не выдержит ни один сервис, не поможет даже k8s, т.к. ресурсы кластера не резиновые - https://kubernetes.io/docs/setup/best-practices/cluster-large/ да и подымаются новые ноды не мгновенно. Следовательно, от DDOS должна защищать сетевая инфраструктура, прикладной разработчик тут ничего сделать не может.
Другое дело DOS - RPS на порядки меньше, эксплуатируются уязвимости в коде. Вопрос - откуда злоумышленники про них узнают?
Во-первых они могут действовать наугад, во-вторых - всегда могут быть болтливые сотрудники, а главное - защита типа "об этом никто никогда не узнает" - плохая защиты.
Суть всех уязвимостией при DOS одна - поднять на сервере столько потоков одновременно, чтобы закончилась память или загрузка процессора ушла под 100%
Итак, как можно улучшить код для защиты от DOS.

1) проводить нагрузочное тестирование (НТ). НТ позволяет точно определеить сколько нужно серверов, чтобы держать расчетную и пиковую нагрузку. Пиковую нагрузку можно взять как расчетная умножить на два. Утечки памяти, неоптимальный код - все это с большой вероятностью можно увидеть на НТ

2) нет бесконечным и большим таймаутам. Если смежник упал, а у нас бесконечный таймаут - потоки и память кончатся быстро. Что касается больших таймаутов - это минуты, или таймауты необоснованные с точки зрения бизнес-задачи.

2) таймауты должны быть согласованы. Если мы обрабатываем запрос с таймутом 5 секунд, он синхронный, а вызываем смежника с таймутом 10 секунд - мы зря тратим его и свои ресурсы. Согласование может быть ручным, либо можно слать, например в заголовках, свой таймаут смежнику, чтобы он не ждал зря.

3) использовать circuit breaker, он же предохранитель, он же техперерыв. Если известно, что смежная система прилегла - не надо ее добивать и тратить на это свои ресурсы. Берем данные из кэша если это возможно или возвращаем клиенту ошибку. Принцип fail fast. Стоит отметить, что настройку таймаутов, предохранителя, и числа повторов можно делать либо в коде, либо отдать на откуп Istio или аналогичной системе если мы в облаке. Что лучше - это отдельная тема

4) защищаться от уязвимостей типа Injection. Суть их в том, что злоумышленник передает в параметрах входящего запроса что-то, что приводит к нелинейному потреблению ресуросов или тяжелым запросам в БД. Примеры первого вида DTD схемы https://habr.com/ru/post/170333/, регулярки - https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS, второго - SQL Injection со сложными JOIN. Решение: валидация параметров, регулярно сканировать библиотеки на наличие уязвимостей, регулярно обновляться в части хотфиксов

5) логика сервиса не должна линейно зависеть от числа входных параметров либо число параметров должно ограничено. Чем-то похоже на предыдущий пункт, но тут приложение спроектировано криво, поэтому никакие уязвимости не нужны)

6) использовать пулы потоков. Во многих случаях они уже используются - обработка входящих веб-запросов, JDBC запросы к БД. Но есть потоки, которые создаем мы сами. Если на каждый входной запрос мы будем создавать дополнительно хотя бы +1 поток, то это примерно удвоит потребление ресурсов. А если больше одного... Пул потоков защищает от такой ситуации

7) не забывать закрывать ресурсы - файлы, коннекты к БД. Все что java.io.Closeable. И делать это правильно - try with resources. В отличие от памяти в куче ресурсы никто за вас не закроет. А они жрут память и часто ограничены: максимальное число открытых файлов в Linux, максимальное число запросов, которое может обрабатывать СУБД

8) не использовать тяжелые JOIN и GROUP BY запросы к БД. Создавать индексы, смотреть план выполнения запроса. Об этом должен позаботиться ваш DBA, но, увы, не всегда он есть

9) не использовать излишне сильные уровни блокировки в БД, не использовать блокировки файлов без явной необходимости

#code_quality #security #patterns
Всем привет!

Давно хотел написать про паттерны/шаблоны программирования. Основной вопрос, возникающий при разговоре про паттерны - какая от них польза? Ведь главное - умеет человек кодить или нет.
С одной стороны паттерны - это лишь часть арсенала программиста. Можно заучить все паттерны, но не научиться кодить.
И тут возникает второй вопрос - о каких паттернах мы говорим?
1) самые известные - паттерны проектирования из книги «банды четырёх» https://refactoring.guru/ru/design-patterns/catalog
Это синглтон, фабричный метод, билдер и все все все
2) паттерны Enterprise архитектуры от Фаулера https://martinfowler.com/eaaCatalog/
3) паттерны рефакторинга https://refactoring.com/catalog/ Про них также говорится в книге Идеальная работа Мартина
4) паттерны модульных тестов http://xunitpatterns.com/ и снова в книге Идеальная работа
5) паттерны интеграции корпоративных приложений https://www.enterpriseintegrationpatterns.com/patterns/messaging/toc.html многие из которых можно встретить в стандарте JMS
6) паттерны микросервисных приложений https://microservices.io/patterns/index.html
7) даже у Kubernates есть паттерны https://www.redhat.com/cms/managed-files/cm-oreilly-kubernetes-patterns-ebook-f19824-201910-en.pdf
8) не говоря уже про антипаттерны https://javarush.ru/groups/posts/2622-chto-takoe-antipatternih-razbiraem-primerih-chastjh-1
9) 10) ...
Из этого списка можно сделать вывод, что паттерны могут быть везде. А из этого второй вывод: паттерны - это удобный способ описания какой-то области разработки. Собственно это и есть их ценность. Шаблоны помогают изучить новую технологию, читать статьи, книги и главное читать код и тесты. Ну и проектировать систему, обсуждать ее архитектуру с коллегами. По сути паттерны - это язык проектирования. А идеальный способ их использования - когда они уже реализованы в неком фреймворке: Singleton и MVC в Spring, Builder в Lombok, Sidecar в k8s, или в языке как Singleton и Decorator в Kotlin.

#patterns #refactoring #unittests
Всем привет!

Еще один широиспользуемый паттерн, более низкого уровня, чем описанные ранее: LMAX Disruptor.
https://lmax-exchange.github.io/disruptor/disruptor.html
Это готовая библиотека, решающая следующую задачу: есть упорядоченная очередь из каких-то данных, пишет в нее один поток, обрабатывать данные нужно в несколько потоков без блокировок. Реализована в виде кольцевого буфера и набора указателей на текущую ячейку буфера, по одному для каждого потока-читателя\писателя. В каждый момент времени в буфер пишет один поток, блокировки не ставятся, каждый поток может прочитать указатели других потоков и т.об. понять, с какими ячейками можно работать. Библиотеку достаточно хорошо пиарят, даже сам Мартин Фаулер: https://martinfowler.com/articles/lmax.html Использует log4j https://logging.apache.org/log4j/2.x/manual/async.html#UnderTheHood

Но вернемся к более общим архитектурным принципам: при реализации этой библиотеки используется принцип Mechanical Sympathy https://www.baeldung.com/lmax-disruptor-concurrency#1-mechanical-sympathy.
Суть его в следующем: хотя язык программирования и JVM в случае Java скрывают от нас кишочки компьютера - регистры процессора, кэши процессора 1,2,3 уровня, особенности работы процессора - для максимальной производительности их нужно учитывать. На примере LMAX Disruptor:
1) кольцевой буфер позволяет переиспользовать объекты в куче, уменьшая нагрузку на Garbage Collector
2) кольцевой буфер выделяется одним "куском", поэтому использует последовательные адреса в памяти, что ускоряет пакетное чтение из буфера - как за счет собственно последовательного чтения, так и зачет упреждающего кэширования процессором
3) одновременная запись в память приводит к взаимным сбросам кэша у различных ядер процессора, что плохо сказывается на производительности. В LMAX Disruptor, как я уже говорил, в каждый момент времени пишет в буфер один поток.
Все это вместе с отсутствием блокировок приводит к хорошей производительности.
Но к слову есть люди, считающие библиотеку слишком распиаренной - см. комментарии к статье https://dev.cheremin.info/2011/09/disruptor-1.html

#patterns #library