@liyuj
2017-10-14T22:48:42.000000Z
字数 31551
阅读 10038
Apache-Ignite-2.2.0-中文开发手册
Ignite是:
1.一个以内存为中心的数据平台
2.可持久化、强一致和高可用
3.强大的SQL、键-值存储及相关的API
Ignite的固化内存组件不仅仅将内存作为一个缓存层,还视为一个全功能的存储层。这意味着可以按需将持久化打开或者关闭。如果持久化关闭,那么Ignite就可以作为一个分布式的内存数据库或者内存数据网格,这完全取决于使用SQL和键-值API的喜好。如果持久化打开,那么Ignite就成为一个分布式的,可水平扩展的数据库,它会保证完整的数据一致性以及集群故障的可恢复能力。
Ignite的原生持久化是一个分布式的、支持ACID以及兼容SQL的磁盘存储,它可以作为一个可选的磁盘层与Ignite的固化内存透明地集成,然后将数据和索引存储在SSD、闪存、3D XPoint以及其他类型的非易失性存储中。
打开Ignite的持久化之后,就不需要将所有的数据和索引保存在内存中,或者在节点或者集群重启后对数据进行预热,因为固化内存和持久化紧密耦合之后,会将其视为一个二级存储层,这意味着在内存中数据和索引的一个子集如果丢失了,固化内存会从磁盘上进行获取。
存储在Ignite中的数据,在内存和磁盘上是同时支持ACID的,使Ignite成为一个强一致的系统,Ignite可以在整个网络的多台服务器上保持事务。
Ignite提供了完整的SQL、DDL和DML的支持,可以使用纯SQL而不用写代码与Ignite进行交互,这意味着只使用SQL就可以创建表和索引,以及插入、更新和查询数据。有这个完整的SQL支持,Ignite就可以作为一种分布式SQL数据库。
Ignite的内存数据网格组件是一个完整的事务型分布式键值存储,它可以在有几百台服务器的集群上进行水平扩展。再打开持久化时,Ignite可以存储比内存容量更大的数据,并且在整个集群重启之后仍然可用。
大多数传统数据库是以客户机-服务器的模式运行的,这意味着数据必须发给客户端进行处理,这个方式需要在客户端和服务端之间进行大量的数据移动,通常来说不可扩展。而Ignite使用了另外一种方式,可以将轻量级的计算发给数据,即数据的并置计算,从结果上来说,Ignite扩展性更好,并且使数据移动最小化。
Ignite是一个弹性的、可水平扩展的分布式系统,它支持按需地添加和删除节点,Ignite还可以存储数据的多个副本,这样可以使集群从部分故障中恢复。如果打开了持久化,那么Ignite中存储的数据可以在集群的完全故障中恢复。Ignite集群重启会非常快,因为数据从磁盘上获取,瞬间就具有了可操作性。从结果上来说,数据不需要在处理之前预加载到内存中,而Ignite会缓慢地恢复内存级的性能。
Ignite是不是持久化或者纯内存存储?
都是,Ignite的原生持久化可以打开,也可以关闭。这使得Ignite可以存储比可用内存容量更大的数据集。也就是说,可以只在内存中存储较少的操作性数据集,然后将不适合存储在内存中的较大数据集存储在磁盘上,即为了提高性能将内存作为一个缓存层。
Ignite是不是分布式数据库?
是,在整个集群的多个节点中,Ignite中的数据要么是分区模式的,要么是复制模式的,这给系统带来了伸缩性,增加了弹性。Ignite可以自动化地控制数据如何分区,然而,开发者也可以插入自定义的函数,以及为了提高效率将部分数据并置在一起。
Ignite是不是关系型SQL数据库?
不完整,尽管Ignite的目标是和其他的关系型SQL数据库具有类似的行为,但是在处理约束和索引方面还是有不同的。Ignite支持一级和二级索引,但是只有一级索引支持唯一性,Ignite还不支持外键约束。
从根本上来说,Ignite作为约束不支持任何会导致集群广播消息的更新以及显著降低系统性能和可伸缩性的操作。
Ignite是不是内存数据库?
是,虽然Ignite的固化内存在内存和磁盘中都工作得很好,但是磁盘持久化是可以禁用的,使Ignite作为一个纯粹的内存数据库。
Ignite是不是事务型数据库?
不完整,ACID事务是支持的,但是仅仅在键-值API级别,Ignite还支持跨分区的事务,这意味着事务可以跨越不同服务器不同分区中的键。
在SQL层,Ignite支持原子性,还不是事务型一致性,社区计划在2.2版本中实现SQL事务。
Ignite是不是键-值存储?
是,Ignite提供了键-值API,兼容于JCache (JSR-107),并且支持Java,C++和.NET。
Ignite是不是内存数据网格(IMDG)?
是,Ignite是一个全功能的数据网格,它既可以用于纯内存模式,也可以带有Ignite的原生持久化,它也可以与任何第三方数据库集成,包括RDBMS和NoSQL。
固化内存是什么?
Ignite的固化内存架构使得Ignite可以将内存计算延伸至磁盘,它基于一个页面化的堆外内存分配器,它通过写前日志(WAL)的持久化来对数据进行固化,当持久化禁用之后,固化内存就会变成一个纯粹的内存存储。
并置处理是什么?
Ignite是一个分布式系统,因此,有能力将数据和数据以及数据和计算进行并置就变得非常重要,这会避免分布式数据噪声。当执行分布式SQL关联时数据的并置就变得非常的重要。Ignite还支持将用户的逻辑(函数,lambda等)直接发到数据所在的节点然后在本地进行数据的运算。
这部分帮助你开始一个新的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.4.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
如果项目里用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.8,所有其他的模块也必须导入1.8版本。
下面的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
实现,查看1.8.资源注入
章节可以了解更多的信息。
当前支持如下生命周期事件类型:
BEFORE_NODE_START
:Ignite节点的启动程序初始化之前调用AFTER_NODE_START
:Ignite节点启动之后调用BEFORE_NODE_STOP
:Ignite节点的停止程序初始化之前调用AFTER_NODE_STOP
:Ignite节点停止之后调用Ignite的多数API即可以支持同步模式,也可以支持异步模式,异步方法后面追加了Async
后缀。
// Synchronous get
V get(K key);
// Asynchronous get
IgniteFuture<V> getAsync(K key);
异步操作返回的是一个IgniteFuture
或其子类的实例,通过如下方式可以获得异步操作的结果,或者调用阻塞的IgniteFuture.get()
方法,或者通过IgniteFuture.listen()
方法或者IgniteFuture.chain()
方法注册一个闭包,然后等待当操作完成后调用闭包。
下面列出的接口可以用于同步或者异步模式:
IgniteCompute
IgniteCache
Transaction
IgniteServices
IgniteMessaging
IgniteEvents
要在非阻塞模式下等待异步操作的结果(IgniteFuture.get()
),可以使用IgniteFuture.listen()
方法或者IgniteFuture.chain()
方法注册一个闭包,当操作完成后,闭包会被调用,比如:
IgniteCompute compute = ignite.compute();
// Execute a closure asynchronously.
IgniteFuture<String> fut = compute.callAsync(() -> {
return "Hello World";
});
// Listen for completion and print out the result.
fut.listen(f -> System.out.println("Job result: " + f.get()));
闭包执行和线程池
异步操作完成后,如果通过IgniteFuture.listen()
或者IgniteFuture.chain()
方法传递了闭包,那么闭包就会被调用线程以同步的方式执行,否则,闭包就会随着操作的完成异步地执行。
根据操作的类型,闭包可能被系统线程池中的线程调用(异步缓存操作),或者被公共线程池中的线程调用(异步计算操作)。因此需要避免在闭包实现中调用同步的缓存和计算操作,否则可能导致死锁。
要实现Ignite计算操作异步嵌套执行,可以使用自定义线程池,相关内容可以查看1.9.线程池
中的相关内容。
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在所有节点上使用的所有地址应该是可以相互访问的。
Ignite中,预定义的资源都是可以进行依赖注入的,同时支持基于属性和基于方法的注入。任何加注正确注解的资源都会在初始化之前注入相对应的任务、作业、闭包或者SPI。
可以通过在一个属性或者方法上加注注解来注入资源。当加注在属性上时,Ignite只是在注入阶段简单地设置属性的值(不会理会该属性的访问修饰符)。如果在一个方法上加注了资源注解,他会访问一个与注入资源相对应的输入参数的类型,如果匹配,那么在注入阶段,就会将适当的资源作为输入参数,然后调用该方法。
基于属性:
Ignite ignite = Ignition.ignite();
Collection<String> res = ignite.compute().broadcast(new IgniteCallable<String>() {
// Inject Ignite instance.
@IgniteInstanceResource
private Ignite ignite;
@Override
public String call() throws Exception {
IgniteCache<Object, Object> cache = ignite.getOrCreateCache(CACHE_NAME);
// Do some stuff with cache.
...
}
});
基于方法:
public class MyClusterJob implements ComputeJob {
...
private Ignite ignite;
...
// Inject Ignite instance.
@IgniteInstanceResource
public void setIgnite(Ignite ignite) {
this.ignite = ignite;
}
...
}
有很多的预定义资源可供注入:
资源 | 描述 |
---|---|
CacheNameResource |
由CacheConfiguration.getName() 提供,注入网格缓存名 |
CacheStoreSessionResource |
注入当前的CacheStoreSession 实例 |
IgniteInstanceResource |
注入当前的Ignite实例 |
JobContextResource |
注入ComputeJobContext 的实例。作业的上下文持有关于一个作业执行的有用的信息。比如,可以获得包含与作业并置的条目的缓存的名字。 |
LoadBalancerResource |
注入ComputeLoadBalancer 的实例,注入后可以用于任务的负载平衡。 |
LoggerResource |
注入IgniteLogger 的实例,他可以用于向本地节点的日志写消息。 |
ServiceResource |
通过指定服务名注入Ignite的服务。 |
SpringApplicationContextResource |
注入Spring的ApplicationContext 资源。 |
SpringResource |
从Spring的ApplicationContext 注入资源,当希望访问在Spring的ApplicationContext XML配置中指定的一个Bean时,可以用它。 |
TaskContinuousMapperResource |
注入一个ComputeTaskContinuousMapper 的实例,持续映射可以在任何时点从任务中发布作业,即使过了map的初始化阶段。 |
TaskSessionResource |
注入ComputeTaskSession 资源的实例,它为一个特定的任务执行定义了一个分布式的会话。 |
Ignite创建并且维护着一组线程池,根据使用的API不同分别用于不同的目的。本章节中会列出一些众所周知的内部线程池,然后会展示如何自定义线程池。在IgniteConfiguration
的javadoc中,可以看到Ignite中可用的完整线程池列表。
系统线程池处理所有与缓存相关的操作,除了SQL以及其他的查询类型,它们会使用查询线程池,同时这个线程池也负责处理Ignite计算任务的取消操作。
默认的线程池数量为CPU总核数*2
,使用IgniteConfiguration.setSystemThreadPoolSize(...)
可以进行调整。
公共线程池负责Ignite的计算网格,所有的计算任务都由这个线程池接收然后处理。
默认的线程池数量为CPU总核数*2
,使用IgniteConfiguration.setPublicThreadPoolSize(...)
可以进行调整。
查询线程池处理集群内所有的SQL、扫描和SPI查询。
默认的线程池数量为CPU总核数*2
,使用IgniteConfiguration.setQueryThreadPoolSize(...)
可以进行调整。
Ignite的服务网格调用使用的是服务线程池,Ignite的服务和计算网格组件都有专用的线程池,可以避免当一个服务实现希望调用一个计算(或者反之)时的线程争用和死锁。
默认的线程池数量为CPU总核数*2
,使用IgniteConfiguration.setServiceThreadPoolSize(...)
可以进行调整。
平行线程池通过将操作展开为多个平行的执行,有助于显著加速基本的缓存操作以及事务,因为可以避免相互竞争。
默认的线程池数量为CPU总核数*2
,使用IgniteConfiguration.setStripedPoolSize(...)
可以进行调整。
数据流处理器线程池用于处理来自IgniteDataStreamer
的所有消息和请求,各种内置的使用IgniteDataStreamer
的流适配器也可以。
默认的线程池数量为CPU总核数*2
,使用IgniteConfiguration.setDataStreamerThreadPoolSize(...)
可以进行调整。
对于Ignite的计算任务,也可以配置自定义的线程池,当希望同步地从一个计算任务调用另一个的时候很有用,因为可以避免死锁。要保证这一点,需要确保执行嵌套任务的线程池不同于上级任务的线程池。
自定义线程池需要在IgniteConfiguration
中进行定义,并且需要有一个唯一的名字:
Java:
IgniteConfiguration cfg = ...;
cfg.setExecutorConfiguration(new ExecutorConfiguration("myPool").setSize(16));
XML:
<bean id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
...
<property name="executorConfiguration">
<list>
<bean class="org.apache.ignite.configuration.ExecutorConfiguration">
<property name="name" value="myPool"/>
<property name="size" value="16"/>
</bean>
</list>
</property>
...
</bean>
这样,假定下面的计算任务由上面定义的myPool
线程池中的线程执行:
public class InnerRunnable implements IgniteRunnable {
@Override public void run() {
System.out.println("Hello from inner runnable!");
}
}
怎么做呢,需要使用IgniteCompute.withExecutor()
,他会被上级任务的实现马上执行,像下面这样:
public class OuterRunnable implements IgniteRunnable {
@IgniteInstanceResource
private Ignite ignite;
@Override public void run() {
// Synchronously execute InnerRunnable in custom executor.
ignite.compute().withExecutor("myPool").run(new InnerRunnable());
}
}
上级任务的执行可通过如下方式触发,对于这个场景,它会由公共线程池执行:
ignite.compute().run(new OuterRunnable());
未定义线程池
如果应用请求在自定义线程池执行计算任务,而该线程池在Ignite节点中未定义,那么一个特定的警告消息就会在节点的日志中输出,然后任务就会被公共线程池接管执行。
从1.6版本开始,Ignite引入了一个在缓存中存储数据的新概念,名为二进制对象
,这个新的序列化格式提供了若干个优势:
二进制对象只可以用于使用默认的二进制编组器时(即没有在配置中显式地设置其他的编组器)
限制
BinaryObject
格式实现也带来了若干个限制:
1. 在内部Ignite不会写属性以及类型的名字,但是使用一个小写的名字哈希来标示一个属性或者类型,这意味着属性或者类型不能有同样的名字哈希。即使序列化不会在哈希冲突的情况下工作,但Ignite在配置级别提供了一种方法来解决此冲突;
2.同样的原因,BinaryObject
格式在类的不同层次上也不允许有同样的属性名;
3.默认会忽略Externalizable
接口。如果使用了BinaryObject
格式,Externalizable
类型会与Serializable
类型是同样的处理方式,没有writeExternal()
和readExternal()
方法。如果由于某些原因这样不行,需要实现Binarylizable
接口,加入一个自定义BinarySerializer
或者切换到OptimizedMarshaller
。
IgniteBinary
入口,可以从Ignite的实例获得,包含了操作二进制对象的所有必要的方法。
在绝大多数情况下不需要额外地配置二进制对象。
但是,如果需要覆写默认的类型和属性ID计算或者加入BinarySerializer
,可以为IgniteConfiguration
定义一个BinaryConfiguration
对象,这个对象除了为每个类型指定映射以及序列化器之外还可以指定一个全局的Name映射、一个全局ID映射以及一个全局的二进制序列化器。对于每个类型的配置,通配符也是支持的,这时提供的配置会适用于匹配类型名称模板的所有类型。
配置二进制类型:
<bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
<property name="binaryConfiguration">
<bean class="org.apache.ignite.configuration.BinaryConfiguration">
<property name="nameMapper" ref="globalNameMapper"/>
<property name="idMapper" ref="globalIdMapper"/>
<property name="typeConfigurations">
<list>
<bean class="org.apache.ignite.binary.BinaryTypeConfiguration">
<property name="typeName" value="org.apache.ignite.examples.*"/>
<property name="serializer" ref="exampleSerializer"/>
</bean>
</list>
</property>
</bean>
</property>
...
Ignite默认使用反序列化值作为最常见的使用场景,要启用BinaryObject
处理,需要获得一个IgniteCache
的实例然后使用withKeepBinary()
方法。启用之后,如果可能,这个标志会确保从缓存返回的对象都是BinaryObject
格式的。将值传递给EntryProcessor
和CacheInterceptor
也是同样的处理。
平台类型
注意当通过withKeepBinary()
方法启用BinaryObject
处理时并不是所有的对象都会表示为BinaryObject
,会有一系列的平台
类型,包括基本类型,String,UUID,Date,Timestamp,BigDecimal,Collections,Maps和Arrays,他们不会被表示为BinaryObject
。
注意在下面的示例中,键类型为Integer
,他是不会被修改,因为他是平台
类型。
获取BinaryObject:
// Create a regular Person object and put it to the cache.
Person person = buildPerson(personId);
ignite.cache("myCache").put(personId, person);
// Get an instance of binary-enabled cache.
IgniteCache<Integer, BinaryObject> binaryCache = ignite.cache("myCache").withKeepBinary();
// Get the above person object in the BinaryObject format.
BinaryObject binaryPerson = binaryCache.get(personId);
BinaryObject
实例是不能修改的,要更新属性或者创建新的BinaryObject
,必须使用BinaryObjectBuilder
的实例。
BinaryObjectBuilder
的实例可以通过IgniteBinary
入口获得。他可以使用类型名创建,这时返回的对象不包含任何属性,或者他也可以通过一个已有的BinaryObject
创建,这时返回的对象会包含从给定的BinaryObject
中拷贝的所有属性。
获取BinaryObjectBuilder
实例的另外一个方式是调用已有BinaryObject
实例的toBuilder()
方法,这种方式创建的对象也会从BinaryObject
中拷贝所有的数据。
BinaryObjectBuilder和哈希值
注意如果构造的BinaryObject
用做缓存键,很重要的是要为BinaryObjectBuilder
设置正确的哈希值,因为构造器不会自动计算哈希值,然后导致返回的BinaryObject
哈希值为0。
下面是一个使用BinaryObject
API来处理服务端节点的数据而不需要将程序部署到服务端以及不需要实际的数据反序列化的示例:
EntryProcessor内的BinaryObject:
// The EntryProcessor is to be executed for this key.
int key = 101;
cache.<Integer, BinaryObject>withKeepBinary().invoke(
key, new CacheEntryProcessor<Integer, BinaryObject, Object>() {
public Object process(MutableEntry<Integer, BinaryObject> entry,
Object... objects) throws EntryProcessorException {
// Create builder from the old value.
BinaryObjectBuilder bldr = entry.getValue().toBuilder();
//Update the field in the builder.
bldr.setField("name", "Ignite");
// Set new value to the entry.
entry.setValue(bldr.build());
return null;
}
});
像上面描述的那样,二进制对象结构可以在运行时进行修改,因此获取一个存储在缓存中的一个特定类型的信息也可能是有用的,比如属性名,属性类型,属性类型名,关系属性名,Ignite通过BinaryType
接口满足这样的需求。
这个接口还引入了一个属性getter的更快的版本,叫做BinaryField
。这个概念类似于Java的反射,可以缓存BinaryField
实例中读取的属性的特定信息,他有助于从一个很大的二进制对象集合中读取同一个属性。
Collection<BinaryObject> persons = getPersons();
BinaryField salary = null;
double total = 0;
int cnt = 0;
for (BinaryObject person : persons) {
if (salary == null)
salary = person.type().field("salary");
total += salary.value(person);
cnt++;
}
double avg = total / cnt;
在缓存API上调用withKeepBinary()
方法对于将用户对象传入CacheStore
的方式不起作用,这么做是故意的,因为大多数情况下单个CacheStore
实现要么使用反序列化类,要么使用BinaryObject
表示。要控制对象传入Store的方式,需要使用CacheConfiguration
的storeKeepBinary
标志,当该标志设置为false
时,会将反序列化值传入Store,否则会使用BinaryObject
表示。
下面是一个使用BinaryObject
的Store的伪代码实现的示例:
public class CacheExampleBinaryStore extends CacheStoreAdapter<Integer, BinaryObject> {
@IgniteInstanceResource
private Ignite ignite;
/** {@inheritDoc} */
@Override public BinaryObject load(Integer key) {
IgniteBinary binary = ignite.binary();
List<?> rs = loadRow(key);
BinaryObjectBuilder bldr = binary.builder("Person");
for (int i = 0; i < rs.size(); i++)
bldr.setField(name(i), rs.get(i));
return bldr.build();
}
/** {@inheritDoc} */
@Override public void write(Cache.Entry<? extends Integer, ? extends BinaryObject> entry) {
BinaryObject obj = entry.getValue();
BinaryType type = obj.type();
Collection<String> fields = type.fieldNames();
List<Object> row = new ArrayList<>(fields.size());
for (String fieldName : fields)
row.add(obj.field(fieldName));
saveRow(entry.getKey(), row);
}
}
在内部,Ignite不会写属性或者类型名字的完整字符串,而是因为性能的原因,为类型或者属性名写一个整型哈希值作为替代。经过测试,在类型相同时,属性名或者类型名的哈希值冲突实际上是不存在的,为了获得性能,使用哈希值是安全的。对于当不同的类型或者属性确实冲突的场合,BinaryNameMapper
和BinaryIdMapper
可以为该类型或者属性名覆写自动生成的哈希值。
BinaryNameMapper
- 映射类型/类和属性名到不同的名字;
BinaryIdMapper
- 映射从BinaryNameMapper
来的类型和属性名到ID,以便于Ignite内部使用。
Ignite提供了下面的开箱即用的映射器实现:
BinaryBasicNameMapper
:BinaryNameMapper
的一个基本实现,对于一个给定的类,根据使用的setSimpleName(boolean useSimpleName)
属性值,会返回一个完整或者简单的名字;BinaryBasicIdMapper
:BinaryIdMapper
的一个基本实现,他有一个setLowerCase(boolean isLowerCase)
配置属性,如果属性设置为false
,那么会返回一个给定类型或者属性名的哈希值,如果设置为true
,会返回一个给定类型或者属性名的小写形式的哈希值。如果仅仅使用Java客户端并且在BinaryConfiguration
中没有指定映射器,那么Ignite会使用BinaryBasicNameMapper
并且simpleName
属性会被设置为false
,使用BinaryBasicIdMapper
并且lowerCase
属性会被设置为true
。
如果使用了.Net或者C++客户端并且在BinaryConfiguration
中没有指定映射器,那么Ignite会使用BinaryBasicNameMapper
并且simpleName
属性会被设置为true
,使用BinaryBasicIdMapper
并且lowerCase
属性会被设置为true
。
如果使用Java、.Net或者C++,默认是不需要任何配置的,只有当需要平台协同、名字转换复杂的情况下,才需要配置映射器。
Ignite支持各种日志库和框架,可以开箱即用地使用Log4j、Log4j2、JCL和SLF4J,本文会描述如何使用它们。
如果在启动独立集群节点时要使用Log4j模块,需要在执行ignite.{sh|bat}
脚本前,将optional/ignite-log4j
文件夹移动到Ignite发行版的lib
目录下,这时这个模块目录中的内容会被添加到类路径。
如果项目中使用maven进行依赖管理,那么需要添加如下的依赖:
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-log4j</artifactId>
<version>${ignite.version}</version>
</dependency>
将${ignite.version}
替换为实际使用的Ignite版本。
要使用Log4j进行日志记录,需要配置IgniteConfiguration
的gridLogger
属性,如下所示:
XML:
<bean class="org.apache.ignite.configuration.IgniteConfiguration">
<property name="gridLogger">
<bean class="org.apache.ignite.logger.log4j.Log4JLogger">
<constructor-arg type="java.lang.String" value="log4j.xml"/>
</bean>
</property>
<!-- Other Ignite configurations -->
...
</bean>
Java:
IgniteConfiguration cfg = new IgniteConfiguration();
IgniteLogger log = new Log4JLogger("log4j.xml");
cfg.setGridLogger(log);
// Start Ignite node.
Ignite ignite = Ignition.start(cfg);
ignite.log().info("Info Message Logged!");
在上面的配置中,log4j.xml
的路径要么是绝对路径,要么是相对路径,相对路径可以相对于META-INF
,也可以相对于IGNITE_HOME
。
如果在启动独立集群节点时要使用Log4j2模块,需要在执行ignite.{sh|bat}
脚本前,将optional/ignite-log4j2
文件夹移动到Ignite发行版的lib
目录下,这时这个模块目录中的内容会被添加到类路径。
如果项目中使用maven进行依赖管理,那么需要添加如下的依赖:
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-log4j2</artifactId>
<version>${ignite.version}</version>
</dependency>
将${ignite.version}
替换为实际使用的Ignite版本。
要使用Log4j2进行日志记录,需要配置IgniteConfiguration
的gridLogger
属性,如下所示:
XML:
<bean class="org.apache.ignite.configuration.IgniteConfiguration">
<property name="gridLogger">
<bean class="org.apache.ignite.logger.log4j2.Log4J2Logger">
<constructor-arg type="java.lang.String" value="log4j2.xml"/>
</bean>
</property>
<!-- Other Ignite configurations -->
...
</bean>
Java:
IgniteConfiguration cfg = new IgniteConfiguration();
IgniteLogger log = new Log4J2Logger("log4j2.xml");
cfg.setGridLogger(log);
// Start Ignite node.
Ignite ignite = Ignition.start(cfg);
ignite.log().info("Info Message Logged!");
在上面的配置中,log4j2.xml
的路径要么是绝对路径,要么是相对路径,相对路径可以相对于META-INF
,也可以相对于IGNITE_HOME
。
如果在启动独立集群节点时要使用JCL模块,需要在执行ignite.{sh|bat}
脚本前,将optional/ignite-jcl
文件夹移动到Ignite发行版的lib
目录下,这时这个模块目录中的内容会被添加到类路径。
如果项目中使用maven进行依赖管理,那么需要添加如下的依赖:
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-jcl</artifactId>
<version>${ignite.version}</version>
</dependency>
将${ignite.version}
替换为实际使用的Ignite版本。
要使用JCL进行日志记录,需要配置IgniteConfiguration
的gridLogger
属性,如下所示:
XML:
<bean class="org.apache.ignite.configuration.IgniteConfiguration">
<property name="gridLogger">
<bean class="org.apache.ignite.logger.jcl.JclLogger">
<constructor-arg type="org.apache.commons.logging.Log">
<bean class="org.apache.commons.logging.impl.Log4JLogger">
<constructor-arg type="java.lang.String" value="log4j.xml"/>
</bean>
</constructor-arg>
</bean>
</property>
<!-- Other Ignite configurations -->
...
</bean>
Java:
IgniteConfiguration cfg = new IgniteConfiguration();
IgniteLogger log = new JclLogger(new
org.apache.commons.logging.impl.Log4JLogger("log4j.xml"));
cfg.setGridLogger(log);
// Start Ignite node.
Ignite ignite = Ignition.start(cfg);
ignite.log().info("Info Message Logged!");
如果在启动独立集群节点时要使用SLF4J模块,需要在执行ignite.{sh|bat}
脚本前,将optional/ignite-slf4j
文件夹移动到Ignite发行版的lib
目录下,这时这个模块目录中的内容会被添加到类路径。
如果项目中使用maven进行依赖管理,那么需要添加如下的依赖:
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-slf4j</artifactId>
<version>${ignite.version}</version>
</dependency>
将${ignite.version}
替换为实际使用的Ignite版本。
要使用JCL进行日志记录,需要配置IgniteConfiguration
的gridLogger
属性,如下所示:
XML:
<bean class="org.apache.ignite.configuration.IgniteConfiguration">
<property name="gridLogger">
<bean class="org.apache.ignite.logger.slf4j.Slf4jLogger"/>
</property>
<!-- Other Ignite configurations -->
...
</bean>
Java:
IgniteConfiguration cfg = new IgniteConfiguration();
IgniteLogger log = new Slf4jLogger();
cfg.setGridLogger(log);
// Start Ignite node.
Ignite ignite = Ignition.start(cfg);
ignite.log().info("Info Message Logged!");
要了解更多的信息,可以看SLF4J手册。
Ignite默认会使用$IGNITE_HOME/config/java.util.logging.properties
配置文件,然后将日志写入$IGNITE_HOME/work/log
文件夹,要修改这个目录,需要使用IGNITE_LOG_DIR
环境变量。
另外,Ignite启动于quiet模式,会阻止INFO
和DEBUG
日志的输出。要关闭quiet模式,可以使用-DIGNITE_QUIET=false
系统属性。注意,quiet模式的所有信息都是输出到标准输出(STDOUT)的。
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)以及在每个缓存基础上安全地赋予权限。