Sesje

HTTP to podstawowy protokół komunikacyjny wykorzystywany w sieci Internet. Jego największą zaletą jest niewątpliwie prostota, która jednak komplikuje realizację niektórych zadań. Jednym z nich jest przekazanie informacji o użytkowniku pomiędzy kolejnymi żądaniami. Przez lata wymyślono kilka sposobów rozwiązanie tego problemu, a najpopularniejszy z nich to tzw. sesje. Składa się na niego wewnętrzny system do przechowywania danych wraz z identyfikatorem sesji (ang. session id, sid), który pozwala pobrać zapisane wcześniej dane sesyjne. O ile w przypadku identyfikatora obecnie standardem jest przekazywanie go pomiędzy kolejnymi żądaniami z wykorzystaniem mechanizmu ciasteczek (ang. cookies), o tyle rozwiązań wspomagających przechowywanie danych sesyjnych po stronie serwera jest wiele. Najprostsze z nich wykorzystują pliki zapisane na dysku twardym serwera, bazę danych albo pamięć operacyjną. Bardziej zaawansowane systemy łączą trwałość zapisu danych na dysku, z szybkością dostępu do pamięci RAM. W niniejszym artykule zostaną omówione wady i zalety dotychczasowego system obsługi sesji w Onet. oraz zostanie przedstawiony jego następca.

System sesji w Onet

Przez ostatnie lata w portalu funkcjonował mechanizm oparty o serwery aplikacyjne napisane w C++ i korzystające z Memcached oraz MySQL. Dane mogły być składowane w pamięci (co jest szybkie) albo, gdy wymagały większej trwałości, w bazie danych (ale to rozwiązanie było oczywiście dużo wolniejsze). Dodatkowo sesje były rozdzielone pomiędzy różne przestrzenie nazw, a cały system miał zapewniony mechanizm automatycznej zmiany konfiguracji w przypadku awarii jednego z serwerów, tak aby zapewnić ciągłość działania (ang. failover). Niestety oprócz problemów wydajnościowych borykaliśmy się z ograniczonymi możliwościami w zakresie skalowania, utrzymania i rozwoju, co było wynikiem dużego stopnia skomplikowania całego rozwiązania.

Nowe rozwiązanie

W czasie prac nad nowym rozwiązaniem sesyjnym przyjęliśmy założenie, że dane zawarte w sesjach użytkowników nie są krytyczne, zaś najważniejsza jest szybkość działania, skalowanie i łatwe utrzymanie. Swoją uwagę skupiliśmy na nowoczesnych nierelacyjnych systemach bazodanowych (tzw. NoSQL), które oferują dużą różnorodność i lepsze dopasowanie do konkretnych potrzeb.
Podczas pracy nad nowym systemem sesji zostały wzięte pod uwagę bazy: CouchDB, Project Voldemort, MongoDB, Redis.

Wybrane cechy porównywanych baz danych:

Voldemort CouchDB MongoDB Redis
Język Java Erlang C++ C
Trwałość danych poprzez pluginy:
BerkleyDB
Mysql
dysk: własny format dysk: własny format dysk: własny format, asynchroniczne zapisywanie zmian do append-only file
Model danych blob/text dokument,
JSON
dokument,
binarny JSON
klucz-wartość, lista, zbiór
Typ danych serializowany różne różne różne
Replikacja master-master
(trzeba obsługiwać
ew. konflikty)
master-master (trzeba obsługiwać
ew. konflikty)
master – slave (możliwa
replikacja master-master,
jednak problemy gdy
występują jednoczesne
zapisy)
master-slave
Partycjonowanie wbudowane nie faza alfa (2010) nie
Interfejs JAVA API REST natywne, biblioteki dla różnych języków natywne, biblioteki dla różnych języków
Zapewnienie
spójności
wersjonowanie,
read-repair
MVCC atomowe operacje na dokumentach jeden wątek bazy danych – wszystkie operacje są atomowe
Kto za tym stoi? LindedIn Apache 10gen Salvatore Sanfilippo (aka antirez), projekt sponsoruje vmware

