Question

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

Essentiellement, le logiciel reçoit des requêtes pour transmettre les données d'un client d'une entreprise à une autre via des services Web. Toutefois, le client peut ou non exister sur l’autre système. Si ce n'est pas le cas, nous le créons via une méthode de service Web. Cela nécessite donc une sorte de test-and-set, mais il me faut un sémaphore pour empêcher les autres machines de créer des conditions de concurrence. Il m'est déjà arrivé de créer deux fois un client distant pour un seul client local, ce qui n'est pas vraiment souhaitable.

Les solutions auxquelles j'ai joué conceptuellement sont les suivantes:

  1. Utilisation de notre système de fichiers partagé à tolérance de pannes pour créer un "verrou". fichiers 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 verrouillé.

  3. Utilisation de Terracotta, un logiciel serveur open source facilitant la mise à l'échelle, mais utilisant un modèle en étoile.

  4. Utilisation d'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 préparé quelque chose en interne ou avez-vous un produit tiers préféré?

Était-ce utile?

La solution

vous pouvez envisager d’utiliser des Hazelcast , des verrous distribués. Super léger et facile.

java.util.concurrent.locks.Lock lock = Hazelcast.getLock ("mymonitor");
lock.lock ();
try {
// do your stuff
}finally {
   lock.unlock();
}

Hazelcast - File d'attente distribuée, Carte, Ensemble, Liste, Verrouillage

Autres conseils

Nous utilisons Terracotta, je voudrais donc voter pour cela.

J'ai suivi Hazelcast et cela ressemble à une autre technologie prometteuse, mais je ne peux pas voter pour elle car je ne l'utilise pas, et sachant qu'elle utilise un système basé sur le P2P à l'audition, je ne ferais vraiment pas confiance pour les grands besoins d’échelle.

Mais j'ai aussi entendu parler de Zookeeper, qui est sorti de Yahoo et qui évolue sous le parapluie Hadoop. Si vous êtes aventureux à l’essai avec une nouvelle technologie, c’est vraiment très prometteur, car elle est très maigre et méchante, et vous concentrez uniquement sur la coordination. J'aime la vision et la promesse, même si elle est peut-être encore trop verte.

La terre cuite est plus proche d'un "étage". model - toutes les applications client communiquent avec un réseau de serveurs Terracotta (et, plus important encore, ne se parlent pas entre elles). Terracotta Server Array peut être mis en cluster à la fois pour l’échelle et la disponibilité (en miroir, pour la disponibilité, et en bandes, pour l’échelle).

Dans tous les cas, comme vous le savez probablement, Terracotta vous permet d'exprimer la concurrence sur le cluster de la même manière que vous le faites dans une seule machine virtuelle en utilisant POJO synchronized / wait / notify ou en utilisant l'une des primitives java.util.concurrent. tels que ReentrantReadWriteLock, CyclicBarrier, AtomicLong, FutureTask, etc.

Il existe de nombreuses recettes simples démontrant l'utilisation de ces primitives dans le livre de recettes Terracotta. .

Par exemple, je publierai l'exemple ReentrantReadWriteLock (notez qu'il n'y a pas de version "Terracotta" du verrou - vous utilisez simplement Java ReentrantReadWriteLock normal)

import java.util.concurrent.locks.*;

public class Main
{
    public static final Main instance = new Main();
    private int counter = 0;
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(true);

    public void read()
    {
        while (true) {
            rwl.readLock().lock();
                try {
                System.out.println("Counter is " + counter);
            } finally {
                rwl.readLock().unlock();
            }
            try { Thread.currentThread().sleep(1000); } catch (InterruptedException ie) {  }
        }
    }

    public void write()
    {
        while (true) {
            rwl.writeLock().lock();
            try {
               counter++;
               System.out.println("Incrementing counter.  Counter is " + counter);
            } finally {
                 rwl.writeLock().unlock();
            }
            try { Thread.currentThread().sleep(3000); } catch (InterruptedException ie) {  }
        }
    }

    public static void main(String[] args)
    {
        if (args.length > 0)  {
            // args --> Writer
            instance.write();
        } else {
            // no args --> Reader
            instance.read();
        }
    }
}

Je recommande d'utiliser Redisson . Il implémente plus de 30 structures de données distribuées et services, notamment java.util.Lock . Exemple d'utilisation:

Config config = new Config();
config.addAddress("some.server.com:8291");
Redisson redisson = Redisson.create(config);

Lock lock = redisson.getLock("anyLock");
lock.lock();
try {
    ...
} finally {
   lock.unlock();
}

redisson.shutdown();

Je voulais vous conseiller sur l’utilisation de memcached en tant que mémoire RAM distribuée très rapide pour la conservation des journaux; mais il semble qu'EHCache soit un projet similaire mais plus java-centrique.

