Files
elte-ik-pti-bsc-zarovizsga/19. Osztott rendszerek és konkurens programozás/19. Osztott rendszerek és konkurens programozás.tex
2021-07-25 20:36:30 +02:00

843 lines
41 KiB
TeX

\documentclass[margin=0px]{article}
\usepackage{listings}
\usepackage[utf8]{inputenc}
\usepackage{graphicx}
\usepackage{float}
\usepackage[a4paper, margin=0.7in]{geometry}
\usepackage{amsthm}
\usepackage{amssymb}
\usepackage{fancyhdr}
\usepackage{setspace}
\onehalfspacing
\renewcommand{\figurename}{ábra}
\newenvironment{tetel}[1]{\paragraph{#1}}{}
\pagestyle{fancy}
\lhead{\it{PTI BSc Záróvizsga tételek}}
\rhead{19. Osztott rendszerek és konkurens programozás}
\title{\textbf{{\Large ELTE IK - Programtervező Informatikus BSc} \vspace{0.2cm} \\ {\huge Záróvizsga tételek}} \vspace{0.3cm} \\ 19. Osztott rendszerek és konkurens programozás}
\author{}
\date{}
\begin{document}
\maketitle
TODO: Csak a "C" van kidolgozva a "2018"-as nem!
\begin{tetel}{Osztott rendszerek és konkurens programozás}
\begin{itemize}
\item[] \textbf{A, C}: Folyamat fogalma, elosztott rendszerek tulajdonságai és felépítése, elnevezési rendszerek, kommunikáció, szinkronizáció, konzisztencia.
\item[] \textbf{B}: Feladatok specifikációja biztonsági és haladási feltételekkel, absztrakt párhuzamos program tulajdonságai, megoldás fogalma, nevezetes feladatok megoldása párhuzamos és elosztott programokkal.
\item[] \textbf{2018}: Szálkezelés. Ütemezés, kontextusváltás. Race condition. Szinkronizáció. Blokkoló műveletek. Memória (stack és heap) használata a szálakban. A konkurens programozás nyelvi eszközei. Szinkronizációhoz és kommunikációhoz használható adatszerkezetek.
\end{itemize}
\end{tetel}
\section{Folyamatok, szálak}
\noindent \textbf{Szál}: A szál (thread) a processzor egyfajta szoftveres megfelelője, minimális kontextussal. Ha a szálat
megállítjuk, a kontextus elmenthető és továbbfuttatáshoz visszatölthető.\\
\noindent \textbf{Folyamat}: A folyamat (process vagy task) egy vagy több szálat összefogó nagyobb egység. Egy folyamat
szálai közös memóriaterületen (címtartományon) dolgoznak, azonban különböző folyamatok nem látják egymás memóriaterületét.\\
\noindent \textbf{Kontextusváltás}: A másik folyamatnak/szálnak történő vezérlésátadás, így egy processzor több szálat/ folyamatot
is végre tud hajtani.\\
\noindent \textbf{Szál vs. folyamat}: A szálak közötti váltáshoz nem kell igénybe venni az oprendszer szolgáltatásait, míg
a folyamatok közötti váltásnál ahhoz, hogy a régi és új folyamat memóriaterülete elkülönüljön a memóriavezérlő (MMU)
tartalmának jó részét át kell írni, amihez csak a kernel szintnek van joga. A folyamatok létrehozása, törlése és a kontextusváltás
közöttük sokkal költségesebb a szálakénál.
\section{Elosztott rendszerek tulajdonságai és felépítése}
\noindent \textbf{Elosztott rendszer fogalma}: Az elosztott rendszer önálló számítógépek olyan összessége, amely kezelői számára
egyetlen koherens rendszernek tűnik.
\subsection{Az elosztott rendszer céljai, tulajdonságai}
Az elosztott rendszer céljai a következők:
\begin{itemize}
\item Távoli erőforrások elérhetővé tétele
\item Átlátszóság (transparency)
\item Nyitottság (openness)
\item Skálázhatóság
\end{itemize}
\subsubsection{Átlátszóság}
Az átlátszóság nem más, mint az erőforrásokkal kapcsolatos különböző információk elrejtése a felhasználó elől.
Az alapján, hogy mit rejtünk el, többféle fajtája létezik:
\begin{figure}[H]
\centering
\includegraphics[width=0.8\linewidth]{img/atlatszosag}
\caption{Az átlátszóság különböző típusai.}
\label{fig:atlatszosag}
\end{figure}
\subsubsection{Nyitottság}
A rendszer képes más nyitott rendszerek számára szolgáltatásokat nyújtani, és azok szolgáltatásait igénybe venni:
\begin{itemize}
\item A rendszerek jól definiált interfészekkel rendelkeznek.
\item Az alkalmazások hordozhatóságát (portability) minél inkább támogatják.
\item Könnyen elérhető a rendszerek együttműködése.
\end{itemize}
\noindent A nyitott elosztott rendszer legyen könnyen alkalmazható heterogén környezetben, azaz
különböző hardvereken, platformokon, programozási nyelveken.\\
\noindent \textbf{Implementálása}:
\begin{itemize}
\item Fontos, hogy a rendszer könnyen cserélhető elemekből álljon.
\item Belső interfészek használata, nem egyetlen monolitikus rendszer.
\item A rendszernek minél jobban paraméterezhetőnek kell lennie.
\item Egyetlen komponens megváltoztatása/cseréje lehetőleg minél kevésbé
hasson a rendszer más részeire.
\end{itemize}
\subsubsection{Skálázhatóság}
Többféle jelentése van, 3 fontos dimenzió:
\begin{enumerate}
\item méret szerinti skálázhatóság: a felhasználók és/vagy folyamatok száma
\item földrajzi skálázhatóság: a csúcsok közötti legnagyobb távolság
\item adminisztrációs skálázhatóság: az adminisztrációs tartományok száma
\end{enumerate}
Ezek közül a legtöbb rendszer a méret szerinti skálázhatóságot kezeli, ennek egy lehetséges megvalósítási
módja erősebb szerverek használata. A másik kettőt nehezebb kezelni.\\
\noindent Technikák a skálázhatóság megvalósítására:
\begin{itemize}
\item A kommunikációs késleltetés elfedése azzal, hogy a válaszra várás közben más tevékenységet végzünk. Ehhez
aszinkron kommunikáció szükséges.
\item Elosztás: az adatokat és számításokat több számítógép tárolja/végzi (pl. amit lehet, a klienssel számoltatunk ki,
elosztott elnevezési rendszerek használata, stb.)
\item Replikáció/cache-elés: Több számítógép tárolja egy adat másolatait
\end{itemize}
A skálázhatóságnak ára van. Több másolat fenntartása inkonzisztenciához vezethet (ha módosítjuk az egyiket, az eltérhet a többitől).
Ez globális szinkronizációval kikerülhető (minden egyes változtatás után az összes másolatot frissítjük), viszont a globális
szinkronizáció rosszul skálázódik. Emiatt sok esetben fel kell hagynunk a globális szinkronizációval, ez viszont bizonyos
mértékű inkonzisztenciát eredményez. Rendszerfüggő, hogy ez milyen mértékben megengedett. A cél az, hogy az inkonzisztencia mértéke
a megengedett szint alatt maradjon.
\subsection{Elosztott rendszerek típusai}
\noindent \textbf{Főbb típusok}:
\begin{itemize}
\item Elosztott számítási rendszerek:
\item Elosztott információs rendszerek
\item Elosztott átható rendszerek
\end{itemize}
\subsubsection{Elosztott számítási rendszerek}
Célja számítások végzése nagy teljesítménnyel.\\
\noindent \textbf{Cluster (fürt)}: Lokális hálózatra kapcsolt számítógépek összessége. Homogén rendszer (ugyanaz az oprendszer,
hardveresen hasonlóak), központosított vezérléssel (általában egy gépre).\\
\noindent \textbf{Grid (rács)} Nagyméretű hálózatokra is kiterjedhet, akár több szervezeti egységen is átívelhet. Heterogén
architektúra jellemzi.\\
\noindent \textbf{Cloud(felhő)}: Többrétegű architektúra: hardver, infrastruktúra, platform, alkalmazás.
\subsubsection{Elosztott információs rendszerek}
Az elsődleges cél általában adatok kezelése, illetve más információs rendszerek elérése. Például tranzakciókezelő rendszerek.
A tranzakció adatok összességén (pl. egy adatbázison, adatbázis objektumon, stb.) végzett művelet (lehetnek részműveletei).
A tranzakciókkal szemben az alábbi követelményeket szokás támasztani (ACID):
\begin{itemize}
\item Oszthatatlan, elemi (atomicity): Vagy a teljes tranzakció végbemegy minden részműveletével, vagy az
adattárház egyáltalán nem változik.
\item Konzisztens (consistency): Az adattárra akkor mondjuk, hogy érvényes, ha bizonyos, az adott adattárra
megfogalmazott feltételek teljesülnek. Egy tranzakció konzisztens, ha érvényes állapotot állít elő a tranzakció
végén.
\item Elkülöníthető, sorosítható (isolation): Egyszerre zajló tranzakciók olyan eredményt adnak, mintha
egymás után hajtódtak volna végre.
\item Tartósság (durability): Végrehajtás után az eredményt tartós adattárolóra mentjük, így az összeomlás esetén
visszaállítható.
\end{itemize}
\subsection{Elosztott rendszerek felépítése}
Alapötlet: A rendszer elemeit szervezzük logikai szerepük szerint különböző komponensekbe, és ezeket osszuk
el a rendszer gépein.\\
\subsubsection{Központosított architektúrák}
\noindent \textbf{Kliens-szerver modell}: Egyes folyamatok (szerverek) szolgáltatásokat ajánlanak, míg más folyamatok (kliensek)
ezeket a szolgáltatásokat szeretnék használni. A kliens kérést küld a szervernek, amire a szerver válaszol, így veszi igénybe
a szolgáltatást. A kliens és szerver folyamatok különböző gépeken lehetnek.
\subsubsection{Többrétegű architektúrák}
Az elosztott információs rendszerek gyakran három logikai rétegre (layer vagy tier) vannak tagolva:
\begin{itemize}
\item Megjelenítés: az alkalmazás felhasználói felületét alkotó komponensekből áll.
\item Üzleti logika: az alkalmazás működését írja le konkrét adatok nélkül
\item Perzisztencia: az adatok tartós tárolása
\end{itemize}
\subsubsection{Decentralizált architektúrák}
\noindent \textbf{Peer-to-peer (P2P)}: A csúcsok (peer-ek) között többnyire nincsenek kitüntetett szerepűek.\\
\noindent \textbf{Overlay hálózat}: A gráfban szomszédos csúcsok fizikailag lehetnek távol egymástól,
a rendszer elfedi, hogy a köztük lévő kommunikáció több gépen keresztül zajlik. A legtöbb P2P
rendszer overlay hálózatra épül.\\
\noindent \textbf{P2P rendszerek fajtái}:
\begin{itemize}
\item Strukturált P2P: A csúcsok által kiadott gráfszerkezet rögzített. A csúcsokat valamilyen struktúra
szerint overlay hálózatba szervezzük és a csúcsoktól az azonosítójuk alapján lehet szolgáltatásokat
igénybe venni. Pl.: elosztott hasítótábla (DHT).
\item Struktúrálatlan P2P: Az ilyen rendszerek igyekeznek véletlen gráfstruktúrát fenntartani. Mindegyik
csúcsnak csak részleges nézete van a gráfról. Minden $P$ csúcs időnként véletlenszerűen kiválaszt egy $Q$
szomszédot. $P$ és $Q$ információt cserélnek és elküldik egymásnak az általuk ismert csúcsokat.
\item Hibrid P2P: néhány csúcsnak speciális szerepe van
\end{itemize}
\noindent \textbf{Superpeer}: Olyan csúcs, aminek külön feladata van, pl. kereséshez index fenntartása, a hálózat
állapotának felügyelete, csúcsok közötti kapcsolatok létrehozása.
\section{Elnevezési rendszerek}
Az elosztott rendszerek entitásai a kapcsolódási pontjaikon (access point) keresztül érhetőek el. Ezeket távolról
a címük azonosítja, amely megnevezi az adott pontot.
Célszerű lehet az entitást a kapcsolódási pontjaitól függetlenül is elnevezni. Az ilyen nevek helyfüggetlenek (location
independent).\\
\noindent \textbf{Egyszerű név}: Nincs szerkezete, tartalmaz véletlen szöveg. Csak összehasonlításra használható.\\
\noindent \textbf{Azonosító}: Egy név azonosító, ha egy-egy kapcsolatban áll a megnevezett entitással, és ez
a hozzárendelés maradandó, azaz a név később nem hivatkozhat más egyedre.
\subsection{Strukturálatlan nevek}
\subsubsection{Egyszerű megoldások}
\noindent \textbf{Broadcasting}: Kihirdetjük az azonosítót a hálózaton. Az egyed visszaküldi jelenlegi címét.
Hátrányai:
\begin{itemize}
\item Lokális hálózatokon túl nem skálázódik.
\item A hálózaton minden gépnek figyelnie kell a beérkező kérésre.
\end{itemize}
\noindent \textbf{Továbbítómutató}: Amikor az egyed elköltözik, egy mutató marad utána az új helyére.
\begin{itemize}
\item A kliens elől el van fedve, hogy a szoftver továbbítómutató-láncot old fel.
\item A megtalált címet vissza lehet küldeni a klienshez, így a további feloldások gyorsabban mennek.
\item Földrajzi skálázási problémák:
\begin{itemize}
\item A hosszú láncok nem hibatűrőek.
\item A feloldás hosszú időbe telik.
\item Külön mechanizmus szükséges a láncok rövidítésére.
\end{itemize}
\end{itemize}
\subsubsection{Otthon alapú megoldások}
\noindent \textbf{Egyrétegű rendszer}: Az egyedhez tartozik egy otthon, ez tartja számon az egyed jelenlegi címét. Az
egyed otthoni címe (home address - HA) be van jegyezve egy névszolgáltatásba. Az otthon számon tartja a jelenlegi
címet (foreign address - FA). A kliens az otthonhoz kapcsolódik, onnan kapja meg a címet.\\
\noindent \textbf{Kétrétegű rendszer}: Az egyes környékeken feljegyezzük, hogy mely egyedek tartózkodnak a közelben.
A névfeloldás először ezt a jegyzéket vizsgálja meg és ha az egyed nincs a környéken, akkor kell az otthonhoz fordulni.
\subsubsection{Elosztott hasítótábla}
Elosztott hasítótáblát (DHT) készítünk, ebben csúcsok tárolnak egyedeket. Az $N$ csúcs gyűrű overlay szerkezetbe
van szervezve. Minden csúcshoz hozzárendelünk egy $m$ bites azonosítót, és mindegyik entitáshoz egy $m$ bites kulcsot
($N \leq 2^{m}$). A $k$ kulcsú egyed felelőse az az $id$ azonosítójú csúcs, amelyre $k \leq id$, és nincs köztük másik
csúcs. Ezt a csúcsot a kulcs rákövetkezőjének is szokás nevezni: $succ(k)$. Mindegyik $p$ csúcs egy $FT_{p}$ finger
table-t tárol $m$ bejegyzéssel: $FT_{p}[i] = succ (p+2^{i-1})$. Bináris (jellegű) keresést szeretnénk elérni, ezért
minden lépés felezi a keresési tartományt. A $k$ kulcsú egyed kikereséséhez (ha nem a jelenlegi csúcs tartalmazza)
a kérést továbbítjuk ahhoz a $j$ indexű csúcshoz, melyre $FT_{p}[j] \leq k < FT_{p}[j+1]$, illetve, ha
$p<k<FT_{p}[1]$, akkor is $FT_{p}[1]$-hez irányítjuk a kérést.
\begin{figure}[H]
\centering
\includegraphics[width=0.6\linewidth]{img/dht}
\caption{Példa DHT-re finger table-el.}
\label{fig:dht}
\end{figure}
\subsubsection{Hierarchikus módszerek}
\noindent \textbf{Hierarchical Location Services(HLS)}: A hálózatot osszuk fel tartományokra, és mindegyik tartományhoz
tartozzon katalógus. Építsünk hierarchiát a katalógusokból.\\
\noindent A csúcsokban tárolt adatok:
\begin{itemize}
\item Az $E$ egyed címe egy levélben található.
\item A gyökértől az $E$ leveléig vezető úton minden belső csúcsban van egy mutató a lefelé következő csúcsra
az úton.
\item Mivel a gyökér minden út kiindulópontja, minden egyedről van információja.
\end{itemize}
\noindent Keresés a fában: A kliens tartományából indul a keresés. Felmegyünk addig a fában, amíg olyan csúcshoz nem
érünk, amelyik tud $E$-ről, majd követjük a mutatókat a levélig, amely tudja $E$ címét. Mivel a gyökér minden
egyedet ismer, a terminálás garantált.\\
\noindent Beszúrás a fában: Ugyanaddig megyünk felfelé a fában, mint keresésnél, majd a belső csúcsokban mutatókat
helyezünk el.
\begin{figure}[H]
\centering
\includegraphics[width=0.8\linewidth]{img/hls_insert}
\caption{Beszúrás a fában HLS-nél.}
\label{fig:hls_insert}
\end{figure}
\subsection{Strukturált nevek}
\noindent \textbf{Névtér}: Gyökeres, irányított, élcímkézett gráf, a levelek tartalmazzák a megnevezett egyedeket,
a belső csúcsokat katalógusoknak vagy könyvtáraknak nevezzük. Az egyedhez vezető út címkéit összeolvasva
kapjuk az egyed egy nevét. A bejárt út, ha a gyökértől indul, abszolút útvonalnév, ha belső csúcsból indul,
relatív útvonalnév. Mivel egy egyedhez több út is vezethet, több neve is lehet.
\begin{figure}[H]
\centering
\includegraphics[width=0.8\linewidth]{img/nevter}
\caption{Példa névtérre.}
\label{fig:nevter}
\end{figure}
A névtér csúcsaiban (akár levélben, akár belső csúcsban) különféle attribútumokat is eltárolhatunk, pl. az egyed
típusát, azonosítóját, helyét/címét, más neveit, stb.\\
\noindent \textbf{Névfeloldás}: Kiinduló csúcsra van szükség a névfeloldás megkezdéséhez. A gyökér elérhetőségét
a név jellegétől függő környezet biztosítja, pl.:
\begin{itemize}
\item www.inf.elte.hu : egy DNS névszerver
\item /home/steen/mbox : a lokális NFS fájlszerver
\item 0031204447784 : a telefonos hálózat
\item 157.181.161.79 : a www.inf.elte.hu webszerverhez vezető út
\end{itemize}
\noindent \textbf{Névtér implementációja - DNS}: Ha nagy névterünk van, el kell osztani a gráfot a gépek között, hogy
hatékonnyá tegyük a névfeloldást és a névtér kezelését. Ilyen nagy névtér a DNS (Domain Name System).
\noindent A DNS névtérnek alapvetően 3 szintjét különböztetjük meg:
\begin{itemize}
\item Globális szint: Ide tartozik a gyökér és a felsőbb csúcsok (TLD-k, pl. országokhoz tartozó csúcsok - .hu, .uk, stb.).
A szervezetek ezt közösen kezelik.
\item Szervezeti szint: Egy-egy szervezet által kezelt csúcsok szintje (pl. elte.hu, stb.).
\item Kezelői szint: Egy adott szervezeten belül kezelt csúcsok (pl. elte.hu-n belüli csúcsok)
\end{itemize}
\begin{figure}[H]
\centering
\includegraphics[width=0.8\linewidth]{img/dns}
\caption{A DNS névtér egy része.}
\label{fig:dns}
\end{figure}
\noindent \textbf{A névfeloldás különöző megközelítései}: DNS névtér esetén alapvetően két különböző névfeloldási
megközelítést alkalmazunk:
\begin{itemize}
\item Rekurzív névfeloldás: A rekurzív névfeloldás során a névszerverek egymás között kommunikálva oldják fel
a neveket, a kliensoldali névfeloldóhoz rögtön a válasz érkezik.
\item Iteratív névfeloldás: A névfeloldást a gyökér névszerverek egyikétől indítjuk. Az iteratív névfeloldás
során a névnek mindig csak egy komponensét oldjuk fel, a megszólított névszerver az ehhez tartozó névszerver
címét adja vissza (ha a kliensoldali névfeloldó megkapja ezt a címet, a következő komponens feloldását ettől
a névszervertől kéri - ez addig megy, míg teljesen fel nem oldjuk a nevet).
\end{itemize}
\noindent \textbf{Skálázhatóság}: Mivel sok kérést kell kezelni rövid idő alatt, ezért a globális szint névszerverei
nagy terhelést kapnának. Mivel a felső szinteken a gráf ritkán változik, ezért az ezeken a szinteken található
csúcsok adatairól több szerveren is tarthatunk másolatot, így a keresést közelebbről indíthatjuk (pl. van
több gyökér névszerver, a hozzánk legközelebbihez fordulunk).
\subsubsection{Attribútumalapú nevek}
Az egyedeket sokszor kényelmes lehet tulajdonságaik (attribútumaik) alapján keresni, viszont ha bármilyen
kombinációban megadhatunk attribútumértékeket, akkor a kereséshez az összes egyedet érintenünk kell, ami
nem hatékony. \\
\noindent \textbf{X.500, LDAP}: A katalógusszolgáltatásokban az attribútumokra megkötések érvényesek (X.500 szabvány), amelyet
az LDAP protokollon keresztül szokás elérni. Az elnevezési rendszer fastruktúrájú, élei attribútum-érték párokkal címzettek.
Az egyedekre az útjuk jellemzői vonatkoznak, és további párokat is tartalmazhatnak.
\section{Kommunikáció}
\subsection{Köztesréteg}
A köztesrétegbe (middleware) olyan szolgáltatásokat és protokollokat szokás sorolni, amelyek sokfajta
alkalmazáshoz lehetnek hasznosak és alapvetően a rendszer egyedei közötti összekötő kapocsként szolgálnak.
\begin{itemize}
\item Kommunikációs protokollok
\item Sorosítás (szerializáció, marshalling), adatok reprezentációjának átalakítása
\item Elnevezési protokollok az erőforrások megosztásának könnyítésére
\item Biztonsági protokollok a kommunikáció biztonságosabbá tételére
\item Skálázási mechanizmusok adatok replikációjára és gyorsítótárazására
\end{itemize}
\subsection{A kommunikáció fajtái}
A kommunikáció lehet:
\begin{itemize}
\item időleges (transient) vagy megtartó (persistent):
\begin{itemize}
\item időleges: a kommunikációs rendszer elveti az üzenetet, ha az nem kézbesíthető
\item megtartó: a kommunikációs rendszer hajlandó huzamosabb ideig tárolni az üzenetet
\end{itemize}
\item szinkron vagy aszinkron
\begin{itemize}
\item szinkron: a küldő vár a válaszra, addig blokkolódik
\item aszinkron: a küldő nem vár a válaszra, hanem más tevékenységet folytat
\end{itemize}
\end{itemize}
\subsubsection{Kliens-szerver modell}
A kliens-szerver modell jellemzően időleges, szinkron kommunikációt végez, ahol a kliensnek és a szervernek egyidejűleg
kell aktívnak lenni. A kliens a kérés küldése után blokkolódik, vár a szerver válaszára. A szerver csak a kliensek
fogadásával és a kérések feldolgozásával foglalkozik.
\subsubsection{Távoli eljáráshívás (RPC)}
A távoli eljáráshívásnál egy távoli gépen szeretnénk futtatni egy alprogramot. Ehhez hálózati kommunikáció szükséges,
amit elfedünk egy eljáráshívással.\\
\noindent \textbf{A hívás lépései}:
\begin{enumerate}
\item A kliensfolyamat lokálisan meghívja a klienscsonkot (client stub).
\item A klienscsonk becsomagolja az eljárás azonosítóját és paramétereit. Meghívja az oprendszert.
\item A lokális gép oprendszere elküldi a csomagot a távoli gép oprendszerének.
\item Az átadja az üzenetet a szervercsonknak (server stub).
\item A szervercsonk kicsomagolja az azonosítót és a paramétereket, amiket átad a szerverfolyamatnak.
\item A szerverfolyamat lokálisan meghívja az eljárást, megkapja a visszatérési értéket.
\item A visszatérési érték visszaküldése a kliensfolyamatnak hasonlóan történik, fordított irányban.
\end{enumerate}
\begin{figure}[H]
\centering
\includegraphics[width=0.8\linewidth]{img/rpc}
\caption{A távoli eljáráshívás lépései.}
\label{fig:rpc}
\end{figure}
\subsubsection{Socket}
Az időleges kommunikáció egy módja.
\begin{figure}[H]
\centering
\includegraphics[width=0.8\linewidth]{img/socket}
\caption{Kommunikáció socket-el.}
\label{fig:socket}
\end{figure}
\subsubsection{Üzenetorientált köztesréteg (MOM)}
Az üzenetorientált köztesréteg (MOM - message-oriented middleware) egy megtartó, aszinkron kommunikációs architektúra.
Segítségével a folyamatok üzeneteket küldhetnek egymásnak. A küldő félnek nem kell a válaszra várnia, addig
foglalkozhat mással.
A MOM várakozási sorokat tart fenn a rendszer gépein. A kliensek az alábbi műveleteket
használhatják a várakozási sorokra:
\begin{itemize}
\item PUT: Üzenetet tesz a sor végére.
\item GET: Blokkol, amíg a sor üres, majd kiveszi az első üzenetet
\item POLL: Lekérdezi, hogy van-e üzenet. Ha van, leveszi az elsőt.
Ha nincs, nem blokkol, folytatja a tevékenységét.
\item NOTIFY: Kezelőrutint telepít a várakozási sorhoz, amely minden
beérkező üzenetre meghívódik.
\end{itemize}
Az üzenetsorkezelő rendszerek feltételezik, hogy a rendszer minden eleme közös protokollt használ, azaz az üzenetek
szerkezete és adatábrázolása megegyezik. A kérdés: mi van akkor, ha heterogén a rendszerünk? Erre szolgál
az üzenetközvetítő (message broker), amely heterogén rendszerben gondoskodik a megfelelő konverziókról, azaz
átalakítja az üzenetet a fogadó által használt formátumra. Általában proxy-ként is működik, azaz a közvetítés
mellett más funkciókat is nyújt, pl. biztonsági funkciókat.
\subsubsection{Folyam (stream)}
Az eddig tárgyalt kommunikációfajtákban közös, hogy az adategységek közötti időbeli kapcsolat nem befolyásolja
azok jelentését, folyamatos médiánál (pl. audio, videó, szenzoradatok) viszont az adatok időfüggőek, ezért a
kommunikáció időbeliségével kapcsolatban izokrón megkötést teszünk, ami felső és alsó korlátot is ad
a csomagok átvitelének idejére.\\
\noindent \textbf{Folyam}: Ilyen izokrón adatátvitelt lehetővé tevő kommunikációs forma a folyam. Főbb jellemzői:
\begin{itemize}
\item Egyirányú
\item Legtöbbször egy forrástól irányul egy vagy több nyelő felé
\item A forrás és/vagy nyelő gyakran közvetlenül kapcsolódik olyan hardverelemekhez, mint pl. egy kamera, képernyő,
mikrofon, stb.
\end{itemize}
\noindent \textbf{Főbb típusai}:
\begin{itemize}
\item Egyszerű folyam: egyfajta adatot továbbít, pl. egyetlen audiocsatornát, vagy csak videót.
\item Összetett folyam: Többfajta adatot továbbít egyszerre, pl. videót többcsatornájú audióval (sztereó, 5.1, stb.).
Az összetett folyam esetében biztosítani kell, hogy az alfolyamok a nyelőnél időben ne csússzanak el egymáshoz képest.
Ennek egyik módja a szinkronizáció. Egy másik lehetséges módszer a multiplexálás és demultiplexálás. Ekkor a forrás
egyetlen folyamot készít (multiplexálás). Itt az alfolyamok garantáltan szinkronban vannak egymással. A nyelőnél kell
szétbontani a folyamot alfolyamokra (demultiplexálás).
\end{itemize}
\noindent \textbf{QoS}: A folyamokkal kapcsolatban sokfajta követelmény írható elő, ezeket összefoglaló néven
a szolgáltás minőségének (QoS - Quality of Service) nevezzük. Ilyen jellemzők például a következők:
\begin{itemize}
\item Az átviteli sebesség, azaz a bitráta.
\item A folyam elindításának legnagyobb megengedett késleltetése.
\item A folyam adategységeinek megadott idő alatt el kell jutniuk a forrástól a nyelőig.
\item Remegés (jitter): az adategységek beérkezési idejének egyenetlensége. Ennek csökkentésének
egy módja a pufferelés.
\end{itemize}
\section{Szinkronizáció}
\subsection{Órák szinkronizálása}
Néha a pontos időt szeretnénk megtudni, néha elég, hogy ha két időpont közül megállapítható, hogy melyik volt korábban.
A világidő: UTC.\\
\subsubsection{Fizikai órák}
\noindent \textbf{A fizikai idő elterjesztése}: Ha a rendszerünkben van UTC-vevő, az megkapja a pontos időt. Ezt a
következők figyelembevételével terjeszthetjük el a rendszeren belül.
\begin{itemize}
\item A $p$ gép saját órája szerint az idő a $t$ UTC-időpillanatban $C_{p}(t)$
\item Ideális esetben az óra mindig pontos, azaz $C_{p}(t) = t$ minden $t$ UTC-időpillanatra. Másképpen
fogalmazva az óra sebessége mindig 1, azaz $dC/dt = 1$.
\item A valóságban $p$ órája vagy túl gyors, vagy túl lassú, de viszonylag pontos:
\begin{displaymath}
1-\rho \leq \frac{dC}{dt} \leq 1+\rho
\end{displaymath}
\begin{figure}[H]
\centering
\includegraphics[width=0.4\linewidth]{img/clockspeed}
\caption{Az óra sebessége.}
\label{fig:clockspeed}
\end{figure}
\end{itemize}
\noindent \textbf{Cristian algoritmusa}: Csak megadott $\delta$ eltérést akarunk megengedni az óra sebességében. Mindegyik
gép egy központi időszerverről kéri le a pontos időt legfeljebb $\frac{\delta}{2\rho}$ másodpercenként (ekkor tudunk $\delta$
eltérésen belül maradni). Az órát nem a megkapott időpontra kell állítani: bele kell számolni, hogy a szerver kezelte a kérést
és a válasznak vissza kellett érkeznie a hálózaton.\\
\noindent \textbf{Berkeley algoritmusa}: Nem a pontos idő beállítása a cél, csak az, hogy a rendszeren belül minden gép ideje
azonos legyen. Az időszerver időnként minden gép idejét bekéri, amiből átlagot von, majd mindenkit értesít, hogy a saját
óráját mennyivel kell átállítania. Az idő egyik gépnél sem folyhat visszafelé, ezért ha valamelyik órát vissza kellene állítani,
akkor ehelyett lelassítja az óráját addig, amíg a kívánt idő be nem áll.
\subsubsection{Logikai órák}
\noindent \textbf{Az előbb-történt reláció}: Az előbb-törént (happened-before) reláció az alábbi tulajdonságokkal bíró reláció.
Annak jelölése, hogy $a$ előbb történt, mint $b$: $a \to b$.
\begin{itemize}
\item Ha ugyanabban a folyamatban $a$ előbb következett be, mint $b$, akkor $a \to b$.
\item Ha $a$ esemény egy üzenet küldése, $b$ pedig ennek az üzenetnek a fogadása, akkor $a \to b$.
\item Tranzitív: Ha $a \to b$ és $b \to c$, akkor $a \to c$.
\end{itemize}
\noindent \textbf{Az idő és az előbb-történt reláció}: Minden $e$ eseményhez időbélyeget rendelünk, ami egy egész szám. Jelölése:
$C(e)$, és megköveteljük az alábbi tulajdonságokat:
\begin{itemize}
\item Ha $a \to b$ egy folyamat eseményeire, akkor $C(a)<C(b)$
\item Ha $a$ esemény egy üzenet küldése, $b$ pedig ennek az üzenetnek a fogadása, akkor $C(a)<C(b)$.
\end{itemize}
Ha van globális óra, akkor az időbélyeg elkészíthető. A továbbiakban azzal foglalkozunk, hogy mi van akkor, ha nincs globális
óra.\\
\noindent \textbf{Lampert-féle időbélyeg}: Minden $P_{i}$ folyamat egy $C_{i}$ számlálót tart nyilván az alábbiak szerint:
\begin{itemize}
\item $P_{i}$ minden eseménye eggyel növeli $C_{i}$-t.
\item Az elküldött $m$ üzenetre ráírjuk az időbélyeget: $ts(m) = C_{i}$.
\item Ha az $m$ üzenet beérkezik $P_{j}$ folyamathoz, ott a számláló új értéke
$C_{j} = \max \left\{C_{j},ts(m)\right\}+1$ lesz
\item $P_{i}$ és $P_{j}$ egybeeső időbélyegjei közül tekintsük a $P_{i}$-belit elsőnek, ha $i<j$.
\end{itemize}
\noindent \textbf{Pontosan sorbarendezett csoportcímzés}: A $P_{i}$ folyamat minden műveletet időbélyeggel ellátott
üzenetben küld el. $P_{i}$ egyúttal beteszi a küldött üzenetet a saját $queue_{i}$ prioritásos sorába. A $P_{j}$
folyamat a beérkező üzeneteket az ő $queue_{j}$ prioritásos sorába teszi be az időbélyegnek megfelelő prioritással.
Az üzenet érkezéséről mindegyik folyamatot értesíti.
$P_{j}$ akkor adja át a $msg_{i}$ üzenet feldolgozásra, ha:
\begin{itemize}
\item $msg_{i}$ a $queue_{j}$ elején található, azaz az ő időbélyege a legkisebb
\item a $queue_{j}$ sorban minden $P_{k}, k \not = i$ folyamatnak megtalálható legalább egy üzenete, amelynek
$msg_{i}$-nél későbbi az időbélyege
\end{itemize}
\noindent \textbf{Időbélyeg-vektor}:
\begin{itemize}
\item $P_{i}$ most már az összes folyamat idejét is számon tartja egy $VC_{i}[1..n]$ tömbben, ahol
$VC_{i}[j]$ azon $P_{j}$-ben bekövetkezett események száma, amiről $P_{i}$ tud.
\item Az $m$ üzenet elküldése során $P_{i}$ megnöveli eggyel $VC_{i}[i]$ értékét és a teljes $VC_{i}$
időbélyeg-vektort ráírja az üzenetre.
\item Amikor az $m$ üzenet megérkezik $P_{j}$-hez, amelyen a $ts(m)$ időbélyeg van, akkor
\begin{enumerate}
\item $VC_{j}[k] := \max \left\{VC_{j}[k], ts_{m}[k]\right\}$
\item $VC_{j}[j]$ megnő eggyel
\end{enumerate}
\end{itemize}
\subsection{Kölcsönös kizárás}
Több folyamat egyszerre szeretne hozzáférni egy adott erőforráshoz. Ezt egyszerre csak egynek engedhetjük meg
közülük, különben az erőforrás helytelen állapotba kerülhet.
\subsubsection{Kölcsönös kizárás központi szerver használatával}
Egy központi szerver a koordinátor, ő szabályozza az erőforráshoz való hozzáférést. Van egy várakozási sora.
Ha az erőforrás szabad, akkor ha kérés érkezik rá, a szerver megadja a hozzáférést és foglalttá teszi. Ezután
ha valaki más hozzá akar férni az erőforráshoz, akkor bekerül a várakozási sorba. Miután az első kliens
elengedte az erőforrást, az ahhoz kerül, aki a sor elején van. Ha kiürült a sor és az utolsó kliens is
elengedte az erőforrást, az újra szabaddá válik.
\begin{figure}[H]
\centering
\includegraphics[width=0.6\linewidth]{img/kolcskizar_kozp}
\caption{Példa központosított kölcsönös kizárásra.}
\label{fig:kolcskizar_kozp}
\end{figure}
\subsubsection{Decentralizált kölcsönös kizárás}
Tegyük fel, hogy az erőforrás $n$-szeresen többszörözött, és minden replikátumhoz tartozik egy azt kezelő
koordinátor. A hozzáférésről többségi szavazás dönt: legalább $m$ koordinátor szükséges, ahol $m>\frac{n}{2}$.
Feltesszük, hogy egy esetleges összeomlás után a koordinátor felépül, de a kiadott engedélyeket
elfelejti.
\subsubsection{Elosztott kölcsönös kizárás}
Többszörözött az erőforrás. Amikor a kliens hozzá szeretne férni az erőforráshoz, kérést küld a koordinátornak
időbélyeggel ellátva. Választ (hozzáférési engedélyt) akkor kap, ha:
\begin{itemize}
\item A koordinátor nem igényli az erőforrást, vagy
\item a koordinátor is igényli az erőforrást, de kisebb az időbélyege.
\item Különben a koordinátor átmenetileg nem válaszol.
\end{itemize}
\begin{figure}[H]
\centering
\includegraphics[width=0.6\linewidth]{img/kolcskizar_elosztott}
\caption{Példa elosztott kölcsönös kizárásra.}
\label{fig:kolcskizar_elosztott}
\end{figure}
\subsubsection{Kölcsönös kizárás token ring-gel}
A folyamatokat egy logikai gyűrűbe szervezzük. Egy tokent küldünk körbe. Amelyik folyamat birtokolja a tokent, az
férhet hozzá az erőforráshoz.
\subsection{Vezetőválasztás}
Sok algoritmusnak szüksége van arra, hogy kijelöljön egy folyamatot, amely a további lépéseket koordinálja.
\subsubsection{Zsarnok-algoritmus}
A folyamatoknak sorszámot adunk, melyek közül a legnagyobb sorszámút szeretnénk vezetőnek választani.\\
\noindent A zsarnok-algoritmus lépései:
\begin{enumerate}
\item A vezetőválasztás kezdeményezése. Bármelyik folyamat kezdeményezheti. Mindegyik olyan folyamatnak,
amelyről nem tudja, hogy kisebb lenne az övénél a sorszáma, elküld egy üzenetet.
\item Ha a nagyobb sorszámú folyamat üzenetet kap egy kisebb sorszámútól, akkor visszaküld neki egy
olyan üzenetet, amivel kiveszi a kisebb sorszámút a választásból.
\item Amelyik folyamat nem kap letiltó üzenet egy bizonyos időn belül, akkor ő lesz a vezető. Erről
értesíti a többi folyamatot egy-egy üzenettel.
\end{enumerate}
\begin{figure}[H]
\centering
\includegraphics[width=0.6\linewidth]{img/zsarnok}
\caption{Példa a zsarnok-algoritmus működésére.}
\label{fig:zsarnok}
\end{figure}
\subsubsection{Vezetőválasztás gyűrűben}
Logikai gyűrűnk van, a folyamatoknak vannak sorszámai. A legnagyobb sorszámú folyamatot szeretnénk vezetőnek választani.
Bármelyik folyamat kezdeményezhet vezetőválasztást: elindít egy üzenetet a gyűrűn körbe, amelyre mindenki ráírja a
a sorszámát. Ha egy folyamat összeomlott, az kimarad az üzenetküldésből. Amikor az üzenet visszajut a kezdeményezőhöz,
minden aktív folyamat sorszáma szerepel rajta. Ezek közül a legnagyobb sorszámú lesz a vezető. Ezt egy másik
üzenet körbeküldése tudatja mindenkivel.
Ha több folyamat kezdeményez egyszerre választást, az nem probléma, ugyanaz az eredmény adódik. Ha az üzenetek
elvesznének, akkor újra lehet kezdeni a választást.
\subsubsection{Superpeer-választás}
A superpeer-eket úgy szeretnénk megválasztani, hogy teljesüljön rájuk:
\begin{itemize}
\item A többi csúcs alacsony késleltetéssel éri el őket.
\item Egyenletesen vannak elosztva a hálózaton.
\item A csúcsok megadott hányadát választjuk superpeer-nek.
\item Egy superpeer korlátozott számú peer-t szolgál ki.
\end{itemize}
\noindent \textbf{Megvalósítás DHT esetén}: Ha $m$-bites azonosítókat használunk, és $S$ superpeer-re van szükség, akkor
a $k= \lceil \log_{2} S \rceil$ felső bitet foglaljuk le a superpeer-ek számára. Így N csúcs esetén kb. $2^{k-m}N$
superpeer lesz.
A $p$ kulcshoz tartozó superpeer a $p$ AND $\underbrace{11...11}_{k}\underbrace{00..00}_{m-k}$ kulcs felelőse lesz.
\section{Konzisztencia}
\noindent \textbf{Konfliktusos műveletek}: A replikátumok konzisztensen tartásához biztosítani kell, hogy az egymással
konfliktusba kerülhető műveletek minden replikátumon egyforma sorrendben futnak le. Írás-olvasás és írás-írás
konfliktusok fordulhatnak elő.\\
\noindent \textbf{Konzisztenciamodell}: A konzisztenciamodell megszabja, milyen módokon használhatják a folyamatok
az adatbázist. Ha a feltételek teljesülnek, az adattárat érvényesnek tekintjük.\\
\noindent \textbf{Konzisztencia mértéke}: A konzisztencia többféle módon is sérülhet: eltérhet a replikátumok
számértéke, frissessége, meg nem történt frissítési műveletek száma.\\
\noindent \textbf{Conit}: Az olyan adategység, amelyre közös feltételrendszer vonatkozik, a conit (consistency unit).
\subsection{Soros konzisztencia}
A feltételeket nem számértékekre, hanem írások/olvasások tényére alapozzuk.
Jelölések:
\begin{itemize}
\item W(x) : x változót írta a folyamat
\item R(x) : x változót olvasta a folyamat
\end{itemize}
Soros konzisztencia esetén azt várjuk el, hogy a végrehajtás eredménye olyan legyen, mintha az összes folyamat
összes művelete egy meghatározott sorrendben történt volna meg, megőrizve bármely adott folyamat saját műveletinek
sorrendjét.
\begin{figure}[H]
\centering
\includegraphics[width=0.7\linewidth]{img/konz_soros}
\caption{Példa: az (a) teljesíti, (b) nem a soros konzisztencia követelményeit.}
\label{fig:konz_soros}
\end{figure}
\subsection{Okozati konzisztencia}
A potenciálisan okozati összefüggésben álló műveleteket kell mindegyik folyamatnak azonos sorrendben látnia.
A konkurens írásokat a különböző folyamatok különböző sorrendben láthatják.
\begin{figure}[H]
\centering
\includegraphics[width=0.4\linewidth]{img/konz_okozati}
\caption{Példa: a (b) teljesíti, (a) nem az okozati konzisztencia követelményeit.}
\label{fig:konz_okozati}
\end{figure}
\subsection{Kliensközpontú konzisztencia}
Azt helyezzük most előtérbe, hogy a szervereken tárolt adatok hogyan látszanak egy adott kliens számára. A kliens
mozog: különböző szerverekhez csatlakozik, és írási/olvasási műveleteket hajt végre.
Az $A$ szerver után a $B$ szerverhez csatlakozva különböző problémák léphetnek fel:
\begin{itemize}
\item Az $A$-ra feltöltött frissítések lehet, hogy nem jutottak még el $B$-hez.
\item $B$-n lehet, hogy újabb adatok találhatóak, mint $A$-n.
\item A $B$-re feltöltött frissítések ütközhetnek az $A$-ra feltöltöttekkel.
\end{itemize}
A cél az, hogy a kliens azokat az adatokat, amiket az $A$ szerveren kezelt, ugyanolyan állapotban
lássa $B$-n is. Ekkor az adatbázis konzisztensnek látszik a kliens számára.\\
\subsubsection{Monoton olvasás}
Ha egyszer a kliens kiolvasott egy értéket $x$-ből, minden ezután következő
olvasás ezt adja, vagy ennél frissebb értéket.
Például levelezőkliens esetén minden korábban letöltött levelünknek meg kell lennie az új szerveren is.
\subsubsection{Monoton írás}
A kliens akkor írhatja $x$-et, ha kliens korábbi írásai $x$-re már befejeződtek.
Például verziókezelésnél minden korábbi verziónak meg kell lennie a szerveren, ha új verziót akarunk feltölteni.
\subsubsection{Olvasd az írásodat}
Ha kliens olvassa $x$-et, a saját legutolsó írásának eredményét kapja, vagy frissebbet.
Például a kliens a honlapját szerkeszti, majd megnézi az eredményt. Ahelyett, hogy a böngésző gyorsítótárából
egy régebbi változat kerülne elő, a legfrissebbet szeretné látni.
\subsubsection{Írás olvasás után}
Ha a kliens kiolvasott egy értéket $x$-ből, minden ezután kiadott frissítési művelete $x$-nek legalább
ennyire friss értékét módosítja.
Például egy fórumon a kliens csak olyan hozzászólásra tud válaszolni, amit már látott.
\subsection{Tartalom replikálása}
Különböző jellegű folyamatok tárolhatják a másolatokat:
\begin{itemize}
\item Tartós másolat: eredetszerver (origin server)
\item Szerver által kezdeményezett másolat: replikátum kihelyezése egy szerverre, amikor az igényli
az adatot
\item Kliens által kezdeményezett másolat: kliensoldali gyorsítótár
\end{itemize}
\subsubsection{Frissítés terjesztése}
Megváltozott tartalmat több különféle módon lehet kliens-szerver architektúrában átadni:
\begin{itemize}
\item Kizárólag a frissítésről szóló értesítés/érvénytelenítés elterjesztése.
\item Passzív replikáció: adatok átvitele egyik másolatról a másikra
\item Aktív replikáció: frissítési művelet átvitele
\end{itemize}
A frissítést kezdeményezheti a szerver (küldésalapú frissítés), ekkor a szerver a kliens kérése nélkül elküldi a frissítést
a kliensnek, vagy kezdeményezheti a kliens, aki kérvényezi a frissítést a szervertől (rendelésalapú frissítés).\\
\noindent \textbf{Haszonbérlet (lease)}: A szerver ígéretet tesz a kliensnek, hogy átküldi a frissítést, amíg a haszonbérlet aktív.
\end{document}