许多企业都结合使用 Microsoft .NET Framework 和 Java 应用程序,尤其是那些出于各种考虑不能只依赖于单一技术的大中型企业。 通常,企业采用 Web 应用程序、面向服务的体系结构 (SOA) Web 服务以及其他服务器应用程序来处理大量事务。
其中很多应用程序在运行时需要相互共享数据。 通常,这些应用程序全都是对数据库中所存储的常用业务数据进行操作。 它们面对的一般是连续数据流(如金融交易应用程序),而且需要在运行时多次处理数据并与其他应用程序共享结果。
虽然数据库是永久存储数据的主要存储区,但并不太适合运行时数据共享。 其中一个原因在于,从数据库读取数据时不能始终保证高性能。 再者,在处理事务方面数据库的扩展性并不好,因此很可能很快会成为瓶颈,并降低依赖于它的所有应用程序的速度。
此外,也无法实时、有效地共享数据。 实时数据共享要求一旦某个应用程序更新了数据,则对该数据感兴趣的其他所有应用程序都应当立即收到通知。 同样,某些应用程序可能正等待某些数据类型被创建并可用,一旦这些操作发生,这些应用程序应当立即收到通知。
不论需要共享数据的那些应用程序是全都基于 .NET Framework,还是有一部分基于 .NET 而另一部分基于 Java,这都是常见问题。 事实上,如果应用程序混合采用 .NET 和 Java,问题会更严重,因为对于此类应用程序而言,在应用程序层面上根本没有以本机形式共享数据的自动方法。
解决方案:企业分布式缓存
幸运的是,企业分布式缓存可以解决这些问题。 这种内存中存储可跨越多个服务器,将服务器的内存集中在一块,因而内存存储容量是可扩展的。 事务容量也变得可扩展,添加的服务器越多,能够处理的事务负载越大。
企业分布式缓存还提供了事件通知机制,应用程序在更新数据后可以相互通知。 由此,您可以拥有异步事件通知机制,其中一个应用程序生成数据,其他应用程序可以使用该数据,从而创建了生产者/使用者模型或发布/订阅模型。 多个应用程序可订阅某些数据类型,当该数据发布时这些应用程序将收到通知。
还有一种通读/通写机制,即企业分布式缓存本身可以从数据源和应用程序读取大量数据。 无论应用程序是基于 Java 还是 .NET,其代码都可以变得更简单,因为可以从企业分布式缓存中读取数据, 而无需嵌入数据库访问代码。 图 1 是一个使用企业分布式缓存的 .NET Framework 应用程序的简单示例。
图 1 使用企业分布式缓存的 .NET 应用程序
- using System;
- ...
- using Alachisoft.NCache.Web.Caching;
-
- namespace Client
- {
- class Program
- {
- static string _sCacheName = "myAppCache";
- static Cache _sCache = NCache.InitializeCache(_sCacheName);
-
- static void Main(string[] args)
- {
- string employeeId = "1000";
- string key = "Employee:EmployeeId:" + employeeId;
-
- // First check the cache for this employee
- Employee emp = _sCache.Get(key);
-
- // If cache doesn't have it then make database call
- if (emp == null)
- {
- emp = LoadEmployeeFromDb(employeeId);
-
- // Now add it to the cache for next time
- _sCache.Insert(key, emp);
- }
- }
- }
- }
-
除此之外,企业分布式缓存可以根据其他第三方应用程序对数据库所做的任何数据更改进行同步。 它与数据库之间存在连接,因此只要数据库中某个数据类型发生变化,便会收到通知。 图 2 给出了 .NET 和 Java 应用程序如何在运行时通过企业分布式缓存相互共享数据的图解说明。
图 2 .NET 和 Java 应用程序通过分布式缓存共享数据
.NET 和 Java 应用程序共享数据
借助企业分布式缓存,多个应用程序(不论是基于 .NET 还是 Java)可以访问同一个缓存并通过缓存共享数据。 如果只是 .NET 应用程序(或只是 Java 应用程序)通过分布式缓存共享数据,则应用程序可以将对象存储为本机二进制格式并对其进行序列化/反序列化。 但如果是两种类型的应用程序之间相互共享数据,则需要将数据以可移植数据格式存储在分布式缓存中。
这是因为当 .NET 应用程序在分布式缓存中存储对象时,实际上会将对象转换为 XML 文档并存储该 XML。 另一方面,当 Java 应用程序从分布式缓存读取该数据时,会将 XML 转换为 Java 对象。 实际上,XML 被用作可移植数据存储机制,因为 .NET 对象被转换为 XML,然后又从 XML 转换为 Java,反之亦然。
有许多开放源代码库可以帮助您将 .NET 或 Java 对象转换为 XML,然后转换回对象格式。 当然,您也可以自己开发,不过我建议您选择开放源代码库。 我个人比较喜欢 Carlos Jaimez 和 Simon Lucas 开发的 Web Objects in XML(WOX,woxserializer.sourceforge.net)。 本文中将使用摘自其网站的 Java 到 .NET 转换示例(已征得他们同意)。 图 3 显示了采用 Java 和 C# 定义的 Student 和 Course 类。
图 3 用 Java 和 C# 编写的 Student 和 Course 类
- // Java classes
- public class Student
- {
- private String name;
- private int registrationNumber;
- private Course[] courses;
- }
- public class Course
- {
- private int code;
- private String name;
- private int term;
- }
-
- // ***************************************************
- // .NET classes in C#
- public class Student
- {
- private String name;
- private Int32 registrationNumber;
- private Course[] courses;
- }
- public class Course
- {
- private Int32 code;
- private String name;
- private Int32 term;
- }
-
如果我们使用 .NET 和 Java 应用程序将以上 Student 和 Course 对象存储在企业分布式缓存中,那么随后可以使用 WOX 库将这些对象转换为 XML。 之后,如果应用程序想要从企业分布式缓存中读取这些对象,则再次读取 WOX 库,将 XML 转换回 Java 或 .NET 对象格式。 图 4 显示了转换为 XML 格式的 Student 和 Course 类。
图 4 转换为 XML 的 Java 和 .NET 类
- <object type="Student" id="0">
- <field name="name" type="string" value="Carlos Jaimez"/>
- <field name="registrationNumber" type="int" value="76453"/>
- <field name="courses">
- <object type="array" elementType="Course" length="3" id="1">
- <object type="Course" id="2">
- <field name="code" type="int" value="6756"/>
- <field name="name" type="string"
- value="XML and Related Technologies"/>
- <field name="term" type="int" value="2"/>
- </object>
- <object type="Course" id="3">
- <field name="code" type="int" value="9865"/>
- <field name="name" type="string"
- value="Object Oriented Programming"/>
- <field name="term" type="int" value="2"/>
- </object>
- <object type="Course" id="4">
- <field name="code" type="int" value="1134"/>
- <field name="name" type="string" value="E-Commerce Programming"/>
- <field name="term" type="int" value="3"/>
- </object>
- </object>
- </field>
- </object>
-
在您的应用程序中,应当从缓存层或数据访问层调用 WOX。
基于项的事件通知
事件通知机制功能强大,多个应用程序(.NET 和 Java)可通过该机制协调异步数据共享。 在该机制的帮助下,应用程序可避免执行代价高昂的数据库轮询。 该机制可在 .NET 和 Java 应用程序间共享,因此能无缝地相互通知。
事件通知的一个常用类型即基于项的通知。 在此类型中,应用程序登记感兴趣的各个缓存项密钥(可能已存在,也可能尚未存在于缓存中),只要任何人因任何原因在分布式缓存中添加、更新或删除了该项,应用程序都将分别收到通知。 例如,即使某项由于到期或被逐出而遭删除,也将触发项删除事件通知。
.NET 和 Java 应用程序都可以登记对相同缓存项的兴趣,并接收有关该项的通知。 通知中通常还包括受影响的缓存项,如上一部分中所述,这些项将根据应用程序的类型转换为 .NET 或 Java 格式。
应用程序生成的自定义事件通知
对于 .NET 和 Java 应用程序,企业分布式缓存同时也是强有力的事件传播平台。 与企业分布式缓存相连的任何应用程序都可以在缓存中触发自定义事件,而后,不论应用程序位于何处,只要登记了对这些自定义事件感兴趣,那么都会收到缓存的通知。 这本身就在企业分布式缓存中提供了一个强有力的独立于语言和平台的事件传播机制。
应用程序可借助该功能协调异步数据共享。 例如,如果某个应用程序将数据放入分布式缓存中,然后触发一个自定义事件,计划以后使用或处理该数据的其他应用程序将立即收到通知。
基于连续查询的事件通知
基于项的事件通知虽然功能强大,但是要求应用程序知晓缓存项的密钥。 如果将基于项的事件通知与企业分布式缓存中经常提供的其他分组功能(如标记、组/子组等)结合使用,那么几乎可以处理需要根据各个缓存项所发生的情况通知应用程序的所有情况。
不过,基于项的事件存在两个限制。 首先,前面提到过,应用程序必须知晓想要收到通知的所有缓存项的密钥。 其次,不论这些项有何变化,应用程序都将收到通知。 应用程序无法设定更详细的标准,以便仅在数据发生特定变化时才收到通知。
为应对此类情况,企业分布式缓存提供了连续查询,这是一种类似 SQL 的查询,可捕获应用程序所感兴趣数据的相关业务规则。 连续查询并非搜索查询,而是企业分布式缓存保持的某种“标准”。只要分布式缓存中添加或更新了内容,便会将该操作与连续查询标准进行比较。 如果标准匹配,则触发事件,并通知发布连续查询标准的应用程序。
通过连续查询,应用程序可以等待更复杂的更改,并仅当发生这些更改时才收到通知。
通读和通写处理程序
很多时候,应用程序尝试读取的数据并不在企业分布式缓存中,必须从数据库中读取。 此时,应用程序可以直接访问数据库并读取该数据,但这意味着所有应用程序都必须复制相同的数据访问代码(尤其是在 .NET 和 Java 中)。 或者,也可以在需要数据时,要求企业分布式缓存为其从数据库中读取该数据。
有了通读/通写功能,企业分布式缓存可从数据源直接读取数据。 应用程序可简化其代码,从而无需访问数据库。 它们只需要求企业分布式缓存为其提供数据,如果缓存中无该数据,则访问数据源并读取该数据。 图 5 显示了企业分布式缓存是如何使用通读和通写功能的。
图 5 通读/通写的使用原理
有一点需要注意。 虽然让分布式缓存从数据库读取数据有很大的好处,但仍有许多数据类型最好由应用程序直接从数据库读取。 如果要读取的是包含复杂联接的数据集,那么最好由应用程序亲自读取,然后将其放入分布式缓存中。
数据库同步
由于大量数据被放入了企业分布式缓存中,因此必须确保这些数据与主数据源(通常为关系数据库)保持同步才有意义。 企业分布式缓存提供了此功能。
通过数据库同步功能,应用程序可以指定缓存项与数据库表中行之间的关系(依赖关系)。 只要数据库中的数据发生更改,数据库服务器便会触发 .NET 事件(即使是 SQL Server 2005/2008 数据库),并将此更改通知企业分布式缓存。 对于不支持 .NET 事件的其他数据库,企业分布式缓存也提供了可配置的轮询,以便分布式缓存可以轮询数据库(比如每隔 15 秒钟一次),并在数据更改时进行同步。
随后分布式缓存将从缓存中删除该数据,或读取该数据的全新副本(如果配置了通读功能)。 图 6 显示了企业分布式缓存与 SQL Server 是如何同步的。
图 6 分布式缓存中的数据库同步
高可用性:自修复动态群集
企业分布式缓存可用作多个应用程序之间的运行时数据库共享平台(.NET 到 .NET、.NET 到 Java 以及 Java 到 Java)。 很多情况下,这些应用程序对于您的公司而言至关重要。
由于许多关键任务应用程序都依赖于企业分布式缓存,因此分布式缓存必须具备高可用性。 企业分布式缓存不能瘫痪或停止工作,并且应当完全不需要停机以进行维护或其他正常操作。
企业分布式缓存通过可自修复的动态缓存服务器群集来实现高可用性。 此处的自修复表示群集清楚其所有成员,当有成员离开或加入时会动态调整。 此外,还可以确保数据是重复的以保证可靠性,并且如果有群集成员离开,其备份数据可自动供应用程序使用。 所有这些功能必须迅速执行,且不会对使用企业分布式缓存的应用程序造成任何中断。
可扩展性:缓存分区和复制
使用企业分布式缓存的许多应用程序都是高事务应用程序。 因此,缓存群集上的负载会迅速增长。不过,如果企业分布式缓存的响应时间延长,其价值会大打折扣。 事实上,在一定范围内,企业分布式缓存要优于关系数据库。由于它可以在动态群集中添加更多的服务器,因而每秒处理的事务要比数据库多得多。 但除非分布式缓存中的数据以智能方式存储,否则无法实现可扩展性。 这一点可通过数据分区来实现,每个分区进行复制以保证可靠性。
多亏了企业分布式缓存,您可以采用分区拓扑来进行扩展。 图 7 显示了分区复制拓扑。
图 7 用于可靠扩展的分区复制拓扑
企业分布式缓存会对缓存中存储的所有数据自动进行分区。 每个分区存储在不同服务器上,同时在另一台服务器上创建和存储该分区的备份。 这确保了即使任何服务器停机,数据也不会丢失。
总之,您可以采用分区技术在动态群集中添加更多的缓存服务器以扩大存储容量,随着服务器的增多,每秒处理的事务量也将提高。 并且,复制确保了数据的可靠性,因为服务器停机不会造成数据丢失。
功能与协作
总而言之,企业分布式缓存是高事务 .NET 和 Java 应用程序相互共享数据的理想途径。 其强大的事件传播机制,包括基于项的事件通知、应用程序生成的自定义事件通知和基于连续查询的事件通知,确保了实时共享数据。
就设计而言,企业分布式缓存不仅速度飞快,而且是可扩展的。 高速度源自在内存中进行操作。 可扩展性源自能添加多台服务器。 它对实际存储进行分区,并将每个分区存储在不同服务器上,同时在另一台服务器(如 RAID 磁盘)上存储该分区的备份。
如今的应用程序与过去相比有着更高的功能要求。 它们需要以更具协作性的方式来共享数据以及彼此互动。 它们不仅需要速度飞快,同时还要满足极高负载需求,以避免损害性能和可扩展性。 而且,它们必须跨多个平台执行操作,使 .NET 应用程序可以透明而有效地与 Java 应用程序合作。 企业分布式缓存可帮助我们达成以上所有目标。
Iqbal Khan 是 Alachisoft (alachisoft.com) 的总裁和技术推广者,该公司提供的 Ncache(.NET 分布式缓存)可提高企业应用程序的性能和可扩展性。Khan 于 1990 年获得印第安那大学的计算机科学硕士学位。您可以通过电子邮件与他联系:iqbal@alachisoft.com。