25. Anwendungsübung: Version 8
25.1. Einführung
Wir werden eine neue Client/Server-Anwendung schreiben. Die neue Funktion des Servers besteht darin, dass er eine Sitzung verwaltet. Anstatt die Steuerverwaltungsdaten in einem Objekt mit [Anwendungs-]Gültigkeitsbereich zu speichern, werden wir sie in einem Objekt mit [Sitzungs-]Gültigkeitsbereich speichern. Dies führt zu einer Verschlechterung der Code-Leistung. Wenn ein Objekt im schreibgeschützten Modus von allen Benutzern gemeinsam genutzt werden kann, ist es vorzuziehen, es als Objekt im [application]-Scope statt im [session]-Scope zu definieren. Wir gewinnen dadurch zumindest etwas Bandbreite, da dies die Größe des Session-Cookies verringert. Wir möchten jedoch eine Client/Server-Anwendung demonstrieren, bei der Client und Server ein Session-Cookie austauschen.
Die Anwendungsarchitektur bleibt unverändert:

25.2. Der Webserver
Die Verzeichnisstruktur der Server-Skripte sieht wie folgt aus:

Der Ordner [http-servers/03] wird zunächst durch Kopieren des Ordners [http-servers/02] erstellt. Anschließend werden Änderungen vorgenommen.
25.2.1. Die Konfiguration
Sie entspricht der |vorherigen Version| mit einigen Änderungen im Skript [config]:
# dépendances absolues
absolute_dependencies = [
# dossiers du projet
# BaseEntity, MyException
f"{root_dir}/classes/02/entities",
# InterfaceImpôtsDao, InterfaceImpôtsMétier, InterfaceImpôtsUi
f"{root_dir}/impots/v04/interfaces",
# AbstractImpôtsdao, ImpôtsConsole, ImpôtsMétier
f"{root_dir}/impots/v04/services",
# ImpotsDaoWithAdminDataInDatabase
f"{root_dir}/impots/v05/services",
# AdminData, ImpôtsError, TaxPayer
f"{root_dir}/impots/v04/entities",
# Constantes, Tranches
f"{root_dir}/impots/v05/entities",
# index_controller
f"{script_dir}/../controllers",
# scripts [config_database, config_layers]
script_dir,
# Logger, SendAdminMail
f"{root_dir}/impots/http-servers/02/utilities",
]
- Zeile 17: Wir werden einen Controller für die [index]-Funktion umschreiben, der die URL / verarbeitet;
- Zeile 21: Wir verwenden die Dienstprogramme aus der |vorherigen Version|;
25.2.2. Das Hauptskript [main]
Das neue [main]-Skript führt einige Änderungen am Hauptskript [main] aus der vorherigen Version ein:
# l'application Flask peut démarrer
app = Flask(__name__)
# clé secrète de la session
app.secret_key = os.urandom(12).hex()
- Zeile 4: Wir erstellen einen geheimen Schlüssel für die Anwendung. Wir wissen, dass dies für die Sitzungsverwaltung erforderlich ist;
Als Nächstes werden im [main]-Code keine Steuerdaten mehr abgefragt. Die folgenden Zeilen werden entfernt:
Zusätzlich akzeptiert der [index_controller]-Controller einen weiteren Parameter, die Flask-Sitzung:
25.2.3. Der [index_controller]-Controller
Der [index_controller]-Controller verwaltet nun eine Sitzung:
- Zeile 14: Der Controller empfängt die aktuelle Sitzung vom Web-Client;
- Zeilen 35–38: Wenn der Client eine Sitzung hat, enthält diese zwei Schlüssel:
- [client_id]: eine Client-ID (Zeile 37);
- [admindata]: Steuerverwaltungsdaten in Form eines Wörterbuchs (Zeile 38);
- Zeile 35: Wir prüfen, ob die Sitzung einen der beiden erwarteten Schlüssel enthält;
- Zeilen 42–51: Fall, in dem die Sitzung des Clients noch nicht initialisiert wurde;
- Zeile 43: Abrufen der Steuerbehörden-Daten aus der [dao]-Schicht;
- Zeile 45: Diese Daten werden in Form eines Wörterbuchs in die Sitzung eingefügt;
- Zeile 48: Dem Client wird eine Zufallszahl zugewiesen. Diese Zahl ist für jeden Client unterschiedlich;
- Zeile 49: Diese Zahl wird in der Sitzung gespeichert;
- Zeile 51: Wir protokollieren, dass die Daten der Steuerbehörde von der [dao]-Schicht abgerufen wurden. Der Zugriff auf die [dao]-Schicht ist in der Regel aufwendig. Deshalb muss er begrenzt werden. Die Idee dabei ist, die Steuerdaten einmalig aus der [dao]-Schicht abzurufen, sie in der Sitzung zu speichern und sie bei nachfolgenden Anfragen desselben Clients von dort abzurufen. Beachten Sie, dass dies nicht die beste Lösung ist. Da die Steuerdaten der Verwaltung für alle Clients gleich sind, gehören sie in ein Objekt im Anwendungsbereich;
- Zeilen 35–40: Fall, in dem die Sitzung des Clients bei einer vorherigen Anfrage initialisiert wurde;
- Zeile 37: Abrufen der Client-ID aus der Sitzung;
- Zeile 38: Wir rufen die Steuerdaten der Verwaltung aus der Sitzung ab;
- Zeile 40: Wir protokollieren, dass der Client die Steuerdaten der Verwaltung aus der Sitzung abgerufen hat;
25.3. Der Web-Client

