@liyuj
2016-12-21T23:06:10.000000Z
字数 16427
阅读 7174
Apache-Ignite-1.8.0-中文开发手册
Apache Ignite内存数据组织是高性能的、集成化的以及分布式的内存平台,他可以实时地在大数据集中执行事务和计算,和传统的基于磁盘或者闪存的技术相比,性能有数量级的提升。
可以将Ignite视为一个独立的、易于集成的内存组件的集合,目的是改进应用程序的性能和可扩展性,部分组件包括:
除了Spark和Hadoop,Ignite也集成了其他的很多技术和产品,这样做的目的是简化Ignite和应用或者服务使用的其他技术的耦合,以便平滑地过渡到Ignite或者有助于将Ignite嵌入已有的系统。
这部分帮助你开始一个新的Ignite程序,你会发现他瞬间就可以跑起来。
Apache Ignite官方在如下环境中进行的测试:
下面是安装Apache Ignite的简要步骤:
从源代码构建
如果你下载的是源代码包,可以用如下命令构建:
# Unpack the source package
$ unzip -q apache-ignite-{version}-src.zip
$ cd apache-ignite-{version}-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.ignite</groupId>
<artifactId>ignite-core</artifactId>
<version>${ignite.version}</version>
</dependency>
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-spring</artifactId>
<version>${ignite.version}</version>
</dependency>
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-indexing</artifactId>
<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.ignite</groupId>
<artifactId>ignite-core</artifactId>
<version>${ignite.version}</version>
</dependency>
然而,很多时候需要其他的依赖,比如,要使用Spring配置或者SQL查询等。
下面就是最常用的可选模块:
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-core</artifactId>
<version>${ignite.version}</version>
</dependency>
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-spring</artifactId>
<version>${ignite.version}</version>
</dependency>
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-indexing</artifactId>
<version>${ignite.version}</version>
</dependency>
可以一个个地导入Ignite模块,唯一必须的就是ignite-core
,其他的都是可选的,所有可选模块都可以像核心模块一样导入,只是构件Id不同。
现在提供如下模块:
ignite-spring
:基于Spring的配置支持ignite-indexing
:SQL查询和索引ignite-geospatial
:地理位置索引ignite-hibernate
:Hibernate集成ignite-web
:Web Session集群化ignite-schedule
:基于Cron的计划任务ignite-log4j
:Log4j日志ignite-jcl
:Apache Commons logging日志ignite-jta
:XA集成ignite-hadoop2-integration
:HDFS2.0集成ignite-rest-http
:HTTP REST请求ignite-scalar
:Ignite Scalar APIignite-slf4j
:SLF4J日志ignite-ssh
;SSH支持,远程机器上启动网格节点ignite-urideploy
:基于URI的部署ignite-aws
:AWS S3上的无缝集群发现ignite-aop
:网格支持AOPignite-visor-console
:开源的命令行管理和监控工具构件版本
注意,导入若干Ignite模块时,他们的版本号应该相同,比如,如果使用ignite-core
1.7,所有其他的模块也必须导入1.7版本。
下面的Ignite模块有LGPL依赖,因此无法部署到Maven中央仓库:
ignite-hibernate
ignite-geospatial
ignite-schedule
要使用这些模块,需要手工从源代码进行构建然后加入自己的项目,比如,要将ignite-hibernate
安装到本地库,可以在Ignite的源代码包中运行如下的命令:
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
实现。
当前支持如下生命周期事件类型:
BEFORE_NODE_START
:Ignite节点的启动程序初始化之前调用AFTER_NODE_START
:Ignite节点启动之后调用BEFORE_NODE_STOP
:Ignite节点的停止程序初始化之前调用AFTER_NODE_STOP
:Ignite节点停止之后调用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">
...
<!-- Enable client mode. -->
<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">
<!-- Configure Ignite here. -->
<property name="communicationSpi">
<bean class="org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi">
<property name="slowClientQueueLimit" value="1000"/>
</bean>
</property>
</bean>
有几种情况客户端会从集群中断开:
当一个客户端发现它与一个集群断开时,会为自己赋予一个新的节点id
然后试图与该服务端重新连接。注意
:这会产生一个副作用,就是当客户端重建连接时本地ClusterNode
的id
属性会发生变化,这意味着,如果业务逻辑依赖于这个id
,就会受到影响。
当客户端处于一个断开状态并且试图重建与集群的连接过程中时,Ignite API会抛出一个特定的异常:IgniteClientDisconnectedException
,这个异常提供了一个future
,当客户端重连成功后他会完成(IgniteCache
API会抛出CacheException
,他有一个IgniteClientDisconnectedException
作为他的cause)。这个future
也可以通过IgniteCluster.clientReconnectFuture()
方法获得。
此外,客户端重连也有一些特定的事件(这些事件是本地化的,也就是说他们只会在客户端节点触发):
下面的例子显示IgniteClientDisconnectedException
如何使用:
计算:
IgniteCompute compute = ignite.compute();
while (true) {
try {
compute.run(job);
}
catch (IgniteClientDisconnectedException e) {
e.reconnectFuture().get(); // Wait for reconnection.
// 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 reconnection.
// 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在所有节点上使用的所有地址应该是可以相互访问的。
1.堆内和堆外内存存储有何不同?
当处理很大的堆时,通过在Java主堆空间外部缓存数据,可以使缓存克服漫长的JVM垃圾收集(GC)导致的暂停,但是数据仍然在内存中。
更多信息
2.Apache Ignite是一个键值存储么?
Apache Ignite是一个具有计算能力的、有弹性的内存中的分布式对象存储。在其最简单的形式中,是的,Apache Ignite可以作为一个键/值存储(缓存),但是也暴露了更丰富的API来与数据交互,比如完整的ANSI99兼容的SQL查询、文本检索、事务等等。
更多信息
3.Apache Ignite是否支持JSON文档?
当前,Apache Ignite并不完整支持JSON文档,但是当前处于beta阶段的Node.js客户端会支持JSON文档。
4.Apache Ignite是否可以用于Apache Hive?
是,Apache Ignite的Hadoop加速器提供了一系列的组件,支持在任何的Hadoop发行版中执行内存中的Hadoop作业执行和文件系统操作,包括Apache Hive。
在Ignite化的Hadoop中运行Apache Hive
5.在事务隔离的悲观模式中,是否锁定键的读和写?
是的,主要的问题在于,在悲观
模式中,访问是会获得锁,而在乐观
模式中,锁是在提交阶段获得的。
更多信息
6.是否可以用Hibernate访问Apache Ignite?
是的,Apache Ignite可以用作Hibernate的二级缓存(或者L2缓存),他可以显著地提升应用的持久化层的速度。
更多信息
7.Apache Ignite是否支持JDBC?
是的,Apache Ignite提供了JDBC驱动,可以在缓存中使用标准SQL查询和JDBC API获得分布式的数据。
更多信息
8.Apache Ignite是否保证消息的顺序?
是的,如果希望收到消息的顺序与发送消息的顺序一致,可以使用sendOrdered(...)
方法。可以传递一个超时时间来指定一条消息在队列中的等待时间,他会等待本来应在其之前发送的消息。如果超时时间过期,所有的还没有到达该节点中一个给定主题的消息都会被忽略。
更多信息
9.是否可以运行Java和.Net闭包?他是如何工作的?
.Net节点可以同时执行Java和.Net闭包,而标准Java节点只能执行Java闭包。当启动ApacheIgnite.exe时,他会使用位于IGNITE_HOME/platforms/dotnet/bin
的一个脚本在同一个进程下同时启动JVM和CLR,.Net闭包会被CLR处理执行。
10.Java和.Net之间的转换成本是什么?
仅有的最小可能的开销是一个额外的数组复制+JNI调用,在本地测试时这个开销可能降低性能,但在真正的分布式负载环境下可以忽略不计。
11.闭包是如何传输的?
每个闭包都是一个特定类的对象。当它要被发送时会序列化成二进制的形式,通过线路发送到一个远程节点然后在那里反序列化。该远程节点在类路径中应该有该闭包类,或者开启peerClassLoading以从发送端加载该类。
12.SQL查询是否被负载平衡?
SQL查询总是被广播到保存有要查询的数据的每个节点,例外就是本地SQL查询(query.setLocal(true)),他只是在一个本地节点执行。
13.用户是否可以控制资源分配?即,是否可以限制用户A为50个节点,但是用户B可以在所有的100个节点上执行任务?
多租户只在缓存中存在,他们可以在创建在一个节点的子集上(可以看CacheConfiguration.setNodeFilter)以及在每个缓存基础上安全地赋予权限。