@liyuj
2017-03-30T22:23:19.000000Z
字数 8915
阅读 2808
Apache-Ignite-1.8.0-中文开发手册
从1.5版本开始,Ignite引入了一个在缓存中存储数据的新概念,名为二进制对象
,这个新的序列化格式提供了若干个优势:
二进制对象只可以用于使用默认的二进制编组器时(即没有在配置中显式地设置其他的编组器)
限制
BinaryObject
格式实现也带来了若干个限制:
1. 在内部Ignite不会写属性以及类型的名字,但是使用一个小写的名字哈希来标示一个属性或者类型,这意味着属性或者类型不能有同样的名字哈希。即使序列化不会在哈希冲突的情况下工作,但Ignite在配置级别提供了一种方法来解决此冲突;
2.同样的原因,BinaryObject
格式在类的不同层次上也不允许有同样的属性名;
3.默认会忽略Externalizable
接口。如果使用了BinaryObject
格式,Externalizable
类型会与Serializable
类型是同样的处理方式,没有writeExternal()
和readExternal()
方法。如果由于某些原因这样不行,需要实现Binarylizable
接口,加入一个自定义BinarySerializer
或者切换到OptimizedMarshaller
。
IgniteBinary
入口,可以从Ignite的实例获得,包含了操作二进制对象的所有必要的方法。
当一个对象被序列化到二进制格式时,Ignite默认会捕获他的哈希值然后将其与二进制对象属性并排地保存,这个恰当而且一致的哈希值可以在集群内的所有节点为所有对象提供。对于equals
比较,Ignite默认会依赖序列化对象的二进制表示进行逐字节地比较,这意味着它会比较一个对象内的所有序列化字段,从第一个直到最后一个。由于这个比较类型,结果依赖于字段序列化的顺序。
这个默认的行为是由BinaryArrayIdentityResolver
接口实现的,每个对象序列化到二进制格式时都会进行设定。
如果这个默认的方式不合适,那么可以通过BinaryIdentityResolver
接口进行自定义实现,或者使用BinaryFieldIdentityResolver
实现进行替代。
Binary Identity Resolver
这个接口可以自定义哈希值生成的逻辑,以及二进制对象进行一致性比较的方式。
public interface BinaryIdentityResolver {
/**
* Compute hash code for binary object.
*/
public int hashCode(BinaryObject obj);
/**
* Compare two binary objects for equality.
*/
public boolean equals(@Nullable BinaryObject o1, @Nullable BinaryObject o2);
}
这个解析器是通过BinaryTypeConfiguration
对象进行配置的,下面的示例显示如何实现:
<bean class="org.apache.ignite.configuration.IgniteConfiguration">
....
<property name="binaryConfiguration">
<bean class="org.apache.ignite.configuration.BinaryConfiguration">
<!-- Listing specific configuration for binary types -->
<property name="typeConfigurations">
<list>
<bean class="org.apache.ignite.binary.BinaryTypeConfiguration">
<!-- Defining types that will used the custom resolver. -->
<property name="typeName" value="org.app.model.*"/>
<!-- Setting the custom resolver for the types -->
<property name="identityResolver">
<bean class="org.app.example.MyCustomIdentityResolver"/>
</property>
</bean>
<bean class="org.apache.ignite.binary.BinaryTypeConfiguration">
<property name="typeName" value="org.app.Person" />
<!-- Setting specific resolver for Person type -->
<property name="identityResolver">
<bean class="org.app.example.CustomPersonIdentityResolver"/>
</property>
</bean>
</list>
</property>
</bean>
</property>
Binary Field Identity Resolver
这个类型的解析器使用特定对象的字段值进行哈希值的计算以及一致性比较。当不希望比较的结果依赖于字段序列化的顺序时,可以用这个解析器替代BinaryArrayIdentityResolver
,当进行哈希值生成或者对象比较时,BinaryFieldIdentityResolver
会只使用配置中列出的字段以及它们在配置中定义的顺序。
<bean class="org.apache.ignite.configuration.IgniteConfiguration">
....
<property name="binaryConfiguration">
<bean class="org.apache.ignite.configuration.BinaryConfiguration">
<!-- Listing specific configuration for binary types -->
<property name="typeConfigurations">
<list>
<bean class="org.apache.ignite.binary.BinaryTypeConfiguration">
<property name="typeName" value="org.app.Person" />
<!-- Setting BinaryFieldIdentityResolver for Person type -->
<property name="identityResolver">
<bean class="org.apache.ignite.binary.BinaryFieldIdentityResolver">
<!--
List of fields, whose values will be used for hash
calculation and equality comparision.
-->
<property name="fieldNames">
<list>
<value>id</value>
<value>firstName</value>
</list>
</property>
</bean>
</property>
</bean>
</list>
</property>
</bean>
</property>
在绝大多数情况下不需要额外地配置二进制对象,在IgniteConfiguration中如果不配置其他的编组器,BinaryObject
编组器会被默认启用。
如果需要覆写默认的类型和属性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>
<bean class="org.apache.ignite.binary.BinaryTypeConfiguration">
<property name="typeName" value="org.app.Person" />
<!-- Setting BinaryFieldIdentityResolver for Person type -->
<property name="identityResolver">
<bean class="org.apache.ignite.binary.BinaryFieldIdentityResolver">
<!--
List of fields, whose values will be used for hash
calculation and equality comparision.
-->
<property name="fieldNames">
<list>
<value>id</value>
<value>firstName</value>
</list>
</property>
</bean>
</property>
</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:
cache.<Integer, BinaryObject>withKeepBinary().invoke(
new CacheEntryProcessor<Integer, BinaryObject, Void>() {
@Override Void process(
MutableEntry<Integer, BinaryObject> entry, Object... args) {
// 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++,默认是不需要任何配置的,只有当需要平台协同、名字转换复杂的情况下,才需要配置映射器。