2008-09-18 41 views
51

He estado trabajando en esto durante unos días y he encontrado varias soluciones, pero ninguna de ellas increíblemente simple o ligera. El problema es básicamente esto: tenemos un clúster de 10 máquinas, cada una de las cuales ejecuta el mismo software en una plataforma ESB multiproceso. Puedo lidiar con problemas de concurrencia entre subprocesos en la misma máquina con bastante facilidad, pero ¿qué pasa con la concurrencia en los mismos datos en diferentes máquinas?Distributed Concurrency Control

Esencialmente, el software recibe solicitudes para alimentar los datos de un cliente de una empresa a otra a través de servicios web. Sin embargo, el cliente puede o no existir aún en el otro sistema. Si no es así, lo creamos a través de un método de servicio web. Por lo tanto, requiere una especie de prueba y configuración, pero necesito un semáforo de algún tipo para evitar que las otras máquinas causen condiciones de carrera. He tenido situaciones en las que se creó un cliente remoto dos veces para un solo cliente local, lo que no es realmente deseable.

Soluciones He jugado con conceptualmente son:

  1. Utilizando nuestro sistema de archivos compartidos con tolerancia a fallos para crear "bloquear" los archivos que serán controlados por cada máquina en función del cliente

  2. Usando una tabla especial en nuestra base de datos, y bloqueando toda la tabla para hacer un "test-and-set" para un registro de bloqueo.

  3. Uso de Terracotta, un software de servidor de código abierto que ayuda a escalar, pero utiliza un modelo de cubo y radio.

  4. Uso de EHCache para la replicación sincrónica de mis "bloqueos" en memoria.

No me puedo imaginar que soy la única persona que ha tenido este tipo de problema. ¿Cómo lo resolvió? ¿Has cocinado algo en la empresa o tienes un producto favorito de terceros?

+0

En lugar de probar y establecer, ¿podría la responsabilidad de asegurarse de que no haya ningún duplicado se transfiera al servicio que crea nuevos clientes? –

Respuesta

33

es posible que desee considerar el uso de Hazelcast bloqueos distribuidos. Super lite y fácil.

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

Hazelcast - Distributed Queue, mapa, Conjunto, Lista, bloqueo

+0

Eso es EXACTAMENTE el tipo de semántica que estaba tratando de usar con mi (no) solución local. Tengo curiosidad de por qué no pude encontrarlo solo, ¡pero gracias por la sugerencia! –

+0

parece que la API ha cambiado ahora. Échale un vistazo http://hazelcast.org/docs/3.2/javadoc/com/hazelcast/core/class-use/ILock.html –

-1

De vuelta en el día, usábamos un "servidor de bloqueo" específico en la red para manejar esto. Bleh.

Su servidor de base de datos puede tener recursos específicos para hacer este tipo de cosas. MS-SQL Server tiene bloqueos de aplicaciones utilizables a través de los procedimientos sp_getapplock/sp_releaseapplock.

0

Hice un servicio RMI simple con dos métodos: bloquear y liberar. ambos métodos toman una clave (mi modelo de datos usaba UUID como pk, por lo que también era la tecla de bloqueo).

RMI es una buena solución para esto porque está centralizado. no se puede hacer esto con los EJB (especialmente en un clúster ya que no se sabe en qué máquina aterrizará su llamada). Además, es fácil.

funcionó para mí.

1

He trabajado mucho con Coherence, lo que permitió varios enfoques para implementar un bloqueo distribuido. El enfoque ingenuo era solicitar bloquear el mismo objeto lógico en todos los nodos participantes. En términos de Coherence, esto estaba bloqueando una clave en un caché replicado. Este enfoque no se escala tan bien porque el tráfico de red aumenta linealmente a medida que agrega nodos. Una forma más inteligente era usar un caché distribuido, donde cada nodo en el clúster es naturalmente responsable de una parte del espacio clave, por lo que bloquear una clave en dicho caché siempre implicaba la comunicación con un nodo como máximo. Podrías desarrollar tu propio enfoque basado en esta idea, o mejor aún, obtener Coherencia. Realmente es el juego de herramientas de escalabilidad de tus sueños.

