54 votes

Contrôle distribué de la concurrence

Je travaille sur ce sujet depuis quelques jours maintenant, et j'ai trouvé plusieurs solutions mais aucune d'entre elles n'est incroyablement simple ou légère. Le problème est essentiellement le suivant : Nous avons un cluster de 10 machines, chacune d'entre elles exécutant le même logiciel sur une plateforme ESB multithread. Je peux gérer les problèmes de concurrence entre les threads sur la même machine assez facilement, mais qu'en est-il de la concurrence sur les mêmes données sur différentes machines ?

Essentiellement, le logiciel reçoit des demandes pour alimenter les données d'un client d'une entreprise à une autre via des services web. Cependant, le client peut ou non exister encore sur l'autre système. S'il n'existe pas, nous le créons via une méthode de service web. Cela nécessite donc une sorte de test-and-set, mais j'ai besoin d'une sorte de sémaphore pour empêcher les autres machines de provoquer des conditions de course. Il m'est déjà arrivé qu'un client distant soit créé deux fois pour un seul client local, ce qui n'est pas vraiment souhaitable.

Les solutions que j'ai envisagées sont les suivantes :

  1. Utilisation de notre système de fichiers partagés tolérant aux pannes pour créer des fichiers "verrouillés" qui seront vérifiés par chaque machine en fonction du client.

  2. Utiliser une table spéciale dans notre base de données, et verrouiller toute la table afin de faire un "test-and-set" pour un enregistrement de verrouillage.

  3. Utilisation de Terracotta, un logiciel de serveur à source ouverte qui aide à la mise à l'échelle, mais qui utilise un modèle en étoile.

  4. J'utilise EHCache pour la réplication synchrone de mes "verrous" en mémoire.

Je ne peux pas imaginer que je suis la seule personne à avoir eu ce genre de problème. Comment l'avez-vous résolu ? Avez-vous conçu quelque chose en interne ou avez-vous un produit tiers préféré ?

1voto

Craig Day Points 1350

J'ai beaucoup travaillé avec Coherence, qui permettait plusieurs approches pour mettre en œuvre un verrou distribué. L'approche naïve consistait à demander le verrouillage du même objet logique sur tous les nœuds participants. En termes de Coherence, il s'agissait de verrouiller une clé sur un cache répliqué. Cette approche n'est pas très efficace car le trafic réseau augmente linéairement avec le nombre de nœuds. Une méthode plus intelligente consistait à utiliser un cache distribué, où chaque nœud du cluster est naturellement responsable d'une partie de l'espace des clés, de sorte que le verrouillage d'une clé dans un tel cache implique toujours une communication avec au plus un nœud. Vous pouvez développer votre propre approche basée sur cette idée ou, mieux encore, utiliser Coherence. C'est vraiment la boîte à outils d'évolutivité de vos rêves.

J'ajouterais que tout mécanisme de verrouillage multi-nœuds basé sur un réseau à moitié décent devrait être raisonnablement sophistiqué pour agir correctement en cas de défaillance du réseau.

1voto

Boris Terzic Points 6148

Je ne suis pas sûr d'avoir compris tout le contexte, mais il semble que vous ayez une seule base de données pour sauvegarder tout cela ? Pourquoi ne pas utiliser le verrouillage de la base de données : si la création du client est un simple INSERT, alors cette seule déclaration peut servir de verrouillage puisque la base de données rejettera un second INSERT qui violerait l'une de vos contraintes (par exemple, le fait que le nom du client soit unique).

Si l'opération d'insertion d'un client n'est pas atomique et qu'il s'agit d'un lot d'instructions, j'introduirais (ou j'utiliserais) un INSERT initial qui crée un enregistrement de base simple identifiant votre client (avec les contraintes d'UNIQUE nécessaires), puis je ferais toutes les autres insertions/mises à jour dans la même transaction. Encore une fois, la base de données s'occupera de la cohérence et toute modification simultanée entraînera l'échec de l'une d'entre elles.

0voto

entzik Points 509

J'ai créé un service RMI simple avec deux méthodes : lock et release. Les deux méthodes prennent une clé (mon modèle de données utilise les UUIDs comme pk, donc c'est aussi la clé de verrouillage).

RMI est une bonne solution pour cela car elle est centralisée. Vous ne pouvez pas faire cela avec les EJBs (en particulier dans un cluster car vous ne savez pas sur quelle machine votre appel va atterrir). de plus, c'est facile.

ça a marché pour moi.

0voto

Jonathan Points 1032

Si vous pouvez configurer l'équilibrage de la charge de manière à ce que les demandes d'un même client soient toujours dirigées vers le même serveur, vous pouvez gérer ce problème par le biais de la synchronisation locale. Par exemple, prenez votre ID client mod 10 pour trouver lequel des 10 noeuds utiliser.

Même si vous ne souhaitez pas le faire dans le cas général, vos nœuds pourraient se mettre en relation par proxy pour ce type de demande spécifique.

En supposant que vos utilisateurs sont suffisamment uniformes (c'est-à-dire que vous en avez une tonne) pour que vous ne vous attendiez pas à ce que des points chauds apparaissent là où un nœud est surchargé, cela devrait encore s'adapter assez bien.

0voto

Slava Imeshev Points 482

Vous pouvez également envisager Cacheonix pour les verrous distribués. Contrairement à tout ce qui est mentionné ici, Cacheonix prend en charge les verrous de type ReadWrite avec une escalade de verrouillage de la lecture à l'écriture si nécessaire :

ReadWriteLock rwLock = Cacheonix.getInstance().getCluster().getReadWriteLock();
Lock lock = rwLock.getWriteLock();
try {
  ...
} finally {
  lock.unlock();
}

Divulgation complète : je suis un développeur de Cacheonix.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X