IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Web & réseau Delphi Discussion :

[D10.2] Utilisation CPU excessive sur un service Windows


Sujet :

Web & réseau Delphi

  1. #1
    Membre à l'essai
    Inscrit en
    avril 2002
    Messages
    28
    Détails du profil
    Informations forums :
    Inscription : avril 2002
    Messages : 28
    Points : 19
    Points
    19
    Par défaut [D10.2] Utilisation CPU excessive sur un service Windows
    Bonjour à tous.

    Je sèche sur un problème de CPU...
    J'ai un service Windows créé avec Delphi qui fonctionne correctement pendant quelques jours (3 à 4 jours), et soudain pour une raison inconnue, l'utilisation du CPU devient assez importante.
    Ce phénomène n’est pas généralisé sur tous les postes sur lesquels le service est installé.
    Voici une capture qui montre l’état de mon service avant :
    Nom : IMG_Without_CPU.png
Affichages : 249
Taille : 29,6 Ko
    Voici une capture de son état après quelques jours de fonctionnement :
    Nom : IMG_With_CPU.png
Affichages : 247
Taille : 30,0 Ko

    But de mon service :
    Le service est destiné à enregistrer les connexions entre un programme et un serveur SQL.
    Le programme envoie une demande au service et reçoit en retour une réponse du service.
    De plus le service gère le contenu d'un petit fichier local.

    Technique utilisée :
    Pour recevoir et répondre à la demande, j’utilise le composant « TIdTCPServer », l’événement OnExecute se charge de traiter la demande ainsi :
    La demande vient sous un format xml crypté, elle est stockée dans un objet dédié « TXMLCmd ».
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
     
    procedure TdmServiceServer.IdTCPServerExecute(AContext: TIdContext);
    var
      CommandData : string;
      Command : TXMLCmd;
      Response : TXMLCmd;
    begin
      try
        CommandData := AContext.Connection.Socket.ReadLn;
      except
        on E : Exception do
        begin
          if (E is EIdSilentException) then
            CommandData := EmptyStr
          else
            raise;
        end;
      end;
      if (CommandData <> EmptyStr) then
      begin
        // Chaque demande est exécutée dans un thread séparé, il faut initialiser
        // COM pour pouvoir utiliser XML
        CoInitializeEx(nil, COINIT_MULTITHREADED);
        try
          Command := TXMLCmd.Create;
          Command.XMLText := Uncrypt(CommandData);
     
          Response := TXMLCmd.Create;
          try
            Response.CMDId := Command.CMDId;
            Response.CMDName := Command.CMDName;
            try
              case Command.CMDId of
                CMD_CONNECT_ID :
                begin
                  ExecConnect(Command, Response);
                end;
                CMD_COMP_SUB_CONNECT_ID :
                begin
                  ExecCompSubConnect(Command, Response);
                end;
                CMD_DISCONNECT_ID :
                begin
                  ExecDisconnect(Command, Response);
                end;
                CMD_GET_SQL_CONNECTION_DATA_ID :
                begin
                  ExecGetSQLConnectionData(Command, Response);
                end;
                .
                .
                .
              else
                // Commande invalide
                Response.CMDId := CMD_ERROR_ID;
                Response.CMDName := CMD_ERROR_NAME;
                Response.ParamAsString['Message'] := Format(rsInvalidCommand, [Command.CMDName]);
              end;
              AContext.Connection.Socket.WriteLn(Crypt(Response.XMLText));
            finally
              Response.Free;
            end;
          finally
            Command.Free;
          end;
        finally
          CoUnInitialize;
        end;
      end;
    end;
    Rien de bien méchant...
    Mis à part quelques boucles "for", rien de spécial, rien qui bloque son fonctionnement, mais une consommation CPU étrange.

    Après toutes une série d'analyses, de log, je tombe sur un élément suspect, les « threads » utilisés.
    Au démarrage du service, les threads sont au nombre de 8. Ensuite, ce nombre monte et redescend de 8 à 9 à 8. Mais à un certain moment, ce nombre de threads monte et ne redescend pas…
    Par exemple, dans ma deuxième image, le nombre de Threads est à 11 et le CPU à 33.

    J'ai commencé à m'intéresser à ces Threads, mais c'est un sujet que je ne maîtrise pas vraiment.
    Dans Delphi, après avoir attaché le service depuis Delphi, j'ai une liste de Threads qui ressemble à ça :
    Nom : IMG_Delphi_Threads.png