Yo agregaría que cualquier mecanismo de bloqueo basado en la red de varios nodos medio decente tendría que ser razonablemente sofisticado para actuar correctamente en caso de falla de la red.

1

No estoy seguro si entiendo todo el contexto pero parece que tiene 1 sola base de datos respaldando esto? ¿Por qué no utilizar el bloqueo de la base de datos? Si la creación del cliente es un único INSERT entonces esta declaración sola puede servir como un bloqueo ya que la base de datos rechazará un segundo INSERT que violaría una de sus restricciones (por ejemplo, el nombre del cliente único por ejemplo).

Si la operación de "inserción de un cliente" no es atómica y es un lote de declaraciones, entonces introduciría (o usaría) un INSERT inicial que crearía un registro básico simple identificando a su cliente (con las restricciones de UNIQUEness necesarias) y luego haga todas las otras inserciones/actualizaciones en la misma transacción. De nuevo, la base de datos se ocupará de la coherencia y cualquier modificación concurrente hará que una de ellas falle.

+1

Disculpe, sé que mi "pregunta" no era la más clara del mundo; el ESB interactúa a través de servicios, por lo que no tenemos control sobre las restricciones. Debo preguntar a través de los servicios si su Cliente existe, y luego hacer una solicitud por separado para crear uno si no es así. –

+1

¿Puedes actualizar la pregunta? –

4

terracota está más cerca de un modelo de "niveles" - todas las aplicaciones cliente hable con una matriz de terracota servidor (y más importante para la escala que Don hablar el uno con el otro). Terracotta Server Array se puede agrupar tanto en escala como en disponibilidad (reflejado, disponibilidad y rayas, para escala).

En cualquier caso, como probablemente sepa, Terracotta le da la capacidad de expresar la simultaneidad en el clúster de la misma manera que lo hace en una sola JVM utilizando POJO sincronizado/wait/notify o utilizando cualquiera de los java.util.concurrent primitivas como ReentrantReadWriteLock, CyclicBarrier, AtomicLong, FutureTask, etc.

Hay muchas recetas simples que demuestran el uso de estas primitivas en el Terracotta Cookbook.

A modo de ejemplo, voy a publicar el ejemplo ReentrantReadWriteLock (tenga en cuenta que no hay "Terracota" versión de la cerradura - que acaba de utilizar Java normal ReentrantReadWriteLock)

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(); 
     } 
    } 
} 
+2

Deberías usar 'Thread.sleep (...)' en lugar de 'Thread.currentThread() .sleep (...) 'porque es un método estático; su código podría tentarlo a hacer 'someOtherThread.sleep (...)' que no duerma 'someOtherThread'. – newacct

13

Utilizamos terracota, así que me gustaría vota por eso.

He estado siguiendo Hazelcast y parece otra tecnología prometedora, pero no puedo votar porque no la he usado, y sabiendo que utiliza un sistema basado en P2P, no lo haría. confíe en esto para grandes necesidades de escalado.

Pero también he oído hablar de Zookeeper, que salió de Yahoo y se está moviendo bajo el paraguas de Hadoop. Si eres aventurero probando alguna tecnología nueva, esto realmente promete mucho, ya que es muy delgada y mala, y se centra en la coordinación. Me gusta la visión y la promesa, aunque podría ser demasiado verde.

+1

No veo por qué el hecho de que Hazelcase use P2P lo haría poco confiable para el uso a gran escala. – Dunaril

+0

+1 por mencionar una preocupación sobre la escalabilidad del bloqueo P2P. @Dunaril Piense en cuántos nodos deben notificarse acerca de la emisión de un bloqueo. En el caso ingenuo, se trata de un problema de N cuadrado (cluster_size^2): es necesario notificar a cada nodo sobre los bloqueos emitidos en todos los demás nodos. En la práctica, la fragmentación o los quórumes pueden mitigar estos problemas, pero aún así es un problema difícil y es improbable que una biblioteca que publicita el uso "plug and play" lo resuelva. El bloqueo P2P tiene escalabilidad invertida: el rendimiento * se degrada * a medida que se agregan nodos adicionales. – npgall

