【问题标题】:Java Solutions for Distributed Transactions and/or Data Shared in Cluster用于集群中分布式事务和/或数据共享的 Java 解决方案
【发布时间】:2009-10-23 22:37:23
【问题描述】:

集群/分发 Java 服务器应用程序的最佳方法是什么? 我正在寻找一种方法,允许您通过添加更多应用程序服务器和更多数据库服务器来进行水平扩展。

  • 您建议使用哪些技术(软件工程技术或特定技术)来解决此类问题?
  • 您使用什么技术来设计持久层以扩展到许多读取器/写入器 扩展应用程序事务并扩展对共享数据的访问(最好的方法是消除共享数据;您可以应用哪些技术来消除共享数据)。
  • 似乎需要不同的方法,具体取决于您的事务是读取还是写入繁重,但我觉得您是否可以优化“写入”繁重的应用程序,该应用程序对于“读取”也很有效

“最佳”解决方案允许您为单个节点编写 Java 应用程序,并希望“隐藏”访问/锁定共享数据的大部分细节。

在分布式环境中,最困难的问题总是归结为让多个事务访问共享数据。似乎有两种常见的并发事务方法。

  1. Explicit locks(在分布式系统中跨多个节点协调非常容易出错并且速度很慢)
  2. Software transactional memory (STM) AKA 乐观并发,如果事务发现共享状态已更改(并且事务可以稍后重试),则在提交期间回滚事务。 哪种方法可以更好地扩展,分布式系统中的权衡是什么?

我一直在研究扩展解决方案(以及提供如何扩展示例的一般应用程序),例如:

  1. Terracotta - 通过扩展 Java 内存模型以包括使用 Java 的并发锁定机制(同步、ReentrantReadWriteLocks)的分布式共享内存来提供“透明”扩展。
  2. Google App Engine Java - 允许您编写 Java(或 python)应用程序,这些应用程序将分布在“云”服务器中,您在其中分发处理事务的服务器并使用 BigTable 存储持久数据(不确定访问共享数据的事务的方式或处理锁争用以便能够有效扩展)
  3. Darkstar MMO Server - Darkstar 是 Sun 的开源 MMO(大型多人在线)游戏服务器,它们以线程事务方式扩展事务,允许给定事务仅运行一定数量并提交,如果需要很长时间,它将回滚(有点像软件事务内存)。他们一直在研究 supporting a multi-node server setup 以进行扩展。
  4. Hibernate's optimistic locking - 如果你使用 Hibernate,你可以使用他们的乐观并发支持来支持 software transactional memory 类型行为
  5. Apache CouchDB 应该自然地“缩放”到网格配置中的许多读写器数据库。 (有没有一个很好的例子来说明如何管理锁定数据或确保事务隔离?):
  6. JCache - 通过将结果缓存到您可以在 Google appengine 中用于访问 memcached 并缓存其他经常读取的数据的常见查询中来扩展“读取”繁重的应用程序。

Terracotta 似乎是最完整的解决方案,因为您可以“轻松”修改现有服务器应用程序以支持缩放(在定义 @Root 对象和 @AutoLockRead/Write 方法之后)。问题是要真正从分布式应用程序中获得最大的性能,分布式系统的优化并不是事后才想到的,你必须在设计它时知道对象访问可能会被网络 I/O 阻塞。

为了正确扩展,它似乎总是归结为分区数据和负载平衡事务,以便给定“执行单元”(cpu 核心 -> 线程 -> 分布式应用程序节点 -> 数据库主节点)

似乎要通过集群使任何应用程序正确扩展,您需要能够根据数据访问读取/写入对事务进行分区。人们提出了哪些解决方案来分发他们的应用程序数据(Oracle、Google BigTable、MySQL、数据仓库),以及通常如何管理分区数据(许多写入主机,具有更多读取数据库等)。

在扩展您的数据持久层方面,哪种类型的配置在将您的数据分区到许多读者/许多作者方面表现最好(通常我会根据给定用户(或任何核心实体)对我的数据进行分区通常是您的“根”对象实体)由单个主数据库拥有)

【问题讨论】:

  • “一个有效的复杂系统总是从一个有效的简单系统演变而来的。相反的命题似乎也是正确的:从零开始设计的复杂系统永远不会工作,也不能让它工作. 你必须重新开始,从一个工作简单的系统开始。” ——约翰·高尔

标签: java concurrency transactions scalability optimistic-locking


【解决方案1】:

以为我找到了一个很棒的 Java 集群/分布式平台,想重新打开这个-

结帐http://www.hazelcast.com

我运行了测试程序,它非常酷,非常轻巧且易于使用。它会自动检测对等配置中的集群成员。机会是无限的。

