Skip to content

29. Uygulama alıştırması: sürüm 11

29.1. Giriş

Vergi hesaplama istemci/sunucu uygulamasının önceki sürümlerinde, bu hesaplamanın iş kurallarını uygulayan [métier] katmanı sunucu tarafındaydı. Şimdi bu katmanı istemci tarafına taşımayı planlıyoruz. Bunun faydası nedir? Sunucunun yaptığı işlerin bir kısmı istemci tarafına taşınacak. N sayıda istemci tarafından sorgulanan bir sunucu durumunu düşünürsek, N adet vergi hesaplaması istemciler tarafından yapılacaktır. Önceki sürümlerde, bu N adet hesaplamayı sunucu yapıyordu. Artık hesaplamayı yapmayacağı için sunucu, istemcilere daha hızlı yanıt verecek ve böylece aynı anda daha fazla istemciye hizmet verebilecektir.

İstemci/sunucu mimarisi şu şekilde olur:

Image

  • [métier] [10] katmanı, istemci tarafında [12] olarak kopyalandı;
  • istemciye yeni bir [main2] [11] komut dosyası eklenmiştir;

Web istemcisi, [3]'te bulunan vergi mükellefleri listesinin vergisini hesaplamak için iki seçeneğe sahip olacaktır:

  • önceki sürümdeki yöntemi kullanmak. Bu yöntem, sunucudaki [métier] ve [10] katmanlarını kullanır. [main] komut dosyası bu yöntemi kullanacaktır;
  • sadece sunucudan [2-4] vergi idaresi verilerini isteyip, ardından istemcide yerel olan [métier] ve [12] katmanlarını kullanmak;

İki yöntemin performanslarını karşılaştıracağız.

29.2. Web sunucusu

Web sunucusunun dizin yapısı şu şekilde olacaktır:

Image

  • [http-servers/06] klasörü, başlangıçta [http-servers/05] klasörünün kopyalanmasıyla elde edilir. Aslında önceki 10. sürümün kazanımlarını koruyacağız. Sadece ona yeni bir işlev ekleyeceğiz. Bu işlev, yeni bir denetleyici olan [get_admindata_controller] [1]'in varlığıyla somutlaşmaktadır. Diğer denetleyici olan [calculate_tax_controller] ise, eski [index_controller] denetleyicisinin yeniden adlandırılmış halinden başka bir şey değildir;

29.3. Yapılandırma

Sunucu, iki URL hizmeti sunacaktır:

  • [/calculate-tax], bir POST'in gövdesinde aktarılan vergi mükellefleri listesinin vergisini hesaplamak için. Dolayısıyla bu hizmet, önceki 10. sürümdeki URL ve [/] hizmetlerine karşılık gelir;
  • [/get-admindata], vergi idaresi verilerini içeren jSON dizesini döndürür;

[config] yapılandırması, bu URL'lerin her birini, onu işleyen denetleyiciyle ilişkilendirir:


    # web uygulamasının denetleyicileri sözlüğü
    import calculate_tax_controller, get_admindata_controller
    controllers = {"calculate-tax": calculate_tax_controller, "get-admindata": get_admindata_controller}
    config['controllers'] = controllers

29.4. Ana komut dosyası [main]

Ana komut dosyası [main], önceki sürümdeki [main] komut dosyasını yeniden yapılandırır:


# mysql veya pgres parametresi beklenir
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()

# uygulama yapılandırılıyor
import config
config = config.configure({'sgbd': sgbd})

# bağımlılıklar


# Vergi İdaresi verilerinin alınması
erreur = False
try:
    # admindata, uygulama kapsamında salt okunur bir veri olacaktır
    config["admindata"] = config["layers"]["dao"].get_admindata()
    # başarı günlüğü
    logger.write("[serveur] connexion à la base de données réussie\n")
except ImpôtsError as ex:
    # hata kaydedilir
    …

# hata oluşursa işlem durdurulur
if erreur:
    sys.exit(2)

# Flask uygulaması başlatılabilir
app = Flask(__name__)

