Skip to content

Latest commit

 

History

History
1747 lines (1333 loc) · 141 KB

MinimumInterview.md

File metadata and controls

1747 lines (1333 loc) · 141 KB

Вопросы для собеседования минимум

1 Что такое java характеристики языка

  • ООП.
  • Строгая типизация.
  • По умолчанию запрет прямого доступа к памяти (исключение sun.misc.Unsafe).
  • Нет множественного наследования классов. Вместо этого интерфейсы.
  • Write once, run anywhere. Не зависит от ОС. Т.к. исползуется JVM.
  • Особенность ява, связанная с транслированием в байт-код, положительно сказывается и на производительности.
  • Многопоточный.

к оглавлению

2 Что значит высокоуровневый язык программирования

Высокоуровневые языки программирования были разработаны для платформенной независимости сути алгоритмов. Зависимость от платформы перекладывается на инструментальные программы — трансляторы, компилирующие текст, написанный на языке высокого уровня, в элементарные машинные команды (инструкции).

к оглавлению

3 Какие бывают языки программирования

  • Объектно-ориентированные.
  • Процедурные. Процедуры меняют какое-то общее состояние. Процедурный стиль - это использование операторов последовательного исполнения, ветвления и безусловного перехода
const array = ['first', 'second'];

let output;

function allOdd(words) {
  let result = true;

  for (let i = 0; i < words.length; ++i) {
    const len = words[i].length;

    if (len % 2 !== 0) {
      result = false;
      break;
    }
  }

  return result;
}

output = allOdd(array);

alert(output);
  • Функциональные. ФП не предполагает наличия изменяемого состояния. Функциональный стиль - это использование только суперпозиции функций.
function length(string) {
  return prop('length', string);
}

function odd(number) {
  return equals(modulus(number, 2), 0); 
}

function allOdd(...words) {
  return every(compose(odd, length), words);
}

alert(allOdd('first', 'second'));

к оглавлению

4 Принципы ООП как реализована инкапсуляция в Java полиморфизм

  • Абстракция - выделение значимой информации и исключение из рассмотрения незначимой. В Java: через классы.
  • Инкапсуляция - свойство системы, позволяющее объединить данные и методы, работающие с ними, в классе. Для Java корректно будет говорить, что инкапсуляция это «сокрытие реализации». В Java: Используются модификаторы доступа public, private, package;
  • Наследование - cвойство системы, позволяющее описать новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью. Класс, от которого производится наследование, называется базовым, родительским или суперклассом. В Java: Extends, Impliments;
  • Полиморфизм - свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта. В Java: через интерфейсы;

к оглавлению

5 Модификаторы доступа

  • private (закрытый) — доступ к члену класса не предоставляется никому, кроме методов этого класса. Другие классы того же пакета также не могут обращаться к private-членам.
  • default, package, friendly, доступ по умолчанию, когда никакой модификатор не присутствует — член класса считается открытым внутри своего собственного пакета, но не доступен для кода, расположенного вне этого пакета. Т.е. если package2.Class2 extends package1.MainClass, то в Class2 методы без идентификатора из MainClass видны не будут.
  • protected (защищённый) — доступ в пределах пакета и классов наследников. Доступ в классе из другого пакета будет к методам public и protected главного класса. Т.е. если package2.Class2 extends package1.MainClass, то внутри package2.Class2 методы с идентификатором protected из MainClass будут видны.
  • public (открытый) — доступ для всех из любого другого кода проекта
  • Модификаторы в списке расположены по возрастающей видимости в программе.
  • Во время наследования возможно изменения модификаторов доступа в сторону большей видимости. Так сделано для того, чтобы не нарушался принцип LSP для наследуемого класса. protected -> public package -> protected, public

к оглавлению

6 Принципы SOLID

  • S - Принцип единственной ответственности. Данный принцип гласит: никогда не должно быть больше одной причины изменить класс.
  • O - Принцип открытости закрытости. Класс должен быть открыт для расширения и закрыт для изменения.
  • L - Принцип подстановки Барбары Лисков. объекты в программе можно заменить их наследниками без изменения свойств программы.
  • I - Принцип разделения интерфейса. Много интерфейсов специального назначения лучше, чем один интерфейс общего назначения.
  • D - Принцип подстановки зависимостей. Зависимость на абстракцию. Нету зависимости на конкретную реализацию.

SOLID

к оглавлению

7 Опишите примитивные типы java

  • byte - 1 байт (8 бит). Мин -2^7 макс 2^7-1
  • short - 2 байта (16 бит). Мин -2^15 Макс 2^15-1
  • char - 2 байта (16 бит). 2^16-1
  • int - 4 байта (32 бит). Мин -2^31-1 Макс 2^31
  • long - 8 байт (64 бит). Мин -2^63-1 Макс 2^63
  • float - 4 байта (32 бит). Мин -2^31-1 Макс 2^31
  • double - 8 байт (64 бит). Мин -2^63-1 Макс 2^63
  • boolean - по спецификации не определен в среднем 1 байт

String (string pool)

не примитив. Внутри char[], c версии java 9 private final byte[] value;. Строки immutable. Есть пул строк.

String first = "Baeldung"; 
String second = "Baeldung"; 
System.out.println(first == second); // True

String third = new String("Baeldung");
String fourth = new String("Baeldung"); 
System.out.println(third == fourth); // False

String str3 = (new String("TopJava")).intern();
String str4 = (new String("TopJava")).intern();
System.out.println(str3 == str4); // True

Экземпляр класса String хранится в памяти, именуемой куча (heap), но есть некоторые нюансы. Если строка, созданная при помощи конструктора хранится непосредственно в куче, то строка, созданная как строковый литерал, уже хранится в специальном месте кучи — в так называемом пуле строк (string pool). В нем сохраняются исключительно уникальные значения строковых литералов, а не все строки подряд. Процесс помещения строк в пул называется интернирование (от англ. interning).

Integer pool

Есть числовой пул от -128 до 127

Integer x = 5; // autoboxing
// на самом деле выполняется
Integer x = Integer.valueOf(5);

Сравнивать Integer надо через equals

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

к оглавлению

8 Последовательность инициализации блоков класса и его родителя

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

к оглавлению

9 Классы внутренние вложенные абстрактные

Особенности абстрактных классов:

  • Если есть хоть один абстрактный метод, то класс должен быть абстрактным.
  • Абстрактные классы нельзя создавать с помощью new, но можно использовать в качестве ссылки, для полиморфизма.

Вложенный класс (nested classes.)

  • Non-static nested classes — нестатические вложенные классы. По-другому их еще называют inner classes — внутренние классы.
  • Static nested classes — статические вложенные классы.

Внутренний класс (inner classes)

  • локальным классом (local class)
  • анонимным классом (anonymous class)

Вложенные классы

к оглавлению

10 Замыкания

Лямбда-выражения Java 8 — это замыкания. Что такое замыкание? При замыкании используются переменные, расположенные вне области действия функции. local variables referenced from a lambda expression must be final or effectively final (локальные переменные, на которые ставится ссылка из лямбда-выражения, должны быть финальными или фактически финальными).

Оказывается, что лямбда-выражения в Java замыкаются только вокруг значений, но не вокруг переменных. Java требует, чтобы эти значения были неизменны, как если бы мы объявили их final. Итак, они должны быть final независимо от того, объявляли вы их таким образом или нет. То есть, «фактически финальными». Поэтому в Java есть «замыкания с ограничениями», а не «полноценные» замыкания, которые, тем не менее, довольно полезны.

к оглавлению

11 Методы класса Object

  • public final native Class getClass() — возвращает в рантайме класс данного объекта.
  • public native int hashCode() — возвращает хеш-код
  • public boolean equals(Object obj) — сравнивает объекты.
  • protected native Object clone() throws CloneNotSupportedException — клонирование объекта
  • public String toString() — возвращает строковое представление объекта.
  • public final native void notify() — просыпается один поток, который ждет на “мониторе” данного объекта.
  • public final native void notifyAll() — просыпаются все потоки, которые ждут на “мониторе” данного объекта.
  • public final native void wait(long timeout) throws InterruptedException — поток переходит в режим ожидания в течение указанного времени.
  • public final void wait() throws InterruptedException — приводит данный поток в ожидание, пока другой поток не вызовет notify() или notifyAll() методы для этого объекта.
  • public final void wait(long timeout, int nanos) throws InterruptedException — приводит данный поток в ожидание, пока другой поток не вызовет notify() или notifyAll() для этого метода, или пока не истечет указанный промежуток времени.
  • protected void finalize() throws Throwable — вызывается сборщиком мусора, когда garbage collector определил, что ссылок на объект больше нет.

к оглавлению

12 Ключевое слово volatile Понятие атомарной операции и как это связано с volatile

volatile - этот модификатор вынуждает потоки отключить оптимизацию доступа и использовать единственный экземпляр переменной. Если переменная примитивного типа – этого будет достаточно для обеспечения потокобезопасности. Если же переменная является ссылкой на объект – синхронизировано будет исключительно значение этой ссылки. Все же данные, содержащиеся в объекте, синхронизированы не будут!

к оглавлению

13 Финализация объектов в Java

Called by the garbage collector on an object when garbage collection determines that there are no more references to the object.

к оглавлению

