Skip to content

29. Toepassingsopdracht: versie 11

29.1. Inleiding

In eerdere versies van de client/server-applicatie voor belastingberekening bevond de laag [métier], die de bedrijfsregels voor deze berekening implementeert, zich aan de serverzijde. We stellen nu voor om deze naar de clientzijde te verplaatsen. Wat is het voordeel hiervan? Een deel van het werk dat de server deed, wordt naar de clientzijde verplaatst. Stel dat een server door N clients wordt benaderd, dan worden er N bedrijfsrekeningen voor de belasting door de clients uitgevoerd. In eerdere versies voerde de server deze N bedrijfsrekeningen uit. Omdat de server de bedrijfsrekening niet meer uitvoert, zal hij sneller reageren op zijn clients en daardoor meer clients tegelijkertijd kunnen bedienen.

De client/server-architectuur ziet er als volgt uit:

Image

  • de laag [métier] [10] is op de client gedupliceerd als [12];
  • er is een nieuw script [main2] [11] aan de client toegevoegd;

De webclient heeft twee manieren om de belasting te berekenen voor de lijst met belastingplichtigen die is gevonden in [3]:

  • de methode van de vorige versie gebruiken. Hierbij wordt gebruikgemaakt van de serverlaag [métier] [10]. Het script [main] zal deze methode gebruiken;
  • vraag gewoon de gegevens van de belastingdienst [2-4] op bij de server en gebruik vervolgens de lokale laag [métier] [12] van de client;

We zullen de prestaties van beide methoden vergelijken.

29.2. De webserver

De structuur van de webserver ziet er als volgt uit:

Image

  • Het dossier [http-servers/06] wordt in eerste instantie aangemaakt door het dossier [http-servers/05] te kopiëren. We behouden namelijk de verworvenheden van de vorige versie 10. We voegen er alleen een nieuwe functionaliteit aan toe. Deze komt tot uiting in de aanwezigheid van een nieuwe controller [get_admindata_controller] [1]. De andere controller [calculate_tax_controller] is niets anders dan de oude controller [index_controller], die een nieuwe naam heeft gekregen;

29.3. Configuratie

De server biedt twee URL-services aan:

  • [/calculate-tax] voor het berekenen van de belasting van een lijst met belastingplichtigen die in de body van een POST is doorgegeven. Deze komt dus overeen met de URL [/] uit de vorige versie 10;
  • [/get-admindata] levert de reeks jSON met gegevens van de belastingdienst;

De configuratie [config] koppelt elk van deze URL-codes aan de controller die deze verwerkt:


    # woordenboek van de controllers van de webapplicatie
    import calculate_tax_controller, get_admindata_controller
    controllers = {"calculate-tax": calculate_tax_controller, "get-admindata": get_admindata_controller}
    config['controllers'] = controllers

29.4. Het hoofdscript [main]

Het hoofdscript [main] herstructureert het script [main] uit de vorige versie:


# er wordt een mysql- of pgres-parameter verwacht
import sys
syntaxe = f"{sys.argv[0]} mysql / pgres"
erreur = len(sys.argv) != 2
if not erreur:
    sgbd = sys.argv[1].lower()
    erreur = sgbd != "mysql" and sgbd != "pgres"
if erreur:
    print(f"syntaxe : {syntaxe}")
    sys.exit()

# de applicatie wordt geconfigureerd
import config
config = config.configure({'sgbd': sgbd})

# afhankelijkheden


# gegevens ophalen van de belastingdienst
erreur = False
try:
    # admindata wordt een alleen-lezen applicatie-variabele
    config["admindata"] = config["layers"]["dao"].get_admindata()
    # succeslogboek
    logger.write("[serveur] connexion à la base de données réussie\n")
except ImpôtsError as ex:
    # de fout wordt genoteerd
    …

# als er een fout is opgetreden, wordt de uitvoering gestopt
if erreur:
    sys.exit(2)

# de Flask-applicatie kan worden gestart
app = Flask(__name__)

