ТЗ на файловый сервер
h2. Решаемые проблемы
- При интенсивной работе с файлами по сети возникают задержки при открытии файлов. Они вызваны проверками прав доступа, антивирусами итд. На данный момент не видно другого решения проблемы.
- Права доступа к файловому хранилищу. Если мы обращаемся к файлам только через свой сервис, уходит проблема с тем, что у пользователя есть права на запись в файловом хранилище, что небезопасно.
- Резервирование. Возможность перенаправить все обращения на другой сервер.
Используемые модули¶
TCP_MANAGER - поддержка TCP
BASE_SERVICE - windows service
DBG_LOG - Модуль записи отладочных логов, настройка, какие логи писать
PARAM_FILE - Хранение настроек. Редактирование настроек.
NC_CONTAINER - дерево параметров аналогично XML
Новые модули¶
Пишем два модуля: серверный и клиентский. Связь между модулями осуществляется по TCP. Запрос и ответ передается в виде NC_CONTAINER. Возможно также использование XML запросов благо конвертация в обе стороны не проблема. Сервер обрабатывает запросы клиента на предмет работы с файлами. В дальнейшем появятся и другие запросы.
Резервирование¶
- Серверные модули могут работать в паре (основной и резервный). Каждый на своем сервере.
- Между ними есть прямое сетевое соединение с использованием отдельных сетевых карт и без использования switch’а для повышения надежности.
- В каждый момент времени активен (обрабатывает запросы клиентов) только один из серверов.
- В случае обрыва соединения между серверами активен резервный сервер.
- Автоматически может активизироваться только резервный сервер. В случае обрыва соединения с основным или его выходе из строя.
- Основной сервер возвращается в активное состояние по явной команде (иначе можно получить ситуацию, когда они по очереди активизируются, что нехорошо).
- При нормальной работе активный модуль обрабатывает запросы клиентов, а резервный резервирует на свой сервер содержимое хранилища.
- При запуске клиентский модуль коннектится к обоим серверам и выясняет кто из них активен.
- При получении от сервера с которым он работал сообщения, что сервер больше не активен или обрыва соединения, клиент запрашивает оба сервера кто из них активен до получения ответа от одного из них, что тот активен, после чего работает с активным сервером. Вообще клиент посылает запросы только активному серверу.
Серверный модуль¶
- TCP server
- Windows service
- Допускает одновременное подключение значительного количества клиентов (несколько тысяч).
- По запросу клиента выполняет любую файловую операцию (!CreateFile, ReadFile, WriteFile, CloseHandle, LockFile etc)
- Нотификация клиентов об изменениях файлов в каталогах
- Для одного клиента допускается передача нового запроса до окончания обработки предыдущего. Это не касается случаев запросов к одному файлу по одному хэндлу.
- Ответ на более поздний запрос может быть отправлен раньше.
- Запросы выполняются параллельно. То есть, есть некоторое кол-во потоков. Каждый новый запрос передается свободному потоку (если таковой есть).
- Режим кеширования. В этом режиме доступ к хранилищу осуществляется только через серверный модуль. в этом случае допускается кэширование файлов на чтение и запись (настраивается)
- Резервирование. Серверные модули могут работать в паре (активный и резервный). Каждый на своем сервере. Соединяются по TCP. При нормальной работе активный модуль обрабатывает запросы клиентов, а резервный резервирует на свой сервер содержимое хранилища. После восстановления рухнувшей половинки, она становится резервной. В общем, система во многом похожа на кластер.
- Логи. Настраиваемые. Пишет все операции, длительные операции, ошибки. Для записи логов используем модуль DBG_LOG.
- Допускается подключение через инет. То есть одно медленное соединение не влияет на другие.
- Для хранения и редактирования настроек (в том числе системы записи логов) используем модуль PARAM_FILE. Пример использования модуля записи логов совместно с редактором настроек можно посмотреть в VIDEO_SERVICE’е
Клиентский модуль¶
- TCP client
- Используется в джине. Вся работа с файлами в хранилище осуществляется через этот модуль.
- Режим, в котором модуль вместо обращения к серверному модулю выполняет все операции сам.
- Резервирование. Клиент соединяется с активным и резервным серверными модулями. В случае проблем запросы перенаправляются.
- Логи. Настраиваемые. Пишет все операции, длительные операции, ошибки.
__Протокол клиент-сервер
* В качестве запроса или ответа всегда используется NC_CONTAINER
* В нем есть обязательные поля.
* ID запроса - уникальный идентификатор запроса-ответа генерируется клиентом для запроса. Для ответа берется из запроса.
* Код операции (строчный, осмысленный) - генерируется клиентом. Копируется в ответ сервером.
- И необязательный - параметры, зависящие от кода операции.
- При использовании XML протокола, сразу по получении сервером XML запроса он конвертируется в NC_CONTAINER. Ответ конвертируется в XML непосредственно перед отправкой
Примечания
- Клиентский модуль должен работать совместно с системой резервирования файлов. Если есть резервная копия на локальном диске работает с ней. Алгоритм взаимодействия с системой резервирования указывается при запросе на открытие файла. Учесть при внедрении клиентского модуля в джин.
Open points:
- нужно более детальное описание требований. Что такое "резервная копия на локальном диске"? Структура каталогов/файлов, совпадающая с лежащей удаленно на сервере, или что-то иное? Пока непонятно.
Поддерживаемые файловые операции¶
Вместо HANDLE используем некоторый собственный класс. У класса клиента должны быть методы полностью аналогичные по параметрам функциям API.
FILER_FILE_HANDLE FILER_FILE_HANDLE CreateFile();
bool CloseHandle(FILER_FILE_HANDLE handle);
bool ReadFile();
bool WriteFile();
!+int64 SetFilePointer();
bool LockFileEx();
bool UnlockFileEx();
FindFirstFile()
FindNextFile()
FindClose()
GetFileAttributesEx()
CreateDirectory();
DeleteFile();
MoveFile();
MoveFileEx();
GetFileSize();
SetEndOfFile();
FlushFileBuffers();
FindFirstChangeNotification();
ReadDirectoryChangesW();
поддержка overlapped операций нужна и используется для следующих функций:
FindFirstChangeNotification();
ReadDirectoryChangesW();
Список будет расширяться. Хочется, чтобы добавление новой функции не было большой проблемой с точки зрения программирования. То есть минимизировать кол-во мест, куда нужно внести изменения.
Open points:
- уточнить смысл фразы "в случае проблем запросы перенаправляются". я думаю, что должно быть строгое деление серверов на активный и резервный. попытка использовать стратегию "выполнить запрос на резервном сервере если он не прошел на активном" для части запросов принесет больше проблем, чем пользы.
Open points:
- логины и аутентификация клиентов на сервере?
- механизм определения того, что сервер жив, варианты:
— периодическое пингование хостов по адресам указанным для активного и резервного серверов в конфигурации. Период?
— периодический обмен keep-alive сообщениями по отдельному сокету (один на сервер). Период?
— keep-alive сообщения в каждом клиентском соединении.
— общая стратегия: каждый клиент сам определяет, кто из серверов активный, кто резервный, или это делается как-то централизованно и для всех клиентов сразу? Как вариант: клиент коннектится к обоим серверам, и резервный сервер следит за тем, не упал ли активный. Когда упал, резервный переключает присоединенных клиентов на себя.
- Зачем обязательно нужно использовать XML, если протокол инкапсулирован внутри клиентского модуля и сервера, и приложениям все равно не виден? Можно было бы использовать что-то попроще, например, запрос в текстовой форме CSV или аналогичной в формате:
ID-запроса,операция,параметры,…,контрольная-сумма
аналогично для ответов сервера на запросы
Использование XML по-моему только увеличит объем передаваемого по сокетам и усложнит упаковку/распаковку запросов, и не даст никакого выигрыша (хотя, возможно, я просто чего-то не знаю про Джинн. - Л.М.)
- для длительных и/или асинхронных (overlapped) операций, сервер может посылать несколько ответов, то есть помимо финального ответа, завершающего транзакцию, есть еще промежуточные. С точки зрения клиентского интерфейса это означает, что должен быть способ извещения клиента о событиях - callback или что-то подобное.
Open points:
Файловая операция CreateFile()¶
прототип:
FILER_FILE_HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
);
Примечания:
аналог Win32 API имеет также параметры
LPSECURITY_ATTRIBUTES lpSecurityAttributes
HANDLE hTemplateFile
которые клиентским модулем не поддерживаются.
На серверной стороне поддерживается модель хранилища, аналогичная файловой системе с деревом каталогов. Открывать и получать хэндлы с помощью этой операции можно только для файлов и каталогов. Все другие способы употребления CreateFile в Win32 API не поддерживаются (тома и физические диски; магнитные ленты; коммуникационные порты; консоли; мэйлслоты и пайпы).
Параметры:
lpFileName — имя файла (поддерживается ANSI кодировка)
dwDesiredAccess
не поддерживаемые флаги: FILE_CREATE_PIPE_INSTANCE, FILE_EXECUTE, SYNCHRONIZE, STANDARD_RIGHTS_EXECUTE, GENERIC_EXECUTE,
??GENERIC_READ, ??GENERIC_WRITE (содержат SYNCHRONIZE),
FILE_ALL_ACCESS??
open point: пересмотреть вообще этот параметр, в стиле RO/RW ??
dwShareMode
поддерживаются все флаги: FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE
dwCreationDisposition
поддерживаются все флаги: CREATE_ALWAYS, CREATE_NEW, OPEN_ALWAYS, OPEN_EXISTING, TRUNCATE_EXISTING
dwFlagsAndAttributes
не поддерживаются: FILE_FLAG_NO_BUFFERING, FILE_FLAG_OPEN_NO_RECALL, FILE_FLAG_OPEN_REPARSE_POINT, FILE_FLAG_POSIX_SEMANTICS, FILE_ATTRIBUTE_OFFLINE
Файловая операция CloseHandle()¶
прототип:
void CloseHandle( FILER_FILE_HANDLE )
Файловая операция ReadFile()¶
прототип:
int ReadFile(
in FILER_FILE_HANDLE hFile,
out LPVOID lpBuffer,
in DWORD nNumberOfBytesToRead,
out_opt LPDWORD lpNumberOfBytesRead
);
Примечания:
аналог Win32 API имеет также параметры LPOVERLAPPED lpOverlapped которые клиентским модулем не поддерживаются.
Возвращаемое значение:
0 == ok, иначе код ошибки (Win32 API возвращает bool)
Файловая операция WriteFile()¶
прототип:
int WriteFile(
in FILER_FILE_HANDLE hFile,
in LPCVOID lpBuffer,
in DWORD nNumberOfBytesToWrite,
out_opt LPDWORD lpNumberOfBytesWritten
);
Примечания:
аналог Win32 API имеет также параметры LPOVERLAPPED lpOverlapped которые клиентским модулем не поддерживаются.
Возвращаемое значение:
0 == ok, иначе код ошибки (Win32 API возвращает bool)
Файловая операция LockFileEx()¶
Open points:
- используется ли блокировка файла частями?
- exclusive/shared?
- immediately?
- реализация не на основе Win32 API (на серверной стороне)?