2

que iba a asesoramiento sobre el uso memcached como, distribuida de almacenamiento de memoria RAM muy rápido para mantener registros; pero parece que EHCache es un proyecto similar pero más centrado en Java.

Cualquiera de los dos es el camino a seguir, siempre y cuando esté seguro de utilizar actualizaciones atómicas (memcached los admite, no sabe acerca de EHCache). Es, de lejos, la solución más escalable.

Como un punto de datos relacionado, Google usa 'Chubby', un almacenamiento de bloqueo distribuido rápido basado en RAM como la raíz de varios sistemas, entre ellos BigTable.

0

Si puede configurar su balanceo de carga para que las solicitudes de un solo cliente siempre se asignen al mismo servidor, entonces puede manejar esto a través de la sincronización local. Por ejemplo, tome su ID de cliente mod 10 para encontrar cuál de los 10 nodos usará.

Incluso si no desea hacer esto en el caso general, sus nodos podrían proxy entre sí para este tipo específico de solicitud.

Suponiendo que sus usuarios son lo suficientemente uniformes (es decir, si tiene una tonelada de ellos) que no espera que aparezcan puntos calientes en los que un nodo se sobrecarga, esto todavía debería escalar bastante bien.

-5

Hemos estado desarrollando un marco de sincronización distribuida de código abierto, actualmente DistributedRentrantLock y DistributedReentrantReadWrite lock se han implementado, pero todavía están en fase de prueba y refactorización. En nuestra arquitectura, las claves de bloqueo se dividen en segmentos y cada nodo es responsable de cierta cantidad de segmentos. De manera efectiva para solicitudes de bloqueo exitosas, solo hay una solicitud de red. También estamos utilizando la clase AbstractQueuedSynchronizer como estado de bloqueo local, por lo que todas las solicitudes de bloqueo fallidas se gestionan localmente, esto reduce drásticamente el tráfico de red. Estamos utilizando JGroups (http://jgroups.org) para comunicación grupal y Hessian para serialización.

para obtener más información, consulte http://code.google.com/p/vitrit/.

Por favor envíeme sus comentarios valiosos.

Kamran

+4

Su cliente no tiene permiso para obtener URL/p/vitrit/de este servidor – stepancheg

0

También podría considerar Cacheonix para cerraduras distribuidos. A diferencia de cualquier otra cosa mencionado aquí Cacheonix apoyar cerraduras ReadWrite con la extensión de bloqueo de lectura para escribir cuando sea necesario:

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

La revelación completa: Soy un desarrollador Cacheonix.

3

Recomiendo usar Redisson. Implementa más de 30 estructuras de datos distribuidos y servicios, incluido java.util.Lock.Ejemplo de uso:

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(); 
+0

No utilizaría Redis como un servicio de bloqueo, de ninguna manera: http://aphyr.com/posts/283-call-me-maybe -redis –

+0

@mbonaci El objeto de bloqueo tiene pruebas concurrentes y lo pasa cada vez. Aquí hay una respuesta antirez a esta http://antirez.com/news/55 ACTUALIZACIÓN de antirez: mientras tanto, la arquitectura e implementación de Redis Sentinel cambió significativamente, además una característica de Redis para dejar de aceptar escrituras si no hay suficientes esclavos conectados fue agregado, así que básicamente es posible alcanzar un mejor tipo de garantía de consistencia usando Sentinel. –

+0

Lo sé, y aquí está la redis recurrente de aphyr y las ([y más recientes] (http://antirez.com/news/66)) comentarios de antirez/whimps: http://aphyr.com/posts/307-call- me-tal vez-redis-redux. Redis, cuando se combina con Sentinel, solo sirve para el almacenamiento en caché. Almacenamiento de sesiones en el mejor de los casos (en entornos no críticos). Hasta que su modelo sea revisado por pares, eso es. –

0

Puesto que ya se está conectando a una base de datos, antes de añadir otra pieza infra, echar un vistazo a JdbcSemaphore, es fácil de usar:

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(); 
} 

Es parte de spf4j biblioteca .

Cuestiones relacionadas