# hoofdcontroller
def main_controller() -> tuple:
    # de gevraagde actie wordt opgehaald
    dummy, action=request.path.split('/')
    logger = None
    try:
        # logger
        logger = Logger(config["logsFilename"])
        # deze wordt opgeslagen in een configuratie die aan de thread is gekoppeld
        thread_config = {"logger": logger}
        thread_name = threading.current_thread().name
        config[thread_name] = {"config": thread_config}
        # het verzoek wordt gelogd
        logger.write(f"[index] requête : {request}\n")
        # de thread wordt onderbroken indien dit is gevraagd
        sleep_time = config["sleep_time"]
        if sleep_time != 0:
            # de pauze is willekeurig, zodat sommige threads worden onderbroken en andere niet
            aléa = randint(01)
            if aléa == 1:
                # logboek vóór de pauze
                logger.write(f"[index] mis en pause du thread pendant {sleep_time} seconde(s)\n")
                # onderbreking
                time.sleep(sleep_time)
        # de aanvraag wordt uitgevoerd door de aanvraagcontroller
        controller = config['controllers'][action]
        résultat, status_code = controller.execute(request, config)
        # is er een fatale fout opgetreden?
        if status_code == status.HTTP_500_INTERNAL_SERVER_ERROR:
            # er wordt een e-mail verzonden naar de beheerder van de applicatie
            config_mail = config["adminMail"]
            config_mail["logger"] = logger
            SendAdminMail.send(config_mail, json.dumps(résultat, ensure_ascii=False))
        # het antwoord wordt gelogd
        logger.write(f"[index] {résultat}\n")
        # het antwoord wordt verzonden
        print(résultat)
        return json_response(résultat, status_code)
    except BaseException as erreur:
        # de fout wordt gelogd indien mogelijk
        if logger:
            logger.write(f"[index] {erreur}")
        # het antwoord aan de klant wordt voorbereid
        résultat = {"réponse": {"erreurs"[f"{erreur}"]}}
        # het antwoord wordt verzonden
        return json_response(résultat, status.HTTP_500_INTERNAL_SERVER_ERROR)
    finally:
        # het logbestand wordt gesloten als het geopend was
        if logger:
            logger.close()

# de belasting wordt berekend
@app.route('/calculate-tax', methods=['POST'])
@auth.login_required
def calculate_tax():
    # het beheer wordt overgedragen aan de hoofdcontroller
    return main_controller()

# gegevens ophalen bij de belastingdienst
@app.route('/get-admindata', methods=['GET'])
@auth.login_required
def get_admindata():
    # het wordt overgedragen aan de hoofdcontroleur
    return main_controller()

# alleen handmatige bediening
if __name__ == '__main__':
    # de server wordt gestart
    app.config.update(ENV="development", DEBUG=True)
    app.run(threaded=True)
  • regels 88-93: de functie [calculate_tax] verwerkt URL en [/calculate-tax];
  • regels 95-100: de functie [get_admindata] verwerkt de URL en [/get-admindata];
  • deze twee functies doen op zichzelf niets. Ze geven onmiddellijk het woord door aan de hoofdcontroller [main_controller] in de regels 37-86;
  • regels 37-86: de hoofdcontroller [main_controller] is niets anders dan de functie [index] uit de vorige versie, op één detail na: waar de functie [index] slechts één enkele URL verwerkte, verwerkt [main_controller] hier twee URL. Deze moeten dus door een van de twee controllers [calculate_tax_controller, get_admin,data_controller] worden verwerkt;
  • regels 39-40: de gevraagde actie [calculate_tax] of [get_admindata] wordt opgehaald. Deze informatie bevindt zich in het pad van de URL [request.path]. Afhankelijk van het geval is [request.path] gelijk aan [/get-admindata] of [/calculate_tax]. Het splitsen van regel 40 levert twee elementen op:
    • de lege tekenreeks voor het gedeelte vóór de /;
    • de naam van de gevraagde actie voor het gedeelte dat volgt op de /;
  • regels 62-63: zodra de actie van URL is opgehaald, weten we welke controller we moeten gebruiken om URL te verwerken. Deze informatie staat in de configuratie van [config];

29.5. De controllers

De controller [calculate_tax_controller] is niets anders dan de controller [index_controller] uit de vorige versie.

De controller [get_admindata_controller] is de volgende:


from flask_api import status
from werkzeug.local import LocalProxy