Wydajność części rozwiązań była niezadowalająca, inne nie posiadały wyraźnej przewagi nad pozostałymi. Ostateczny wybór padł na Redis, wydajną bazę klucz-wartość przechowującą dane w pamięci. Redis zapewnia replikację, separację danych w ramach osobnych przestrzeni nazw oraz replikację danych na dysk twardy. Ważnym czynnikiem wyróżniającym Redisa na tle innych rozwiązań jest zastosowanie mechanizmu „append-only file”, tj. zapisywanie informacji o zmianach na końcu pliku, dzięki czemu jest w stanie zapewnić wysoką wydajność.

Mając wybrany mechanizm przechowywania danych przygotowaliśmy serwer aplikacyjny umożliwiający zarządzanie sesjami. Do komunikacji wykorzystaliśmy wzorzec REST, logikę zaimplementowaliśmy w języku Python z wykorzystaniem serwera Tornado, a dystrybucję zapytań na dwa serwery zapewnia nginx. Praca Redisa w trybie master-slave oraz skrypty przełączające ruch na działający serwer w przypadku awarii jednego z nich zapewniły nam odporność na awarie.

Ewolucja: Membase

Po pewnym czasie dały zauważyć się słabości nowego systemu. Redisem można było zarządzać tylko przez autorski interfejs, nie mieliśmy automatycznej klastrowalności. Aby zwiększyć wydajność systemu, po stronie tornado potrzebne by było napisanie warstwy zapewniającej partycjonowanie poziome – zapisywanie danych z tej samej przestrzeni nazw do różnych instacji bazy (ang. sharding).

Naprzeciw tym słabościom wychodzi Membase, baza klucz-wartość która ma możliwość zapisu danych na dysk (w przeciwieństwie do Memcached). Wspiera klastrowalność ze zmianą ilości serwerów w czasie działania, zapewnia odporność na awarię jednego z serwerów oraz ma gotowy interfejs administracyjny z monitoringiem wydajności. Implementacja pierwszej wersji systemu w języku Python pozwoliła na łatwą podmianę warstwy przechowywania danych na Membase.

Wydajność

Porównanie przepustowości(ilość zapytań na sekundę) systemów sesji, w pojedyńczej konfiguracji produkcyjnej, dla średniego obciążenia 25% procesorów:

Tornado z Redisem zapewnia o wiele lepszą wydajność niż bardziej skomplikowany system oparty o MySQL. Jednak nie należy wnioskować z tego, że Redis jest lepszą bazą niż MySQL. Te dwa systemy spełniają różne wymagania, ten oparty o relacyjną bazę danych ma dużo wyższe parametry bezpieczeństwa przechowywania danych. Sesje oparte o Redis dzięki mniejszej ilości operacji dyskowych mogą mieć wyższą wydajność.

Różnica w wydajności pomiędzy sesjami opartymi o Redis i Membase, również bierze się z różnych wymagań jakie spełniają te systemy. Architektura Membase pozwala na łatwe zwiększenie wydajności klastra oraz łatwą administrację systemem, przez co ma nieco mniejszą przepustowość. Można sobie wyobrazić sytuację, żeby podobne rozwiązanie zaimplementować dla Redisa, jednak nie wiadomo czy dałoby to większą wydajność, natomiast zwiększyłoby znacznie koszt całego projektu.

Podsumowanie

Ewolucja systemu sesji w Onet pokazuje, że nawet prosty funkcjonalnie system może stać się bardzo skomplikowany, jeżeli jest potrzeba zapewnienia odporności na awarię i łatwości administracji. Wymyślanie koła na nowo to dodatkowy koszt, lepiej skorzystać z gotowych rozwiązań OpenSource, wykorzystując elementy, które mają zapewniony ciągły rozwój. Integracja gotowych rozwiązań pozwoliła na szybkie zbudowanie nowego systemu sesji. Podział na moduły daje łatwość wymiany pojedynczego komponentu bez ingerowania w pozostałe części, dzięki czemu podmiana silnika bazy klucz-wartość była stosunkowo prostą do wykonania operacją. W wyniku ciągle prowadzonych prac optymalizacyjnych udało się zastąpić stary i skomplikowany system nowym, bardziej wydajnym, który dobrze się skaluje i jest łatwy w administracji.

 

Marcin Wróbel
programista