# ana denetleyici
def main_controller() -> tuple:
    # istenen eylem alınır
    dummy, action=request.path.split('/')
    logger = None
    try:
        # günlük kaydedici
        logger = Logger(config["logsFilename"])
        # iş parçacığıyla ilişkili bir yapılandırmada saklanıyor
        thread_config = {"logger": logger}
        thread_name = threading.current_thread().name
        config[thread_name] = {"config": thread_config}
        # isteği günlüğe kaydedilir
        logger.write(f"[index] requête : {request}\n")
        # istenmişse iş parçacığını durduruyoruz
        sleep_time = config["sleep_time"]
        if sleep_time != 0:
            # Bazı iş parçacıklarının durdurulması, diğerlerinin ise durdurulmaması için duraklama süresi rastgele belirlenir
            aléa = randint(01)
            if aléa == 1:
                # duraklama öncesi günlük kaydı
                logger.write(f"[index] mis en pause du thread pendant {sleep_time} seconde(s)\n")
                # duraklama
                time.sleep(sleep_time)
        # istek, istek denetleyicisi tarafından yürütülür
        controller = config['controllers'][action]
        résultat, status_code = controller.execute(request, config)
        # ölümcül bir hata mı oluştu?
        if status_code == status.HTTP_500_INTERNAL_SERVER_ERROR:
            # uygulama yöneticisine bir e-posta gönderilir
            config_mail = config["adminMail"]
            config_mail["logger"] = logger
            SendAdminMail.send(config_mail, json.dumps(résultat, ensure_ascii=False))
        # yanıt günlüğe kaydediliyor
        logger.write(f"[index] {résultat}\n")
        # yanıt gönderiliyor
        print(résultat)
        return json_response(résultat, status_code)
    except BaseException as erreur:
        # mümkünse hatayı günlüğe kaydedilir
        if logger:
            logger.write(f"[index] {erreur}")
        # müşteriye verilecek yanıt hazırlanır
        résultat = {"réponse": {"erreurs"[f"{erreur}"]}}
        # yanıt gönderilir
        return json_response(résultat, status.HTTP_500_INTERNAL_SERVER_ERROR)
    finally:
        # Açılmışsa günlük dosyasını kapatıyoruz
        if logger:
            logger.close()

# vergi hesaplanıyor
@app.route('/calculate-tax', methods=['POST'])
@auth.login_required
def calculate_tax():
    # kontrol ana denetleyiciye devredilir
    return main_controller()

# vergi dairesinden veriler alınır
@app.route('/get-admindata', methods=['GET'])
@auth.login_required
def get_admindata():
    # kontrol ana modülüne devrediliyor
    return main_controller()

# sadece kontrol
if __name__ == '__main__':
    # sunucuyu başlat
    app.config.update(ENV="development", DEBUG=True)
    app.run(threaded=True)
  • 88-93. satırlar: [calculate_tax] işlevi, URL ve [/calculate-tax]'i işler;
  • 95-100. satırlar: [get_admindata] işlevi, URL ve [/get-admindata] işlevlerini işler;
  • bu iki işlev tek başlarına hiçbir işlem yapmaz. Hemen 37-86. satırlardaki ana denetleyici [main_controller]’e kontrolü devrederler;
  • 37-86. satırlar: [main_controller] ana denetleyicisi, bir ayrıntı dışında önceki sürümdeki [index] işlevinden başka bir şey değildir: [index] işlevi yalnızca tek bir URL'i işlerken, burada [main_controller] iki adet URL'i işlemektedir. Bu nedenle, bu dosyaların iki [calculate_tax_controller, get_admin,data_controller] denetleyicisinden biri tarafından işlenmesini sağlamalıdır;
  • 39-40. satırlar: İstenen eylem olan [calculate_tax] veya [get_admindata] alınır. Bu bilgi, URL ve [request.path] yolunda bulunur. Duruma göre, [request.path], [/get-admindata] veya [/calculate_tax] değerine eşittir. 40. satırın bölünmesi iki öğe verecektir:
    • / işaretinden önceki kısım için boş dize;
    • / işaretinden sonraki kısım için istenen eylemin adı;
  • 62-63. satırlar: URL eylemi alındıktan sonra, URL'i işlemek için hangi denetleyicinin kullanılacağı bilinir. Bu bilgiler [config] yapılandırmasında bulunur;

29.5. Denetleyiciler

[calculate_tax_controller] denetleyicisi, önceki sürümdeki [index_controller] denetleyicisinden başka bir şey değildir.

[get_admindata_controller] denetleyicisi ise şöyledir:


