分布式并发控制

我已经在这个工作了几天,我已经find了几个解决scheme,但没有一个是令人难以置信的简单或轻量级。 问题基本上是这样的:我们有10台机器的集群,每台机器都在multithreadingESB平台上运行相同的软件。 我可以很容易地处理同一台机器上的线程之间的并发问题,但是对于不同机器上相同数据的并发性呢?

本质上,软件接收请求,通过Web服务将客户的数据从一个业务提供给另一个业务。 但是,客户可能会或可能不会在另一个系统上存在。 如果没有,我们通过Web服务方法创build它。 所以它需要一种testing和设置,但是我需要某种信号量来locking其他机器导致竞争状态。 在一个本地客户创build了两次远程客户之前,我已经遇到过这种情况,这并不是真正需要的。

我在概念上玩弄的解决scheme是:

  1. 使用我们的容错共享文件系统创build“locking”文件,这将由每台机器根据客户检查

  2. 在我们的数据库中使用一个特殊的表,并locking整个表,以便为锁logging做一个“testing和设置”。

  3. 使用Terracotta,这是一个开源服务器软件,可以协助扩展,但是使用了hub-and-spoke模式。

  4. 使用EHCache同步复制我的内存“锁”。

我无法想象我是唯一一个遇到这种问题的人。 你是怎么解决的? 你有没有在内部做点什么,或者你有一个最喜欢的第三方产品?

你可能要考虑使用Hazelcast分布式锁。 超级轻而易举。

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

Hazelcast – 分布式队列,地图,设置,列表,locking

我们使用兵马俑,所以我想为此投票。

我一直在关注Hazelcast,它看起来像另一个有前途的技术,但不能投票,因为我没有使用它,知道它使用基于P2P的系统在听到,我真的不会相信它的大扩大需求。

但是我也听说过雅虎出来的Zookeeper,并且正在Hadoop的保护下。 如果你冒险尝试一些新技术,这真的有很多的承诺,因为它非常精简和意味着,只关注协调。 我喜欢这个愿景和承诺,尽pipe它可能太绿了。

兵马俑更接近“分层”模式 – 所有的客户端应用程序都与Terracotta服务器arrays对话(更重要的是,他们之间不能互相通话)。 兵马俑服务器arrays能够针对规模和可用性进行集群(镜像,可用性和条带化,可扩展)。

无论如何,正如你可能知道的那样,Terracotta通过使用POJO synchronized / wait / notify或者使用任何java.util.concurrent基元(比如ReentrantReadWriteLock),可以像使用单个JVM一样在整个集群中expression并发性,CyclicBarrier,AtomicLong,FutureTask等等。

在“ 兵马俑烹饪手册”中有很多简单的食谱来展示这些原始的使用。

作为一个例子,我将发布ReentrantReadWriteLock示例(注意,没有“Terracotta”版本的锁 – 您只需使用普通的Java 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(); } } } 

我build议使用Redisson 。 它实现了超过30个分布式数据结构和服务,包括java.util.Lock 。 用法示例:

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

我打算build议使用memcached作为一个非常快速的分布式RAM存储器来保存日志; 但似乎EHCache是​​一个类似的项目,但更多的是以Java为中心的。

要么是要走的路,只要你确定要使用primefaces更新(memcached支持它们,不知道EHCache)。 这是迄今为止最具扩展性的解决scheme。

作为一个相关的数据点,Google使用基于RAM的快速分布式锁存储“Chubby”作为几个系统的根源,其中包括BigTable。

我已经使用Coherence做了很多工作,可以使用多种方法来实现分布式锁。 天真的方法是请求locking所有参与节点上的同一个逻辑对象。 在一致性方面,这是locking复制caching的一个关键。 这种方法不能很好地扩展,因为networkingstream量在添加节点时会线性增加。 更聪明的方法是使用分布式caching,其中群集中的每个节点自然负责一部分密钥空间,因此将密钥locking在这样的caching中总是涉及与至多一个节点的通信。 你可以基于这个想法推出你自己的方法,或者更好的方法是获得Coherence。 这真的是你梦想中的可扩展性工具箱。

我想补充一点,任何一个体面的基于多节点networking的locking机制都必须相当复杂,才能在发生networking故障时正确运行。

不知道,如果我理解整个上下文,但它听起来像你有一个单一的数据库支持这个? 为什么不利用数据库的locking:如果创build客户是一个单独的INSERT,那么这个语句本身就可以作为一个锁,因为数据库将拒绝违反你的约束之一的第二个INSERT(例如,客户名是独特的例子)。

如果“插入一个客户”操作不是primefaces的,并且是一批语句,那么我会介绍(或使用)一个初始的INSERT,它创build一些简单的基本logging来标识你的客户(必要的UNIQUEITY约束),然后做所有其他插入/更新在同一个交易。 数据库将再次保持一致性,任何并发修改都会导致其中一个失败。

我用两种方法做了一个简单的RMI服务:locking和释放。 两个方法都取一个键(我的数据模型使用UUID作为PK,所以这也是locking键)。

RMI是一个很好的解决scheme,因为它是集中的。 你不能用EJB做这个(特别是在一个集群中,因为你不知道你的调用将在哪个机器上着陆)。 加上,这很容易。

它为我工作。

如果您可以设置您的负载平衡,以便对单个客户的请求总是映射到同一个服务器,那么您可以通过本地同步来处理这个问题。 例如,将您的客户ID为10的模块find要使用的10个节点中的哪一个。

即使您不想在通常情况下执行此操作,您的节点也可以为这种特定types的请求进行代理。

假设你的用户是足够统一的(即如果你有很多),你不希望在一个节点过载的地方出现热点,这应该仍然可以很好地扩展。

你也可以考虑Cacheonix的分布式锁。 不同于此处提到的任何其他内容,Cacheonix支持ReadWritelocking,在需要时从读取到写入locking升级:

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

充分披露:我是一个Cacheonix开发人员。

由于您已经连接到数据库,所以在添加另一个infra块之前,请看一下JdbcSemaphore ,它很简单:

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

它是spf4j库的一部分。

当天,我们将使用networking上特定的“locking服务器”来处理这个问题。 的Bleh。

你的数据库服务器可能有专门的资源来做这种事情。 MS-SQL Server具有可通过sp_getapplock / sp_releaseapplock过程使用的应用程序locking。

我们一直在开发一个开源的,分布式的同步框架,目前DistributedReentrantLock和DistributedReentrantReadWrite的锁已经实现了,但是仍然处于testing和重构阶段。 在我们的体系结构中,locking键分为桶,每个节点对于一定数量的桶都是有效的。 所以对于成功的locking请求有效,只有一个networking请求。 我们也使用AbstractQueuedSynchronizer类作为本地locking状态,所有失败的locking请求在本地处理,这大大减less了networkingstream量。 我们使用JGroups( http://jgroups.org )进行组通信,使用Hessian进行序列化。

有关详情,请参阅http://code.google.com/p/vitrit/

请给我你的宝贵意见。

卡姆兰

Interesting Posts