Affichages : 250
Taille : 16,4 Ko

    Du coup, je voulais essayer d'identifier ce que font ces Threads.
    Mais comment ?
    Il y a la colonne "Location", comment je peux savoir quelle est le code attaché derrière cette info ?
    Ensuite, vu que ces "Threads" semblent augmenter, y a t-il un moyen depuis Delphi, à l'intérieure de ma programmation du service, de lister ces threads et de connaître le code exécuté ?

    Merci.

  2. #2
    Membre expert
    Avatar de pprem
    Homme Profil pro
    MVP Embarcadero - formateur&développeur Delphi, PHP et JS
    Inscrit en
    juin 2013
    Messages
    1 876
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : MVP Embarcadero - formateur&développeur Delphi, PHP et JS
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : juin 2013
    Messages : 1 876
    Points : 3 609
    Points
    3 609
    Par défaut
    Bonjour

    Les threads sont générés à chaque connexion par TIdTCPServer. Si ça s'emballe c'est qu'ils ne se terminent pas correctement ou entrent en conflit entre eux.

    Quelles sont les versions de Delphi, Indy et de windows concernées ?

    Si ça ne le fait pas partout, y a-t-il plus ou moins d'utilisateurs sur ceux qui plantent ? Des connexions réseaux instables ?

  3. #3
    Membre à l'essai
    Inscrit en
    avril 2002
    Messages
    28
    Détails du profil
    Informations forums :
    Inscription : avril 2002
    Messages : 28
    Points : 19
    Points
    19
    Par défaut
    Bonjour pprem

    Le service Windows est actuellement compilé avec Delphi 10.2 SP 3
    La version des composants Indy : 10.6.2.5366
    Sur une machine concernée, c'est Windows Serveur 2016

    Sur ce serveur, ce n'est pas une grosse installation, j'ai des serveurs où beaucoup plus d'accès sont effectués sans aucun souci.
    Je n'ai pas connaissances de soucis réseaux sur le serveur concernés. A priori pas de souci.

  4. #4
    Membre expert
    Avatar de pprem
    Homme Profil pro
    MVP Embarcadero - formateur&développeur Delphi, PHP et JS
    Inscrit en
    juin 2013
    Messages
    1 876
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : MVP Embarcadero - formateur&développeur Delphi, PHP et JS
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : juin 2013
    Messages : 1 876
    Points : 3 609
    Points
    3 609
    Par défaut
    ce serait bien d'avoir une trace des exceptions qui peuvent se produire sur la lecture

    de ce que j'ai vu en faisant une rapide recherche il semble qu'un sleep() de quelques millisecondes serait conseillé dans le cas où la lecture ne trouve rien, à faire dans le else du if global, faudrait voir le code de Indy pour savoir pourquoi cette suggestion revient en solution à chaque fois qu'un CPU est surchargé par un onExecute qui ne fait rien, peut-être que ça a été corrigé dans cette version. En tout cas, à tester.

  5. #5
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : novembre 2002
    Messages : 8 964
    Points : 29 179
    Points
    29 179
    Par défaut
    sleep c'est utilisé dans une boucle trop rapide pour permettre au processeur de souffler un peu...mais ça veux dire que la boucle n'a rien à faire...exemple dans la boucle principale d'un jeu vidéo où on veux un maximum de frames, on peut laisser une respiration à la CPU par un sleep.

    mais sinon il y a des tas d'autres solutions, comme select() pour les sockets.

    pour le cas de ce serveur, je pense qu'il va falloir passer par des logs pour savoir ce qu'il se passe car il y a probablement un des threads (on peut faire maintenant des thread nommés) qui est dans un état non prévu et qui consomme de la CPU pour rien.

    Indy est plutôt stable, je ne pense pas qu'il soit en cause directement....mais par exemple si dans un boucle tu cherches à lire sur un socket fermé en ignorant les erreurs tu peux mettre la CPU à plat.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  6. #6
    Membre à l'essai
    Inscrit en
    avril 2002
    Messages
    28
    Détails du profil
    Informations forums :
    Inscription : avril 2002
    Messages : 28
    Points : 19
    Points
    19
    Par défaut
    Merci pprem
    Je vais analyser ceci.
    J'ai fait une petite adaptation de mon code pour loguer des infos du OnExecute :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
     
    procedure TdmServiceServer.IdTCPServerExecute(AContext: TIdContext);
    var
      CommandData : string;
      Command : TXMLCmd;
      Response : TXMLCmd;
      CommandIsGood : Boolean; // <-- Ajout de cette variable
    begin
      try
        CommandData := AContext.Connection.Socket.ReadLn;
      except
        on E : Exception do
        begin
          if (E is EIdSilentException) then
          begin
            CommandData := EmptyStr;
            ABLogFileManager.LogLowDebugInfos2('EIdSilentException is run'); // <-- Je log une erreur silencieuse
          end
          else
          begin
            ABLogFileManager.LogLowDebugInfos2(E.ClassName + ' is run (Other Exception)'); // <-- Je log les autres erreurs non traitées
            raise;
          end;
        end;
      end;
      if (CommandData <> EmptyStr) then
      begin
        CommandIsGood := True;
        // Chaque demande est exécutée dans un thread séparé, il faut initialiser
        // COM pour pouvoir utiliser XML
        CoInitializeEx(nil, COINIT_MULTITHREADED);
        try
          Command := TXMLCmd.Create;
          Command.XMLText := Uncrypt(CommandData);
     
          Response := TXMLCmd.Create;
          try
            Response.CMDId := Command.CMDId;
            Response.CMDName := Command.CMDName;
            try
              case Command.CMDId of
                CMD_CONNECT_ID :
                begin
                  ExecConnect(Command, Response);
                end;
                CMD_COMP_SUB_CONNECT_ID :
                begin
                  ExecCompSubConnect(Command, Response);
                end;
                CMD_DISCONNECT_ID :
                begin
                  ExecDisconnect(Command, Response);
                end;
                CMD_GET_SQL_CONNECTION_DATA_ID :
                begin
                  ExecGetSQLConnectionData(Command, Response);
                end;
                .
                .
                .
              else
                // Commande invalide
                Response.CMDId := CMD_ERROR_ID;
                Response.CMDName := CMD_ERROR_NAME;
                Response.ParamAsString['Message'] := Format(rsInvalidCommand, [Command.CMDName]);
                CommandIsGood := False;
                ABLogFileManager.LogLowDebugInfos2(rsInvalidCommand); // <-- Je log une commande invalide (Ne devrait jamais arriver)
              end;
              AContext.Connection.Socket.WriteLn(Crypt(Response.XMLText));
            finally
              Response.Free;
            end;
          finally
            Command.Free;
          end;
        finally
          CoUnInitialize;
        end;
        if CommandIsGood then
          ABLogFileManager.LogLowDebugInfos2('CommandData is treated !'); // <-- Je log que mon process est correctement traité
      end
      else
      begin
        ABLogFileManager.LogLowDebugInfos2('Empty CommandData !'); // <-- Je log une commande vide
        Sleep(10); // <-- Je fais le Sleep 10 pour faire respirer.
      end;
    J'ai déjà un résultat de log avec des différences :
    - Sur ma machine, quelques EIdSilentException sont interceptés, mais pas énormément.
    - Sur un serveur fonctionnel, il y en a beaucoup, mais n'ont aucune incidence sur le bon fonctionnement de mon service.
    - Sur le serveur qui provoque une surcharge CPU, il y a quelques EIdSilentException, par contre l'erreur EIdSocketError est intercepté assez régulièrement (Ligne 21). Je vais voir si je trouve des infos. Mais je suis preneur de tous conseils…

    Pour l'instant pas de surcharge, je vais surveiller cela ces prochains jours. Peut-être que dans le cas d'une erreur non traitées, je peux enlever le "raise", j'explorerai cette voie si nécessaire aussi.

    Salut Paul TOTH
    Mon service n'a pas de boucle particulière, que quelques-unes et n’ont pas de souci.
    Je ne crée pas manuellement de Threads dans mon code, j’ai simplement repéré (capture no 2) que mon processus avait énormément de threads lorsque le CPU est surchargé.
    Je pose la question suivante : Depuis Delphi, est-il possible de lister tous les threads de l’application ? Et d’en connaitre la source ?

  7. #7
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : novembre 2002
    Messages : 8 964
    Points : 29 179
    Points
    29 179
    Par défaut
    pour le debugage j'utilise madExcept, il n'est pas très cher et rend des services inestimables.

    par contre quand je développe un service Windows, j'utilise toujours mon vieux code qui me permet facilement de transformer un service en application console, car elles sont en général plus simples à débuguer.

    mais tu devrais ajouter une option Verbose a ton appli qui log non seulement les erreurs mais aussi les étapes car si ton process bouffe de la CPU c'est qu'il n'est pas planté par une exception, mais qu'il bosse ... reste à savoir où

    exemple...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
     
    procedure TdmServiceServer.IdTCPServerExecute(AContext: TIdContext);
    var
      CommandData : string;
      Command : TXMLCmd;
      Response : TXMLCmd;
      CommandIsGood : Boolean; // <-- Ajout de cette variable
    begin
      if Verbose then
        ABLogFileManager.LogLowDebugInfos2('IdTCPServerExecute : $' + IntToHex(NativeInt(AContext), 8);
      try
        if Verbose then
          ABLogFileManager.LogLowDebugInfos2('IdTCPServerExecute : $' + IntToHex(NativeInt(AContext) + ' Before ReadLn', 8);
        CommandData := AContext.Connection.Socket.ReadLn;
        if Verbose then
          ABLogFileManager.LogLowDebugInfos2('IdTCPServerExecute : $' + IntToHex(NativeInt(AContext) + ' After ReadLn', 8);
      except
        on E : Exception do
        begin
          if Verbose then
            ABLogFileManager.LogLowDebugInfos2('IdTCPServerExecute : $' + IntToHex(NativeInt(AContext) + ' ReadLn failed ' + e.ClassName + ':' + e.Message, 8);
          if (E is EIdSilentException) then
          begin
            CommandData := EmptyStr;
            ABLogFileManager.LogLowDebugInfos2('EIdSilentException is run'); // <-- Je log une erreur silencieuse
          end
          else
          begin
            ABLogFileManager.LogLowDebugInfos2(E.ClassName + ' is run (Other Exception)'); // <-- Je log les autres erreurs non traitées
            raise;
          end;
        end;
      end;
      if (CommandData <> EmptyStr) then
      begin
        CommandIsGood := True;
        // Chaque demande est exécutée dans un thread séparé, il faut initialiser
        // COM pour pouvoir utiliser XML
        if Verbose then
          ABLogFileManager.LogLowDebugInfos2('IdTCPServerExecute : $' + IntToHex(NativeInt(AContext) + ' CoInitializeEx', 8);
        CoInitializeEx(nil, COINIT_MULTITHREADED);
        try
          if Verbose then
             ABLogFileManager.LogLowDebugInfos2('IdTCPServerExecute : $' + IntToHex(NativeInt(AContext) + ' Uncrypt', 8);
          Command := TXMLCmd.Create;
          Command.XMLText := Uncrypt(CommandData);
     
          Response := TXMLCmd.Create;
          try
            Response.CMDId := Command.CMDId;
            Response.CMDName := Command.CMDName;
            try
              case Command.CMDId of
                CMD_CONNECT_ID :
                begin
                  ExecConnect(Command, Response);
                end;
                CMD_COMP_SUB_CONNECT_ID :
                begin
                  ExecCompSubConnect(Command, Response);
                end;
                CMD_DISCONNECT_ID :
                begin
                  ExecDisconnect(Command, Response);
                end;
                CMD_GET_SQL_CONNECTION_DATA_ID :
                begin
                  ExecGetSQLConnectionData(Command, Response);
                end;
                .
                .
                .
              else
                // Commande invalide
                Response.CMDId := CMD_ERROR_ID;
                Response.CMDName := CMD_ERROR_NAME;
                Response.ParamAsString['Message'] := Format(rsInvalidCommand, [Command.CMDName]);
                CommandIsGood := False;
                ABLogFileManager.LogLowDebugInfos2(rsInvalidCommand); // <-- Je log une commande invalide (Ne devrait jamais arriver)
              end;
              AContext.Connection.Socket.WriteLn(Crypt(Response.XMLText));
            finally
            if Verbose then
               ABLogFileManager.LogLowDebugInfos2('IdTCPServerExecute : $' + IntToHex(NativeInt(AContext) + ' Done', 8);
              Response.Free;
            end;
          finally
            Command.Free;
          end;
        finally
          CoUnInitialize;
          if Verbose then
             ABLogFileManager.LogLowDebugInfos2('IdTCPServerExecute : $' + IntToHex(NativeInt(AContext) + ' CoUnInitialize', 8);
        end;
        if CommandIsGood then
          ABLogFileManager.LogLowDebugInfos2('CommandData is treated !'); // <-- Je log que mon process est correctement traité
      end
      else
      begin
        ABLogFileManager.LogLowDebugInfos2('Empty CommandData !'); // <-- Je log une commande vide
        Sleep(10); // <-- Je fais le Sleep 10 pour faire respirer.
      end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  8. #8
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    septembre 2008
    Messages
    5 565
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : septembre 2008
    Messages : 5 565
    Points : 12 781
    Points
    12 781
    Par défaut
    Mais ça ne va pas donner beaucoup d'informations sur le problème, on voit bien que certaines tâches ne se terminent pas et tournent plein pot (les 3 tâches de ton exemple sur un système 8 cœurs = ~36%).

    Tu as un problème d'accès concurrents. Ca fonctionne tant que les connexions se font une à une mais bloque (et tourne en boucle) dès que tu as plusieurs demandes simultanées. C'est vraiment les différentes procédures qu'il faudrait nous montrer.

    Sinon, tes blocs try..finally ne sont pas forcément bien placés. Par exemple si une erreur survenait au déchiffrage de la commande ou à la création de Response, Command ne sera pas libéré.

  9. #9
    Membre à l'essai
    Inscrit en
    avril 2002
    Messages
    28
    Détails du profil
    Informations forums :
    Inscription : avril 2002
    Messages : 28
    Points : 19
    Points
    19
    Par défaut
    Paul TOTH
    Merci, j'ai placé ce Verbose dans mon code.
    J'ai fait un premier test sur ma machine pour sortir le résultat du log.
    Ca donne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    16.08.2022 16:59:29:100 ------------------------ Start $00F9CBB0------------------------
    16.08.2022 16:59:29:101 IdTCPServerExecute : $00F9CBB0
    16.08.2022 16:59:29:101 IdTCPServerExecute : $00F9CBB0 Before ReadLn
    16.08.2022 16:59:29:102 IdTCPServerExecute : $00F9CBB0 After ReadLn
    16.08.2022 16:59:29:102 IdTCPServerExecute : $00F9CBB0 CoInitializeEx
    16.08.2022 16:59:29:103 IdTCPServerExecute : $00F9CBB0 Uncrypt
    16.08.2022 16:59:29:104 IdTCPServerExecute : $00F9CBB0 CMDId : 1
    16.08.2022 16:59:29:104 IdTCPServerExecute : $00F9CBB0 Crypt
    16.08.2022 16:59:29:105 IdTCPServerExecute : $00F9CBB0 Done Response.Free
    16.08.2022 16:59:29:106 IdTCPServerExecute : $00F9CBB0 Done Command.Free
    16.08.2022 16:59:29:106 ------------------------ Start $00F9CCF0------------------------
    16.08.2022 16:59:29:107 IdTCPServerExecute : $00F9CCF0
    16.08.2022 16:59:29:107 IdTCPServerExecute : $00F9CCF0 Before ReadLn
    16.08.2022 16:59:29:108 IdTCPServerExecute : $00F9CBB0 CoUnInitialize
    16.08.2022 16:59:29:108 IdTCPServerExecute : $00F9CBB0 CommandData is treated !
    16.08.2022 16:59:29:108 ------------------------ End   $00F9CBB0------------------------
    16.08.2022 16:59:29:109 IdTCPServerExecute : $00F9CCF0 After ReadLn
    16.08.2022 16:59:29:109 IdTCPServerExecute : $00F9CCF0 CoInitializeEx
    16.08.2022 16:59:29:110 IdTCPServerExecute : $00F9CCF0 Uncrypt
    16.08.2022 16:59:29:110 IdTCPServerExecute : $00F9CCF0 CMDId : 8
    16.08.2022 16:59:29:111 IdTCPServerExecute : $00F9CCF0 Crypt
    16.08.2022 16:59:29:112 IdTCPServerExecute : $00F9CCF0 Done Response.Free
    16.08.2022 16:59:29:112 IdTCPServerExecute : $00F9CCF0 Done Command.Free
    16.08.2022 16:59:29:112 IdTCPServerExecute : $00F9CCF0 CoUnInitialize
    16.08.2022 16:59:29:113 IdTCPServerExecute : $00F9CCF0 CommandData is treated !
    16.08.2022 16:59:29:114 ------------------------ End   $00F9CCF0------------------------
    16.08.2022 17:00:29:101 ------------------------ Start $00F9C2A0------------------------
    16.08.2022 17:00:29:103 IdTCPServerExecute : $00F9C2A0
    16.08.2022 17:00:29:103 IdTCPServerExecute : $00F9C2A0 Before ReadLn
    16.08.2022 17:00:29:104 IdTCPServerExecute : $00F9C2A0 After ReadLn
    16.08.2022 17:00:29:104 IdTCPServerExecute : $00F9C2A0 CoInitializeEx
    16.08.2022 17:00:29:105 IdTCPServerExecute : $00F9C2A0 Uncrypt
    16.08.2022 17:00:29:105 IdTCPServerExecute : $00F9C2A0 CMDId : 1
    16.08.2022 17:00:29:106 IdTCPServerExecute : $00F9C2A0 Crypt
    16.08.2022 17:00:29:107 IdTCPServerExecute : $00F9C2A0 Done Response.Free
    16.08.2022 17:00:29:107 IdTCPServerExecute : $00F9C2A0 Done Command.Free
    16.08.2022 17:00:29:108 IdTCPServerExecute : $00F9C2A0 CoUnInitialize
    16.08.2022 17:00:29:108 IdTCPServerExecute : $00F9C2A0 CommandData is treated !
    16.08.2022 17:00:29:109 ------------------------ End   $00F9C2A0------------------------
    16.08.2022 17:00:29:109 ------------------------ Start $00F9CB60------------------------
    16.08.2022 17:00:29:110 IdTCPServerExecute : $00F9CB60
    16.08.2022 17:00:29:110 IdTCPServerExecute : $00F9CB60 Before ReadLn
    16.08.2022 17:00:29:111 IdTCPServerExecute : $00F9CB60 After ReadLn
    16.08.2022 17:00:29:111 IdTCPServerExecute : $00F9CB60 CoInitializeEx
    16.08.2022 17:00:29:111 IdTCPServerExecute : $00F9CB60 Uncrypt
    16.08.2022 17:00:29:112 IdTCPServerExecute : $00F9CB60 CMDId : 8
    16.08.2022 17:00:29:113 IdTCPServerExecute : $00F9CB60 Crypt
    16.08.2022 17:00:29:113 IdTCPServerExecute : $00F9CB60 Done Response.Free
    16.08.2022 17:00:29:114 IdTCPServerExecute : $00F9CB60 Done Command.Free
    16.08.2022 17:00:29:114 IdTCPServerExecute : $00F9CB60 CoUnInitialize
    16.08.2022 17:00:29:115 IdTCPServerExecute : $00F9CB60 CommandData is treated !
    16.08.2022 17:00:29:115 ------------------------ End   $00F9CB60------------------------
    16.08.2022 17:01:29:115 ------------------------ Start $00F9CB10------------------------
    16.08.2022 17:01:29:116 IdTCPServerExecute : $00F9CB10
    16.08.2022 17:01:29:117 IdTCPServerExecute : $00F9CB10 Before ReadLn
    16.08.2022 17:01:29:117 IdTCPServerExecute : $00F9CB10 After ReadLn
    16.08.2022 17:01:29:118 IdTCPServerExecute : $00F9CB10 CoInitializeEx
    16.08.2022 17:01:29:118 IdTCPServerExecute : $00F9CB10 Uncrypt
    16.08.2022 17:01:29:119 IdTCPServerExecute : $00F9CB10 CMDId : 1
    16.08.2022 17:01:29:120 IdTCPServerExecute : $00F9CB10 Crypt
    16.08.2022 17:01:29:121 IdTCPServerExecute : $00F9CB10 Done Response.Free
    16.08.2022 17:01:29:122 IdTCPServerExecute : $00F9CB10 Done Command.Free
    16.08.2022 17:01:29:122 ------------------------ Start $00F9CCF0------------------------
    16.08.2022 17:01:29:123 IdTCPServerExecute : $00F9CCF0
    16.08.2022 17:01:29:123 IdTCPServerExecute : $00F9CCF0 Before ReadLn
    16.08.2022 17:01:29:124 IdTCPServerExecute : $00F9CB10 CoUnInitialize
    16.08.2022 17:01:29:125 IdTCPServerExecute : $00F9CB10 CommandData is treated !
    16.08.2022 17:01:29:125 ------------------------ End   $00F9CB10------------------------
    16.08.2022 17:01:29:126 IdTCPServerExecute : $00F9CCF0 After ReadLn
    16.08.2022 17:01:29:126 IdTCPServerExecute : $00F9CCF0 CoInitializeEx
    16.08.2022 17:01:29:127 IdTCPServerExecute : $00F9CCF0 Uncrypt
    16.08.2022 17:01:29:127 IdTCPServerExecute : $00F9CCF0 CMDId : 8
    16.08.2022 17:01:29:128 IdTCPServerExecute : $00F9CCF0 Crypt
    16.08.2022 17:01:29:129 IdTCPServerExecute : $00F9CCF0 Done Response.Free
    16.08.2022 17:01:29:129 IdTCPServerExecute : $00F9CCF0 Done Command.Free
    16.08.2022 17:01:29:130 IdTCPServerExecute : $00F9CCF0 CoUnInitialize
    16.08.2022 17:01:29:130 IdTCPServerExecute : $00F9CCF0 CommandData is treated !
    16.08.2022 17:01:29:131 ------------------------ End   $00F9CCF0------------------------
    16.08.2022 17:02:29:128 ------------------------ Start $00F9C2F0------------------------
    16.08.2022 17:02:29:129 IdTCPServerExecute : $00F9C2F0
    16.08.2022 17:02:29:129 IdTCPServerExecute : $00F9C2F0 Before ReadLn
    16.08.2022 17:02:29:130 IdTCPServerExecute : $00F9C2F0 After ReadLn
    16.08.2022 17:02:29:130 IdTCPServerExecute : $00F9C2F0 CoInitializeEx
    16.08.2022 17:02:29:131 IdTCPServerExecute : $00F9C2F0 Uncrypt
    16.08.2022 17:02:29:132 IdTCPServerExecute : $00F9C2F0 CMDId : 1
    16.08.2022 17:02:29:133 IdTCPServerExecute : $00F9C2F0 Crypt
    16.08.2022 17:02:29:134 IdTCPServerExecute : $00F9C2F0 Done Response.Free
    16.08.2022 17:02:29:134 IdTCPServerExecute : $00F9C2F0 Done Command.Free
    16.08.2022 17:02:29:135 IdTCPServerExecute : $00F9C2F0 CoUnInitialize
    16.08.2022 17:02:29:136 IdTCPServerExecute : $00F9C2F0 CommandData is treated !
    16.08.2022 17:02:29:136 ------------------------ End   $00F9C2F0------------------------
    Il y a bien accès concurrentiel, mais à première vue cela ne génère pas d'erreur sur ma machine.
    Je vais poser mon service sur le serveur incriminé pour examiner ce log.
    Pourtant, le Command.Free, Command comme Response sont juste un petit TObject avec quelques infos sur le contenu xml lu via le ReadLn.

    Si c'est cet accès concurrentiel qui pose problème, je testerai l'ajout d'une TCriticalSection pour voir...

    Andnotor
    Salut,
    J'ai déjà fait des logs des différentes procédures, et aucune ne provoque de boucle infinie, j'ai toujours le départ et la fin de la procédure.
    En effet, j'ai les Try Finally qui ne sont pas au bon endroit. Le service à plusieurs années, jamais fait gaffe à cela.
    Je vais de ce pas les corriger.

    En fait, je n'ai pas de réponse concernant ma question principale :
    Peut-on lister les Threads utilisé et en déterminer le code de ces Threads ?

    Je poursuis mes investigations, c'est assez délicat car vu que le souci de CPU arrive qu’à partir du 3ème ou 4ème jour d’exécution ininterrompue, je dois attendre pour analyser le log.
    D’ailleurs, l’erreur EIdSocketError que je reçois sur ce serveur me laisse perplexe… Pourquoi il y a cette erreur ? Une idée ? Par exemple un Antivirus Kasperky par exemple (installé sur le serveur), qui pourrait embêter ou autre chose… ?
    En tout cas, un grand merci à tous pour vos aides.

  10. #10
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : novembre 2002
    Messages : 8 964
    Points : 29 179
    Points
    29 179
    Par défaut
    Citation Envoyé par Yvan_F1 Voir le message
    ...

    En fait, je n'ai pas de réponse concernant ma question principale :
    Peut-on lister les Threads utilisé et en déterminer le code de ces Threads ?
    je ne sais pas, pour cela j'utilise madExcept, il peut te retourner l'état de tous les threads http://help.madshi.net/madStackTraceUnit.htm

    Citation Envoyé par Yvan_F1 Voir le message
    Je poursuis mes investigations, c'est assez délicat car vu que le souci de CPU arrive qu’à partir du 3ème ou 4ème jour d’exécution ininterrompue, je dois attendre pour analyser le log.
    j'ai un service qui plante comme ça au bout de quelques jours, comme je n'ai pas trouvé d'autre solution, j'ai programmé un redémarrage du service tous les matins à 2h...oui c'est pas très propre mais c'est super efficace en attendant que je puisse trouver la raison du problème (c'est un service Linux pour lequel je n'ai pas madExcept).

    Citation Envoyé par Yvan_F1 Voir le message
    D’ailleurs, l’erreur EIdSocketError que je reçois sur ce serveur me laisse perplexe… Pourquoi il y a cette erreur ? Une idée ? Par exemple un Antivirus Kasperky par exemple (installé sur le serveur), qui pourrait embêter ou autre chose… ?
    les erreurs Indy peuvent être dues à un tas de choses...par exemple la boucle de lecture sur un socket qui a été fermé de l'autre côté, donc ce ne sont pas toujours des erreurs anormales.

    Les antivirus agissent surtout sur les connexions, pas trop sur les échanges une fois connecté car ça nécessite une analyse de protocole.

    mais dans la vraie vie tu as des fois des trucs qui rendent fou...comme les LiveBox par exemple qui sont des vraies merdes en boîte...ou (histoire vécue) un câble réseau rongé par un rat...ça te génère des erreurs imprévisibles et impossibles à identifier
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  11. #11
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    septembre 2008
    Messages
    5 565
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : septembre 2008
    Messages : 5 565
    Points : 12 781
    Points
    12 781
    Par défaut
    Citation Envoyé par Yvan_F1 Voir le message
    Peut-on lister les Threads utilisé et en déterminer le code de ces Threads ?
    Gère cette liste toi-même. Un TDictionary avec comme index ThreadID et un record contenant entre autre la commande et surtout l'heure de démarrage. Ajout à l'entrée de la méthode, suppression à la sortie et sauvegarde toutes les minutes dans un fichier. Tu verras rapidement quelle commande dure des plombes.


    Et pourquoi ne te fais-tu pas un client de test, lancé plusieurs fois (ou multi-threads) histoire de simuler plusieurs connexions et stresser ton service ? Si tu dois attendre 3/4 jours entre chaque changement de virgule, t'es pas arrivé

  12. #12
    Membre à l'essai
    Inscrit en
    avril 2002
    Messages
    28
    Détails du profil
    Informations forums :
    Inscription : avril 2002
    Messages : 28
    Points : 19
    Points
    19
    Par défaut
    Nickel, avec vos aides, j'ai de la matière à traiter pour analyser.

    Je vais mettre un TDictionary et le faire évoluer à la volée pour avoir un autre moyen qu'un long fichier log à lire.
    J'ai déjà les clients pour faire les appels au service, mais il est vrai que je n'ai jamais pensé à faire un client pour stresser le service, ça peut être une piste aussi.
    Merci pour les idées Andnotor.

    Merci aussi à Paul TOTH.
    En effet, en dernier recours, j'ai pensé mettre un redémarrage auto, mais celui-ci ne sera mis qu'en place que lorsque je n'aurai plus d'autres voies pour solutionner.
    Pour l’instant, je vais analyser les logs en cours, et ensuite, si nécessaire, je regarderai MADExcept. J’ai Eurekalog, peut-être que je peux m’y inspirer aussi.
    Je vous tiendrai au courant.

    Merci.

  13. #13
    Membre à l'essai
    Inscrit en
    avril 2002
    Messages
    28
    Détails du profil
    Informations forums :
    Inscription : avril 2002
    Messages : 28
    Points : 19
    Points
    19
    Par défaut
    Bonjour à tous.

    Je voulais simplement vous informer que j'ai résolu le problème de surcharge du CPU.
    Chacun d'entre vous m'avez porté une aide précieuse et je vous en remercie tous.

    Finalement, c'est la solution de pprem qui m'a permis de corriger.
    Le sleep de 10 à la fin du code si la lecture ne trouve rien, a bel et bien corrigé le souci.

    Du coup, si cela peut vous aider, un petit moment de pause, même pour les machines, ça peut être profitable !

  14. #14
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    juillet 2006
    Messages
    13 202
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : juillet 2006
    Messages : 13 202
    Points : 24 211
    Points
    24 211
    Par défaut
    Par hasard, tu n'aurais pas un logiciel de monitoring, un outil de supervision qui vérifie que des programmes tournent correctement ?
    Et cela inclut la disponibilité de port TCP ouvert qui accepte les connexions ?

    Ce superviseur, se connecte, n'envoie rien et se déconnecte ... résultat, il se déconnecte tellement vite que tu n'as pas fini de traiter sa connexion qu'il est déjà plus là !

    Non parce que comment tu expliques une absence de donnée ?
    En fait le Readln lit jusqu'à trouvé le EOL ou que le socket se coupe.

    Alors avec un TServerSocket au lieu d'un TIdTCPServer
    J'ai été victime de cela, cela me laissait des sockets ouvertes, une toutes les 30s, donc des threads, j'ai changé mon code pour éviter que le traitement de la déconnexion puisse se faire durant le traitement de la connexion ... oui aussi parce que j'utilise TServerSocket.OnGetThread (mes threads, rien d'implicite) et que ce composant gère la déconnexion (OnClientDisconnect) dans un autre thread que le listen (ça je ne l'avais pas vu venir)
    En D6, TIdTCPServer en mode Thread ne m'avait pas convaincu.
    Je ne l'ai plus vraiment travaillé moi-même depuis

    Actuellement, je maintiens un projet en TIdTCPServer avec comme client du Delphi et PHP, chaque commande c'est une connexion, une commande c'est un protocol maison qui peut contenir, une structure maison comme du XML, JSON, objet PHP sérialisé.
    Je maintiens, je n'ai pas conçu et je n'ai jamais vu de Sleep en cas de non lecture, et ça tourne des mois entiers dans l'état, des millions de requête par jour (répartiteur de charge, plusieurs lignes ...) ... cela n'a vraiment pas le temps de laisser un thread attendre 10s pour rien (tout ce temps, le socket est présent, sous windows, le nombre de socket ouvert à un instant T est limité)

    Et c'est sembles-t-il c'est comme toi, c'est un seul XML à la fois, une connexion, une envoie de donnée, une déconnexion et cela à chaque fois !
    Que ce soit un client Delphi ou PHP, même consigne : "cnx, send, dcnx"


    Pour un plus ancien projet traitant 20 messages de socket par seconde en situation de PROD en Mars 2003, Delphi 5 puis 6, P4 Desktop banal,
    Le client en C++ (API Socket de l'OS RTX de Venturcom en sous-couche de Windows, un PC industriel NT4) ouvraient deux sockets chacune sur un port différent (Port de commande, Port de Statut),
    la gestion de la lecture n'avait rien à voir, fallait supporter des messages partiels, des messages sur plusieurs buffers, un reliquat de message à propager sur les lectures suivantes ...
    J'ai testé à cet époque un stress test avec le maximum de message que possible, la connexion permanente, imbattable en terme de perf ... mais fallait gérer totalement différemment, on lit dans un thread dédié au socket, on met en FIFO, un pool de thread dépile la FIFO et en rempli une autre, un pool de thread dépile les réponses pour les envoyer.
    En mode connexion permanente, faut pas traiter dans le thread de lecture du socket, si le traitement est trop long, le buffer du socket peut se remplir plus vite que l'on peut le dépiler, d'où l'importance d'aspirer la donnée à fond et laisser les pools traités parallèlement.

    Spontanément, depuis 2001, je choisi la connexion permanente car c'est ce que j'ai toujours connu, en fait ce qui était demandé par les programmes tiers*, en full asynchrone.
    Je suis intrigué de ce choix de la connexion TCP ponctuelle, un peu comme celle pour une requête HTTP en synchrone.
    Pourquoi cette façon de faire ?




    *j'ai un début de réponse à ma propre question : C'est plus simple
    un Tiers qui connectait lui 5 sockets sur 5 ports, un par type de message, 3 pour les émettre, 2 pour recevoir
    Je me suis rendu compte que le tiers ne savait pas géré plusieurs messages au sein d'une même trame, ni un message réparti sur plusieurs trames (ça heureusement cela n'arrivait jamais)
    Donc il ne savait pas lui même géré ce qu'il avait demandé ... j'étais apprenti et pourtant j'ai tout de suite constaté ce phénomène et le besoin de le traiter.
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  15. #15
    Membre à l'essai
    Inscrit en
    avril 2002
    Messages
    28
    Détails du profil
    Informations forums :
    Inscription : avril 2002
    Messages : 28
    Points : 19
    Points
    19
    Par défaut
    Non, aucun logiciel de monitoring.

    Je ne sais pas précisément de quand date l'architecture du service, mais c'est ~2007.
    Du coup, je ne connais pas vraiment les motivations précises de la voie technique prise.

    J'ai repris le flambeau il y a quelques années.
    Mais cela me semble correcte de ne pas créer une connexion permanente.
    Le service doit simplement répondre à une demande qui provient de différents poste client, sans garder de trace de connexion...

    Ce service est en place chez sur un grand nombre de serveur, et ceci depuis 2007, et le souci du CPU s'est présenté dernièrement que sur 2 serveurs.
    Pourtant, nous avons analysé les serveurs et nous n'avons rien trouvé de particulier entre ceux qui n'ont pas le souci et ceux qui ont le souci.
    Sûrement qu'on passe à côté de quelque-chose, mais malheureusement, nous n'avons pas trouvé quoi.

    Ensuite, je devais avancer sur le sujet et trouver une solution pour au minimum contourner le problème.
    Après les différentes analyses proposées dans les messages précédents, la seule qui est restée efficace a été le slepp.
    J'ai quand même gardé une option pour faire un Log étendu, gardé un projet de StressTest, etc.
    Tout ce qui m'a été apporté sur cette discussion a été bénéfique.
    Maintenant, c'est aussi financier, je ne peux plus vraiment m'étendre sur ce problème, ou du moins pas, jusqu'à la prochaine faille

    Merci.

  16. #16
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    juillet 2006
    Messages
    13 202
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : juillet 2006
    Messages : 13 202
    Points : 24 211
    Points
    24 211
    Par défaut
    Un élément de réponse c'est que la fréquence des messages est faible, c'est sur qu'une connexion permanente pour un pauvre message à la minute, ce n'est pas utile.

    Et j'ai souvent fait des systèmes de notifications, c'est donc l'inverse, c'est le client qui surveille si on lui envoie un message, comme une sorte d'abonnement.
    On peut le faire avec une abonnement non permanent mais un implique la création de serveur sur l'abonné au lieu d'avoir le serveur côte emetteur.

    Ou lorsque le client est une technologie Web à base de HTTP qui par définition n'est pas un mode connecté permanent puisque c'est requête par requête.


    Fort pénible ces problèmes qui ne se produisent que sur une plateforme et pas sur les millier d'autres, j'ai un soucis similaire en ce même moment mais plutôt niveau métier (Option régionale dans un pool de connexion SQL)
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Utiliser une SoapExtension sur certains services et pas les autres
    Par kheironn dans le forum Développement Web avec .NET
    Réponses: 0
    Dernier message: 19/01/2018, 12h10
  2. Recherche de tutoriaux sur les services Windows
    Par talrashha dans le forum Services Windows
    Réponses: 2
    Dernier message: 04/10/2010, 11h04
  3. Réponses: 5
    Dernier message: 22/08/2008, 10h59
  4. Début sur un service Windows
    Par zooffy dans le forum Windows Forms
    Réponses: 0
    Dernier message: 24/08/2007, 16h16
  5. Question sur les services windows
    Par bilb0t dans le forum Windows
    Réponses: 8
    Dernier message: 09/11/2005, 15h31

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo