Apache Ignite内存数据组织是高性能的、集成化的以及分布式的内存平台,他可以实时地在大数据集中执行事务和计算,和传统的基于磁盘或者闪存的技术相比,性能有数量级的提升。
可以将Ignite视为一个独立的、易于集成的内存组件的集合,目的是改进应用程序的性能和可扩展性,部分组件包括:
这部分帮助你开始一个新的Ignite程序,你会发现他瞬间就可以跑起来。
Apache Ignite官方在如下环境中进行的测试:
JDK:Oracle JDK7及以上
OS:Linux(任何版本),Mac OS X(10.6及以上),Windows(XP及以上),Windows Server(2008及以上)
网络:没有限制(建议10G)
下面是安装Apache Ignite的简要步骤:
从https://ignite.apache.org/下载Apache Ignite的zip压缩包
将zip压缩包解压到系统安装文件夹
设置IGNITE_HOME环境变量指向安装文件夹,确保没有/结尾(这一步可选)
从源代码构建
如果你下载的是源代码包,可以用如下命令构建:
# Unpack the source package
$ unzip -q apache-ignite-1.6.0-src.zip
$ cd apache-ignite-1.6.0-src
# Build In-Memory Data Fabric release (without LGPL dependencies)
$ mvn clean package -DskipTests
# Build In-Memory Data Fabric release (with LGPL dependencies)
$ mvn clean package -DskipTests -Prelease,lgpl
# Build In-Memory Hadoop Accelerator release
# (optionally specify version of hadoop to use)
$ mvn clean package -DskipTests -Dignite.edition=hadoop [-Dhadoop.version=X.X.X]
一个Ignite节点可以从命令行通过默认的配置或者传入外部配置文件的方式启动。可以启动很多很多的节点然后他们会自动地发现对方。
通过默认配置
要启动一个基于默认配置的网格节点,打开命令行然后切换到IGNITE_HOME(安装文件夹),然后输入如下命令:
$ bin/ignite.sh
然后会看到输出大体是如下的样子:
[02:49:12] Ignite node started OK (id=ab5d18a6)
[02:49:12] Topology snapshot [ver=1, nodes=1, CPUs=8, heap=1.0GB]
ignite.sh启动ignite节点会使用默认的配置文件:config/default-config.xml。
传递配置文件
要从命令行显式地传递一个配置文件,只需要在安装文件夹路径下输入ignite.sh <配置文件路径>,比如:
$ bin/ignite.sh examples/config/example-cache.xml
配置文件的路径既可以是绝对路径,也可以是相对于IGNITE_HOME的相对路径,也可以是相对于类路径的META-INF文件夹。
交互式模式
要在一个交互模式传递配置文件,可以加上-i参数,像这样:ignite.sh -i。
在项目里使用Apache Ignite的另一个方式是使用Maven2依赖管理。
Ignite只需要一个ignite-core强依赖,通常还需要添加ignite-spring,来做基于spring的XML配置,还有ignite-indexing,来做SQL查询。
确保将${ignite-version}替换为实际的版本号。
<dependency>
<groupId>org.apache.ignitegroupId>
<artifactId>ignite-coreartifactId>
<version>${ignite.version}version>
dependency>
<dependency>
<groupId>org.apache.ignitegroupId>
<artifactId>ignite-springartifactId>
<version>${ignite.version}version>
dependency>
<dependency>
<groupId>org.apache.ignitegroupId>
<artifactId>ignite-indexingartifactId>
<version>${ignite.version}version>
dependency>
Maven设置
关于如何包含个别的ignite maven模块的更多信息,可以参考1.3.Maven设置章节。
我们写第一个计算应用,他会计算一句话中非空白字符的字符数量。作为一个例子,我们首先将一句话分割为多个单词,然后通过每个计算作业来计算每一个独立单词中的字符数量。最后,我们将从每个作业获得的结果简单相加来获得整个的数量
java8:
try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) {
Collection<IgniteCallable<Integer>> calls = new ArrayList<>();
// Iterate through all the words in the sentence and create Callable jobs.
for (final String word : "Count characters using callable".split(" "))
calls.add(word::length);
// Execute collection of Callables on the grid.
Collection<Integer> res = ignite.compute().call(calls);
// Add up all the results.
int sum = res.stream().mapToInt(Integer::intValue).sum();
System.out.println("Total number of characters is '" + sum + "'.");
}
java7:
try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) {
Collection<IgniteCallable<Integer>> calls = new ArrayList<>();
// Iterate through all the words in the sentence and create Callable jobs.
for (final String word : "Count characters using callable".split(" ")) {
calls.add(new IgniteCallable<Integer>() {
@Override public Integer call() throws Exception {
return word.length();
}
});
}
// Execute collection of Callables on the grid.
Collection<Integer> res = ignite.compute().call(calls);
int sum = 0;
// Add up individual word lengths received from remote nodes.
for (int len : res)
sum += len;
System.out.println(">>> Total number of characters in the phrase is '" + sum + "'.");
}
零部署
注意,由于Ignite的零部署特性,当从IDE运行上面的程序时,远程节点没有经过显式地部署,就获得了计算作业。
我们再来一个小例子,它从/往分布式缓存中获取/添加数据,并且执行基本的事务。
因为在应用中使用了缓存,要确保他是经过配置的,我们可以用Ignite自带的示例配置,他已经做了一些缓存的配置。
$ bin/ignite.sh examples/config/example-cache.xml
Put和Get:
try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) {
IgniteCache<Integer, String> cache = ignite.getOrCreateCache("myCacheName");
// Store keys in cache (values will end up on different cache nodes).
for (int i = 0; i < 10; i++)
cache.put(i, Integer.toString(i));
for (int i = 0; i < 10; i++)
System.out.println("Got [key=" + i + ", val=" + cache.get(i) + ']');
}
原子化操作:
// Put-if-absent which returns previous value.
Integer oldVal = cache.getAndPutIfAbsent("Hello", 11);
// Put-if-absent which returns boolean success flag.
boolean success = cache.putIfAbsent("World", 22);
// Replace-if-exists operation (opposite of getAndPutIfAbsent), returns previous value.
oldVal = cache.getAndReplace("Hello", 11);
// Replace-if-exists operation (opposite of putIfAbsent), returns boolean success flag.
success = cache.replace("World", 22);
// Replace-if-matches operation.
success = cache.replace("World", 2, 22);
// Remove-if-matches operation.
success = cache.remove("Hello", 1);
事务:
try (Transaction tx = ignite.transactions().txStart()) {
Integer hello = cache.get("Hello");
if (hello == 1)
cache.put("Hello", 11);
cache.put("World", 22);
tx.commit();
}
分布式锁:
// Lock cache key "Hello".
Lock lock = cache.lock("Hello");
lock.lock();
try {
cache.put("Hello", 11);
cache.put("World", 22);
}
finally {
lock.unlock();
}
最简单的检查Ignite数据网格中的内容,以及执行其他的一系列的管理和监控操作的方式就是使用Ignite Visor命令行工具。
要启动Visor,简单地执行如下命令即可:
$ bin/ignitevisorcmd.sh
Apache Ignite是由很多Maven模块组成的,如果项目里用Maven管理依赖,可以单独地导入各个Ignite模块,
注意,在下面的例子中,要将ignite.version替换为实际的版本。
Ignite强依赖于ignite-core.jar。
<dependency>
<groupId>org.apache.ignitegroupId>
<artifactId>ignite-coreartifactId>
<version>${ignite.version}version>
dependency>
然而,很多时候需要其他的依赖,比如,要使用Spring配置或者SQL查询等。
下面就是最常用的可选模块:
<dependency>
<groupId>org.apache.ignitegroupId>
<artifactId>ignite-coreartifactId>
<version>${ignite.version}version>
dependency>
<dependency>
<groupId>org.apache.ignitegroupId>
<artifactId>ignite-springartifactId>
<version>${ignite.version}version>
dependency>
<dependency>
<groupId>org.apache.ignitegroupId>
<artifactId>ignite-indexingartifactId>
<version>${ignite.version}version>
dependency>
可以一个个地导入Ignite模块,唯一必须的就是ignite-core,其他的都是可选的,所有可选模块都可以像核心模块一样导入,只是构件Id不同。
现在提供如下模块:
构件版本
注意,导入若干Ignite模块时,他们的版本号应该相同,比如,如果使用ignite-core1.7,所有其他的模块也必须导入1.7版本。
下面的Ignite模块有LGPL依赖,因此无法部署到Maven中央仓库:
mvn clean install -DskipTests -Plgpl -pl modules/hibernate -am
第三方仓库
GridGain提供自己的Maven仓库,包含了Apache Ignite的LGPL构件,比如ignite-hibernate。
注意位于GridGain的Maven库中的构件仅仅为了方便使用,并不是官方的Apache Ignite构件。
Ignite是基于JVM的,一个JVM可以运行一个或者多个逻辑Ignite节点(大多数情况下,一个JVM运行一个Ignite节点)。在整个Ignite文档中,会交替地使用术语Ignite运行时以及Ignite节点,比如说可以该主机运行5个节点,技术上通常意味着主机上启动5个JVM,每个JVM运行一个节点,Ignite也支持一个JVM运行多个节点,事实上,通常作为Ignite内部测试用。
Ignite运行时 == JVM进程 == Ignite节点(多数情况下)
Ignition类在网络中启动每个Ignite节点,注意一个物理机可以运行多个Ignite节点。
下面的代码是在全默认配置下启动网格节点;
Ignite ignite = Ignition.start();
或者传入一个配置文件:
Ignite ignite = Ignition.start(“examples/config/example-cache.xml”);
配置文件的路径既可以是绝对路径,也可以是相对于IGNITE_HOME的相对路径,也可以是相对于类路径的META-INF文件夹。
有时可能希望在Ignite节点启动和停止的之前和之后执行特定的操作,这个可以通过实现LifecycleBean接口实现,然后在spring的配置文件中通过指定IgniteConfiguration的lifecycleBeans属性实现。
<bean class="org.apache.ignite.IgniteConfiguration">
...
<property name="lifecycleBeans">
<list>
<bean class="com.mycompany.MyLifecycleBean"/>
list>
property>
...
bean>
LifecycleBean也可以像下面这样通过编程的方式实现:
// Create new configuration.
IgniteConfiguration cfg = new IgniteConfiguration();
// Provide lifecycle bean to configuration.
cfg.setLifecycleBeans(new MyLifecycleBean());
// Start Ignite node with given configuration.
Ignite ignite = Ignition.start(cfg)
一个LifecycleBean的实现可能如下所示:
public class MyLifecycleBean implements LifecycleBean {
@Override public void onLifecycleEvent(LifecycleEventType evt) {
if (evt == LifecycleEventType.BEFORE_NODE_START) {
// Do something.
...
}
}
}
也可以将Ignite实例以及其他有用的资源注入LifecycleBean实现。
当前支持如下生命周期事件类型:
Ignite API中的所有分布式方法都既可以同步执行也可以异步执行。然而,并不是为每个同步方法复制一个异步的方法(比如get()和getAsync()或者put()和putAsync()等等),Ignite选择了一个更优雅的方式来让方法不用复制。
IgniteAsyncSupport接口为很多Ignite API提供了异步模型,比如,IgniteCompute,IgniteServices,IgniteCache以及IgniteTransactions,都实现了IgniteAsyncSupport接口。
要启用异步模式,需要调用withAsync()方法,他会返回同样API的实例,这样的话,异步功能就启用了。
方法返回值
如果异步功能启用了,实际的同步方法的返回值就被忽略了,异步操作中获得返回值的唯一方式是通过future()方法。
计算网格示例
下面的例子说明了同步计算和异步计算的不同:
同步:
IgniteCompute compute = ignite.compute();
// Execute a job and wait for the result.
String res = compute.call(() -> {
// Print hello world on some cluster node.
System.out.println("Hello World");
return "Hello World";
});
下面是如何将上面的调用异步化:
异步:
// Enable asynchronous mode.
IgniteCompute asyncCompute = ignite.compute().withAsync();
// Asynchronously execute a job.
asyncCompute.call(() -> {
// Print hello world on some cluster node and wait for completion.
System.out.println("Hello World");
return "Hello World";
});
// Get the future for the above invocation.
IgniteFuture<String> fut = asyncCompute.future();
// Asynchronously listen for completion and print out the result.
fut.listen(f -> System.out.println("Job result: " + f.get()));
数据网格示例
下面是一个数据网格中同步和异步调用的例子:
IgniteCache<String, Integer> cache = ignite.cache("mycache");
// Synchronously store value in cache and get previous value.
Integer val = cache.getAndPut("1", 1);
下面是如何将上面的调用异步化:
// Enable asynchronous mode.
IgniteCache<String, Integer> asyncCache = ignite.cache("mycache").withAsync();
// Asynchronously store value in cache.
asyncCache.getAndPut("1", 1);
// Get future for the above invocation.
IgniteFuture<Integer> fut = asyncCache.future();
// Asynchronously listen for the operation to complete.
fut.listen(f -> System.out.println("Previous cache value: " + f.get()));
Ignite API中并不是每个方法都是分布式的,因此也就没必要异步化,为了避免关于哪个方法是分布式的混乱,就是哪个能异步那个不能,Ignite API中的每个分布式方法都被加上了@IgniteAsyncSupported注解。
注意,尽管事实上并不需要,异步模式中对于非分布式操作仍然可以获得future,并且,future也可以完成。
Ignite有一个可选的概念,就是客户端节点和服务端节点,服务端节点参与缓存、计算执行、流式处理等等,而原生的客户端节点提供了远程连接服务端的能力。Ignite原生客户端可以使用完整的Ignite API集合,包括近缓存、事务、计算、流、服务等等。
所有的Ignite节点默认都是以服务端模式启动的,客户端模式需要显式地启用。
可以通过IgniteConfiguration.setClientMode(…)属性配置一个节点,或者为客户端,或者为服务端。
XML:
<bean class="org.apache.ignite.configuration.IgniteConfiguration">
...
<property name="clientMode" value="true"/>
...
bean>
java代码:
IgniteConfiguration cfg = new IgniteConfiguration();
// Enable client mode.
cfg.setClientMode(true);
// Start Ignite in client mode.
Ignite ignite = Ignition.start(cfg);
方便起见,也可以通过Ignition类自己来打开或者关闭客户端模式作为替代,这样可以使客户端和服务端共用一套配置。
Ignition.setClientMode(true);
// Start Ignite in client mode.
Ignite ignite = Ignition.start();
当在Ignite中创建缓存时,不管是通过XML方式,还是通过Ignite.createCache(…)或者Ignite.getOrCreateCache(…)方法,Ignite会自动地在所有的服务端节点中部署分布式缓存。
当分布式缓存创建之后,他会自动地部署在所有的已有或者未来的服务端节点上。
// Enable client mode locally.
Ignition.setClientMode(true);
// Start Ignite in client mode.
Ignite ignite = Ignition.start();
CacheConfiguration cfg = new CacheConfiguration("myCache");
// Set required cache configuration properties.
...
// Create cache on all the existing and future server nodes.
// Note that since the local node is a client, it will not
// be caching any data.
IgniteCache<?, ?> cache = ignite.getOrCreateCache(cfg);
IgniteCompute默认会在所有的服务端节点上执行作业,然而,也可以通过创建相应的集群组来选择是只在服务端节点还是只在客户端节点上执行作业。
服务端节点执行:
IgniteCompute compute = ignite.compute();
// Execute computation on the server nodes (default behavior).
compute.broadcast(() -> System.out.println("Hello Server"));
客户端节点执行:
ClusterGroup clientGroup = ignite.cluster().forClients();
IgniteCompute clientCompute = ignite.compute(clientGroup);
// Execute computation on the client nodes.
clientCompute.broadcast(() -> System.out.println("Hello Client"));
很多部署环境中,客户端节点是在主集群外启动的,机器和网络都比较差,在这些场景中服务端可能产生负载(比如持续查询通知)而客户端没有能力处理,导致服务端的输出消息队列不断增长,这可能最终导致服务端出现内存溢出的情况,或者如果打开背压控制时导致整个集群阻塞。
要处理这样的场景,可以配置允许向客户端节点输出消息的最大值,如果输出队列的大小超过配置的值,该客户端节点会从集群断开以防止拖慢整个集群。
下面的例子显示了如何通过XML或者编程的方式配置慢客户端队列限值:
java:
IgniteConfiguration cfg = new IgniteConfiguration();
// Configure Ignite here.
TcpCommunicationSpi commSpi = new TcpCommunicationSpi();
commSpi.setSlowClientQueueLimit(1000);
cfg.setCommunicationSpi(commSpi);
XML:
<bean id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
<property name="communicationSpi">
<bean class="org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi">
<property name="slowClientQueueLimit" value="1000"/>
bean>
property>
bean>
有几种情况客户端会从集群中断开:
当一个客户端发现它与一个集群断开时会被赋予一个新的本地节点ID然后试图与该服务端重新连接。注意:这会产生一个副作用,就是当客户端重建连接时本地ClusterNode的id属性会发生变化。
当客户端处于一个断开状态并且试图重建与集群的连接过程中时,所有的Ignite API都会抛出一个特定的异常:IgniteClientDisconnectedException,这个异常提供一个future,当客户端重连结束后他会完成(IgniteCacheAPI会抛出CacheException,他有一个IgniteClientDisconnectedException作为他的cause)。这个future也可以通过IgniteCluster.clientReconnectFuture()方法获得。
此外,客户端重连也有一些特定的事件(这些事件是本地化的,也就是说他们只会在客户端节点触发):
IgniteCompute compute = ignite.compute();
while (true) {
try {
compute.run(job);
}
catch (IgniteClientDisconnectedException e) {
e.reconnectFuture().get(); // Wait for reconnect.
// Can proceed and use the same IgniteCompute instance.
}
}
缓存:
IgniteCache cache = ignite.getOrCreateCache(new CacheConfiguration<>());
while (true) {
try {
cache.put(key, val);
}
catch (CacheException e) {
if (e.getCause() instanceof IgniteClientDisconnectedException) {
IgniteClientDisconnectedException cause =
(IgniteClientDisconnectedException)e.getCause();
cause.reconnectFuture().get(); // Wait for reconnect.
// Can proceed and use the same IgniteCache instance.
}
}
}
客户端自动重连可以通过TcpDiscoverySpi的clientReconnectDisabled属性禁用,如果重连被禁用那么当发现与集群断开时客户端节点就停止了。
下面的例子显示了如何禁用客户端重连:
IgniteConfiguration cfg = new IgniteConfiguration();
// Configure Ignite here.
TcpDiscoverySpi discoverySpi = new TcpDiscoverySpi();
discoverySpi.setClientReconnectDisabled(true);
cfg.setDiscoverySpi(discoverySpi);
客户端可以请求网络中的存活服务端节点来启动。
如果不管服务端节点是否存活都要启动客户端节点非常必要,可以以如下的方式在客户端强制服务端模式发现:
IgniteConfiguration cfg = new IgniteConfiguration();
cfg.setClientMode(true);
// Configure Ignite here.
TcpDiscoverySpi discoverySpi = new TcpDiscoverySpi();
discoverySpi.setForceServerMode(true);
cfg.setDiscoverySpi(discoverySpi);
这种情况下,如果网络中的所有节点都是服务端节点时发现就会发生。
重要
这种情况下为了发现能正常工作,发现SPI在所有节点上使用的所有地址应该是可以相互访问的。