def execute(request: LocalProxy, config: dict) -> tuple:
    # het antwoord wordt teruggestuurd
    return {"réponse": {"result": config["admindata"].asdict()}}, status.HTTP_200_OK
  • de URL [/get-admindata] moet de reeks jSON van de gegevens van de belastingdienst weergeven;
  • regel 6: deze gegevens zijn opgehaald door het hoofdscript [main] en opgeslagen in het woordenboek [config] in de vorm van een object [AdminData]. We geven het woordenboek van dit object weer;

29.6. Postman-tests

We starten de webserver, de SGBD en de e-mailserver [hMailServer]. Vervolgens berekenen we met een Postman-client de belasting voor verschillende belastingplichtigen:

Image

In de Postman-console verloopt de communicatie tussen client en server als volgt:


POST /calculate-tax HTTP/1.1
Authorization: Basic YWRtaW46YWRtaW4=
Content-Type: application/json
User-Agent: PostmanRuntime/7.26.2
Accept: */*
Cache-Control: no-cache
Postman-Token: 5e71461a-fec8-4315-85e8-41721de939e5
Host: localhost:5000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 824

[ 
{ 
"marié": "oui", 
"enfants": 2, 
"salaire": 55555 
}, 

{ 
"marié": "oui", 
"enfants": 3, 
"salaire": 200000 
} 
]

HTTP/1.0 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 1461
Server: Werkzeug/1.0.1 Python/3.8.1
Date: Wed, 29 Jul 2020 07:02:07 GMT

{"réponse": {"results": [{"marié": "oui", "enfants": 2, "salaire": 55555, "impôt": 2814, "surcôte": 0, "taux": 0.14, "décôte": 0, "réduction": 0}, {"marié": "oui", "enfants": 2, "salaire": 50000, "impôt": 1384, "surcôte": 0, "taux": 0.14, "décôte": 384, "réduction": 347…]}}

Laten we nu de URL [/get-admindata] opvragen met een GET:

Image

De client/server-dialoog in de Postman-console ziet er als volgt uit:


GET /get-admindata HTTP/1.1
Authorization: Basic YWRtaW46YWRtaW4=
User-Agent: PostmanRuntime/7.26.2
Accept: */*
Cache-Control: no-cache
Postman-Token: 4af342c4-7ecb-4ab2-9e12-d653f81da424
Host: localhost:5000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

HTTP/1.0 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 596
Server: Werkzeug/1.0.1 Python/3.8.1
Date: Wed, 29 Jul 2020 07:07:24 GMT

{"réponse": {"result": {"limites": [9964.0, 27519.0, 73779.0, 156244.0, 93749.0], "coeffr": [0.0, 0.14, 0.3, 0.41, 0.45], "coeffn": [0.0, 1394.96, 5798.0, 13913.7, 20163.4], "plafond_decote_couple": 1970.0, "valeur_reduc_demi_part": 3797.0, "plafond_revenus_celibataire_pour_reduction": 21037.0, "plafond_qf_demi_part": 1551.0, "abattement_dixpourcent_max": 12502.0, "plafond_impot_celibataire_pour_decote": 1595.0, "plafond_decote_celibataire": 1196.0, "plafond_revenus_couple_pour_reduction": 42074.0, "id": 1, "abattement_dixpourcent_min": 437.0, "plafond_impot_couple_pour_decote": 2627.0}}}

29.7. De webclient

Image

Image

De map [http-clients/06] wordt in eerste instantie verkregen door de map [http-clients/05] te kopiëren. De aanpassing bestaat voornamelijk uit:

  • de configuratie [config_layers] aan te passen, zodat deze voortaan een laag [métier] bevat. Voorheen had deze slechts één laag, namelijk [dao];
  • een nieuwe methode toevoegen aan de laag [dao];
  • een script [main2] schrijven dat gebruikmaakt van de laag [métier] van de klant om de belasting van de belastingplichtigen te berekenen;

29.7.1. Configuratie van de lagen van de klant

De configuratie van de lagen vindt op twee punten plaats:

  • in de configuratie [config], waarin de map met de implementatie van de laag [métier] moet worden opgenomen in de afhankelijkheden van de client. Deze map was al opgenomen in de afhankelijkheden:

