[[!meta title="Obsluha mnoha klientů"]] [[!toc]] Cíl === Hlavním cílem této úlohy je implementovat jednoduchou serverovou aplikaci a optimalizovat její výkon tak, aby zvládala obsluhu velkého množství klientů a požadavků. **Pro implementaci serveru si můžete zvolit jakoukoli platformu** (HW, OS, programovací jazyk, framework, …). Vedlejší cíle jsou naučit se pracovat s dokumentací a zdrojovými kódy použitých platforem (zejména těch založených na open source softwaru), aplikace znalostí nabytých na přednáškách a získání přehledu o výkonnosti jednotlivých platforem. Zadání úkolu ============ Vytvořte serverovou aplikaci komunikující protokolem [HTTP][], která bude sloužit k počítání unikátních slov v datech zaslaných klienty. Na server jsou kladeny následující požadavky: - Klienti posílají data pomocí [metody POST][POST] s cestou `/osp/myserver/data`. Data jsou ve formátu čistého textu (text/plain) v kódování UTF-8 komprimovaného metodou [gzip][gzip]. - Server obsahuje "čítač jedinečných slov". Při spuštění serveru je tento čítač roven 0. - Server si vede evidenci o slovech zaslaných v jednotlivých požadavcích a pro každé nové slovo, které ještě nemá v evidenci zvýší čítač jedinečných slov o jedna. - Na požadavek [GET][] s cestou `/osp/myserver/count` server odpoví aktuální hodnotou čítače jedinečných slov a tento čítač vynuluje. Hodnota se přenáší jako dekadické číslo v UTF-8 kódování. - Server musí zvládnou obsluhovat velké množství (cca 100) současně připojených klientů. [HTTP]: http://tools.ietf.org/html/rfc2616 [POST]: http://tools.ietf.org/html/rfc2616#section-9.5 [GET]: http://tools.ietf.org/html/rfc2616#section-9.3 [gzip]: https://tools.ietf.org/html/rfc1952 Příklad ------- Předpokládejme, že náš server běží na lokálním počítači (t.j. localhost) na portu 8080. Použijeme program `curl` k posílání požadavků serveru: curl localhost:8080/osp/myserver/data --data-binary @<(echo první pokus|gzip) curl localhost:8080/osp/myserver/data --data-binary @<(echo druhý pokus|gzip) curl localhost:8080/osp/myserver/count --output - Výstupem posledního příkazu bude řetězec "3". Místo posledního příkazu můžete použít i webový prohlížeč nasměrovaný na adresu . *Poznámka:* Syntaxe `<(příkaz...)` je tzv. *process substitution* v interpreteru `bash`. Jedná se o jeden z možných způsobů předání binárních dat na příkazové řádce. Výkon serveru ------------- Výkon serveru bude měřen testovacím programem, který bude serveru posílat větší množství dat a poté se zeptá na aktuální hodnotu čítače jedinečných slov. Bude se měřit celkový čas od odeslání prvního požadavku typu POST do příjmu hodnoty čítače jedinečných slov. Akceptována budou řešení jejichž **celkový čas bude dvakrát menší** než čas naměřený s níže uvedeným ukázkovým serverem bežícím na školním počítači v laboratoři. Ukázkový server =============== Zde uvádíme jednoduchý server napsaný v jazyce Python, který splňuje výše uvedené požadavky (kromě výkonnosti). Můžete ho použít jako referenci či startovací bod pro vaší implementaci. [[!format py """ #!/usr/bin/python3 from http.server import HTTPServer, BaseHTTPRequestHandler import gzip words = {} class OSPHTTPHandler(BaseHTTPRequestHandler): def do_POST(self): global words if self.path == "/osp/myserver/data": length = int(self.headers.get('Content-Length')) text = gzip.decompress(self.rfile.read(length)).decode("utf-8") for word in text.split(): words[word] = 1; self.send_response(204) # No Content self.end_headers() else: self.send_response(404) self.end_headers() def do_GET(self): global words if self.path == "/osp/myserver/count": self.send_response(200) self.send_header("Content-type", "text/plain") self.end_headers() self.wfile.write(str(len(words)).encode()) words = {} else: self.send_response(404) self.end_headers() class HTTPServerIPv6(HTTPServer): import socket address_family = socket.AF_INET6 # httpd = HTTPServer(('', 8080), OSPHTTPHandler) # Use if your system uses IPv4 by default httpd = HTTPServerIPv6(('', 8080), OSPHTTPHandler) # Use if your system uses IPv6 by default print("Listening on port", httpd.server_port) httpd.serve_forever() """]] Pokud se k tomuto ukázkovému serveru připojí mnoho klientů, bude "přetížen" a čas od času bude klienty odmítat. Takže prvním krokem vašeho řešení může být zjištění příčiny tohoto chování a její napravení. Testování a měření výkonu ========================= Pro testování použijte [odevzdávací systém](https://rtime.felk.cvut.cz/osp/cviceni/server/tester/), kam zadáte informace o vašem serveru a můžete ho nechat otestovat. Pět nejlepších studentů dostane 1 až 5 bonusových bodů. **Odevzdávací systém běží momentálně v testovacím provozu.** Je možné, že naměřené hodnoty budou horší, než jaké lze z vašeho serveru "vymáčknout".