from flask_api import status
from werkzeug.local import LocalProxy

def execute(request: LocalProxy, config: dict) -> tuple:
    # yanıt veriliyor
    return {"réponse": {"result": config["admindata"].asdict()}}, status.HTTP_200_OK
  • URL ve [/get-admindata], vergi idaresi verilerinden jSON dizesini oluşturmalıdır;
  • 6. satır: Bu veriler ana komut dosyası [main] tarafından alınmış ve [config] sözlüğüne bir [AdminData] nesnesi biçiminde eklenmiştir. Bu nesnenin sözlüğü döndürülür;

29.6. Postman Testleri

Web sunucusu, SGBD ve e-posta sunucusu [hMailServer] başlatılır. Ardından bir Postman istemcisi kullanılarak birkaç vergi mükellefinin vergisi hesaplanır:

Image

Postman konsolunda, istemci/sunucu diyaloğu şu şekildedir:


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…]}}

Şimdi URL ve [/get-admindata]'i bir GET ile sorgulayalım:

Image

Postman konsolundaki istemci/sunucu diyaloğu şu şekildedir:


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. Web istemcisi

Image

Image

[http-clients/06] klasörü, başlangıçta [http-clients/05] klasörünün kopyalanmasıyla elde edilir. Değişiklik çalışması temel olarak şunlardan oluşur:

  • [config_layers] yapılandırmasını, artık bir [métier] katmanını içerecek şekilde değiştirmek. Daha önce sadece bir [dao] katmanı vardı;
  • [dao] katmanına yeni bir yöntem eklemek;
  • vergi mükelleflerinin vergisini hesaplamak için istemcinin [métier] katmanını kullanacak bir [main2] komut dosyası yazmak;

29.7.1. Müşteri katmanlarının yapılandırılması

Katmanların yapılandırılması iki noktada gerçekleştirilir:

  • [config] yapılandırmasında, müşterinin bağımlılıkları arasına [métier] katmanının uygulamasını içeren klasör eklenmelidir. Bu klasör, bağımlılıklar arasında zaten yer almaktaydı:

absolute_dependencies = [
        # proje klasörleri
        # 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",
        # Sabitler, dilimler
        f"{root_dir}/impots/v05/entities",
        # ImpôtsDaoWithHttpClient
        f"{script_dir}/../services",
        # yapılandırma komut dosyaları
        script_dir,
        # Günlük Kaydedici
        f"{root_dir}/impots/http-servers/02/utilities",
    ]

Ardından [config_layers] dosyası değiştirilmelidir:


def configure(config: dict) -> dict:
    # uygulama katmanlarının örneklenmesi

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

    # DAO katmanı
    from ImpôtsDaoWithHttpClient import ImpôtsDaoWithHttpClient
    dao = ImpôtsDaoWithHttpClient(config)

    # katman yapılandırması
    return {
        "dao": dao,
        "métier": métier
    }
  • 4-6. satırlar: [métier] katmanının örneklenmesi;
  • 13-16. satırlar: [métier] katmanı katman sözlüğüne eklenir;

29.7.2. [dao] katmanının uygulanması

Image

[dao] katmanı, aşağıdaki [InterfaceImpôtsDaoWithHttpClient] arayüzünü sunacaktır:


from abc import abstractmethod

from AbstractImpôtsDao import AbstractImpôtsDao

class InterfaceImpôtsDaoWithHttpClient(AbstractImpôtsDao):

    # vergi hesaplaması
    @abstractmethod
    def calculate_tax_in_bulk_mode(self, taxpayers: list):
        pass
  • 5. satır: [InterfaceImpôtsDaoWithHttpClient] arayüzü, istemcinin dosya sistemine erişimi yöneten [AbstractImpôtsDao] soyut sınıfından miras alır. Bu sınıfın [get_admindata] adlı bir soyut yöntemi olduğunu hatırlatmak isteriz;
  • 7-10. satırlar: Bir önceki sürümde tanımladığımız [calculate_tax_in_bulk_mode] yöntemi, bir vergi mükellefi listesi için vergi hesaplamasına olanak tanır;

Bu arayüz, aşağıdaki [ImpôtsDaoWithHttpClient] sınıfı tarafından uygulanmaktadır:


# ithalatlar

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):

    # üretici
    def __init__(self, config: dict):
        # üst öğenin başlatılması
        AbstractImpôtsDao.__init__(self, config)
        # yapılandırma öğelerinin kaydedilmesi
        # genel yapılandırma
        self.__config = config
        # sunucu
        self.__config_server = config["server"]
        # hata ayıklama modu
        self.__debug = config["debug"]
        # günlük kaydı
        self.__logger = None

    def get_admindata(self) -> AdminData:
        # istisnaların yukarıya iletilmesine izin verilir

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

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

        # hata ayıklama modu?
        if self.__debug:
            # günlük kaydedici
            if not self.__logger:
                self.__logger = self.__config['logger']
            # günlüğe kaydediliyor
            self.__logger.write(f"{response.text}\n")

        # durum kodu
        status_code = response.status_code
        # sözlük biçiminde sonuç
        résultat = json.loads(response.text)
        # durum kodu 200'den farklıysa OK
        if status_code != status.HTTP_200_OK:
            raise ImpôtsError(58, résultat['réponse']['erreurs'])
        # sonuç (bir sözlük) döndürülür
        return AdminData().fromdict(résultat["réponse"]["result"])

    # toplu modda vergi hesaplaması
    def calculate_tax_in_bulk_mode(self, taxpayers: list):
        # istisnaların yukarıya aktarılmasına izin verilir

        
  • 13. satır: [ImpôtsDaoWithHttpClient] sınıfı, [InterfaceImpôtsDaoWithHttpClient] arayüzünü uygular. Dolayısıyla, [AbstractImpôtsDao] sınıfından türetilmiştir;
  • 65-66. satırlar: önceki sürümde incelenen [calculate_tax_in_bulk_mode] yöntemi;
  • 29-62. satırlar: üst sınıf [AbstractImpôtsDao] tarafından soyut olarak bildirilen [get_admindata] yöntemi. Dolayısıyla bu yöntem, alt sınıfta uygulanmaktadır;
  • satır 33-35: [get-admindata] yönteminin sorgulaması gereken web hizmetinin URL'i belirlenir. Bu hizmet URL'leri, istemcinin [config] yapılandırmasında tanımlanmıştır:

       # vergi hesaplama sunucusu
        "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"
            }
        },
  • (devamı)
    • 9-12. satırlar: web sunucusundaki iki URL;
  • satır 37-44: URL hizmetine senkron bir şekilde sorgu gönderilir;
  • satır 46-42: yapılandırma gerektiriyorsa, sunucunun yanıtı günlüğe kaydedilir;
  • satır 57: sunucunun bir sözlükten jSON dizesini gönderdiği bilinmektedir;
  • satır 58-60: Yanıtın HTTP durumu 200 değilse, bir istisna tetiklenir;
  • 61-62. satırlar: Sunucu tarafından gönderilen vergi idaresi verilerini kapsayan [AdminData] nesnesi döndürülür;

29.8. [main, main2] komut dosyaları

[main] komut dosyası önceki sürümün komut dosyasıdır. [dao] katmanındaki [calculate_tax_in_bulk_mode] yöntemini kullanır ve dolayısıyla sunucunun [métier] katmanını kullanır;

[main2] komut dosyası, [main] komut dosyasıyla aynı işlevi görür, ancak istemcinin [métier] katmanını kullanır:


# uygulama yapılandırılır

import config
config = config.configure({})

# bağımlılıklar
from ImpôtsError import ImpôtsError
from Logger import Logger

logger = None
# kod
try:
    # günlük kaydı
    logger = Logger(config["logsFilename"])
    # yapılandırmada kaydediliyor
    config["logger"] = logger
    # başlangıç günlüğü
    logger.write("début du calcul de l'impôt des contribuables\n")
    # [dao] katmanını alıyoruz
    dao = config["layers"]["dao"]
    # vergi mükellefleri alınır
    taxpayers = dao.get_taxpayers_data()["taxpayers"]
    # vergi mükellefleri mi?
    if not taxpayers:
        raise ImpôtsError(36f"Pas de contribuables valides dans le fichier {config['taxpayersFilename']}")
    # Vergi idaresinin verileri alınır
    admindata = dao.get_admindata()
    # [métier] katmanı aracılığıyla vergi mükelleflerinin vergisi hesaplanıyor
    métier = config['layers']['métier']
    for taxpayer in taxpayers:
        métier.calculate_tax(taxpayer, admindata)
    # sonuçlar jSON dosyasına kaydedilir
    dao.write_taxpayers_results(taxpayers)
