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:

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

- [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(0, 1)
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:

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:

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


[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ı

[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(36, f"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;