25.3.1. Die [dao]-Schicht
25.3.1.1. Die Klasse [ImpôtsDaoWithHttpSession]
Die [dao]-Schicht wird durch die folgende Klasse [ImpôtsDaoWithHttpSession] implementiert:
- Zeile 30: Die [dao]-Schicht verwaltet ein Wörterbuch mit Cookies;
- Zeile 58: Die Eigenschaft [response.cookies] ist ein Wörterbuch, das die vom Server in den [Set-Cookie]-HTTP-Headern gesendeten Cookies enthält;
- Zeile 59: Diese Cookies werden in der [dao]-Schicht gespeichert. Sie werden bei nachfolgenden Anfragen desselben Clients an den Server zurückgesendet;
- Zeilen 60–68: Obwohl dies nicht unbedingt erforderlich ist, rufen wir das Sitzungscookie ab. Im vom Server gesendeten Cookie-Dictionary ist das Sitzungscookie dem Schlüssel [session] zugeordnet;
- Zeilen 62–68: Wir entschlüsseln das Session-Cookie, um den Benutzer anzumelden;
- Zeile 68: Wir werden später auf die Funktion [decode_flask_session] zurückkommen, die das Session-Cookie entschlüsselt;
- Zeilen 52 und 57: Bei jeder Anfrage desselben Clients werden die vom Server gesendeten Cookies an diesen zurückgesendet. Auf diese Weise wird die Flask-Sitzung zwischen Client und Server aufrechterhalten;
Wir müssen nun bedenken, dass die [dao]-Schicht von mehreren Threads gleichzeitig ausgeführt wird. Wir müssen daher alle Eigenschaften der Klasseninstanz untersuchen und prüfen, ob der gleichzeitige Zugriff auf diese Eigenschaften ein Problem darstellt. Hier haben wir in Zeile 30 die Eigenschaft [self.__cookies] hinzugefügt. Diese Eigenschaft wird in Zeile 59 geändert. Wir haben also Schreibzugriff auf Daten, die von allen Threads gemeinsam genutzt werden. Dieser Zugriff stellt jedoch ein Problem dar: Jeder Thread, der einen bestimmten Client repräsentiert, hat sein eigenes Session-Cookie. Tatsächlich enthält es für jeden Client eine eindeutige Client-ID (=Thread). Wenn wir nichts unternehmen, kann Thread T2 die Cookies von Thread T1 überschreiben.
Wir haben bereits eine Methode gesehen, um dieses Problem zu lösen: Wir können für jeden Thread unterschiedliche Schlüssel in der [config]-Datei erstellen, die als Parameter an den Konstruktor übergeben wird (Zeile 17). Beispielsweise können wir den Thread-Namen als Schlüssel verwenden:
- In Zeile 59 könnten wir schreiben:
config[thread_name][‘cookies’]=cookies
- In Zeile 52 könnten wir dann schreiben:
cookies=config[thread_name][‘cookies’]
Hier verwenden wir eine andere Technik: Jeder Thread (=Client) verfügt über eine eigene [dao]-Schicht. Auf diese Weise ist Zeile 59 kein Problem mehr, da die verwendeten Cookies die eines einzelnen Clients sind.
Dazu erstellen wir eine neue Klasse [ImpôtsDaoWithHttpSessionFactory].
25.3.1.2. Die Flask-Funktion zur Dekodierung der Sitzung
Die Funktion [decode_flask_session] ist im Skript [myutils] definiert:

Wir haben das Skript |myutils| bereits behandelt. Dieses Skript ist ein Modul im Maschinenbereich, das die verschiedenen Skripte in diesem Kurs mit der Anweisung importieren können:
Darin definieren wir die Funktion [decode_flask_session] wie folgt:
- Zeile 2: Die URL, unter der ich diese Funktion gefunden habe;
- Zeile 1: Der Parameter [cookie] ist die Zeichenkette, die dem Schlüssel [session] im Cookie-Wörterbuch eines Web-Clients zugeordnet ist;
- Zeilen 3–16: Ich werde diesen Code nicht kommentieren, da ich ihn nicht vollständig verstehe;
Wir fügen der Datei [__init__.py] einen neuen Import hinzu:
from .myutils import set_syspath, json_response, decode_flask_session
Die neue Version von [myutils] wird mit dem Befehl [pip install .] in einem PyCharm-Terminal als systemweites Modul installiert:
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\packages>pip install .
Processing c:\data\st-2020\dev\python\cours-2020\python3-flask-2020\packages
Using legacy setup.py install for myutils, since package 'wheel' is not installed.
Installing collected packages: myutils
Attempting uninstall: myutils
Found existing installation: myutils 0.1
Uninstalling myutils-0.1:
Successfully uninstalled myutils-0.1
Running setup.py install for myutils ... done
Successfully installed myutils-0.1
- Zeile 1: Sie müssen sich im Ordner [packages] befinden, um diesen Befehl einzugeben;
25.3.1.3. Die Klasse [ImpôtsDaoWithHttpSessionFactory]
Die Klasse [ImpôtsDaoWithHttpSessionFactory] sieht wie folgt aus:
- Die Klasse [ImpôtsDaoWithHttpSessionFactory] ermöglicht es uns, mithilfe der Methode [new_instance] in den Zeilen 10–12 eine neue Implementierung der [dao]-Schicht zu erstellen;
25.3.2. Konfiguration
Das Skript [config_layers], das die Web-Client-Schichten konfiguriert, wird wie folgt geändert:
- Zeilen 5–6: Anstatt wie zuvor eine einzelne [DAO]-Schicht zu instanziieren, instanziieren wir eine „Factory“ für diese Schicht (Factory = Objekt-Produktionsfabrik, in diesem Fall die [DAO]-Schicht);
- Zeilen 9–11: Wir geben die Layer-Konfiguration zurück;
25.3.3. Das Hauptskript des Clients
Das [main]-Skript hat sich im Vergleich zur vorherigen Version wie folgt geändert:
- Zeilen 29–30: Jeder Thread hat seine eigene [dao]-Schicht;
25.3.4. Ausführung auf dem Client
Der Webserver wird gestartet, das DBMS wird gestartet, der Mailserver [hMailServer] wird gestartet. Anschließend starten wir das [main]-Skript des Webclients. Die Ausführungsprotokolle in [data/logs/logs.txt] sehen dann wie folgt aus:
2020-07-25 10:21:46.478511, Thread-1 : début du thread [Thread-1] avec 1 contribuable(s)
2020-07-25 10:21:46.479111, Thread-1 : début du calcul de l'impôt de {"id": 1, "marié": "oui", "enfants": 2, "salaire": 55555}
2020-07-25 10:21:46.479111, Thread-2 : début du thread [Thread-2] avec 1 contribuable(s)
2020-07-25 10:21:46.480195, Thread-3 : début du thread [Thread-3] avec 2 contribuable(s)
2020-07-25 10:21:46.480195, Thread-2 : début du calcul de l'impôt de {"id": 2, "marié": "oui", "enfants": 2, "salaire": 50000}
2020-07-25 10:21:46.481137, Thread-4 : début du thread [Thread-4] avec 3 contribuable(s)
2020-07-25 10:21:46.481137, Thread-3 : début du calcul de l'impôt de {"id": 3, "marié": "oui", "enfants": 3, "salaire": 50000}
2020-07-25 10:21:46.482279, Thread-5 : début du thread [Thread-5] avec 3 contribuable(s)
2020-07-25 10:21:46.482622, Thread-6 : début du thread [Thread-6] avec 1 contribuable(s)
2020-07-25 10:21:46.482622, Thread-4 : début du calcul de l'impôt de {"id": 5, "marié": "non", "enfants": 3, "salaire": 100000}
2020-07-25 10:21:46.485923, Thread-5 : début du calcul de l'impôt de {"id": 8, "marié": "non", "enfants": 0, "salaire": 100000}
2020-07-25 10:21:46.485923, Thread-6 : début du calcul de l'impôt de {"id": 11, "marié": "oui", "enfants": 3, "salaire": 200000}
2020-07-25 10:21:46.725910, Thread-4 : cookie de session={"admindata":{"abattement_dixpourcent_max":12502.0,"abattement_dixpourcent_min":437.0,"coeffn":[0.0,1394.96,5798.0,13913.7,20163.4],"coeffr":[0.0,0.14,0.3,0.41,0.45],"id":1,"limites":[9964.0,27519.0,73779.0,156244.0,90000.0],"plafond_decote_celibataire":1196.0,"plafond_decote_couple":1970.0,"plafond_impot_celibataire_pour_decote":1595.0,"plafond_impot_couple_pour_decote":2627.0,"plafond_qf_demi_part":1551.0,"plafond_revenus_celibataire_pour_reduction":21037.0,"plafond_revenus_couple_pour_reduction":42074.0,"valeur_reduc_demi_part":3797.0},"client_id":"fa3c83b82761c83e13217967"}
2020-07-25 10:21:46.725910, Thread-4 : {"réponse": {"result": {"marié": "non", "enfants": 3, "salaire": 100000, "impôt": 16782, "surcôte": 7176, "taux": 0.41, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:46.725910, Thread-4 : fin du calcul de l'impôt de {"id": 5, "marié": "non", "enfants": 3, "salaire": 100000, "impôt": 16782, "surcôte": 7176, "taux": 0.41, "décôte": 0, "réduction": 0}
2020-07-25 10:21:46.726960, Thread-4 : début du calcul de l'impôt de {"id": 6, "marié": "oui", "enfants": 3, "salaire": 100000}
2020-07-25 10:21:47.514108, Thread-3 : cookie de session={"admindata":{"abattement_dixpourcent_max":12502.0,"abattement_dixpourcent_min":437.0,"coeffn":[0.0,1394.96,5798.0,13913.7,20163.4],"coeffr":[0.0,0.14,0.3,0.41,0.45],"id":1,"limites":[9964.0,27519.0,73779.0,156244.0,24999.5],"plafond_decote_celibataire":1196.0,"plafond_decote_couple":1970.0,"plafond_impot_celibataire_pour_decote":1595.0,"plafond_impot_couple_pour_decote":2627.0,"plafond_qf_demi_part":1551.0,"plafond_revenus_celibataire_pour_reduction":21037.0,"plafond_revenus_couple_pour_reduction":42074.0,"valeur_reduc_demi_part":3797.0},"client_id":"700e3f5dc808c7c48f0c9007"}
2020-07-25 10:21:47.514610, Thread-3 : {"réponse": {"result": {"marié": "oui", "enfants": 3, "salaire": 50000, "impôt": 0, "surcôte": 0, "taux": 0.14, "décôte": 720, "réduction": 0}}}
2020-07-25 10:21:47.514939, Thread-3 : fin du calcul de l'impôt de {"id": 3, "marié": "oui", "enfants": 3, "salaire": 50000, "impôt": 0, "surcôte": 0, "taux": 0.14, "décôte": 720, "réduction": 0}
2020-07-25 10:21:47.514939, Thread-3 : début du calcul de l'impôt de {"id": 4, "marié": "non", "enfants": 2, "salaire": 100000}
2020-07-25 10:21:47.527211, Thread-5 : cookie de session={"admindata":{"abattement_dixpourcent_max":12502.0,"abattement_dixpourcent_min":437.0,"coeffn":[0.0,1394.96,5798.0,13913.7,20163.4],"coeffr":[0.0,0.14,0.3,0.41,0.45],"id":1,"limites":[9964.0,27519.0,73779.0,156244.0,90000.0],"plafond_decote_celibataire":1196.0,"plafond_decote_couple":1970.0,"plafond_impot_celibataire_pour_decote":1595.0,"plafond_impot_couple_pour_decote":2627.0,"plafond_qf_demi_part":1551.0,"plafond_revenus_celibataire_pour_reduction":21037.0,"plafond_revenus_couple_pour_reduction":42074.0,"valeur_reduc_demi_part":3797.0},"client_id":"9e14a5d4a3057f69ab95ab2d"}
2020-07-25 10:21:47.527211, Thread-2 : cookie de session={"admindata":{"abattement_dixpourcent_max":12502.0,"abattement_dixpourcent_min":437.0,"coeffn":[0.0,1394.96,5798.0,13913.7,20163.4],"coeffr":[0.0,0.14,0.3,0.41,0.45],"id":1,"limites":[9964.0,27519.0,73779.0,156244.0,22500.0],"plafond_decote_celibataire":1196.0,"plafond_decote_couple":1970.0,"plafond_impot_celibataire_pour_decote":1595.0,"plafond_impot_couple_pour_decote":2627.0,"plafond_qf_demi_part":1551.0,"plafond_revenus_celibataire_pour_reduction":21037.0,"plafond_revenus_couple_pour_reduction":42074.0,"valeur_reduc_demi_part":3797.0},"client_id":"a06e8fd70a44c9e311f4dce0"}
2020-07-25 10:21:47.527211, Thread-5 : {"réponse": {"result": {"marié": "non", "enfants": 0, "salaire": 100000, "impôt": 22986, "surcôte": 0, "taux": 0.41, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:47.527211, Thread-1 : cookie de session={"admindata":{"abattement_dixpourcent_max":12502.0,"abattement_dixpourcent_min":437.0,"coeffn":[0.0,1394.96,5798.0,13913.7,20163.4],"coeffr":[0.0,0.14,0.3,0.41,0.45],"id":1,"limites":[9964.0,27519.0,73779.0,156244.0,90000.0],"plafond_decote_celibataire":1196.0,"plafond_decote_couple":1970.0,"plafond_impot_celibataire_pour_decote":1595.0,"plafond_impot_couple_pour_decote":2627.0,"plafond_qf_demi_part":1551.0,"plafond_revenus_celibataire_pour_reduction":21037.0,"plafond_revenus_couple_pour_reduction":42074.0,"valeur_reduc_demi_part":3797.0},"client_id":"28c38df998f67685b3a482b8"}
2020-07-25 10:21:47.527211, Thread-2 : {"réponse": {"result": {"marié": "oui", "enfants": 2, "salaire": 50000, "impôt": 1384, "surcôte": 0, "taux": 0.14, "décôte": 384, "réduction": 347}}}
2020-07-25 10:21:47.528341, Thread-5 : fin du calcul de l'impôt de {"id": 8, "marié": "non", "enfants": 0, "salaire": 100000, "impôt": 22986, "surcôte": 0, "taux": 0.41, "décôte": 0, "réduction": 0}
2020-07-25 10:21:47.528341, Thread-1 : {"réponse": {"result": {"marié": "oui", "enfants": 2, "salaire": 55555, "impôt": 2814, "surcôte": 0, "taux": 0.14, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:47.528842, Thread-2 : fin du calcul de l'impôt de {"id": 2, "marié": "oui", "enfants": 2, "salaire": 50000, "impôt": 1384, "surcôte": 0, "taux": 0.14, "décôte": 384, "réduction": 347}
2020-07-25 10:21:47.529349, Thread-5 : début du calcul de l'impôt de {"id": 9, "marié": "oui", "enfants": 2, "salaire": 30000}
2020-07-25 10:21:47.529699, Thread-1 : fin du calcul de l'impôt de {"id": 1, "marié": "oui", "enfants": 2, "salaire": 55555, "impôt": 2814, "surcôte": 0, "taux": 0.14, "décôte": 0, "réduction": 0}
2020-07-25 10:21:47.529699, Thread-2 : fin du thread [Thread-2]
2020-07-25 10:21:47.531905, Thread-1 : fin du thread [Thread-1]
2020-07-25 10:21:47.536121, Thread-6 : cookie de session={"admindata":{"abattement_dixpourcent_max":12502.0,"abattement_dixpourcent_min":437.0,"coeffn":[0.0,1394.96,5798.0,13913.7,20163.4],"coeffr":[0.0,0.14,0.3,0.41,0.45],"id":1,"limites":[9964.0,27519.0,73779.0,156244.0,93749.0],"plafond_decote_celibataire":1196.0,"plafond_decote_couple":1970.0,"plafond_impot_celibataire_pour_decote":1595.0,"plafond_impot_couple_pour_decote":2627.0,"plafond_qf_demi_part":1551.0,"plafond_revenus_celibataire_pour_reduction":21037.0,"plafond_revenus_couple_pour_reduction":42074.0,"valeur_reduc_demi_part":3797.0},"client_id":"38499b63076516c02f2770ec"}
2020-07-25 10:21:47.537161, Thread-3 : {"réponse": {"result": {"marié": "non", "enfants": 2, "salaire": 100000, "impôt": 19884, "surcôte": 4480, "taux": 0.41, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:47.537161, Thread-6 : {"réponse": {"result": {"marié": "oui", "enfants": 3, "salaire": 200000, "impôt": 42842, "surcôte": 17283, "taux": 0.41, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:47.538156, Thread-3 : fin du calcul de l'impôt de {"id": 4, "marié": "non", "enfants": 2, "salaire": 100000, "impôt": 19884, "surcôte": 4480, "taux": 0.41, "décôte": 0, "réduction": 0}
2020-07-25 10:21:47.538557, Thread-6 : fin du calcul de l'impôt de {"id": 11, "marié": "oui", "enfants": 3, "salaire": 200000, "impôt": 42842, "surcôte": 17283, "taux": 0.41, "décôte": 0, "réduction": 0}
2020-07-25 10:21:47.538828, Thread-3 : fin du thread [Thread-3]
2020-07-25 10:21:47.538828, Thread-6 : fin du thread [Thread-6]
2020-07-25 10:21:47.546198, Thread-5 : {"réponse": {"result": {"marié": "oui", "enfants": 2, "salaire": 30000, "impôt": 0, "surcôte": 0, "taux": 0.0, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:47.546198, Thread-5 : fin du calcul de l'impôt de {"id": 9, "marié": "oui", "enfants": 2, "salaire": 30000, "impôt": 0, "surcôte": 0, "taux": 0.0, "décôte": 0, "réduction": 0}
2020-07-25 10:21:47.546198, Thread-5 : début du calcul de l'impôt de {"id": 10, "marié": "non", "enfants": 0, "salaire": 200000}
2020-07-25 10:21:47.739643, Thread-4 : {"réponse": {"result": {"marié": "oui", "enfants": 3, "salaire": 100000, "impôt": 9200, "surcôte": 2180, "taux": 0.3, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:47.739643, Thread-4 : fin du calcul de l'impôt de {"id": 6, "marié": "oui", "enfants": 3, "salaire": 100000, "impôt": 9200, "surcôte": 2180, "taux": 0.3, "décôte": 0, "réduction": 0}
2020-07-25 10:21:47.740668, Thread-4 : début du calcul de l'impôt de {"id": 7, "marié": "oui", "enfants": 5, "salaire": 100000}
2020-07-25 10:21:48.557469, Thread-5 : {"réponse": {"result": {"marié": "non", "enfants": 0, "salaire": 200000, "impôt": 64210, "surcôte": 7498, "taux": 0.45, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:48.558715, Thread-5 : fin du calcul de l'impôt de {"id": 10, "marié": "non", "enfants": 0, "salaire": 200000, "impôt": 64210, "surcôte": 7498, "taux": 0.45, "décôte": 0, "réduction": 0}
2020-07-25 10:21:48.558715, Thread-5 : fin du thread [Thread-5]
2020-07-25 10:21:48.753025, Thread-4 : {"réponse": {"result": {"marié": "oui", "enfants": 5, "salaire": 100000, "impôt": 4230, "surcôte": 0, "taux": 0.14, "décôte": 0, "réduction": 0}}}
2020-07-25 10:21:48.753318, Thread-4 : fin du calcul de l'impôt de {"id": 7, "marié": "oui", "enfants": 5, "salaire": 100000, "impôt": 4230, "surcôte": 0, "taux": 0.14, "décôte": 0, "réduction": 0}
2020-07-25 10:21:48.753540, Thread-4 : fin du thread [Thread-4]
- Es gibt insgesamt 6 Threads, d. h. 6 Clients (Zeilen 1, 3, 4, 6, 8, 9), die gleichzeitig den Steuerberechnungsserver abfragen;
- wir verfolgen den Thread [Thread-4], der 3 Steuerzahler (Zeile 6) bearbeitet. Er wird drei aufeinanderfolgende Anfragen an den Steuerberechnungsserver stellen;
- Zeile 10: Die erste Anfrage von [Thread-4];
- Zeile 13: [Thread-4] hat die Antwort auf seine erste Anfrage erhalten. Darin findet er ein Sitzungscookie, das die Nummer [fa3c83b82761c83e13217967] enthält, die ihm vom Server zugewiesen wurde;
- Zeile 14: die Steuer für den ersten Steuerzahler;
- Zeile 16: [Thread-4] stellt eine Anfrage für den zweiten Steuerzahler;
- Zeile 43: [Thread-4] erhält den Steuerbetrag für den zweiten Steuerzahler;
- Zeile 45: [Thread-4] stellt eine Anfrage für den dritten Steuerzahler;
- Zeile 49: [Thread-4] erhält den Steuerbetrag für den dritten Steuerzahler;
- Zeile 51: [Thread-4] hat seine Arbeit beendet;
Sehen wir uns nun an, wie die drei Anfragen von [Thread-4] auf der Serverseite verarbeitet wurden. Wir können sie in den Serverprotokollen anhand ihrer Client-ID [fa3c83b82761c83e13217967] nachverfolgen.
Die serverseitigen Protokolle [data/logs/logs.txt] lauten wie folgt:
2020-07-25 10:21:39.187366, MainThread : [serveur] démarrage du serveur
2020-07-25 10:21:40.439093, MainThread : [serveur] démarrage du serveur
2020-07-25 10:21:46.502011, Thread-2 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=3&salaire=50000' [GET]>
2020-07-25 10:21:46.504049, Thread-2 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:46.505452, Thread-3 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=2&salaire=55555' [GET]>
2020-07-25 10:21:46.506257, Thread-3 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:46.507292, Thread-4 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=non&enfants=0&salaire=100000' [GET]>
2020-07-25 10:21:46.507292, Thread-4 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:46.508301, Thread-5 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=2&salaire=50000' [GET]>
2020-07-25 10:21:46.509293, Thread-5 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:46.511808, Thread-6 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=non&enfants=3&salaire=100000' [GET]>
2020-07-25 10:21:46.517604, Thread-7 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=3&salaire=200000' [GET]>
2020-07-25 10:21:46.517604, Thread-7 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:46.719504, Thread-6 : [index_controller] client [fa3c83b82761c83e13217967], données fiscales prises dans la couche dao
2020-07-25 10:21:46.720003, Thread-6 : [index] {'réponse': {'result': {'marié': 'non', 'enfants': 3, 'salaire': 100000, 'impôt': 16782, 'surcôte': 7176, 'taux': 0.41, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:46.736108, Thread-8 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=3&salaire=100000' [GET]>
2020-07-25 10:21:46.736108, Thread-8 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:47.506709, Thread-2 : [index_controller] client [700e3f5dc808c7c48f0c9007], données fiscales prises dans la couche dao
2020-07-25 10:21:47.507216, Thread-2 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 3, 'salaire': 50000, 'impôt': 0, 'surcôte': 0, 'taux': 0.14, 'décôte': 720, 'réduction': 0}}}
2020-07-25 10:21:47.507216, Thread-3 : [index_controller] client [28c38df998f67685b3a482b8], données fiscales prises dans la couche dao
2020-07-25 10:21:47.508442, Thread-4 : [index_controller] client [9e14a5d4a3057f69ab95ab2d], données fiscales prises dans la couche dao
2020-07-25 10:21:47.508940, Thread-3 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 2, 'salaire': 55555, 'impôt': 2814, 'surcôte': 0, 'taux': 0.14, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:47.510506, Thread-4 : [index] {'réponse': {'result': {'marié': 'non', 'enfants': 0, 'salaire': 100000, 'impôt': 22986, 'surcôte': 0, 'taux': 0.41, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:47.511513, Thread-5 : [index_controller] client [a06e8fd70a44c9e311f4dce0], données fiscales prises dans la couche dao
2020-07-25 10:21:47.514939, Thread-5 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 2, 'salaire': 50000, 'impôt': 1384, 'surcôte': 0, 'taux': 0.14, 'décôte': 384, 'réduction': 347}}}
2020-07-25 10:21:47.520727, Thread-7 : [index_controller] client [38499b63076516c02f2770ec], données fiscales prises dans la couche dao
2020-07-25 10:21:47.523162, Thread-7 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 3, 'salaire': 200000, 'impôt': 42842, 'surcôte': 17283, 'taux': 0.41, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:47.530835, Thread-9 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=non&enfants=2&salaire=100000' [GET]>
2020-07-25 10:21:47.531736, Thread-9 : [index_controller] client [700e3f5dc808c7c48f0c9007], données fiscales prises en session
2020-07-25 10:21:47.531905, Thread-9 : [index] {'réponse': {'result': {'marié': 'non', 'enfants': 2, 'salaire': 100000, 'impôt': 19884, 'surcôte': 4480, 'taux': 0.41, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:47.541899, Thread-10 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=2&salaire=30000' [GET]>
2020-07-25 10:21:47.542488, Thread-10 : [index_controller] client [9e14a5d4a3057f69ab95ab2d], données fiscales prises en session
2020-07-25 10:21:47.542488, Thread-10 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 2, 'salaire': 30000, 'impôt': 0, 'surcôte': 0, 'taux': 0.0, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:47.553628, Thread-11 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=non&enfants=0&salaire=200000' [GET]>
2020-07-25 10:21:47.553628, Thread-11 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:47.736910, Thread-8 : [index_controller] client [fa3c83b82761c83e13217967], données fiscales prises en session
2020-07-25 10:21:47.737191, Thread-8 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 3, 'salaire': 100000, 'impôt': 9200, 'surcôte': 2180, 'taux': 0.3, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:47.748226, Thread-12 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=5&salaire=100000' [GET]>
2020-07-25 10:21:47.748226, Thread-12 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-25 10:21:48.554695, Thread-11 : [index_controller] client [9e14a5d4a3057f69ab95ab2d], données fiscales prises en session
2020-07-25 10:21:48.555070, Thread-11 : [index] {'réponse': {'result': {'marié': 'non', 'enfants': 0, 'salaire': 200000, 'impôt': 64210, 'surcôte': 7498, 'taux': 0.45, 'décôte': 0, 'réduction': 0}}}
2020-07-25 10:21:48.748753, Thread-12 : [index_controller] client [fa3c83b82761c83e13217967], données fiscales prises en session
2020-07-25 10:21:48.748753, Thread-12 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 5, 'salaire': 100000, 'impôt': 4230, 'surcôte': 0, 'taux': 0.14, 'décôte': 0, 'réduction': 0}}}
- Der Client [fa3c83b82761c83e13217967] taucht zum ersten Mal in Zeile 14 auf: Um die Steuer zu berechnen, musste der Server Daten aus der Datenbank der Steuerbehörde abrufen;
- dann sehen wir den Client [fa3c83b82761c83e13217967] erneut in Zeile 36. Diesmal ruft der Server die Daten der Steuerbehörde aus der Sitzung ab, was ihm einen potenziell kostspieligen Zugriff auf die [DAO]-Schicht erspart;
- Wir begegnen dem Client [fa3c83b82761c83e13217967] ein drittes Mal in Zeile 42, wo der Server erneut die Sitzung des Clients nutzt;
Dieses Beispiel veranschaulicht deutlich den Nutzen der Sitzung für einen Client: Sie speichert Daten, die von allen Anfragen dieses Clients gemeinsam genutzt werden und deren Abruf mit hohem Aufwand verbunden ist.
Auf der Client-Seite sind die Ergebnisse in der Datei [data/output/results.json] dieselben wie in früheren Versionen.
25.4. Testen der [DAO]-Schicht
Wie in den |vorherigen Versionen| testen wir die [dao]-Schicht des Clients:

Die Testklasse wird in der folgenden Umgebung ausgeführt:

- Die Konfiguration [2] ist identisch mit der Konfiguration [1], die wir gerade untersucht haben;
Die Testklasse [TestHttpClientDao] sieht wie folgt aus:
- Wir erstellen eine |Ausführungskonfiguration| für diesen Test;
- Wir starten den Webserver mit seiner gesamten Umgebung;
- führen den Test aus;
Die Ergebnisse lauten wie folgt:
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-clients/03/tests/TestHttpClientDao.py
tests en cours...
...........
----------------------------------------------------------------------
Ran 11 tests in 3.392s
OK
Process finished with exit code 0