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

API, COM et SDKs Delphi Discussion :

Serveur DCOM MultiThread


Sujet :

API, COM et SDKs Delphi

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Développeur C++/Delphi
    Inscrit en
    Février 2012
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur C++/Delphi

    Informations forums :
    Inscription : Février 2012
    Messages : 7
    Par défaut Serveur DCOM MultiThread
    Bonjour,

    Je souhaite réaliser une application serveur DCOM (out of process). Elle dispose d’une interface d’échange avec les objets COM clients qui dispose de plusieurs fonctions. Notamment une fonction qui lance l’exécution d’un sleep d’une durée passée en paramètre.

    Actuellement, je suis parvenu à réaliser ce serveur COM avec cette fonction « wait » qui sérialise les appels clients. Les « waits » s’exécutent les uns après les autres sur mon serveur de type MultiInstance, tmApartment.
    Mon objectif maintenant est de pouvoir, sur une seule instance de l’exécutable du serveur effectuer plusieurs « waits » en parallèle.
    Pour cela je suis passé en tmfree (multi-threaded apartment). Je parviens à être réentrant et donc à recevoir les différents appellent de la fonction par le client. Je lance ensuite des threads secondaires qui attendent le temps souhaité pour chaque fonction. Mon thread principal possède une boucle d’attente (qui réalise des processmessages rendant ainsi l’a fonction réentrante) qui attend un message envoyé par les threads qui se terminent.

    A la fin de chaque thread je sort de ma fonction principale.
    Mon problème est que le lancement des threads et des boucles d’attentes se font toujours dans le thread principal de mon application. Les appels de fonction de mon interface COM s’empilent donc et se dépilent à chaque fin de fonction. La première fonction à se terminer est donc toujours la dernière appelée quelque soit son temps d’attente. Cela semble correspondre à cette situation dans la FAQ sur COM/OLE/ATL :

    DCOM ne garantit pas qu'il n'y ait qu'une seule requête en cours d'exécution à chaque instant, même dans un appartement monothreadé. En effet, les méthodes des composants peuvent parfaitement être appelées de manière réentrante. Ces appels peuvent être faits de manière interne par le composant, ou provenir du traitement d'un autre message de la boucle des messages. Ce dernier cas suppose que le thread de l'appartement retourne dans cette boucle, situation qui se produit dès qu'un appel à destination d'un objet d'un autre appartement est fait. Dans ce cas, le thread de l'appartement peut parfaitement recevoir le message provenant d'une requête d'exécution d'une autre méthode, ou de la même méthode qu'il était en train de traiter. DCOM ne contrôle pas cette situation, et tous les composants DCOM doivent donc être réentrants.

    Or, ce que je souhaiterais, c’est que plutôt que les appels réentrants s’empilent, ils soient directement traités dans des threads différents. Je veux que le premier terminé soit le premier sorti quelque soit l’ordre des appels. Et qu’ils n’arrivent donc pas tous dans le même thread principal. Comment puis-je réaliser cela ? Comment puis-je rendre mon serveur multithreadé ? Comment créer les threads appelables par COM ? Comment les appels peuvent ils être dispatchés sur les threads disponibles ?

    Merci pour votre aide.

    Cordialement,

  2. #2
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 086
    Par défaut
    Je ne connais pas énormément le DCOM où je travaille, je l'utilise mais sans plus de compréhension, d'ailleurs comme c'est pénible lors du déploiement (droit windows, UAC, firewall...), on a passé cela en DataSnap, juste le port 211 à ouvrir, bien plus simple, pas tous ces trucs d'interactivité, de droit DCOM, quand je déploie DCOM, j'applique une procédure ultra violente sur les droits windows car la pratique veut que le bouton qui devrait le faire automatiquement ne fonctionne plus ou seulement dans de rares cas !
    Sans parler des fois où Windows se montre capricieux, où il faut dans dcomcfng changer l'utilisateur interactif en l'utilisateur executant, appliquer puis retour en utilisateur interactif, et pouf ça fonctionne mais pourquoi on sait pas

    Nous sommes en mode basique, on traite les messages dans l'ordre d'arrivée !
    Par contre, on a plusieurs threads avec des TthreadList, on crée un nouveau struct qui indique le traitement à effectuer, on ajoute cela dans le TthreadList, un TEvent réveil le Thread de traitement, et l'on rend la main à l'appelant, le client est ainsi bloquer le moins de temps que possible

    le return de la function DCOM n'est souvent qu'un ACK, c'est via un event (une sorte de callback) que l'on va fournir une réponse avancée, cela permet d'avoir un traitement asynchrone entre les clients et le serveur
    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

  3. #3
    Nouveau membre du Club
    Homme Profil pro
    Développeur C++/Delphi
    Inscrit en
    Février 2012
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur C++/Delphi

    Informations forums :
    Inscription : Février 2012
    Messages : 7
    Par défaut
    Merci pour le conseil.

    Dans mon cas, le client et le serveur sont deux processus qui tournent en local sur la même machine. Ça limite déjà les problèmes de droit d'utilisateur.

    Ta proposition est bonne, mais j'ai oublié de mentionner une chose importante. le but de ce "wait" est de bloquer le client pendant un certain temps. Je ne peux donc pas lancer la fonction dans un thread et libérer le client.

  4. #4
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 086
    Par défaut
    Citation Envoyé par Morgar35 Voir le message
    M
    Ta proposition est bonne, mais j'ai oublié de mentionner une chose importante. le but de ce "wait" est de bloquer le client pendant un certain temps. Je ne peux donc pas lancer la fonction dans un thread et libérer le client.
    Effectivement, je ne peux pas t'aider, je cherche à faire systématiquement l'inverse, avec des traitements asynchrones via une gestion de tokens, FIFO\threads et échanges de notification

    Je ne bloque jamais un client, au pire, une fenêtre de patience avec une barre de progression bidon ou une animation pour faire patientez l'utilisateur si sa demande nécessite une attente d'une réponse asynchrone ou alors si c'est une requête de données, ça c'est bloquant mais on fourni généralement un faible volume de données et l'on est actuellement limité à 20 postes, limite que je suis en train de changer justement, donc le traitement séquentiel jusqu'à présent n'a jamais posé problème

    Si la réponse ne vient pas au bout d'un timeout, selon l'opération, cela retente ou abandonne l'opération, il y a un panneau de gestion des commandes et leur avancement
    C'est mon collègue qui a développé cela il y a presque déjà 10 ans !
    Je ne suis qu'un utilisateur de l'existant
    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

  5. #5
    Nouveau membre du Club
    Homme Profil pro
    Développeur C++/Delphi
    Inscrit en
    Février 2012
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur C++/Delphi

    Informations forums :
    Inscription : Février 2012
    Messages : 7
    Par défaut
    J'ai lu quelque part que :

    La souche serveur (“server stub”):
    Procédure coté serveur qui reçoit le message d ’appel,
    Fait réaliser l’exécution sur le site serveur par la procédure serveur
    Récupère les résultats et retransmet les résultats par message.
    Il semblerait que les serveurs DCOM génèrent eux même un stub. Est-il possible via un marshalling de créer moi-même un stub pour pouvoir répartir les appels clients sur différents threads de mon application serveur? si oui comment?

    Il s'agirait de trouver une solution pour gérer manuellement la répartition des appels clients sur des threads de mon serveur.

    Une autre solution serait à la sortie de mes fonctions de changer l'ordre des retours de fonctions. Soit en modifiant ma pile d'appel soit via un système de messages qu'utiliserait DCOM. Il s'agirait cette fois de forcer la réponse vers le client souhaité même si ce n'est pas l'appelant (changer l'ordre des appelants avant de dépiler...). Je n'ai aucune idée de commet je pourrais réaliser une telle chose...

    Y-a-t-il un expert COM/DCOM qui vagabonderait par hasard sur le site?

  6. #6
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 086
    Par défaut
    Dans le code que je maintiens, on a un système d'abonnement !
    Le Client A provoque appel une méthode Toto et Titi du server
    le serveur gère une liste d'interface event qui sont abonnés à des nofitications, ainsi ils peuvent être averti si Toto ou Titi a été appelé, par qui, quand, avec quel paramètre
    C'est le métier de supervision au projet qui nécessite une telle architecture de notification

    le code plus bas est une version extrement simplifié du système de notification, car on ne notifie pas juste l'appelant mais tous les abonnés

    Pour le Marshatruc du DCOM, je te laisse explorer
    Faudrait peut-être créér plusieurs interfaces COM, lors du CoCreateInstance, tu peux créer un tableau de GUID pour créer d'un coup plusieurs objets résidents dans ton serveur DCOM
    possible que tu puisse avoir de la concurrence entre les différentes objets, je t'avoue que je n'ai jamais trop étudié les trucs sur lethread apartment - Choix d'un modèle de thread

    D'ailleurs, tu peux faire plusieurs objets, là où je travaille, on 5 EXE contenant je crois 12 interfaces COM out-process et 10 interfaces d'évènement,
    En général 3 sont invoqués en permanence, chacun un rôle précis,
    le 4 eme est appelé par une autre partie de l'appli, selon conf, ponctuel ou permanent,
    le 5eme est toujours appelé ponctuellement

    Avec 5exe, la gestion des threads est tout naturellement lié à la séparation même des processus !


    Pour changer l'ordre des retours, euh je crois que tu délires un peu, le DCOM ce n'est juste une pil d'appel, il y a tout un mécansime de proxy, de sérialisation des données, de répartition de charge sur les ports TCP\UDP utilisés par le DCOM ...

    Voici une variante en laissant le mode classique séquentiel du COM et un système de CallBack d'Event COM

    Encore une fois, tout cela c'est pour que le client ne soit pas bloqué
    Si tu veux un mode bloquant, il n'y a rien à développer en plus !

    Je pense que tu prends la tête pour les ré-entrances qui avec un code robuste de devrait pas poser problème


    tu as une méthode A et B

    appel Iserver.A, il provoquera un iserverEvent.A
    appel Iserver.B, il provoquera un iserverEvent.B

    Ton code contient un thread A et un thread B
    Chaque thread contient une TThreadList

    Code approximatif :

    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
    funcion ServerImpl.A(Param1, Param2): Integer;
    var
      ACall: TACall; // un TObject; hérite d'un TCallCustom
    begin
      ACall := TACall.Create(Param1, Param2);  // implicitement Token++;
      ThreadA.Add(ACall); // ThreadA est ThreadAction
      Result := ACall.Token;
    end;
     
    funcion  ServerImpl.B(Param1, Param2, Param3): Integer;
    var
      BCall: TBCall; // un TObject; hérite d'un TCallCustom
    begin
      BCall:= TBCall.Create(Param1, Param2, Param3);  // implicitement Token++;
      ThreadB.Add(BCall); // ThreadB est ThreadAction
      Result := BCall.Token;
    end;
    Threada et ThreadB sont pareil ,c'est juste ProcessItem qui change et les types, avec de l'abstraction et polymorphisme, tout ça se fait très facilement

    Il faut voir les ThreadA et ThreadB comme des threads par GROUPE de fonctions selon priorités, tu peux avoir des thread permanent ou des thread créé à la volée, à toi de voir ce que tu préfère

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    procedure ThreadAction.Add(Call: TCallCustom);
    begin
      FThreadList.Add(Call);
      FEvent.SerEvent();
    end;
    Attente des threads est un peu brutal, il bloque trop longtemps la liste à mon gout

    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
    proceudre ThreadAction.Execute();
    var
      itemCall : TCallCustom;
    being
      whiel not terminated do
      begin
        TWaitResult wr = FEvent.WaitFor(1000);
        if wr = wrSignaled then
        begin
          int TmpCount = 0;
          repeat
            itemCall := NULL;
            with FThreadList.LockList do
            try
     	  TmpCount := Count;
              if TmpCount > 0 then
              begin
                itemCall := Items[0] as TCallCustom; // cast selon TThreadList de Pointer ou TThreadList<TCallCustom>
                Delete(0); // tu peux affecter NULL et uitliser PACK, cela réduit les déplacement mémoire, idéal une liste chainée serait une approche sympa aussi à faire
              end;
            finally
              FThreadList.UnlockList; 
            end;
            // je n'aime pas bloquer la liste pendant n itérations
            if Assigned(itemCall ) then
             itemCall.Process();
     
          until TmpCount <= 0; // je ne sais jamais le bon sens du test
     
        end
        else
          // Gérer wrTimeout,wrError ...
     
      end;
    end;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    procedure TACall.Process(); // Process est abstract dans TCallCustom
    var
      Response: TAResponse; 
    begin
      Faire le truc de A
     
      InterfaceEvent.A(this.Token, Response.ValeurA); // un Event de l'objet COM
    end;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    procedure TBCall.Process(); // Process est abstract dans TCallCustom
    var
      Response: TBResponse; 
    begin
      Faire le truc de B
     
      InterfaceEvent.B(this.Token, Response.ValeurB, Response.ValeurBAutre); // un Event de l'objet COM
    end;

    SetSinkEvent


    Pour les appel de InterfaceEvent.A et InterfaceEvent.B, peut-être avoir aussi un thread qui regroupent les appels pour éviter des retours simultanés ou alors des sections critiquent
    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

Discussions similaires

  1. Application serveur DCOM sur Windows Server 2008
    Par maaartchiano dans le forum VB 6 et antérieur
    Réponses: 0
    Dernier message: 08/05/2008, 13h59
  2. [VB.NET 2.0] Serveur en MultiThreading
    Par Aspic dans le forum Windows Forms
    Réponses: 1
    Dernier message: 15/03/2007, 12h46
  3. Réponses: 2
    Dernier message: 04/10/2006, 13h52
  4. Aide pour serveur TCP multithread
    Par kingkong dans le forum Réseau
    Réponses: 3
    Dernier message: 27/04/2006, 12h37
  5. Client-Serveur Dcom = Client+Serveur lancés sur la machine ?
    Par Jilam dans le forum Bases de données
    Réponses: 6
    Dernier message: 27/07/2004, 14h55

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