Je travaille dessus depuis quelques jours maintenant et j'ai trouvé plusieurs solutions mais aucune d'entre elles incroyablement simple ou légère. Le problème est fondamentalement: nous avons un groupe de 10 machines, chacun d'entre eux exécutant le même logiciel sur une plate-forme ESB multithreaded. Je peux faire face à des 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 d'alimentation des données d'une clientèle d'une entreprise à une autre via des services Web. Cependant, 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. Donc, cela nécessite une sorte de test et un séparateur, mais j'ai besoin d'un sémaphore de quelque sorte pour verrouiller les autres machines de causer des conditions de course. J'ai eu des situations avant d'où un client distant a été créé deux fois pour un seul client local, qui n'est pas vraiment souhaitable.
Solutions que j'ai jouées avec conceptuellement:
Utilisation de notre système de fichiers partagé à tolérance de défaut pour créer des fichiers "verrouillage" qui seront vérifiés par chaque machine en fonction du client
Utilisation d'une table spéciale dans notre base de données et verrouillez la table entière afin de faire un "test" pour un enregistrement de verrouillage.
À l'aide de TerreCrotta, un logiciel de serveur open source qui aide à être mis à l'échelle, mais utilise un modèle de moyeu et de la parole.
Utilisation d'EHCache pour une réplication synchrone de mes "serrures" en mémoire.
Je ne peux pas imaginer que je suis la seule personne qui a déjà eu ce genre de problème. Comment l'avez-vous résolu? Avez-vous cuit quelque chose en interne ou avez-vous un produit de 3ème parti préféré?
vous voudrez peut-être envisager d'utiliser Hazelcast Serrures distribuées. Super Lite 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
Nous utilisons TerreAsta, alors je voudrais voter pour cela.
Je suis suivant Hazelcast et cela ressemble à une autre technologie prometteuse, mais je ne peux pas voter pour cela puisque je ne l'ai pas utilisé, et sachant qu'il utilise un système basé sur la P2P à son entendu, je ne le ferais vraiment pas confiance en gros besoins d'échelle.
Mais j'ai également entendu parler de Zookeper, qui est sorti de Yahoo et se déplace sous le parapluie Hadoop. Si vous êtes aventureux, essayez de nouvelles technologies, cela a vraiment beaucoup de promesses puisqu'il est très maigre et méchant, en vous concentrant sur la coordination. J'aime la vision et la promesse, même si cela pourrait être trop vert encore.
TerraCotta est plus proche d'un modèle "à plusieurs niveaux" - Toutes les applications clients parlent à une matrice de serveur en terre cuite (et plus important encore pour l'échelle qu'ils ne se parlent pas les unes aux autres). Le réseau de serveurs de TerraCotta est capable d'être regroupé pour une échelle et une disponibilité (en miroir, pour la disponibilité et à rayures, à l'échelle).
En tout état de cause, car vous savez probablement que la terre cuite vous donne la possibilité d'exprimer une concurrence à travers le groupe de la même manière que vous vous trouvez dans un seul JVM à l'aide de Pojo synchronisé/attente/notify ou en utilisant l'une des primitives Java.util.ContCurrent telles que RetrantreadWritelock , Cyclicbarrier, atomiclong, futuretk et ainsi de suite.
Il y a beaucoup de recettes simples démontrant l'utilisation de ces primitives dans le Cookbook en terre cuite .
À titre d'exemple, je posterai l'exemple ReentranTreadWritelock (note qu'il n'y a pas de version "TerreArapotta" de la serrure - Vous venez d'utiliser normalement Java RETENTANTREADREDWRITOCK)
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 met en œuvre plus de 30 structures et services de données distribués, y compris 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();
J'allais conseiller sur l'utilisation de Memcached comme très rapide, distribué RAM Stockage pour maintenir les journaux; mais il semble que EHCache soit un projet similaire, mais plus Java-Centric.
L'une ou l'autre est la voie à suivre, tant que vous êtes sûr d'utiliser des mises à jour atomiques (Memcached les supporte, ne connaissez pas l'EHCache). C'est de loin la solution la plus évolutive.
En tant que DataPoint associé, Google utilise "Chubby", un stockage de verrouillage distribué rapide et basé sur RAM comme la racine de plusieurs systèmes, parmi lesquels Bigtable.
Je ne sais pas si je comprends tout le contexte, mais il semble que vous ayez 1 base de données unique qui convient à cela? Pourquoi ne pas utiliser le verrouillage de la base de données: si la création du client est un seul insert, cette instruction seule peut servir de verrouille car la base de données rejetera un deuxième insert qui enfreindrait l'une de vos contraintes (par exemple, le fait que le nom du client est unique par exemple).
Si l'opération "Insertion d'un client" n'est pas atomique et est un lot d'instructions, j'invente (ou utilisez-le) un insert initial qui crée un enregistrement de base simple identifiant votre client (avec les contraintes de l'unicité nécessaires), puis faites tout le Autres inserts/mises à jour dans la même transaction. Encore une fois, la base de données s'occupera de la cohérence et de toute modification simultanée entraînera l'une d'entre elles échouant.
J'ai fait beaucoup de travail avec cohérence, ce qui permettait plusieurs approches pour mettre en œuvre une serrure distribuée. L'approche naïve était de demander de verrouiller le même objet logique sur tous les nœuds participants. En termes de cohérence, ceci bloquiez une clé sur un cache répliqué. Cette approche n'apparaît pas que le trafic réseau augmente linéairement lorsque vous ajoutez des nœuds. Un moyen plus intelligent consistait à utiliser un cache distribué, où chaque nœud du cluster est naturellement responsable d'une partie de l'espace clé, le verrouillage donc 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 de l'évolutivité de vos rêves.
J'ajouterais que tout mécanisme de verrouillage de réseau multi-nœud à moitié décent devrait être raisonnablement sophistiqué d'agir correctement en cas de panne de réseau.
Puisque vous vous connectez déjà à une base de données, avant d'ajouter une autre pièce infrasive, jetez un coup d'œil à jdbcsemaphore , il est simple à utiliser:
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();
}
Cela fait partie de SPF4J Bibliothèque.
J'ai fait un simple service RMI avec deux méthodes: verrouillage et libération. Les deux méthodes prennent une clé (mon modèle de données utilisait des uuids sous forme de PK, de sorte que la clé de verrouillage).
RMI est une bonne solution pour cela car il est centralisé. Vous ne pouvez pas faire cela avec EJBS (spécialement dans un cluster comme vous ne savez pas sur quelle machine votre appel va atterrir). De plus, c'est facile.
cela a fonctionné pour moi.
Si vous pouvez configurer votre équilibrage de charge afin que la demande d'un seul client soit toujours mappée sur le même serveur, vous pouvez gérer cette via la synchronisation locale. Par exemple, prenez votre ID client MOD 10 pour rechercher lequel des 10 nœuds à utiliser.
Même si vous ne voulez pas faire cela dans le cas général, vos nœuds pourraient être proxy les uns aux autres pour ce type de demande spécifique.
En supposant que vos utilisateurs soient assez uniformes (c'est-à-dire si vous en avez une tonne) que vous ne vous attendez pas à ce que des points chauds apparaissent là où un nœud est surchargé, cela devrait encore être assez bien évolué.