NB: это очень плохая "студенческая" архитектура
java, db, nosql, distributed
Архитектура: Router - точка входа для клиентов, смотрит хеш id, направляет в нужный шард. развёртывает всю систему
Shard - смотрит тип запроса, направляет либо на Master, либо Slaver (см. ниже). развёртывает Slaver и Master
Slaver - отвечает за чтение. в себе несёт основной in-memory кеш шарда. если нет в кеше - направляет в самый свободный из Slave. равзёртывает Slave. Если пришла запись от мастера - кладёт себе в кеш, говорит мастеру ОК и ассинхронно отправляет запись всем слейвам
DBServer - в зависимсоти от параметра может быть Slave или Master. Пишет в базу. Имеет небольшой кеш для оптимизации частой записи
если Master - после записи пересылает Request на Slaver
Каждый из этих серверов имеет внутри объект класса Node - содержит общие компоненты сервера. развёртывает HttpServer. По хорошему нужно было от него отнаследоваться, но из-за того, что сервера запускаются из static main - сделать абстрактным его не получилось (да и пытался плохо)
так же имеет внутренний класс, отнаследованный от AbstractHandler, который занимается работой с байтиками, и имеет абстрактный метод Response processRequest(Request);
Request имеет адрес назначения, параметры-хедеры и Serializable данные Response имеет код ответа и данные
Как это работает:
Router (а точнее Node внутри него) пытается развернутся на 2300 порту (если порт занят, то делает ++ и пытается снова), разворачивает за собой Shard'ы, которые в свою очередь разворачивают остальные сервера
Запуск нового сервера идёт через служебный класс Launcher, он запускает отдельный java-процесс, работает только через bash
К роутеру можно обратится GET, PUT, DELETE запросами. если работа с данными - в хедере Id со значением, в боди данные (Кстати, в данные можно кидать как сериализумый объект в байтах, либо обычную строку, система распознает что есть что). Если команда, то Command и команда (на момент написания реализована только команда add_shard)
Роутер в запросе делает пометку, что запрос прошёл через него и посылает на следующие сервера. Если пометки нет, то сервера пишут "доступ извне закрыт"
AbstractHandler заботливо дополняет запрос названием сервера, можно отслеживать перемещение
Что я не успел сделать: Нормальный клиент и нормальный конфиг. Сейчас количество шардов и слейвов на шарде задаётся в момент компиляции;
Защита от падений мастера. Только некоторые архитектурые намётки под это дело. Однако, при моей архитектуре, потеря данных возможна только в том случае, если падает мастер и слейвер в момент между записью в кеш слейвера и началом асинхронной записи на слейвов. Осталось только реализовать передачу слейву свойств мастера;
Устойчивое хеширование. есть мысли как, но не успел;
На момент написания перехеширование под добавление нового шарда работает кривовато;
Журналирования нет;
Нормального завершения работы системы. После запуска у вас останутся висеть сервера, и убить их можно будет только киллом процесса :(
Что сделано, но я вижу, что не оптимально:
По прежнему неоптимальная запись в файл - файл переписывается заново при записи. Понятно как это реализовать, но не было времени;
Нет удерживания соединений (не знаю как это реализовать);
Можно сделать прямые соединения роутер->мастер и роутер->слейвер;
Код осознано оптимзирован под учебную задачу (всё крутится на 1 компьютере), под несколько компьютеров его конечно нужно менять