.. module:: cintes Cintes transportadores ====================== En un centre de distribució automatitzat, disposem de desenes de cintes transportadores que informen del seu estat de funcionament (velocitat i alertes) a un servidor mitjançant connexions TCP asíncrones. Servidor bàsic -------------- Per tal d'implementar un servidor bàsic que gestioni les connexions d'un conjunt de cintes, fes el següent: #. Dissenya la corutina següent i desa-la al mòdul :mod:`cintes` (fitxer :file:`cintes.py`): .. function:: gestiona_cinta(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None :async: Llegeix línia a línia de `reader` i respon ``OK`` per `writer`. Enregistra les excepcions mitjançant el mòdul :mod:`logging`. Fa un tancament net de `writer` quan s'acaben les dades de `reader` o s'ha produït una excepció. #. Passa-hi els tests del fitxer :download:`test_gestiona_cinta.py` amb ``pytest``. .. module:: servidor_basic 3. Dissenya un servidor basat en fluxos de dades (*streams*) d\':mod:`asyncio` que usi la corutina :func:`~cintes.gestiona_cinta` per atendre cada una de les connexions. Desa'l al mòdul :mod:`servidor_basic`. * El servidor ha d'escoltar el port 7777 de l'ordinador local. * El servidor ha d'acabar de manera neta en rebre el senyal ``SIGTERM``. Prova'l amb `telnet` o `nc`. Ha de respondre `OK` a cada línia enviada. Per exemple, havent engegat el servidor:: $ nc -N 127.0.0.1 7777 Hola OK Bon dia! OK Qualsevol cosa OK .. admonition:: Solució :download:`cintes.py`, :download:`servidor_basic.py`. .. _servidor_estat: Servidor amb l'estat de les cintes ---------------------------------- El servidor ha d'emmagatzemar l'estat de cada cinta en un diccionari. Les claus del diccionari són els identificadors de les cintes. L'estat pot ser ``"ATURADA"``, ``"MOVIMENT"`` o ``"AVARIADA"``. El servidor ha de calcular l'estat de cada cinta a partir dels missatges rep de les cintes. .. _protocol: El protocol de missatges entre una cinta i el servidor és el següent: * El primer missatge que ha d'enviar una cinta al servidor és ``ID identificador_de_la_cinta`` en què ``identificador_de_la_cinta`` és un text sense espais en blanc. El servidor respon ``OK identificador_de_la_cinta``. * La resta de missatges de la cinta poden ser: * ``VELOCITAT v`` en què ``v`` és un nombre més gran o igual que zero. El servidor ha de respondre ``OK ATURADA`` si ``v`` és igual a zero i ``OK MOVIMENT`` si ``v`` és més gran que zero. * ``AVARIA``. El servidor respon ``OK ATURADA``. Recorda que cada missatge ha d'anar en una línia. Cal que facis el següent: #. Dissenya la corutina següent i desa-la al mòdul :mod:`cintes` (fitxer :file:`cintes.py`): .. function:: gestiona_estat_cinta(estat, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None :async: Llegeix línia a línia de `reader` i respon per `writer` d'acord amb el protocol anterior. Emmagatzema l'estat de la cinta al diccionari `estat`. Enregistra les exepcions mitjançant el mòdul :mod:`logging`. Fa un tancament net de `writer` quan s'acaben les dades de `reader` o s'ha produït una excepció. #. Passa-hi els tests del fitxer :download:`test_gestiona_estat_cinta.py` amb ``pytest``. .. module:: servidor_estat 3. Dissenya un servidor basat en fluxos de dades (*streams*) d\':mod:`asyncio` que usi la corutina :func:`~cintes.gestiona_estat_cinta` per atendre cada una de les connexions i que mantingui l'estat de cada cinta en un diccionari compartit per totes les connexions. Desa'l al mòdul :mod:`servidor_estat`. * El servidor ha d'escoltar el port 7777 de l'ordinador local. * El servidor ha d'acabar de manera neta en rebre el senyal ``SIGTERM``. Prova'l amb `telnet` o `nc`. Recorda que has de seguir el :ref:`protocol de la cinta `. Per exemple, havent engegat el servidor:: $ nc -N 127.0.0.1 7777 ID cinta_1 OK cinta_1 VELOCITAT 100 OK MOVIMENT VELOCITAT 0 OK ATURADA AVARIA OK AVARIADA .. admonition:: Solució :download:`cintes.py`, :download:`servidor_estat.py`. .. module:: servidor_classe Classe servidor --------------- Ara volem reestructurar el codi anterior en una classe que implementi el :ref:`servidor_estat`. Per això cal que facis el següent: #. Dissenya la classe següent i desa-la al mòdul :mod:`servidor_classe`: .. class:: ServidorEstatCintes() Implementa un servidor amb la mateixa funcionalitat que :ref:`servidor_estat`. .. rubric:: Atributs .. attribute:: estat_cintes Diccionari amb l'estat de cada cinta connectada al servidor. .. rubric:: Mètodes .. method:: cinta(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) :async: Equivalent a :func:`~cintes.gestiona_estat_cinta`, llevat que el diccionari és un atribut de l'objecte en comptes de ser un paràmetre. .. method:: run(ip, port) :async: Engega el servidor que escolta del `port` de l'adreça `ip`. #. Dissenya el programa que usi aquesta classe i desa'l al mòdul :mod:`servidor_classe`. Prova'l amb `telnet` o `nc`. S'ha de comportar com :mod:`servidor_estat`. .. #. Afegeix a la classe el mètode asíncron (corutina) següent: .. method:: ServidorEstatCintes.escriu_estat() :async: Escriu el nombre de cintes en cada estat, mitjançat :mod:`logging`, cada mig segon. .. admonition:: Solució :download:`cintes.py`, :download:`servidor_classe.py`. .. module:: cintes_wrk Prova de càrrega ---------------- Volem mesurar el nombre de peticions per segon que poden atendre els servidors anteriors. La prova de càrrega s'ha de comportar com ``wrk``: * Parteix del *nombre màxim de connexions* i de la *durada* de la prova de càrrega en segons. * Ha de calcular: #. El nombre total de peticions fetes al servidor. #. El nombre d'errors produïts. #. El temps total d'execució. #. El rendiment del servidor en nombre de peticions ateses per segon. Cal que facis el següent: #. Dissenya una prova de càrrega per als servidors anteriors que es comporti com ``wrk`` i desa-la al mòdul :mod:`cintes_wrk`. Per exemple:: $ python3 cintes_wrk.py 10 5 Iniciant prova de 5 segons amb 10 connexions concurrents... Protocol: TCP Stream sobre 127.0.0.1:7777 --------------------------------------------- Peticions totals: 115721 Errors totals: 0 Temps real: 5.00 segons Rendiment (RPS): 23140.56 peticions/segon --------------------------------------------- .. admonition:: Solució :download:`cintes.py`, :download:`cintes_wrk.py`.