# BaseException hariç, hata olarak:
# # hatanın görüntülenmesi
# print(f"Aşağıdaki hata oluştu: {hata}")
finally:
    # günlük kaydedici kapatılıyor
    if logger:
        # son kayıt
        logger.write("fin du calcul de l'impôt des contribuables\n")
        # günlük kaydedici kapatılıyor
        logger.close()
    # işlem tamamlandı
    print("Travail terminé...")
  • 26-27. satırlar: vergi idaresine ait veriler sunucudan alınır;
  • 28-31. satırlar: ardından vergi mükelleflerinin vergisi yerel olarak hesaplanır;

29.9. İstemci Testleri

Her bir [main, main2] komut dosyasında, komut dosyasının başlangıcı ve bitişi günlüğe kaydedilir. Böylece komut dosyasının yürütme süresini hesaplayabiliriz. Birkaç tahminde bulunalım:

  • önceki sürümdeki [main] komut dosyası:
    • aynı anda çalışan N sayıda iş parçacığı oluşturur;
    • her iş parçacığı, sunucuya tek bir istek göndererek vergilerini hesaplattığı bir vergi mükellefi grubunu işler;
    • N iş parçacığı aynı anda çalıştığı için, N+1 numaralı istek, N numaralı isteğin yanıtını almadan önce başlatılır. Dolayısıyla N istek, tek bir istekten daha pahalıdır ancak muhtemelen çok fazla değildir. Ayrıca sunucuda 11 (vergi mükelleflerinin sayısı) iş hesaplaması yapılır;
  • bu sürümdeki [main2] komut dosyası:
    • sunucuya tek bir istek gönderir;
    • istemci tarafında yerel olarak 11 iş hesaplaması yapar;

İş hesaplamaları, ister sunucuda ister istemcide olsun, aynı sürede gerçekleşir. Aradaki fark, isteklerde ortaya çıkacaktır. Dolayısıyla, [main]'in yürütme süresinin, [main2]'inkinden biraz daha uzun olması beklenebilir.

Sürüm 11 sunucusunu, SGBD'i ve [hMailServer] e-posta sunucusunu başlatıyoruz. Sunucu tarafında, her iki testin de aynı koşullarda yürütülmesi için [sleep_time] parametresini sıfıra ayarlıyoruz.

1. Çalıştırma: [main]

[main] komutunun çalıştırılması sonucunda aşağıdaki günlük kayıtları elde edilir:


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

Çalıştırma süresi [051214-016079] nanosaniye (satır 17 – satır 1) idi, yani 35 milisaniye ve 135 nanosaniye.

Görüldüğü üzere, sunucuya yapılan ilk istek ile istemci tarafından alınan son yanıt arasında aynı süre olan [051214-016079] (satır 15 – satır 1), yani 35 milisaniye ve 135 nanosaniye geçmiştir.

2. Çalıştırma [main2]

[main2]'in çalıştırılması sonucunda aşağıdaki günlük kayıtları elde edilir:


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

Çalışma süresi [349975-303520] nanosaniye (satır 3 - satır 1) idi, yani 46 milisaniye ve 455 nanosaniye. Hiç beklenmedik bir şekilde, [main], [main2]'ten daha hızlıdır.

Görüldüğü üzere, [main2]'in tek isteği [345084-303520] (satır 2 – satır 1) sürdü, yani 41 milisaniye ve 564 nanosaniye. Vergi hesaplaması ise [349975-345084] (satır 3 – satır 2) kadar sürdü, yani 4 milisaniye ve 91 nanosaniye. Yürütme süresini belirleyen, HTTP isteğidir. Şaşırtıcı bir şekilde, burada [main2]'in tek isteğinin, [41 millisecondes]'in dört eşzamanlı isteğinden daha uzun sürdüğü görülüyor.

Sunucu tarafında günlük kayıtları şu şekildedir:


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}}}
  • 5. satır: [main] istemcisinin ilk isteği;
  • 14. satır: [main] istemcisine verilen son yanıt. İkisi arasında 6 milisaniye ve 647 nanosaniye var;
  • 15-16. satırlar: [main2] istemcisinin tek isteği. Yanıt anında gelir;