【讨论】:

    【解决方案2】:

    感谢您在一个地方很好地总结了所有可能性。

    但这里缺少一种技术。它是 MapReduce-Hadoop。如果可以将问题融入 MapReduce 范式,它可能是最广泛可用的解决方案。我还想知道 Actor 框架模式(JetLang、Kilim 等)是否可以扩展到集群。

    【讨论】:

      【解决方案3】:

      【讨论】:

        【解决方案4】:

        别忘了 Erlang 的 Mnesia

        Mnesia 为您提供诸如在普通数据库中习惯使用的事务之类的东西,但提供实时操作和容错。另外,您可以在不停机的情况下重新配置。缺点是它是一个内存驻留数据库,因此您必须对非常大的表进行分段。最大表大小为 4Gb。

        【讨论】:

        • 这个答案真的是要从技术的角度来理解。您可以通过套接字从 Java 中使用 Mnesia,但您并不想这样做。关键是要从 Mnesia 的做法中学习。
        【解决方案5】:

        虽然 Oracle Coherence 和许多其他建议的解决方案都适用于共享数据,但您只引用了锁定和 STM 作为在分布式环境中管理状态突变的方法;这些通常都是扩展状态管理的非常糟糕的方法。在另一个网站上,我最近发布了以下关于如何实现(例如)序列计数器的内容:

        如果您正在查看计数器,那么使用 Coherence EntryProcessor 之类的东西可以轻松实现“一次且仅一次”的行为,并为任意数量的单调递增序列实现 HA;这是整个实现:

        public class SequenceCounterProcessor
                extends AbstractProcessor
            {
            public Object process(InvocableMap.Entry entry)
                {
                long l = entry.isPresent() ? (Long) entry.getValue() + 1 : 0;
                entry.setValue(l);
                return l;
                }
            }
        

        是的。而已。自动无缝 HA、动态横向扩展弹性、一次性行为等。完成。

        EntryProcessor 是我们在 2005 年引入的一种分布式闭包。

        顺便说一句,在 Java 8(尚未发布)中,Lambda 项目在语言和标准库中引入了官方闭包支持。

        基本上,这个想法是将闭包传递到分布式环境中数据“所有者”的位置。 Coherence 通过使用动态分区来动态管理数据所有权,允许分布式系统在正在运行的各种机器和节点之间对数据进行负载平衡。事实上,默认情况下,所有这些都是 100% 自动化的,所以你永远不会真正告诉它把数据放在哪里,或者有多少数据去哪里。此外,还有其他节点和其他物理服务器上管理的数据的二级(可能还有三级等)副本,以在进程失败或服务器死亡的情况下提供高可用性。同样,这些备份副本的管理默认是完全自动和完全同步的,这意味着系统默认是 100% HA(即没有配置)。

        当闭包到达数据所有者时,它在事务工作区中执行,如果操作成功完成,则将其运送到备份以进行安全保存。数据突变(例如操作的结果)只有在备份成功后才会对系统的其余部分可见。

        对上述内容的一些优化包括添加 ExternalizableLite 和 PortableObject 接口以优化序列化,并通过直接追踪数据的“网络就绪”形式来避免盒装长的序列化:

        public Object process(InvocableMap.Entry entry)
            {
            try
                {
                BinaryEntry binentry = (BinaryEntry) entry;
                long l = entry.isPresent() ? binentry.getBinaryValue()
                        .getBufferInput().readLong() + 1 : 0L;
                BinaryWriteBuffer buf = new BinaryWriteBuffer(8);
                buf.getBufferOutput().writeLong(l);
                binentry.updateBinaryValue(buf.toBinary());
                return l;
                }
            catch (IOException e)
                {
                throw new RuntimeException(e);
                }
            }
        

        既然它是无状态的,为什么不准备一个单例实例呢?

        public static final SequenceCounterProcessor INSTANCE =
                new SequenceCounterProcessor();
        

        从网络上的任何地方使用它就像一行代码一样简单:

        long l = (Long) sequences.invoke(x, SequenceCounterProcessor.INSTANCE);
        

        其中“x”是标识您要使用的特定序列计数器的任何对象或名称。有关详细信息,请参阅 Coherence 知识库:http://coherence.oracle.com/

        Oracle Coherence 是一个分布式系统。每当您启动一个 Coherence 节点时,它都会与其他已经在运行的 Coherence 节点连接,并动态形成一个弹性集群。该集群以分区、高可用性 (HA) 和事务一致的方式托管数据,并托管以“一次且仅一次”的方式对该数据进行操作的操作(如我上面展示的操作)。

        此外,除了能够从任何 Coherence 节点透明地调用任何逻辑或访问任何数据外,您还可以从网络上的任何进程透明地调用任何逻辑或访问任何数据(当然,要经过身份验证和授权)。因此,此代码可以在任何 Coherence 集群节点或任何(Java / C / C++ / C# / .NET)客户端上运行:

        为了全面披露,我在 Oracle 工作。这篇文章中表达的观点和观点是我自己的,并不一定反映我雇主的观点或观点。

        【讨论】:

          【解决方案6】:

          也许那些slides 会有所帮助。根据我们的经验,我推荐 Oracle (Tangosol) Coherence 和 GigaSpaces 作为最强大的数据和处理分发框架。根据问题的确切性质,其中之一可能会发光。兵马俑也相当适用于一些问题。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-08-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-10-06
            • 2014-06-25
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多