14 Иерархия коллекций

  • i Collections extends Iterable.
  • - i Set (коллекция без дублирования)
    • HashSet базирующаяся на HashMap В качестве ключа используется добавляемый элемент, а в качестве значения — объект-пустышка (new Object())
    • LinkedHashSet в основе лежит LinkedHashMap
    • i SortedSet Методы: first, last
    • TreeSet (отсортированный список) содержит в себе объект NavigableMap
  • - i Queue очередь Методы: offer(), element(), peek(), poll(), remove()
    • i Deque двусторонняя очередь addFirst, addLast, getFist, getLast, (push, pop -> использовать как стек)
  • - i List упорядоченная коллекция (сохраняет последовательность элементов. можно получить по индексу, можно повторяющиеся, можно по значению первый найденный)
    • Vector @deprecated реализация динамического массива объектов. Позволяет хранить любые данные, включая null в качестве элемента
    • Stack - данная коллекция является расширением коллекции Vector. реализация стека LIFO
    • ArrayList динамический массив, можно хранить null
    • LinkedList связный список (impl List & Deque)
  • i Map (нет итератора, нельзя перебирать в цикле. Можно получить представление в виде коллекции для перебора) get(), put(), entrySet(), keySet(), values()
  • - i SortedMap (по порядку нарастания ключей)
  • - i NavigableMap
    • TreeMap реализация Map основанная на красно-чёрных деревьях (отсортированная по умолчанию natural odering, можно задать свой компаратор)
  • - AbstractMap
    • HashMap позволяет использовать null как в качестве ключа, так и значения
    • LinkedHashMap это упорядоченная реализация хэш-таблицы. Аналогично LinkedList
  • - WeekHashMap - реализация хэш-таблицы, которая организована с использованием weak references. Другими словами, Garbage Collector автоматически удалит элемент из коллекции при следующей сборке мусора, если на ключ этого элеметна нет жёстких ссылок

collection

к оглавлению

15 Отличие Set от List приведите примеры реализаций

  • Set - Множество. (Здесь могут храниться только уникальные значения, нет дубликатов)

  • Queue (Deque) - Очередь. FIFO (Первый вошел, первый вышел). реализуется LinkedList'ом

  • List - Упорядоченное хранение данных. (В какой последовательности данные положили, в такой они и хранятся)

  • Map - (от Collection он не наследуется) Значения хранятся как пара - ключ-значение. и по ключу получаем значение. у мапы нету итератора

  • List: ArrayList, LinkedList

  • Set: HashSet, LinkedHashSet, TreeSet

  • Map: HashMap, LinkedHashMap, TreeMap

  • HashSet хранит данные в произвольном порядке (хранит свои значения как ключи HashMap).

  • TreeSet хранит данные в отсортированном виде (бинарное дерево).

сет это список ключей от мапы

к оглавлению

16 Когда имеет смысл использовать LinkedList вместо ArrayList

ArrayList реализован на массивах. (используют если чаще читаются элементы, чем добавляются) Хранит свои элементы в массиве.

  • + осуществляется быстрый поиск элементов.
  • + меньше расходует памятина хранение элементов
  • - увеличение ArrayList'a происходит медленно.
  • - при вставке элемента (или удалении) в середину или в начало, приходится переписывать все элементы.

LinkedList является представителем двунаправленного списка. (цепочка) (используется если элементы чаще добавляются чем читаются) Хранит свои элементы в обектах у которых есть ссылки на предыдущий и следующий элементы.

  • + быстрая вставка и удаление в середину списка (переписать next и prev и всё)
  • - долгий поиск в середине (нужно перебрать все элементы)

в среднем, сложности одинаковые. Но я бы не стал рекомендовать использовать LinkedList, за исключением ситуации когда, преобладает удаление или вставка в начало или конец списка. Также ArrayList может работать существенно быстрее при переборе значений за счет того что память для массива выделяется в одном сегменте, а для LinkedList элементы могут быть разбросаны в разных сегментах памяти.

к оглавлению

17 Устройство HashMap