L’un ou l’autre est la voie à suivre, tant que vous êtes sûr d’utiliser des mises à jour atomiques (memcached les prend en charge, je ne connais pas EHCache). C’est de loin la solution la plus évolutive.

En tant que point de données associé, Google utilise "Chubby", un stockage de verrouillage distribué rapide basé sur de la RAM comme racine de plusieurs systèmes, dont BigTable.

J'ai beaucoup travaillé avec Coherence, qui a permis plusieurs approches pour la mise en œuvre d'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 cohérence, il s'agissait de verrouiller une clé sur un cache répliqué. Cette approche ne s'adapte pas très bien car le trafic réseau augmente de manière linéaire à mesure que vous ajoutez des nœuds. Une méthode plus intelligente consistait à utiliser un cache distribué, chaque nœud du cluster étant naturellement responsable d'une partie de l'espace de la clé. Verrouiller une clé dans un tel cache impliquait toujours une communication avec au plus un nœud. Vous pouvez lancer votre propre approche basée sur cette idée, ou mieux encore, obtenir la cohérence. C'est vraiment la boîte à outils d'évolutivité de vos rêves.

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

Je ne sais pas si je comprends tout le contexte, mais il semblerait que vous ne disposiez que d’une seule base de données. Pourquoi ne pas utiliser le verrouillage de la base de données: si la création du client est un INSERT unique, cette instruction peut à elle seule servir de verrou, car la base de données rejettera un deuxième INSERT qui violerait l’une de vos contraintes (par exemple, le nom du client est: unique par exemple).

Si l'insertion d'un client " l'opération n'est pas atomique et consiste en un lot d'instructions, puis je voudrais introduire (ou utiliser) un INSERT initial qui crée un enregistrement de base simple identifiant votre client (avec les contraintes d'UNIQUEness nécessaires), puis effectue 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’elles.

J'ai créé un service RMI simple avec deux méthodes: verrouillage et libération. les deux méthodes prennent une clé (mon modèle de données utilisait des UUID comme pk, donc c'était aussi la clé de verrouillage).

RMI est une bonne solution car il est centralisé. vous ne pouvez pas faire cela avec des EJB (spécialement dans un cluster, car vous ne savez pas sur quelle machine votre appel atterrira). en plus, c'est facile.

cela a fonctionné pour moi.

Si vous pouvez configurer l'équilibrage de la charge de sorte que les demandes d'un seul client soient toujours mappées sur le même serveur, vous pouvez le gérer via une synchronisation locale. Par exemple, utilisez votre identifiant client mod 10 pour trouver l’un des 10 nœuds à utiliser.

Même si vous ne voulez pas faire cela dans le cas général, vos nœuds pourraient se remplacer mutuellement par proxy pour ce type de requête spécifique.

En supposant que vos utilisateurs soient suffisamment uniformes (c'est-à-dire si vous en avez une tonne), vous ne vous attendez pas à ce que des points chauds apparaissent lorsqu'un nœud est surchargé, cela devrait quand même bien évoluer.

Vous pouvez également considérer Cacheonix pour les verrous distribués. Contrairement à ce qui est mentionné ici, Cacheonix prend en charge les verrous ReadWrite avec escalade de verrous de lecture à é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.

Etant donné que vous vous connectez déjà à une base de données, avant d'ajouter une autre information, consultez JdbcSemaphore , son utilisation est simple:

JdbcSemaphore semaphore = new JdbcSemaphore(ds, semName, maxReservations);
boolean acq = semaphore.acquire(acquire, 1, TimeUnit.MINUTES);
if (acq) {
 // do stuff
 semaphore.release();
} else {
  throw new TimeoutException();
}

Elle fait partie de la bibliothèque spf4j .

À l'époque, nous utilisions un "verrou de serveur" spécifique. sur le réseau pour gérer cela. Bleh.

Votre serveur de base de données peut disposer de ressources spécialement conçues pour ce type de tâches. MS-SQL Server dispose de verrous d'application utilisables via les procédures sp_getapplock / sp_releaseapplock .

Nous développons actuellement un cadre de synchronisation distribué open source. Actuellement, le verrou DistributedReentrantLock et DistributedReentrantReadWrite a été mis en œuvre, mais nous en sommes toujours à la phase de test et de refactoring. Dans notre architecture, les clés de verrouillage sont divisées en compartiments et chaque nœud est responsable d'un certain nombre de compartiments. Donc, efficacement pour des requêtes de verrouillage réussies, il n'y a qu'une seule requête réseau. Nous utilisons également la classe AbstractQueuedSynchronizer comme état de verrouillage local. Par conséquent, toutes les demandes de verrouillage ayant échoué sont traitées localement, ce qui réduit considérablement le trafic réseau. Nous utilisons JGroups ( http://jgroups.org ) pour la communication de groupe et Hessian pour la sérialisation.

pour plus de détails, consultez http://code.google.com/p/vitrit/ .

Envoyez-moi vos précieux commentaires.

Kamran

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top