absolute_dependencies = [
        # projectmappen
        # 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",
        # Constanten, segmenten
        f"{root_dir}/impots/v05/entities",
        # ImpôtsDaoWithHttpClient
        f"{script_dir}/../services",
        # configuratiescripts
        script_dir,
        # Logger
        f"{root_dir}/impots/http-servers/02/utilities",
    ]

Vervolgens moet het bestand [config_layers] worden aangepast:


def configure(config: dict) -> dict:
    # instantiëren van de applicatielagen

    # laag [métier]
    from ImpôtsMétier import ImpôtsMétier
    métier = ImpôtsMétier()

    # DAO-laag
    from ImpôtsDaoWithHttpClient import ImpôtsDaoWithHttpClient
    dao = ImpôtsDaoWithHttpClient(config)

    # de configuratie van de lagen wordt weergegeven
    return {
        "dao": dao,
        "métier": métier
    }
  • regels 4-6: instantiëring van de laag [métier];
  • regels 13-16: de laag [métier] wordt opgenomen in het lagenwoordenboek;

29.7.2. Implementatie van de laag [dao]

Image

De laag [dao] zal de volgende interface [InterfaceImpôtsDaoWithHttpClient] hebben:


from abc import abstractmethod

from AbstractImpôtsDao import AbstractImpôtsDao

class InterfaceImpôtsDaoWithHttpClient(AbstractImpôtsDao):

    # belastingberekening
    @abstractmethod
    def calculate_tax_in_bulk_mode(self, taxpayers: list):
        pass
  • regel 5: de interface [InterfaceImpôtsDaoWithHttpClient] erft van de abstracte klasse [AbstractImpôtsDao], die de toegang tot het bestandssysteem van de klant beheert. Ter herinnering: deze klasse heeft een abstracte methode [get_admindata];
  • regels 7-10: de methode [calculate_tax_in_bulk_mode] die we in de vorige versie hebben gedefinieerd, maakt het mogelijk de belasting te berekenen voor een lijst van belastingplichtigen;

Deze interface wordt geïmplementeerd door de volgende klasse [ImpôtsDaoWithHttpClient]:


# import

import json

import requests
from flask_api import status

from AbstractImpôtsDao import AbstractImpôtsDao
from AdminData import AdminData
from ImpôtsError import ImpôtsError
from InterfaceImpôtsDaoWithHttpClient import InterfaceImpôtsDaoWithHttpClient

