Углубленное программирование
на JavaЛекция 3
«Message System»
Виталий Чибриков
1. Frontend и Account Service
2. Concurrent Collections
3. Message System
План лекции
2
UserSession
3
UserSession содержит:
Каждому, кто пришел на сервер - UserSession
Каждой UserSession – sessionId (из HttpSession)
String sessionId
String userName
Long userId
На Frontend-е
Map<String, UserSession> sessionIdToSession;
В одном потоке
4
Назначаем Id для пользовательской сесcии
В методе handle() спрашиваем у Accounter userId по имени
Создаем объект Accounter, который будет скрывать авторизацию
Ждем пока Accounter прочитает эти данные из файла или базы
Создаем на основе сесcии страницу и отдаем ее браузеру
Сохраняем в объекте сессии данные о пользователе
Спрашиваем у пользователя имя
Авторизация
Временная диаграмма
Авторизация
5
Frontend и Account Service
6
Frontend создает пользовательскую сессию
Frontend возвращает страницу созданную на основе сесcии в браузер
Frontend запрашивает у Account Service данные по авторизации
Когда данные приходят, Frontend меняет состояние сессии
Разведем работу с пользователем и Accounter по разным потокам
Frontend ― поток который работает с пользователями
Account Service ― поток который работает с авторизацией
Состояния
7
Ответ 2: sessionId + «Ждите авторизации»
Запрос 0: Первый запрос страницы.
Запрос 2: sessionId
Запрос 3: sessionId
Ответ 3: sessionId + «Ваше имя» + userName + « ваш Id: » + userId
Ответ 0: sessionId + «Введите имя»
Запрос 1: sessionId + Имя
Ответ 1: sessionId + «Ждите авторизации»Запрос на AccountServer
Ответ не пришел
Ответ пришел
Frontend и Account Service
Решение в 2 потока
8
1. Frontend и Account Service
2. Concurrent Collections
3. Message System
План лекции
9
Atomic
10
java.util.concurrent.atomic
AtomicBoolean
AtomicInteger
AtomicLong
Реализованы без использования synchronized
public final int incrementAndGet() {while (true) {
int current = get(); //get() возвращает текущее значение (volatile)int next = current + 1;if (compareAndSet(current, next))
return next;}
}public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}
optimistic locking
Synchronized vs. Concurrent
11
Concurrent ― предназначена для работы с несколькими потоками,но не синхронная (без использования synchronized)
ConcurrentHashMap ― concurrent
java.util.Hashtable ― synchronized
synchronized ― гарантия, что только один поток работает с элементом
Concurrent ― разрешено одновременное чтение и безопасная запись
Concurrent Collections
12
CopyOnWriteArrayList копирование при вставке в ArrayList
CopyOnWriteArraySet Set интерфейс над CopyOnWriteArrayList
ConcurrentHashMap thread safe HashMap
ConcurrentSkipListMap ключи уникальны и отсортированы
ConcurrentSkipListSet set на базе ConcurrentSkipListMap
Контейнеры безопасные для многопоточного доступа
Очереди безопасные для многопоточного доступа
Concurrent Queues
13
BlockingQueue очередь с ограничениме размера
ConcurrentLinkedQueue thread safe очередь
LinkedBlockingQueue
ArrayBlockingQueue
BlockingDeque двухсторонняя «очередь»
ArrayBlockingDeque
1. Frontend и Account Service
2. Concurrent Collections
3. Message System
План лекции
14
Thread-local объекты
Основная идея
Обмен сообщениями
15
Один поток кладет сообщение в коллекцию
Второй поток достает сообщение и исполняет его
Thread-Safe коллекции
Безопасная работа с элементами коллекции
Оптимальная работа
Объекты на которые есть ссылки только из одного потока
MessageSystem ―объект для обмена данными
Message System
16
Одна система сообщений на процесс
Единственный объект доступный из нескольких потоков
По одной очереди сообщений на поток
Каждый поток берет свою очередь из потока и выполняет сообщения
Каждый поток имеет свой адрес
Из любого места потока можно положить сообщение в очередь по адресу
Обмен сообщениями
17
FrontendAccount Service
Account Queue
Frontend Queue
MsgToAccountService MsgToFrontend
MessageSystem
Address и Abonent
18
public class Address {static private AtomicInteger abonentIdCreator = new AtomicInteger();final private int abonentId;
public Address(){this.abonentId = abonentIdCreator.incrementAndGet();
}
public int hashCode() {return abonentId;
}}
public interface Abonent {Address getAddress();
}
Message
19
public abstract class Msg {final private Address from;final private Address to;
public Msg(Address from, Address to){this.from = from;this.to = to;
}
protected Address getFrom(){return from;
}
protected Address getTo(){return to;
}
public abstract void exec(Abonent abonent);}
Message to Account Service
20
public abstract class MsgToAS extends Msg{
public MsgToAS(Address from, Address to) {super(from, to);
}
void exec(Abonent abonent) {if(abonent instanceof AccountService){
exec((AccountService) abonent);}
}
abstract void exec(AccountService accountService);}
Message to Account Service
21
public class MsgGetUserId extends MsgToAS {private String name;private String sessionId;
public MsgGetUserId(Address from, Address to, String name, String sessionId) {super(from, to);this.name= name;this.sessionId = sessionId;
}
void exec(AccountService accountService) {Long id = accountService.getUserId(name);Msg back = new MsgUpdateUserId(getTo(), getFrom(), sessionId, id);accountService.getMessageSystem(). sendMessage(back);
}}
Иерархия сообщений
22
Msg
MsgToAS MsgToFrontend
MsgUpdateUserIdMsgGetUserId
- Address from
- Address to
- String name
- Integer sessionId
- Integer sessionId
- Integer userId
Message System
23
private Map<Address, ConcurrentLinkedQueue<Msg>> messages = new HashMap<Address, ConcurrentLinkedQueue<Msg>>();
public void sendMessage(Msg message){Queue<Msg> messageQueue = messages.get(message.getTo());messageQueue.add(message);
}
public void execForAbonent(Abonent abonent) {Queue<Msg> messageQueue = messages.get(abonent.getAddress());while(!messageQueue.isEmpty()){
Msg message = messageQueue.poll();message.exec(abonent);
}}
MessageSystem ничего не знает о Frontend и AccountService
Все что нужно MessageSystem это Address, Abonent и Msg
Можно добавлять дополнительные сервисы
Абстракция
24
Address Service
25
Часть Message System которая знает адреса абонентов
Может вернуть адрес Account сервиса и Frontend
Производит балансировку, если сервисов несколько
Address Service
26
public class AddressService {private Address accountService;
public Address getAccountService() {return accountService;
}
public void setAccountService(Address accountService) {this.accountService = accountService;
}}
AddressService можно хранить в MessageSystem
Address аккаунт сервера для пользователя можно хранить в UserSession
Обмен сообщениями
27
Метод run()
28
public void run() {while (true) {messageSystem.execForAbonent(this);
Thread.sleep(TICK_TIME);}
}
Демонстрация кода
29
Переключаемся на код и смотрим как он работает
Спасибо за внимание
Виталий Чибриков[email protected]
Recommended