HashMap состоит из «корзин» (bucket`ов). С технической точки зрения «корзины» — это элементы массива, которые хранят ссылки на списки элементов. При добавлении новой пары ключ-значение, вычисляет хеш-код ключа, на основании которого вычисляется номер корзины (номер ячейки массива), в которую попадет новый элемент. Если корзина пустая, то в нее сохраняется ссылка на вновь добавляемый элемент, если же там уже есть элемент, то происходит последовательный переход по ссылкам между элементами в цепочке, в поисках последнего элемента, от которого и ставится ссылка на вновь добавленный элемент. Если в списке был найден элемент с таким же ключом, то он заменяется. Добавление, поиск и удаление элементов выполняется за константное время. Вроде все здорово, с одной оговоркой, хеш-функций должна равномерно распределять элементы по корзинам, в этом случае временная сложность для этих 3 операций будет не ниже lg N, а в среднем случае как раз константное время.

к оглавлению

18 Итераторы Можно ли удалить элемент из коллекции в итерации цикла по коллекции

Удалять надо используя итератор для обхода. Иначе будет ConcurrentModificationException.

к оглавлению

19 Что такое fail fast и fail safe

Сравнение итераторов

Fail-fast итератор генерирует исключение ConcurrentModificationException, если коллекция меняется во время итерации, а fail-safe – нет.

Важно отметить, что fail-fast итераторы работают на основе принципа "по мере возможности", то есть не дается никаких гарантий генерации исключения ConcurrentModificationException в случае конкурентной модификации. Так что полагаться на это не стоит – скорее, их следует использовать для обнаружения ошибок. Большинство неконкурентных коллекций предоставляют fail-fast итераторы.

fail-safe итераторы используются в конкурентных коллекциях ConcurrentHashMap

  • Эти итераторы создают клон фактической Collection и перебирают его
  • Они могут обрабатываться конкурентно с другими операциями
  • Они никогда не генерируют исключение ConcurrentModificationException
  • Они гарантированно обходят существовавшие на момент создания итератора элементы ровно один раз, и могут (но не обязаны) отражать последующие модификации.

к оглавлению

20 Механизм сериализации в Java его внутреннее устройство

Интерфейс маркер Serializable. Для записи обьекта используется ObjectOutputStream с private методом writeObject() для чтения ObjectInputStream с методом private readObject() Thread, OutputStream и его подклассы, и Socket - не сериализуемые Transient - помечает объект который сериализовать не нужно. Если мы не хотим сериализовать наследника чей супер класс помечен Serializable то в этом наследнике переопределив методы writeObject() и readObject() кидаем throw new NotSerializableException();

Интерфейс Externalizable содержит 2 метода public void writeExternal(ObjectOutput out) throws IOException; public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; serialVersionUID - версионность сериализованного объекта (например избавляет от проблемы когда добавил новое поле, пытаемся десериализовать данный объект из сохраненного ранее состояния и получаем InvalidClassException)

к оглавлению

21 Клонирование в Java

Можно использовать конструктор копирования.

В Java это означает возможность создавать объект с аналогичной структурой, как и у исходного объекта. Метод clone() обеспечивает эту функциональность.

Поверхностное копирование копирует настолько малую часть информации, насколько это возможно. По умолчанию, клонирование в Java является поверхностным, т.е. Object class не знает о структуре класса, которого он копирует. При клонировании, JVM делает такие вещи:

  • Если класс имеет только члены примитивных типов, то будет создана совершенно новая копия объекта и возвращена ссылка на этот объект.
  • Если класс содержит не только члены примитивных типов, а и любого другого типа класса, тогда копируются ссылки на объекты этих классов. Следовательно, оба объекта будут иметь одинаковые ссылки. Глубокое копирование дублирует все. Глубокое копирование — это две коллекции, в одну из которых дублируются все элементы оригинальной коллекции. Мы хотим сделать копию, при которой внесение изменений в любой элемент копии не затронет оригинальную коллекцию.

Глубокое клонирование требует выполнения следующих правил:

  • Нет необходимости копировать отдельно примитивные данные;
  • Все классы-члены в оригинальном классе должны поддерживать клонирование. Для каждого члена класса должен вызываться super.clone() при переопределении метода clone();
  • Если какой-либо член класса не поддерживает клонирование, то в методе клонирования необходимо создать новый экземпляр этого класса и скопировать каждый его член со всеми атрибутами в новый объект класса, по одному.

к оглавлению

22 StringBuffer vs StringBuilder

StringBuffer is synchronized, StringBuilder is not. StringBuilder is faster than StringBuffer because it's not synchronized.

к оглавлению

23 Методы обработки XML документов

  • DOM - обход XML документа в оба направлениях, грузит весь документ в память подходит для небольших файлов. Cчитывает сразу весь XML и сохраняет его, создавая иерархию в виде дерева, по которой мы можем спокойно двигаться и получать доступ к нужным нам элементам. По сути, самый частый тип данных в DOM – это Node (узел), который может быть всем. У каждого Node есть разные полезные методы. Например getChildNodes, getParentNode. DOM
  • Stax - событийный, грузит документ частями, подходит для мелких документов. работает по пул модели. Позволяет обрабатывать только те события которые нам необходимы. позволяет писать в XML файл.
try (StaxStreamProcessor processor = new StaxStreamProcessor(Files.newInputStream(Paths.get("payload.xml")))) {
     XMLStreamReader reader = processor.getReader();
     while (reader.hasNext()) {       // while not end of XML
         int event = reader.next();   // read next event
         if (event == XMLEvent.START_ELEMENT && 
"City".equals(reader.getLocalName())) {
             System.out.println(reader.getElementText());
         }
     }
}
  • Sax - событийный, грузит документ частями, позволяет работать с большими документам, работает по пуш модели необходимо обрабатывать все события. Cамые частые и полезные события: startElement, endElement, characters
    • Преимущества: высокая производительность благодаря "прямому" способу считывания данных, низкие затраты памяти.
    • Недостатки: ограниченная функциональность в нелинейных задачах.
public class SAXExample {
    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
    }

    private static class XMLHandler extends DefaultHandler {
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            // Тут будет логика реакции на начало элемента
        }

Sax

к оглавлению

24 Особенности Java 8

  • Лямбды и Функциональные интерфейсы
  • Интерфейсы по умолчанию и статические методы
  • Ссылочные методы cars.forEach( Car::repair )

к оглавлению

25 Зачем нужны лямбды

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

к оглавлению

26 Зачем нужны исключения

Раньше использовались коды возврата:

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

На стадии разработки программы мы «ограждаем» опасные участки кода в отношении исключений с помощью блока try{}, предусматриваем «запасные» пути с помощью блока catch{}, в блоке finally{} мы пишем код, который выполняется в программе при любом исходе.

к оглавлению

27 Иерархия исключений Отличия checked от unchecked исключений Назвать некоторые checked и unchecked исключения

exceptions Все исключения наследуются от Throwable.

  • Error (unchecked)- восстановление дальнейшей нормальной работы программы невозможно.
  • Exception ++ RuntimeException (unchecked) - NPE, ClasCastEx, ++ Checked Exception (checked) - IOEx, SQLEx

к оглавлению

28 Как определить свою аннотацию

Аннотации используются для анализа кода, компиляции или выполнения. Аннотированы могут быть пакеты, классы, методы, переменные и параметры. We use the @Retention annotation to say where in our program’s lifecycle our annotation applies.

To do this, we need to configure @Retention with one of three retention policies:

  • RetentionPolicy.SOURCE – visible by neither the compiler nor the runtime
  • RetentionPolicy.CLASS – visible by the compiler
  • RetentionPolicy.RUNTIME – visible by the compiler and the runtime

Для этого вам нужно создать новый тип, который использует Java тип @interface, который будет содержать элементы, которые в свою очередь определяют детали метаданных.

public @interface About{
   String info() default "";
}

к оглавлению

29 Зачем нужны immutable объекты

Неизменяемые объекты можно свободно использовать одновременно из разных нитей. Обычно immutable классы содержат различные методы, которые «как бы» меняют объект, но вместо изменения самого объекта эти методы просто создают новый объект и возвращают его.

В случае же immutable-объекта объект после окончания конструктора не изменяется вообще. Одного лишь модификатора final для этого недостаточно, необходимо, чтобы все подбъекты были тоже неизменяемыми. Вы в принципе можете держать внутри ссылку на изменяемый объект, но обращаться с ним так, чтобы он не менялся.

неизменяемые объекты не требуют синхронизации при многопоточном доступе.

To create an immutable class in java, you have to do following steps.

  • Declare the class as final so it can’t be extended.
  • Make all fields private so that direct access is not allowed.
  • Don’t provide setter methods for variables
  • Make all mutable fields final so that it’s value can be assigned only once.
  • Initialize all the fields via a constructor performing deep copy.
  • Perform cloning of objects in the getter methods to return a copy rather than returning the actual object reference.

к оглавлению

30 Назовите паттерны проектирования которые вы использовали

Creational patterns

  • Builder - java.lang.StringBuilder#append() Когда конструктор содержить много параметров. Есть в @Builder в Lombok.

  • Factory Method (Фабрика) - Calendar calendar = Calendar.getInstance()
    (Если мы посмотрим в конструктор, то увидим, что в зависимости от условий создаются разные реализации Calendar) Возвращает объекты через абстрактные типы или интерфейсы. Необходим для ликвидирования зависимости кода от создания конкретных объектов. Для создания объектов различных типов одним интерфейсом.

    • Creator — создатель объявляет фабричный метод, который возвращает объект типа Product. Может также содержать реализацию этого метода «по умолчанию»; может вызывать фабричный метод для создания объекта типа Product;
    • ConcreteCreator — конкретный создатель переопределяет фабричный метод таким образом, чтобы он создавал и возвращал объект класса ConcreteProduct.
  • Abstract Factory - Абстрактная фабрика предоставляет интерфейс для создания целых семейств объектов без указания конкретных классов. Объекты каждого семейства должны быть логически связаны между собой. Паттерн можно определить по методам, возвращающим фабрику, которая, в свою очередь, используется для создания конкретных продуктов, возвращая их через абстрактные типы или интерфейсы.

  • Singleton - Изначально, как замена глобальным переменным. Он один и следовательно все обращения к нему несут изменения во всём проекте. В одном месте вы создали подключение к базе и дальше можете уже использовать его в любой части программы, не пересоздавая подключение и не передавая каждый раз его как аргумент функции. Not Lazy (потокобезопасная)

public class Singleton {
	public static final Singleton INSTANCE = new Singleton();
}

LAZY

public class Singleton {
        private static volatile Singleton instance;
	
        public static Singleton getInstance() {
		Singleton localInstance = instance;
		if (localInstance == null) {
			synchronized (Singleton.class) {
				localInstance = instance;
				if (localInstance == null) {
					instance = localInstance = new Singleton();
				}
			}
		}
		return localInstance;
	}
}

Structural patterns

  • Decorator (Wrapper) - декоратор оборачивается вокруг чего-то, что передали на вход. Было побайтовое чтение, раз и добавили оберткой буферизацию, а потом еще что-нибудь. All subclasses of java.io.InputStream, OutputStream, Reader and Writer have a constructor taking an instance of same type.
  • Adapter - Он похож на декоратор — на вход декоратор принимает один объект и возвращает обёртку над этим объектом. Отличие в том, что цель у этого не изменение функционала, а адаптация одного интерфейса к другому. java.util.Arrays#asList()
  • Facade - methods which internally uses instances of different independent abstract/interface types
  • Proxy - methods which returns an implementation of given abstract/interface type which in turn delegates/uses a different implementation of given abstract/interface type java.lang.reflect.Proxy

Behavioral patterns

  • Chain of responsibility - methods which (indirectly) invokes the same method in another implementation of same abstract/interface type in a queue
  • Command - methods in an abstract/interface type which invokes a method in an implementation of a different abstract/interface type which has been encapsulated by the command implementation during its creation. All implementations of java.lang.Runnable
  • Iterator - All implementations of java.util.Iterator
  • Strategy - methods in an abstract/interface type which invokes a method in an implementation of a different abstract/interface type which has been passed-in as method argument into the strategy implementation.
  • Visitor - java.nio.file.FileVisitor Позволяет добавлять в программу новые операции, не изменяя классы объектов, над которыми эти операции могут выполняться.

к оглавлению

31 Scope'ы бинов в Spring'e

  • singleton Определяет один единственный бин для каждого контейнера Spring IoC (используется по умолчанию).
  • prototype Позволяет иметь любое количество экземпляров бина. Spring does not manage the complete lifecycle of a prototype bean: the container instantiates, configures, decorates and otherwise assembles a prototype object, hands it to the client and then has no further knowledge of that prototype instance. This means that while initialization lifecycle callback methods will be called on all objects regardless of scope, in the case of prototypes, any configured destruction lifecycle callbacks will not be called.
  • request Создаётся один экземпляр бина на каждый HTTP запрос. Касается исключительно ApplicationContext.
  • session Создаётся один экземпляр бина на каждую HTTP сессию. Касается исключительно ApplicationContext.
  • global-session Создаётся один экземпляр бина на каждую глобальную HTTP сессию. Касается исключительно ApplicationContext.

к оглавлению

32 Какие книги читали по Java

  • Head first
  • Герберт Шилдт. Java 8. Руководство для начинающих

к оглавлению

33 Как работает сборщик мусора в Java Можно ли вызвать сборку мусора

JVM обычно запускает сборщик мусора при низком уровне свободной памяти. Но работа сборщика мусора не гарантирует, что всегда будет оставаться достаточно свободной памяти. Если памяти недостаточно даже после восстановления, JVM генерирует исключение OutOfMemoryError. Обратите внимание, что перед генерированием исключения JVM обязательно запускает сборщик мусора как минимум 1 раз.

  • System.gc()
  • Runtime.getRuntime().gc()

Объект может подлежать утилизации в разных случаях:

  • Если переменная ссылочного типа, которая ссылается на объект, установлена в положение "0", объект подлежит утилизации, в том случае, если на него нет других ссылок.
  • Если переменная ссылочного типа, которая ссылается на объект, создана для ссылки на другой объект, объект подлежит утилизации, в том случае, если на него нет других ссылок.
  • Объекты, созданные локально в методе, подлежат утилизации, когда метод завершает работу, если только они не экспортируются из этого метода (т.е, возвращаются или генерируются как исключение).
  • Объекты, которые ссылаются друг на друга, могут подлежать утилизации, если ни один из них не доступен живому потоку.

к оглавлению

34 Как устроена память в Java

Для оптимальной работы приложения JVM делит память на область стека (stack) и область кучи (heap). Stack

  • Он заполняется и освобождается по мере вызова и завершения новых методов
  • Переменные в стеке существуют до тех пор, пока выполняется метод в котором они были созданы
  • Если память стека будет заполнена, Java бросит исключение java.lang.StackOverFlowError
  • Доступ к этой области памяти осуществляется быстрее, чем к куче
  • является потокобезопасным, поскольку для каждого потока создается свой отдельный стек

Heap

  • Эта область памяти используется для объектов и классов. Новые объекты всегда создаются в куче, а ссылки на них хранятся в стеке.
  • Когда эта область памяти полностью заполняется, Java бросает java.lang.OutOfMemoryError
  • Доступ к ней медленнее, чем к стеку
  • Эта память, в отличие от стека, автоматически не освобождается. Для сбора неиспользуемых объектов используется сборщик мусора
  • В отличие от стека, куча не является потокобезопасной и ее необходимо контролировать, правильно синхронизируя код
    • Young Generation — область где размещаются недавно созданные объекты. Когда она заполняется, происходит быстрая сборка мусора
    • Old (Tenured) Generation — здесь хранятся долгоживущие объекты. Когда объекты из Young Generation достигают определенного порога "возраста", они перемещаются в Old Generation
    • Permanent Generation — эта область содержит метаинформацию о классах и методах приложения, но начиная с Java 8 данная область памяти была упразднена.

к оглавлению

35 В какой области памяти хранятся локальные переменные и объекты

Новые объекты всегда создаются в куче, а ссылки на них хранятся в стеке. variables of a method are stored in stack and class variables are stored in heap. Локальные переменные в стеке. Объекты в куче.

к оглавлению

36 Написать код который 100 приведет к OutOfMemoryError а также к StackOverflowException

OutOfMemoryError

List<long[]> list = new LinkedList<long[]>();
while (true) {
  list.add(new long[65536]); // an arbitrary number
  // sleep(1) perhaps?
}

StackOverflowException

public class StackOverflowErrorExample {

    public static void recursivePrint(int num) {
        System.out.println("Number: " + num);

        if(num == 0)
            return;
        else
            recursivePrint(++num);
    }

    public static void main(String[] args) {
        StackOverflowErrorExample.recursivePrint(1);
    }
}

к оглавлению

37 Что такое happens before Приведите примеры

Пусть есть поток X и поток Y (не обязательно отличающийся от потока X). И пусть есть операции A (выполняющаяся в потоке X) и B (выполняющаяся в потоке Y).

В таком случае, A happens-before B означает, что все изменения, выполненные потоком X до момента операции A и изменения, которые повлекла эта операция, видны потоку Y в момент выполнения операции B и после выполнения этой операции.

вот некоторые условия синхронизации памяти:

  • В рамках одной нити любая команда happens-before (читается «случается перед») любой операцией, следующей за ней в исходном коде.
  • Освобождение лока (unlock) happens-before захватом того же лока (lock).
  • Выход из synchronized блока/метода happens-before вход в synchronized блок/метод на том же мониторе.
  • Запись volatile поля happens-before чтение того же самого volatile поля.
  • Завершение метода run экземпляра класса Thread happens-before выход из метода join() или возвращение false методом isAlive() экземпляром той же нити.
  • Вызов метода start() экземпляра класса Thread happens-before начало метода run() экземпляра той же нити.
  • Завершение конструктора happens-before начало метода finalize() этого класса
  • Вызов метода interrupt() на нити happens-before, когда нить обнаружила, что данный метод был вызван, либо путем выбрасывания исключения InterruptedException, либо с помощью методов isInterrupted() или interrupted()

к оглавлению

38 Что такое deadlock Как его избежать

Взаимная блокировка (deadlock) - явление при котором все потоки находятся в режиме ожидания. Простейший способ избежать взаимной блокировки – не допускать цикличного ожидания. Этого можно достичь, получая мониторы разделяемых ресурсов в определённом порядке и освобождая их в обратном порядке.

  • Взаимная блокировка порядка синхронизации
public void transferMoney(Account fromAccount, Account toAccount, Amount amount) throws InsufficientFundsException {
	synchronized (fromAccount) {
		synchronized (toAccount) {
			if (fromAccount.getBalance().compareTo(amount) < 0)
				throw new InsufficientFundsException();
			else {
				fromAccount.debit(amount);
				toAccount.credit(amount);
			}
		}
	}

к оглавлению

39 Что такое состояние гонок

Состояние гонки (race condition) - ошибка проектирования многопоточной системы или приложения, при которой эта работа напрямую зависит от того, в каком порядке выполняются потоки. Состояние гонки возникает когда поток, который должен исполнится в начале, проиграл гонку и первым исполняется другой поток: поведение кода изменяется, из-за чего возникают недетерменированные ошибки.

Распространённые способы решения:

  • Использование локальной копии — копирование разделяемой переменной в локальную переменную потока. Этот способ работает только тогда, когда переменная одна и копирование производится атомарно (за одну машинную команду), использование volatile.
  • Синхронизация - операции над разделяемым ресурсом происходят в синхронизированном блоке (при использовании ключевого слова synchronized).
  • Комбинирование методов - вышеперечисленные способы можно комбинировать, копируя «опасные» переменные в синхронизированном блоке. С одной стороны, это снимает ограничение на атомарность, с другой — позволяет избавиться от слишком больших синхронизированных блоков. Очевидных способов выявления и исправления состояний гонки не существует. Лучший способ избавиться от гонок — правильное проектирование многозадачной системы.

к оглавлению

40 Что такое Executor'ы Зачем они нужны

Начиная с Java 1.5 Java API предоставляет фреймворк Executor, который позволяет создавать различные типы пула потоков:

  • Executor - упрощенный интерфейс пула, содержит один метод для передачи задачи на выполнение;
  • ExecutorService - расширенный интерфейс пула, с возможностью завершения всех потоков;
  • AbstractExecutorService - базовый класс пула, реализующий интерфейс ExecutorService;
  • Executors - фабрика объектов связанных с пулом потоков, в том числе позволяет создать основные типы пулов;
  • ThreadPoolExecutor - пул потоков с гибкой настройкой, может служить базовым классом для нестандартных пулов;
  • ForkJoinPool - пул для выполнения задач типа ForkJoinTask;
  • ... и другие.

parallelStream() использует CommonThredPool (могут быть проблемы с загрузкой ЦП до 100%) и ForkJoinPool Могут быть особенности при выполнении параллельно:

List<Integer> listOfNumbers = Arrays.asList(1, 2, 3, 4);
int sumParallel = listOfNumbers.parallelStream().reduce(5, Integer::sum); // not equal 15
int sum = listOfNumbers.stream().reduce(5, Integer::sum); // equal 15

к оглавлению

41 В чем отличия между equals и == Контракты по equals и hashcode

Метод equals() обозначает отношение эквивалентности объектов. Эквивалентным называется отношение, которое является симметричным, транзитивным, рефлексивным и постоянным.

  1. Рефлексивность: для любого ненулевого x, x.equals(x) вернет true;
  2. Транзитивность: для любого ненулевого x, y и z, если x.equals(y) и y.eqals(z) вернет true, тогда и x.equals(z) вернет true;
  3. Постоянство: для любых объектов x и y x.equals(y) возвращает одно и тоже, если информация, используемая в сравнениях, не меняется;
  4. Симметричность: для любого ненулевого x и y, x.equals(y) должно вернуть true, тогда и только тогда, когда y.equals(x) вернет true.

Метод хешкод Если хешкод двух объектов равен - это не значит что 2 объекта равны по equals Если хешкод двух объектов не равен - значит объекты не равны по equals

к оглавлению

42 написать реализацию equals и хеш код для произвольного класса

    @Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + varA;
		result = prime * result + varB;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		BlackBox other = (BlackBox) obj;
		if (varA != other.varA)
			return false;
		if (varB != other.varB)
			return false;
		return true;
	}

к оглавлению

43 Что такое дерево Можно ли сделать так чтобы обычное двоичное дерево выродилось в связный список

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

к оглавлению

44 Какие есть способны конфигурирования ApplicationContext Spring'a

3 ways to configure the Spring Container

  • XML - based configuration
    <?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" ...>

<bean id="accountService" class="com.wiley.beginningspring.ch2.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"/>
</bean>

<bean id="accountDao" class="com.wiley.beginningspring.ch2.AccountDaoInMemoryImpl">
</bean>
``` and using then ```java ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/com/wiley/beginningspring/ch2/ch2-beans.xml"); ```
  • Java Based Configuration (In this Configuration method, there will be a class for the configuration)
@Configuration
public class Ch2BeanConfiguration {
    @Bean
    public AccountDao accountDao() {
        AccountDaoInMemoryImpl bean = new AccountDaoInMemoryImpl();
        return bean;
    }
}
  • Annotation Based Configuration

к оглавлению

45 Как в Spring заинжектить два бина одного типа

@Autowired @Qualifier("jdbcDeviceDao")

к оглавлению

46 Что такое SpringBoot

Spring Boot — это полезный проект, целью которого является упрощение создания приложений на основе Spring. Он позволяет наиболее простым способом создать web-приложение, требуя от разработчиков минимум усилий по его настройке и написанию кода. Чтобы ускорить процесс управления зависимостями, Spring Boot неявно упаковывает необходимые сторонние зависимости для каждого типа приложения на основе Spring и предоставляет их разработчику посредством так называемых starter-пакетов (spring-boot-starter-web, spring-boot-starter-data-jpa и т.д.)

Starter-пакеты представляют собой набор удобных дескрипторов зависимостей, которые можно включить в свое приложение.

Аннотация @SpringBootApplication включает сканирование компонентов и автоматическую конфигурацию приложения. На самом деле, за этой аннотацией скрывается целых три:

  • @Configuration (Spring) – помечает класс как конфигурационный (в контексте java-based конфигурации).
  • @ComponentScan (Spring) – включает сканирование компонентов, то есть классы-контроллеры и другие компоненты, которые вы создаете, будут автоматически зарегистрированы как бины в application context.
  • @EnableAutoConfiguration (Spring Boot) – самая магическая аннотация из всех возможных. Именно она под капотом запускает весь механизм автоматической конфигурации Spring Boot.

Инициализация бина по условию

@Component
@ConditionalOnExpression("${logger:true}")
public class Logger {
    public void log(String message){
        System.out.println("log");
    }
}

Пропускать при отсутсвии. Если нет будет заинжектен null. Перед использованием бина надо проверить на null.

@Autowired(required=false)

Несколько бинов одного типа. Есть аннотация @Primary задает бин, который будет внедрен по умолчанию (при отсутствии других указаний). Аннотация @Qualifier("beanName") позволяет уточнить имя бина, который надо внедрить.

к оглавлению

47 Принципы организации многопоточности в java

Процесс - это Отдельно запущенное приложение и у него есть своя область памяти (минимум 1 поток).

Поток - это паралельные нити выполнения внутри одного процесса которые разделяют общую память.

к оглавлению

48 Как запустить поток

  • Создать потомка класса Thread и переопределить его метод run();
  • Создать объект класса Thread, передав ему в конструкторе экземпляр класса, реализующего интерфейс Runnable. Эти интерфейс содержит метод run(), который будет выполняться в новом потоке. Поток закончит выполнение, когда завершится его метод run().
  • Вызвать метод submit() у экземпляра класса реализующего интерфейс ExecutorService, передав ему в качестве параметра экземпляр класса реализующего интерфейс Runnable или Callable (содержит метод call(), в котором описывается логика выполнения).

к оглавлению

49 Состояния потоков

Потоки могут находиться в одном из следующих состояний:

  • Новый (New). После создания экземпляра потока, он находится в состоянии Новый до тех пор, пока не вызван метод start(). В этом состоянии поток не считается живым.
  • Работоспособный (Runnable). Поток переходит в состояние Работоспособный, когда вызывается метод start(). Поток может перейти в это состояние также из состояния Работающий или из состояния Блокирован. Когда поток находится в этом состоянии, он считается живым.
  • Работающий (Running). Поток переходит из состояния Работоспособный в состояние Работающий, когда Планировщик потоков выбирает его как работающий в данный момент.
  • Живой, но не работоспособный (Alive, but not runnable). Поток может быть живым, но не работоспособным по нескольким причинам:
  • Ожидание (Waiting). Поток переходит в состояние Ожидания, вызывая метод wait(). Вызов notify() или notifyAll() может перевести поток из состояния Ожидания в состояние Работоспособный.
  • Сон (Sleeping). Метод sleep() переводит поток в состояние Сна на заданный промежуток времени в миллисекундах.
  • Блокировка (Blocked). Поток может перейти в это состояние, в ожидании ресурса, такого как ввод/вывод или из-за блокировки другого объекта. В этом случае поток переходит в состояние Работоспособный, когда ресурс становится доступен.
  • Мёртвый (Dead). Поток считается мёртвым, когда его метод run() полностью выполнен. Мёртвый поток не может перейти ни в какое другое состояние, даже если для него вызван метод start().

к оглавлению

50 SQL

Чем отличаются реляционные БД от нереляционных?

  • Реляционные базы - хранят данные в таблицах, в которых строки соответствуют разным записям, а колонки разным полям.
  • Нереляционные базы - это все остальные. Некоторые из них хранят записи в виде больших наборов "ключ-значение"

Какие ключи бывают в БД?

  • первичный ключ;
  • внешний ключ;
  • простой;
  • составной ключ;

Что такое агрегатные функции?

Агрегатная функция выполняет вычисление на наборе значений и возвращает одиночное значение. Агрегатные функции, за исключением COUNT, не учитывают значения NULL. Агрегатные функции часто используются в выражении GROUP BY инструкции SELECT.

LEFT, RIGHT, INNER JOIN чем отличаются?

Основное различие в том, как соединяются таблицы, если нет общих записей.

  • Простой JOIN - тоже самое что INNER JOIN и означает показывать только общие записи обоих таблиц. Каким образом записи считаются общими определяется полями в join-выражении. Например следующая запись
FROM t1 JOIN t2 on t1.id = t2.id

означает что будут показаны записи с одинаковыми id, существующие в обоих таблицах.

  • LEFT JOIN (или LEFT OUTER JOIN) означает показывать все записи из левой таблицы (той, которая идет первой в join-выражении) независимо от наличия соответствующих записей в правой таблице.
  • RIGHT JOIN (или RIGHT OUTER JOIN) действует в противоположность LEFT JOIN - показывает все записи из правой (второй) таблицы и только совпавшие из левой (первой) таблицы.

Что такое GROUP BY?

Предложение GROUP BY используется для определения групп выходных строк, к которым могут применяться агрегатные функции (COUNT, MIN, MAX, AVG и SUM).

Чем отличаются HAVING от WHERE?

Основное отличие WHERE от HAVING заключается в том, что WHERE сначала выбирает строки, а затем группирует их и вычисляет агрегатные функции (таким образом, она отбирает строки для вычисления агрегатов), тогда как HAVING отбирает строки групп после группировки и вычисления агрегатных функций.

SQL

к оглавлению

51 Транзакции ACID Феномены чтения

Транзакции


ACID принципы

  • Atomicity — Атомарность - Все операции либо будут выполнены вместе либо не выполнены вообще.
  • Consistency — Согласованность - После коммита в БД сохраняются все изменения. Это свойство вытекает из предыдущего. Благодаря тому, что транзакция не допускает промежуточных результатов, база остается консистентной. Есть такое определение транзакции: «Упорядоченное множество операций, переводящих базу данных из одного согласованного состояния в другое».
  • Isolation — Изолированность - Изоляция от внешних воздействий / паралельных транзакций. Во время выполнения транзакции параллельные транзакции не должны оказывать влияния на её результат.
  • Durability — Надёжность - Если пользователь получил подтверждение от системы, что транзакция выполнена, он может быть уверен, что сделанные им изменения не будут отменены из-за какого-либо сбоя. Обесточилась система, произошел сбой в оборудовании? На выполненную транзакцию это не повлияет.

Феномены чтения

  • Unrepeatable read(Неповторяемость чтения) - Ситуация когда транзакция которая уже получила данные видит уже измененные данные другой транзакции при повторном запросе
  • Phantom read - Ситуация, когда при повторном чтении в рамках одной транзакции одна и та же выборка дает разные множества строк.
  • Dirty read - Чтение/Добавление/Изменение данных транзакции которая откатиться

Уровни изоляций

  • Read-uncommited - Чтение незафиксированных данных
  • Read-commited - Чтение зафиксированных данных
  • Repetable-read - Повторяемость чтения. Предотвращает проблему потерянного обвноления. Не позволяет одновременный доступ к одной и той же строке.
  • Serializable - Упорядочиваемость. Самый медленный спасает от всех проблем. Эффект как будто транзакции выполняются последовательно друг за другом.

к оглавлению

53 Что такое Maven

Apache Maven — фреймворк для автоматизации сборки проектов на основе описания их структуры в файлах на языке POM (англ. Project Object Model), являющемся подмножеством XML

  • Концентрирование информации о модуле в одном месте и в одном формате. многие IDE могут импортировать проекты из POM.
  • Единая система идентификации модулей.
  • Один инструмент. Хотя это, прямо скажем, не уникальная фича Maven. проект должен собираться стандартными и переносимыми средствами. Сборка любимой IDE Первого Разработчика Проекта — путь в ад.
  • Автоматическое управление зависимостями.

к оглавлению

53 Что такое область видимости зависимостей DEPENDENCY SCOPE в Maven

  • compile - область видимости по умолчанию. Используется, когда область видимости явно не указана. Компилирует зависимости, доступные во всех classpath проекта.
  • provided - эта область видимости очень похожа на compile, но показывает, что JDK или среда исполнения должна предоставить эти зависимости во время выполнения.
  • runtime - показывает, что зависимость не нужна для компиляции, но нужна во время выполнения.
  • test - показывает, что эти зависимости не нужны для работы приложения и используются только в фазе тестирования.
  • system - очень похожа на provided за исключением того, что вы предоставите архив, который содержит эту зависимость. Артефакт всегда доступен, поэтому его поиск в репозитории не производится.
  • import (Maven версии 2.0.9 или выше) - эта область видимости используется только в зависимости типа pom в секции . Она показывает, что указанный pom должен быть заменён зависимостями из pom'a, который указан в dependencyManagement.

provided - For example, all web containers (eg: tomcat) include the jars for servlets. You should use provided for the servlet classes so you can compile your code locally, but you don't want to override the servlet classes that tomcat provides for you when you deploy to it.

к оглавлению

54 Какие вы знаете репозитории Maven

Существует три типа репозиториев Maven:

  • локальные (local)
  • центральные (central)
  • удалённые (remote)

к оглавлению

55 Внутреннее устройство хранилища git

git

к оглавлению

56 Сервлеты JSP

Servlet API - спецификация java расширяющая возможности сервлет контейнера позволяющая обрабатывать HTTP запросы клиента


жизненный цикл сервлета состоит из следующих шагов:

  • В случае отсутствия сервлета в контейнере. Класс сервлета загружается контейнером. Контейнер создает экземпляр класса сервлета.
  • Контейнер вызывает метод init(). Этот метод инициализирует сервлет и вызывается в первую очередь, до того, как сервлет сможет обслуживать запросы. За весь жизненный цикл метод init() вызывается только один раз.
  • Обслуживание клиентского запроса. Каждый запрос обрабатывается в своем отдельном потоке. Контейнер вызывает метод service() для каждого запроса. Этот метод определяет тип пришедшего запроса и распределяет его в соответствующий этому типу метод для обработки запроса. Разработчик сервлета должен предоставить реализацию для этих методов. Если поступил запрос, метод для которого не реализован, вызывается метод родительского класса и обычно завершается возвращением ошибки инициатору запроса.
  • В случае если контейнеру необходимо удалить сервлет, он вызывает метод destroy(), который снимает сервлет из эксплуатации. Подобно методу init(), этот метод тоже вызывается единожды за весь цикл сервлета.

loadOnStartUp в web.xml также Сервлет АПИ поддерживает фильтры(метод doFilter) и слушатели событий (можно использовать для инициализации при загрузке приложения)

Java Server Pages - спецификация


Технология позволяющая веб-разработчикам создавать содержимое, которое имеет как статические так и динамические компоненты Код JSP в рантайме компилируется в обычный Servlet а потом джава машина его компилирует в байт код Внутри JSP можно использовать EL/Скриплеты/taglibs для написание кода

к оглавлению

57 Spring core

Фреймворк для упрощения разработки энтерпрайз приложений. Построен на принципе IOC (Инверсии управления) Использует для этого контейнер джава бинов. Джава бин класс жизненным циклом которого управляет спринг. Также спринг использует подход DI Внедрение зависимости используются аннотации @Authowired внедрять можно на поля сеттеры конструкторы / аннотации наследники @Component (@Service / @Repository / @Controller) Также используются и другие аннотации например @Qualifier для уточнения реализации которую необходимо внедрить У спринг бинов существуют следующие скоупы:

    1. Singleton - Возвращает один и тот же экземляр бина на каждый запрос контейнера Spring IoC (по умолчанию).
    1. Prototype - Создает и возвращает новый экземляр бина на каждый запрос.
    1. Request - Создает и возвращает экземляр бина на каждый HTTP запрос*.
    1. Session - Создает и возвращает экземляр бина для каждой HTTP сессии*.
    1. Global-session - Создает и возвращает экземляр бина для глобальной HTTP сессии*.

к оглавлению

58 Spring MVC

DispatcherServlet - основной контроллер фреймворка отвечающий за мапинг запросов на методы контроллеров Основные используемые аннотации @RestController / @Controller

  • @RequestMapping - позволяет задать шаблон маппинга URI в методе обработчике контроллера. Можно задать для всего класса или для отдельного метода.
@Controller
@RequestMapping("/my/special/path")
public class SomeController {
  @RequestMapping("/somedata")
  public String loadSomeData() {
    //---
  }
}
  • @RequestBody - тело запроса может быть распознано как параметр в методе контроллера.
@RequestMapping(value = "/page", method = RequestMethod.POST)
public String savePage(@RequestBody String pageContent) {
  //---
}
  • @ResponceBody - результат работы метода в контроллере в тело ответа. Аннотация @ResponseBody дает фреймворку понять, что объект, который вы вернули из метода надо прогнать через HttpMessageConverter, чтобы получить готовое к отправке на клиент представление. У Spring есть список HttpMessageConverters. HttpMessageConverter обязан конвертировать тело запроса к определенному классу и и класс к телу ответа, в зависимости от типа. Каждый раз, когда происходит запрос с аннотацией @ResponseBody, Spring ищет среди всех HttpMessageConverters подходящий и использует его.
@RequestMapping(value = "/page", method = RequestMethod.POST)
@ResponseBody
public String savePage(@RequestBody String pageContent) {
  //---
}
  • @PathVariable - параметры методов включены в путь.
@RequestMapping(value = "/pages/{id}")
public String loadPage(@PathVariable(value = "id") Long id) {
  //---
}
  • @RequestParam - параметры методов как параметры запроса.
@RequestParam(value="param1", required=true) String param1,
        @RequestParam(value="param2", required=false) String param2){
	...

к оглавлению

59 JPA

Основные аннотации @Entity, @Column, @Table, @Embedable, @MappedSupperClass, @Id, @ManyToMany, @ManyToOne, @OneToMany EntityManager - интерфейс содержащий основные операции над сущностями (persist / refresh / detach/ remove / merge)

jpa

Управления сущностями JPA

@Entity
@Table(name = "EMPLOYEE")
public class Employee {
   @Id @GeneratedValue
   @Column(name = "id")
   private int id;
}

@MappedSuperclass - позволяет включать класс и его jpa аннотации в производный класс, не делая базовый класс сущностью. Типичное использование в примере выше — абстрактный базовый класс, несущий в себе суррогатный первичный ключ. В базе данных всё будет выглядеть, как если бы поля базового класса были определены непосредственно в производном классе.

@Embeddable annotation to declare that a class will be embedded by other entities

@Embeddable
public class ContactPerson {
 
    private String firstName;
 
    private String lastName;
 
    private String phone;
 
    // standard getters, setters
}
@Entity
public class Company {
 
    @Id
    @GeneratedValue
    private Integer id;
 
    private String name;
 
    private String address;
 
    private String phone;
 
    @Embedded
    private ContactPerson contactPerson;
 
    // standard getters, setters
}

As a result, we have our entity Company, embedding contact person details, and mapping to a single database table

к оглавлению

60 Hibernate

Hibernate

ORM - маппинг объектов на БД. Жизненный цикл Entiity:

  • Transient: состояние, при котором объект никогда не был связан с какой-либо сессией и не является персистентностью.
  • Persistent: когда объект связан с уникальной сессией он находится в состоянии persistent (персистентности). Любой экземпляр, возвращаемый методами get() или load() находится в состоянии persistent.
  • Detached: если объект был персистентным, но сейчас не связан с какой-либо сессией, то он находится в отвязанном (detached) состоянии. Такой объект можно сделать персистентным используя методы update(), saveOrUpdate(), lock() или replicate(). Состояния transient или detached так же могут перейти в состояние persistent как новый объект персистентности после вызова метода merge(). SessionFactory immutable (неизменяемый), то да, он потокобезопасный.

Session

Это основной интерфейс, который отвечает за связь с базой данных.

  • является оберткой для jdbc подключения к базе данных
  • является фабрикой для транзакций

Hibernate session обладает различными методами для загрузки данных из базы данных. Наиболее часто используемые методы для этого — get() и load().

  • get() загружает данные сразу при вызове, в то время как load() использует прокси объект и загружает данные только тогда, когда это требуется на самом деле. В этом плане load() имеет преимущество в плане ленивой загрузки данных.
  • load() бросает исключение, когда данные не найдены. Поэтому его нужно использовать только при уверенности в существовании данных.

flush vs commit

  • flush() синхронизирует вашу базу данных с текущим состоянием объекта/объектов, хранящихся в памяти, но не совершает транзакцию.
  • commit() сделает данные, хранящиеся в базе данных постоянными. Вы не можете отменить свою транзакцию после успешного завершения commit()

Методы

  • save() используется для сохранения сущности в базу данных. немедленно возвращает сгенерированный идентификатор/
  • persist() аналогичен save() с транзакцией. persist() не возвращает сгенерированный идентификатор сразу.
  • saveOrUpdate() использует запрос для вставки или обновления, основываясь на предоставленных данных.
  • merge() может быть использован для обновления существующих значений

In summary save() method saves records into database by INSERT SQL query, Generates a new identifier and return the Serializable identifier back. On the other hand saveOrUpdate() method either INSERT or UPDATE based upon existence of object in database. If persistence object already exists in database then UPDATE SQL will execute and if there is no corresponding object in database than INSERT will run.

save() flushes the entity to the database when you make the call. persist() actually just marks the entity to be persisted in the upcoming flush. The save method is an “original” Hibernate method. Its purpose is basically the same as persist. he call of save on a detached instance creates a new persistent instance and assigns it a new identifier, which results in a duplicate record in a database

Transaction

Вместо вызовов session.openTransaction() и session.commit() используется аннотация @Transactional

Eager vs Lazy

  • Eager Loading is a design pattern in which data initialization occurs on the spot. Загружаются все данные по цепочке.
  • Lazy Loading is a design pattern which is used to defer initialization of an object as long as it's possible. Данные подгружаются при обращении.

Named SQL

Именованные запросы поддерживают как HQL, так и Native SQL. Создать именованный запрос можно с помощью JPA аннотаций @NamedQuery, @NamedNativeQuery

Аннотации

  • javax.persistence.Entity: используется для указания класса как entity bean.
  • javax.persistence.Table: используется для определения имени таблицы из БД, которая будет отображаться на entity bean.
  • javax.persistence.Access: определяет тип доступа, поле или свойство. Поле — является значением по умолчанию и если нужно, чтобы hibernate использовал методы getter/setter, то их необходимо задать для нужного свойства.
  • javax.persistence.Id: определяет primary key в entity bean.
  • javax.persistence.EmbeddedId: используется для определения составного ключа в бине.
  • javax.persistence.Column: определяет имя колонки из таблицы в базе данных.
  • javax.persistence.GeneratedValue: задает стратегию создания основных ключей. Используется в сочетании с javax.persistence.GenerationType enum.
  • javax.persistence.OneToOne: задает связь один-к-одному между двумя сущностными бинами. Соответственно есть другие аннотации OneToMany, ManyToOne и ManyToMany.
  • org.hibernate.annotations.Cascade: определяет каскадную связь между двумя entity бинами. Используется в связке с org.hibernate.annotations.CascadeType.
  • *javax.persistence.PrimaryKeyJoinColumnм: определяет внешний ключ для свойства. Используется вместе с org.hibernate.annotations.GenericGenerator и org.hibernate.annotations.Parameter.

Criteria API

Session session = HibernateUtil.getHibernateSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Item> cr = cb.createQuery(Item.class);
Root<Item> root = cr.from(Item.class);
cr.select(root);
 
Query<Item> query = session.createQuery(cr);
List<Item> results = query.getResultList();

Уровин кеша в Hibernate

Работа с кешем в SpringBoot

  • First level: Кеширование на уровне сессии (Session). Самый простой вид кеширования (его еще называют кэшем первого уровня) реализован на уровне Hibernate-сессии. Hibernate всегда по умолчанию использует этот кэш и его нельзя отключить. Пример ниже выполниться только один запрос в базу.
Employee director1 = session.get(Employee.class, 4);
Employee director2 = session.get(Employee.class, 4);

assertTrue(director1 == director2);
  • Second Level: кэш второго уровня привязан к объекту SessionFactory. Кэширование второго уровня по умолчанию отключено.

Методы работы с кешем:

  • Session.evict(): to remove the cached/stored entity.
  • refresh(): method to refresh the cache.
  • clear(): method to remove all the entities from the cache.

N+1 Problem

The N+1 problem is the situation when, for a single request, for example, fetching Users, we make additional requests for each User to get their information. Although this problem often is connected to lazy loading, it’s not always the case.

Итоги

  • Для сохранения сущности следует использовать метод JPA persist
  • Для копирования состояния detached-сущности предпочтительным является merge
  • Метод update полезен только для задач пакетной обработки.
  • Методы save и saveOrUpdate — это просто псевдонимы для update, и вам не следует использовать их вообще.
  • Некоторые разработчики используют save, даже если объект уже управляется, но это ошибка и вызывает лишнее событие, так как для управляемых сущностей UPDATE автоматически обрабатывается Persistence context во время flush.

к оглавлению

61 Способы аутентификации пользователя

  • Идентификация — это заявление о том, кем вы являетесь. В зависимости от ситуации, это может быть имя, адрес электронной почты, номер учетной записи, итд.
  • Аутентификация — предоставление доказательств, что вы на самом деле есть тот, кем идентифицировались (от слова “authentic” — истинный, подлинный).
  • Авторизация — проверка, что вам разрешен доступ к запрашиваемому ресурсу.

По паролю: HTTP authentication

  • Сервер, при обращении неавторизованного клиента к защищенному ресурсу, отсылает HTTP статус “401 Unauthorized” и добавляет заголовок “WWW-Authenticate” с указанием схемы и параметров аутентификации.

  • Браузер, при получении такого ответа, автоматически показывает диалог ввода username и password. Пользователь вводит детали своей учетной записи.

  • Во всех последующих запросах к этому веб-сайту браузер автоматически добавляет HTTP заголовок “Authorization”, в котором передаются данные пользователя для аутентификации сервером.

  • Сервер аутентифицирует пользователя по данным из этого заголовка. Решение о предоставлении доступа (авторизация) производится отдельно на основании роли пользователя, ACL или других данных учетной записи.

  • Basic — наиболее простая схема, при которой username и password пользователя передаются в заголовке Authorization в незашифрованном виде (base64-encoded). Однако при использовании HTTPS (HTTP over SSL) протокола, является относительно безопасной.

  • Digest — challenge-response-схема, при которой сервер посылает уникальное значение nonce, а браузер передает MD5 хэш пароля пользователя, вычисленный с использованием указанного nonce. Более безопасная альтернативв Basic схемы при незащищенных соединениях, но подвержена man-in-the-middle attacks (с заменой схемы на basic). Кроме того, использование этой схемы не позволяет применить современные хэш-функции для хранения паролей пользователей на сервере.

Forms authentication Работает это по следующему принципу: в веб-приложение включается HTML-форма, в которую пользователь должен ввести свои username/password и отправить их на сервер через HTTP POST для аутентификации. В случае успеха веб-приложение создает session token, который обычно помещается в browser cookies. При последующих веб-запросах session token автоматически передается на сервер и позволяет приложению получить информацию о текущем пользователе для авторизации запроса.

Существует всего несколько мест, где можно передать username и password в HTTP запросах:

  • URL query — считается небезопасным вариантом, т. к. строки URL могут запоминаться браузерами, прокси и веб-серверами.
  • Request body — безопасный вариант, но он применим только для запросов, содержащих тело сообщения (такие как POST, PUT, PATCH).
  • HTTP header —оптимальный вариант, при этом могут использоваться и стандартный заголовок Authorization (например, с Basic-схемой), и другие произвольные заголовки.

к оглавлению

62 Способы конфигурации бинов

  • С помощью аннотаций @Component, @Service и т.п. Через конструкторы, сеттеры и @Autowired
  • С помощь XML
    <bean id="engine" class="ru.javalang.injection.Engine" />
      <bean class="ru.javalang.injection.CarWithConstructor">
          <constructor-arg ref="engine" />
      </bean>
      <bean class="ru.javalang.injection.CarWithSetter">
          <property name="engine" ref="engine" />
      </bean>
  • С помощью @Configuration и аннотации @Bean (например для классов из других пакетов)

к оглавлению

63 How to define immutable object

  • Don't provide "setter" methods — methods that modify fields or objects referred to by fields.
  • Make all fields final and private.
  • Don't allow subclasses to override methods. The simplest way to do this is to declare the class as final. A more sophisticated approach is to make the constructor private and construct instances in factory methods.
  • If the instance fields include references to mutable objects, don't allow those objects to be changed: ++ Don't provide methods that modify the mutable objects. ++ Don't share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.

к оглавлению

64 Spring DispatcherServlet how it works

DispatcherServlet acts as front controller for Spring based web applications. It provides a mechanism for request processing where actual work is performed by configurable, delegate components. It is inherited from javax.servlet.http.HttpServlet, it is typically configured in the web.xml file.

DispatcherServlet uses Spring configuration classes to discover the delegate components it needs for request mapping, view resolution, exception handling etc.

WebApplicationContext is an extension of a plain ApplicationContext. it is web aware ApplicationContext i.e it has Servlet Context information. When DispatcherServlet is loaded, it looks for the bean configuration file of WebApplicationContext and initializes it.

  • An ApplicationContext cannot have more than 1 parent ApplicationContext.
  • When a given ApplicationContext cannot resolve a bean, it will pass on the resolution request to its parent.

к оглавлению

65 Какие существуют два вида методов для работы со стримами

  • Промежуточные: filter(), skip(), map(),
  • Конечные: findFirst(), findAny(), collect(), forEach(), count().

к оглавлению

66 Чем отличается DI от IoC

  • IoC - отделить объекты от реализации. Это некий абстрактный принцип, набор рекомендаций для написания слабо связанного кода. Суть которого в том, что каждый компонент системы должен быть как можно более изолированным от других, не полагаясь в своей работе на детали конкретной реализации других компонентов.
  • DI - связывание объектов. сеттер, конструктор, интерфейс. Это одна из реализаций IoC

к оглавлению

67 Что такое бин Spring

Любой объект управляемый контейнером.

к оглавлению

68 Каков жизненный цикл бина Spring

Спринг бины инициализируются при инициализации котейнера. И происходит внедрение зависимостей. Для выполнения действии перед инициализацией или удалением:

  • @PreDestroy
  • @PostConstruct

к оглавлению

69 Spring security

Spring security

к оглавлению

70 JWT

Традиционный подход аутентификации (подтверждение личности пользователя) основан на cookies. При авторизации пользователю выдаются session cookies. Сопоставление cookies и пользователя хранится на сервере. Допустим у нас есть несколько серверов. Пользователь авторизовался на одном и получил cookies, а следующий запрос ушел на другой сервер где сопоставления с этими cookies нет, то возникает проблема.

Для решения этой проблемы придумали jwt token.

JWT состоит из:

  • header {"alg":"HS256"}
  • payload (данные пользователя)
  • signature (подпись)

При ассимитричном шифровании на сервере авторизации хранится закрытый ключ которым подписывается токен, а на серверах приложений публичный ключ которым верифицируется подпись. Так же jwt токен периодически обновляется.

к оглавлению

71 Монолит vs микросервисы

Монолит

Плюсы:

  • Простота разработки, запуска и деплоимента
  • Простота отладки и трассировки
  • Простота тестирования

Минусы:

  • Долгий запуск и билд
  • Ограничение при скейлинге
  • Сложность внедрения новых технологий

Микросервисы

Плюсы:

  • Разделение ответственности
  • Возможность использовать разные технологии
  • Возможность скейлинга за счет запуска новых инстансов
  • Работоспособность при отказе нескольких микросервисов
  • Возможность регулировать нагрузку (запуск дополнительных инстансов)

Минусы:

  • Сложность деплоя
  • Сложность трассировки и тестирования
  • Необходимость вносить изменения в несколько микросервисов при добавлении функционала. Надо следить за версиями при деплое.

к оглавлению

72 Паттерны микросервисов

Api Gateway

Распределение внешних запросов по микросервисам

Service Discovery

Определение и регистрация новых инстансов

Database per service

Keep each microservice’s persistent data private to that service and accessible only via its API. A service’s transactions only involve its database.

There are a few different ways to keep a service’s persistent data private. You do not need to provision a database server for each service. For example, if you are using a relational database then the options are:

  • Private-tables-per-service – each service owns a set of tables that must only be accessed by that service
  • Schema-per-service – each service has a database schema that’s private to that service
  • Database-server-per-service – each service has it’s own database server.

There are various patterns/solutions for implementing transactions and queries that span services:

  • Implementing transactions that span services - use the Saga pattern.
  • Implementing queries that span services: ++ API Composition - the application performs the join rather than the database. For example, a service (or the API gateway) could retrieve a customer and their orders by first retrieving the customer from the customer service and then querying the order service to return the customer’s most recent orders. ++ Command Query Responsibility Segregation (CQRS) - maintain one or more materialized views that contain data from multiple services. The views are kept by services that subscribe to events that each services publishes when it updates its data. For example, the online store could implement a query that finds customers in a particular region and their recent orders by maintaining a view that joins customers and orders. The view is updated by a service that subscribes to customer and order events.

API Composition

Implement a query by defining an API Composer, which invoking the services that own the data and performs an in-memory join of the results. Drawbacks: Some queries would result in inefficient, in-memory joins of large datasets.

CQRS - Command and Query Responsibility Segregation

CQRS – подход проектирования программного обеспечения, при котором код, изменяющий состояние, отделяется от кода, просто читающего это состояние.

Основная идея CQS в том, что в объекте методы могут быть двух типов:

  • Queries: Методы возвращают результат, не изменяя состояние объекта. Другими словами, у Query не никаких побочных эффектов.
  • Commands: Методы изменяют состояние объекта, не возвращая значение.

You have applied the Microservices architecture pattern and the Database per service pattern. As a result, it is no longer straightforward to implement queries that join data from multiple services. Also, if you have applied the Event sourcing pattern then the data is no longer easily queried.

Solution: Define a view database, which is a read-only replica that is designed to support that query. The application keeps the replica up to data by subscribing to Domain events published by the service that own the data.

Event sourcing

The command must atomically update the database and send messages in order to avoid data inconsistencies and bugs. However, it is not viable to use a traditional distributed transaction (2PC) that spans the database and the message broker The database and/or the message broker might not support 2PC. And even if they do, it’s often undesirable to couple the service to both the database and the message broker.

Problem: How to atomically update the database and send messages to a message broker?

Solution: A good solution to this problem is to use event sourcing. Event sourcing persists the state of a business entity such an Order or a Customer as a sequence of state-changing events. Whenever the state of a business entity changes, a new event is appended to the list of events. Since saving an event is a single operation, it is inherently atomic. The application reconstructs an entity’s current state by replaying the events.

Retry pattern

Determine the number of retry attempts and interval between it. Ensure that you design each step as an idempotent operation.

Timeout Pattern

The Timeout pattern involves setting a maximum time for an operation or request to complete. If the operation exceeds this time limit, it is terminated, and the system can take appropriate action, like retrying the request, logging an error, or notifying the user.

  • Prevent System Freezes: Avoids the application getting stuck waiting for a response.
  • Improve User Experience: Ensures users aren’t waiting indefinitely for an action to complete.
  • Enhance System Reliability: Protects the system from overloads caused by slow or unresponsive external services.

Circuit Breaker

Problem: How to prevent a network or service failure from cascading to other services?

Solution: A service client should invoke a remote service via a proxy that functions in a similar fashion to an electrical circuit breaker. When the number of consecutive failures crosses a threshold, the circuit breaker trips, and for the duration of a timeout period all attempts to invoke the remote service will fail immediately. After the timeout expires the circuit breaker allows a limited number of test requests to pass through. If those requests succeed the circuit breaker resumes normal operation. Otherwise, if there is a failure the timeout period begins again.

The CircuitBreaker is implemented via a finite state machine with three normal states: CLOSED, OPEN and HALF_OPEN and two special states DISABLED and FORCED_OPEN.

Bulkhead Pattern

This pattern is used to prevent the cascading failure of a system by partitioning it into isolated parts or “bulkheads.” The bulkheads act as a barrier between different parts of the system, limiting the damage that can be caused by any one component. It does this by partitioning system resources, such as threads or connections, into separate pools that can be allocated to specific components. This prevents a single component from monopolizing resources and causing a system-wide failure.

Saga pattern

Problem: How to implement transactions that span services? A saga is a sequence of local transactions. Each local transaction updates the database and publishes a message or event to trigger the next local transaction in the saga. If a local transaction fails because it violates a business rule then the saga executes a series of compensating transactions that undo the changes that were made by the preceding local transactions.

There are two ways of coordination sagas:

  • Choreography - each local transaction publishes domain events that trigger local transactions in other services
  • Orchestration - an orchestrator (object) tells the participants what local transactions to execute

basic

An e-commerce application that uses this approach would create an order using a choreography-based saga that consists of the following steps:
  • The Order Service receives the POST /orders request and creates an Order in a PENDING state
  • It then emits an Order Created event
  • The Customer Service’s event handler attempts to reserve credit
  • It then emits an event indicating the outcome
  • The OrderService’s event handler either approves or rejects the Order

basic

An e-commerce application that uses this approach would create an order using an orchestration-based saga that consists of the following steps:

  • The Order Service receives the POST /orders request and creates the Create Order saga orchestrator
  • The saga orchestrator creates an Order in the PENDING state
  • It then sends a Reserve Credit command to the Customer Service
  • The Customer Service attempts to reserve credit
  • It then sends back a reply message indicating the outcome
  • The saga orchestrator either approves or rejects the Order

SAGA Pattern vs 2 Phase Commit

The Two-Phase Commit (2PC) protocol is a distributed algorithm used to ensure that all nodes in a distributed system agree to commit or abort a transaction.

2PC works by coordinating transactions between a coordinator node and multiple participant nodes. The coordinator sends a request to the participants to prepare for the transaction, and once all participants respond with a positive acknowledgement, the coordinator sends a commit message to each participant to commit the transaction.

2PC guarantees that all nodes will commit or abort a transaction, but it can be slow and vulnerable to failure. The 2PC protocol is useful in situations where all participants of the distributed transaction must commit or roll back the transaction together. It ensures atomicity and consistency of the transaction but can lead to blocking and performance issues in highly distributed systems.

The main disadvantage is that 2PC is a blocking protocol: the other servers need to wait for the transaction manager to issue a decision about whether to commit or abort each transaction. If the transaction manager goes offline while transactions are waiting for its final decision, they will be stuck and hold their database locks until the transaction manager comes online again and issues its decision. This extended holding of locks may be disruptive to other applications that are using the same databases

In contrast, SAGA pattern is useful in situations where the transaction is too large to be managed by a single 2PC protocol. SAGA breaks the transaction down into smaller, local transactions that can be independently managed by each microservice.

Transactional outbox

How to atomically update the database and send messages to a message broker?

The solution is for the service that sends the message to first store the message in the database as part of the transaction that updates the business entities. A separate process then sends the messages to the message broker.

The participants in this pattern are:

  • Sender - the service that sends the message
  • Database - the database that stores the business entities and message outbox
  • Message outbox - if it’s a relational database, this is a table that stores the messages to be sent. Otherwise, if it’s a + NoSQL database, the outbox is a property of each database record (e.g. document or item)
  • Message relay - sends the messages stored in the outbox to the message broker

The Message relay might publish a message more than once. It might, for example, crash after publishing a message but before recording the fact that it has done so. When it restarts, it will then publish the message again. As a result, a message consumer must be idempotent

к оглавлению

73 CAP Theorem

The CAP Theorem states that a distributed system can only meet 2 of 3 properties.

CAP is an abbreviation of Consistency, Availability, and Partition tolerance. Let’s discuss these three concepts in simple words:

  • Consistency means that every read operation will result in getting the latest record. All the information is guaranteed to be up to date.
  • Availability is a property that indicates a distributed system will always be available. One or more nodes of such a system might turn off; however, the system will still be accessible through other nodes.
  • Partition tolerance represents the ability of the system to be partitioned. Thus it means that every node can work independently from the other ones.

к оглавлению

74 Kubernetes

Механизм для оркестрации контейнеров Kubernetes, in short, is a system for orchestration of containerized applications across a cluster of nodes, including networking and storage infrastructure. Some of the most important features are:

  • Resource scheduling: it ensures, that Pods are distributed optimally over all available nodes
  • Auto-scaling: with increasing load, the cluster can dynamically allocate additional nodes, and deploy new Pods on them
  • Self-healing: the cluster supervises containers and restarts them, if required, based on defined policies
  • Service-discovery: Pods and Services are registered and published via DNS
  • Rolling updates/rollbacks: supports rolling updates based on sequential redeployment of Pods and containers
  • Secret/configuration management: supports secure handling of sensitive data like passwords or API keys
  • Storage orchestration: several 3rd party storage solutions are supported, which can be used as external volumes to persist data

Basic Objects

  • Pod is a basic unit that Kubernetes deals with. It encapsulates one or more closely related containers, storage resources, a unique network IP, and configurations on how the container(s) should run, and thereby represents a single instance of an application.
  • Service is an abstraction which groups together logical collections of Pods and defines how to access them. Services are an interface to a group of containers so that consumers do not have to worry about anything beyond a single access location.
  • Volumes, containers can access external storage resources (as their file system is ephemeral), and they can read files or store them permanently. Volumes also support the sharing of files between containers. A long list of Volume types is supported.
  • With Namespaces, Kubernetes provides the possibility to run multiple virtual clusters on one physical cluster. Namespaces provide scope for names of resources, which have to be unique within a namespace.

Object metadata

Mandatory attributes are:

  • Each object must have a Namespace (we already discussed that before). If not specified explicitly, an object belongs to the default Namespace.
  • A Name is a unique identifier for an object in its Namespace.
  • A Uid is a value unique in time and space. It helps to distinguish between objects, which have been deleted and recreated.

Service Types

Service Type Use Case Accessibility Resource Allocation
ClusterIP Internal communication between application components Within the cluster only Minimal resources needed
NodePort External accessibility for web applications or APIs Accessible from outside the cluster via a high-numbered port on the node Additional resources needed
LoadBalancer Production environments with high traffic volumes Accessible from outside the cluster via a load balancer Significant resources needed

Ingress vs. Load Balancer

A Kubernetes service is an abstraction that exposes a group of pods as a network service. The service handles all the complexity of identifying running pods and their IP addresses. Every service gets a unique URL that is accessible across the cluster. So instead of using IPs to communicate, pods simply need to use the provided service URLs.

By default, a Kubernetes service is private to the cluster. This means only applications inside the cluster can access them. There are a number of ways around this, and one of the best is an ingress.

In Kubernetes, an ingress lets us route traffic from outside the cluster to one or more services inside the cluster. Typically, the ingress works as a single point of entry for all incoming traffic. Perhaps the most popular is the nginx ingress controller.

к оглавлению