class ImpôtsDaoWithHttpClient(InterfaceImpôtsDaoWithHttpClient):

    # fabrikant
    def __init__(self, config: dict):
        # initialisatie van bovenliggende laag
        AbstractImpôtsDao.__init__(self, config)
        # opslag van configuratie-elementen
        # algemene configuratie
        self.__config = config
        # server
        self.__config_server = config["server"]
        # debugmodus
        self.__debug = config["debug"]
        # logger
        self.__logger = None

    def get_admindata(self) -> AdminData:
        # uitzonderingen doorgeven

        # service-URL
        config_server = self.__config_server
        config_services = config_server['url_services']
        url_service = f"{config_server['urlServer']}{config_services['get-admindata']}"

        # verbinding
        if config_server['authBasic']:
            response = requests.get(url_service,
                                    auth=(
                                        config_server["user"]["login"],
                                        config_server["user"]["password"]))
        else:
            response = requests.get(url_service)

        # debugmodus?
        if self.__debug:
            # logger
            if not self.__logger:
                self.__logger = self.__config['logger']
            # wordt gelogd
            self.__logger.write(f"{response.text}\n")

        # statuscode
        status_code = response.status_code
        # resultaat in de vorm van een woordenboek
        résultat = json.loads(response.text)
        # als de statuscode niet 200 is OK
        if status_code != status.HTTP_200_OK:
            raise ImpôtsError(58, résultat['réponse']['erreurs'])
        # het resultaat (een woordenboek) wordt weergegeven
        return AdminData().fromdict(résultat["réponse"]["result"])

    # belastingberekening in bulkmodus
    def calculate_tax_in_bulk_mode(self, taxpayers: list):
        # uitzonderingen worden doorgegeven

        
  • regel 13: de klasse [ImpôtsDaoWithHttpClient] implementeert de interface [InterfaceImpôtsDaoWithHttpClient]. Ze is dus afgeleid van de klasse [AbstractImpôtsDao];
  • regels 65-66: de methode [calculate_tax_in_bulk_mode] die in de vorige versie is besproken;
  • regels 29-62: de methode [get_admindata] die door de bovenliggende klasse [AbstractImpôtsDao] als abstract is gedeclareerd. Deze wordt dus geïmplementeerd in de onderliggende klasse;
  • regels 33-35: hier wordt de URL van de webservice bepaald die de methode [get-admindata] moet opvragen. Deze URL-services zijn gedefinieerd in de [config]-configuratie van de client:

       # de server voor belastingberekening
        "server": {
            "urlServer""http://127.0.0.1:5000",
            "authBasic"True,
            "user": {
                "login""admin",
                "password""admin"
            },
            "url_services": {
                "calculate-tax""/calculate-tax",
                "get-admindata""/get-admindata"
            }
        },
  • (vervolg)
    • regels 9-12: de twee URL van de webserver;
  • regels 37-44: de URL-service wordt synchroon opgevraagd;
  • regels 46-42: indien de configuratie dit vereist, wordt het antwoord van de server gelogd;
  • regel 57: we weten dat de server een tekenreeks jSON uit een woordenboek heeft verzonden;
  • regels 58-60: als de status HTTP van het antwoord niet 200 is, wordt er een uitzondering gegenereerd;
  • regels 61-62: het object [AdminData], dat de door de server verzonden gegevens van de belastingdienst bevat, wordt geretourneerd;

29.8. De scripts [main, main2]

Het script [main] is dat van de vorige versie. Het maakt gebruik van de methode [calculate_tax_in_bulk_mode] van de laag [dao] en gebruikt dus de laag [métier] van de server;

Het script [main2] doet hetzelfde als het script [main], maar maakt daarbij gebruik van de laag [métier] van de client:


# de applicatie configureren

import config
config = config.configure({})

# afhankelijkheden
from ImpôtsError import ImpôtsError
from Logger import Logger

logger = None
# code
try:
    # logger
    logger = Logger(config["logsFilename"])
    # wordt opgeslagen in de configuratie
    config["logger"] = logger
    # startlog
    logger.write("début du calcul de l'impôt des contribuables\n")
    # we halen de laag op [dao]
    dao = config["layers"]["dao"]
    # we halen de belastingplichtigen op
    taxpayers = dao.get_taxpayers_data()["taxpayers"]
    # van de belastingplichtigen?
    if not taxpayers:
        raise ImpôtsError(36f"Pas de contribuables valides dans le fichier {config['taxpayersFilename']}")
    # we halen de gegevens van de belastingdienst op
    admindata = dao.get_admindata()
    # berekening van de belasting van de belastingplichtigen door de laag [métier]
    métier = config['layers']['métier']
    for taxpayer in taxpayers:
        métier.calculate_tax(taxpayer, admindata)
    # de resultaten worden opgeslagen in het bestand jSON
    dao.write_taxpayers_results(taxpayers)
# behalve BaseException als fout:
# # weergave van de fout
# print(f"De volgende fout is opgetreden: {fout}")
finally:
    # de logger wordt gesloten
    if logger:
        # eindlog
        logger.write("fin du calcul de l'impôt des contribuables\n")
        # logger wordt afgesloten
        logger.close()
    # klaar
    print("Travail terminé...")
  • regels 26-27: de gegevens van de belastingdienst worden opgehaald van de server;
  • regels 28-31: vervolgens wordt de belasting van de belastingplichtigen lokaal berekend;

29.9. Tests van de client

In elk van de scripts [main, main2] wordt het begin en einde van het script gelogd. Zo kunnen we de uitvoeringstijd van het script berekenen. Laten we een paar voorspellingen doen:

  • het script [main] uit de vorige versie:
    • maakt N threads aan die gelijktijdig worden uitgevoerd;
    • elke thread verwerkt een batch belastingplichtigen, waarvan de belasting wordt berekend via één enkele verzoek aan de server;
    • omdat de N threads gelijktijdig worden uitgevoerd, wordt het N+1-verzoek gestuurd voordat het N-verzoek een antwoord heeft ontvangen. Daardoor zijn de N verzoeken duurder dan één enkel verzoek, maar waarschijnlijk niet veel duurder. Er vinden bovendien 11 (het aantal belastingplichtigen) bedrijfsberekeningen plaats op de server;
  • het script [main2] van deze versie:
    • voert één enkele verzoek naar de server uit;
    • voert 11 bedrijfsberekeningen lokaal op de client uit;

De bedrijfsberekeningen zullen even lang duren, of ze nu op de server of op de client worden uitgevoerd. Het verschil zit hem dus in de verzoeken. We kunnen dan ook verwachten dat de uitvoeringstijd van [main] iets langer zal zijn dan die van [main2].

We starten de server van versie 11, SGBD en de e-mailserver [hMailServer]. Aan de serverzijde stellen we de parameter [sleep_time] in op nul, zodat beide tests onder dezelfde omstandigheden worden uitgevoerd.

Uitvoering 1 [main]

De uitvoering van [main] levert de volgende logbestanden op:


2020-07-29 14:35:50.016079, MainThread : début du calcul de l'impôt des contribuables
2020-07-29 14:35:50.016079, Thread-1 : début du calcul de l'impôt des 1 contribuables
2020-07-29 14:35:50.016079, Thread-2 : début du calcul de l'impôt des 4 contribuables
2020-07-29 14:35:50.016079, Thread-3 : début du calcul de l'impôt des 2 contribuables
2020-07-29 14:35:50.016079, Thread-4 : début du calcul de l'impôt des 2 contribuables
2020-07-29 14:35:50.024426, Thread-5 : début du calcul de l'impôt des 2 contribuables
2020-07-29 14:35:50.050473, Thread-1 : {"réponse": {"results": [{"marié": "oui", "enfants": 2, "salaire": 55555, "impôt": 2814, "surcôte": 0, "taux": 0.14, "décôte": 0, "réduction": 0}]}}
2020-07-29 14:35:50.050473, Thread-1 : fin du calcul de l'impôt des 1 contribuables
2020-07-29 14:35:50.050473, Thread-3 : {"réponse": {"results": [{"marié": "oui", "enfants": 3, "salaire": 100000, "impôt": 9200, "surcôte": 2180, "taux": 0.3, "décôte": 0, "réduction": 0}, {"marié": "oui", "enfants": 5, "salaire": 100000, "impôt": 4230, "surcôte": 0, "taux": 0.14, "décôte": 0, "réduction": 0}]}}
2020-07-29 14:35:50.051214, Thread-3 : fin du calcul de l'impôt des 2 contribuables
2020-07-29 14:35:50.051214, Thread-5 : {"réponse": {"results": [{"marié": "non", "enfants": 0, "salaire": 200000, "impôt": 64210, "surcôte": 7498, "taux": 0.45, "décôte": 0, "réduction": 0}, {"marié": "oui", "enfants": 3, "salaire": 200000, "impôt": 42842, "surcôte": 17283, "taux": 0.41, "décôte": 0, "réduction": 0}]}}
2020-07-29 14:35:50.051214, Thread-5 : fin du calcul de l'impôt des 2 contribuables
2020-07-29 14:35:50.051214, Thread-2 : {"réponse": {"results": [{"marié": "oui", "enfants": 2, "salaire": 50000, "impôt": 1384, "surcôte": 0, "taux": 0.14, "décôte": 384, "réduction": 347}, {"marié": "oui", "enfants": 3, "salaire": 50000, "impôt": 0, "surcôte": 0, "taux": 0.14, "décôte": 720, "réduction": 0}, {"marié": "non", "enfants": 2, "salaire": 100000, "impôt": 19884, "surcôte": 4480, "taux": 0.41, "décôte": 0, "réduction": 0}, {"marié": "non", "enfants": 3, "salaire": 100000, "impôt": 16782, "surcôte": 7176, "taux": 0.41, "décôte": 0, "réduction": 0}]}}
2020-07-29 14:35:50.051214, Thread-2 : fin du calcul de l'impôt des 4 contribuables
2020-07-29 14:35:50.051214, Thread-4 : {"réponse": {"results": [{"marié": "non", "enfants": 0, "salaire": 100000, "impôt": 22986, "surcôte": 0, "taux": 0.41, "décôte": 0, "réduction": 0}, {"marié": "oui", "enfants": 2, "salaire": 30000, "impôt": 0, "surcôte": 0, "taux": 0.0, "décôte": 0, "réduction": 0}]}}
2020-07-29 14:35:50.051214, Thread-4 : fin du calcul de l'impôt des 2 contribuables
2020-07-29 14:35:50.051214, MainThread : fin du calcul de l'impôt des contribuables

De uitvoeringstijd bedroeg [051214-016079] nanoseconden (regel 17 – regel 1), d.w.z. 35 milliseconden en 135 nanoseconden.

We zien dat tussen het eerste verzoek aan de server en het laatste antwoord dat de client heeft ontvangen, dezelfde tijd [051214-016079] (regel 15 – regel 1) is verstreken, namelijk 35 milliseconden en 135 nanoseconden.

Uitvoering 2 [main2]

De uitvoering van [main2] levert de volgende logbestanden op:


2020-07-29 14:41:03.303520, MainThread : début du calcul de l'impôt des contribuables
2020-07-29 14:41:03.345084, MainThread : {"réponse": {"result": {"limites": [9964.0, 27519.0, 73779.0, 156244.0, 13500.0], "coeffr": [0.0, 0.14, 0.3, 0.41, 0.45], "coeffn": [0.0, 1394.96, 5798.0, 13913.7, 20163.4], "plafond_decote_couple": 1970.0, "valeur_reduc_demi_part": 3797.0, "plafond_revenus_celibataire_pour_reduction": 21037.0, "plafond_qf_demi_part": 1551.0, "abattement_dixpourcent_max": 12502.0, "plafond_impot_celibataire_pour_decote": 1595.0, "plafond_decote_celibataire": 1196.0, "plafond_revenus_couple_pour_reduction": 42074.0, "id": 1, "abattement_dixpourcent_min": 437.0, "plafond_impot_couple_pour_decote": 2627.0}}}
2020-07-29 14:41:03.349975, MainThread : fin du calcul de l'impôt des contribuables

De uitvoeringstijd bedroeg [349975-303520] nanoseconden (regel 3 – regel 1), d.w.z. 46 milliseconden en 455 nanoseconden. Volledig onverwacht is [main] sneller dan [main2].

We zien dat de enige verzoek van [main2] [345084-303520] (regel 2 – regel 1) heeft geduurd, d.w.z. 41 milliseconden en 564 nanoseconden. De berekening van de belasting duurde vervolgens [349975-345084] (regel 3 – regel 2), d.w.z. 4 milliseconden en 91 nanoseconden. Het is de verzoek HTTP die de uitvoeringstijd bepaalt. Verrassend genoeg zien we hier dat de enige verzoek van [main2] langer duurde dan de vier gelijktijdige verzoeken van [main] en [35 millisecondes].

Aan de serverzijde zien de logbestanden er als volgt uit:


2020-07-29 14:35:27.047721, MainThread : [serveur] démarrage du serveur
2020-07-29 14:35:27.140927, MainThread : [serveur] connexion à la base de données réussie
2020-07-29 14:35:28.790716, MainThread : [serveur] démarrage du serveur
2020-07-29 14:35:28.847518, MainThread : [serveur] connexion à la base de données réussie
2020-07-29 14:35:50.039178, Thread-2 : [index] requête : <Request 'http://127.0.0.1:5000/calculate-tax' [POST]>
2020-07-29 14:35:50.039178, Thread-3 : [index] requête : <Request 'http://127.0.0.1:5000/calculate-tax' [POST]>
2020-07-29 14:35:50.043220, Thread-4 : [index] requête : <Request 'http://127.0.0.1:5000/calculate-tax' [POST]>
2020-07-29 14:35:50.044307, Thread-5 : [index] requête : <Request 'http://127.0.0.1:5000/calculate-tax' [POST]>
2020-07-29 14:35:50.045796, Thread-2 : [index] {'réponse': {'results': [{'marié': 'oui', 'enfants': 3, 'salaire': 100000, 'impôt': 9200, 'surcôte': 2180, 'taux': 0.3, 'décôte': 0, 'réduction': 0}, {'marié': 'oui', 'enfants': 5, 'salaire': 100000, 'impôt': 4230, 'surcôte': 0, 'taux': 0.14, 'décôte': 0, 'réduction': 0}]}}
2020-07-29 14:35:50.045796, Thread-3 : [index] {'réponse': {'results': [{'marié': 'oui', 'enfants': 2, 'salaire': 55555, 'impôt': 2814, 'surcôte': 0, 'taux': 0.14, 'décôte': 0, 'réduction': 0}]}}
2020-07-29 14:35:50.046825, Thread-6 : [index] requête : <Request 'http://127.0.0.1:5000/calculate-tax' [POST]>
2020-07-29 14:35:50.046825, Thread-6 : [index] {'réponse': {'results': [{'marié': 'oui', 'enfants': 2, 'salaire': 50000, 'impôt': 1384, 'surcôte': 0, 'taux': 0.14, 'décôte': 384, 'réduction': 347}, {'marié': 'oui', 'enfants': 3, 'salaire': 50000, 'impôt': 0, 'surcôte': 0, 'taux': 0.14, 'décôte': 720, 'réduction': 0}, {'marié': 'non', 'enfants': 2, 'salaire': 100000, 'impôt': 19884, 'surcôte': 4480, 'taux': 0.41, 'décôte': 0, 'réduction': 0}, {'marié': 'non', 'enfants': 3, 'salaire': 100000, 'impôt': 16782, 'surcôte': 7176, 'taux': 0.41, 'décôte': 0, 'réduction': 0}]}}
2020-07-29 14:35:50.046825, Thread-4 : [index] {'réponse': {'results': [{'marié': 'non', 'enfants': 0, 'salaire': 200000, 'impôt': 64210, 'surcôte': 7498, 'taux': 0.45, 'décôte': 0, 'réduction': 0}, {'marié': 'oui', 'enfants': 3, 'salaire': 200000, 'impôt': 42842, 'surcôte': 17283, 'taux': 0.41, 'décôte': 0, 'réduction': 0}]}}
2020-07-29 14:35:50.046825, Thread-5 : [index] {'réponse': {'results': [{'marié': 'non', 'enfants': 0, 'salaire': 100000, 'impôt': 22986, 'surcôte': 0, 'taux': 0.41, 'décôte': 0, 'réduction': 0}, {'marié': 'oui', 'enfants': 2, 'salaire': 30000, 'impôt': 0, 'surcôte': 0, 'taux': 0.0, 'décôte': 0, 'réduction': 0}]}}
2020-07-29 14:41:03.341582, Thread-7 : [index] requête : <Request 'http://127.0.0.1:5000/get-admindata' [GET]>
2020-07-29 14:41:03.341582, Thread-7 : [index] {'réponse': {'result': {'limites': [9964.0, 27519.0, 73779.0, 156244.0, 13500.0], 'coeffr': [0.0, 0.14, 0.3, 0.41, 0.45], 'coeffn': [0.0, 1394.96, 5798.0, 13913.7, 20163.4], 'plafond_decote_couple': 1970.0, 'valeur_reduc_demi_part': 3797.0, 'plafond_revenus_celibataire_pour_reduction': 21037.0, 'plafond_qf_demi_part': 1551.0, 'abattement_dixpourcent_max': 12502.0, 'plafond_impot_celibataire_pour_decote': 1595.0, 'plafond_decote_celibataire': 1196.0, 'plafond_revenus_couple_pour_reduction': 42074.0, 'id': 1, 'abattement_dixpourcent_min': 437.0, 'plafond_impot_couple_pour_decote': 2627.0}}}
  • regel 5: het eerste verzoek van de client [main];
  • regel 14: het laatste antwoord aan de client [main]. Er zitten 6 milliseconden en 647 nanoseconden tussen beide;
  • regels 15-16: het enige verzoek van de klant [main2]. Het antwoord komt onmiddellijk;