@liyuj
2017-02-03T21:07:25.000000Z
字数 16397
阅读 4304
Apache-Ignite-1.8.0-中文开发手册
Ignite可以用做Hibernate的二级缓存,它可以显著地提升应用持久化层的性能。
Hibernate是著名的、应用广泛的对象关系映射框架(ORM),在与SQL数据库紧密互动的同时,他通过对查询结果集的缓存来最小化昂贵的数据库请求。
Hibernate数据库映射对象的所有工作都是在一个会话中完成的,通常绑定到一个worker线程或者Web会话。默认的话,Hibernate只会使用Session级的缓存(L1),因此,缓存在一个会话中的对象,对于另一个会话是不可见的。然而,如果用一个全局的二级缓存的话,他缓存的所有对象对于用同一个缓存配置的所有会话都是可见的。这通常会带来性能的显著提升,因为每一个新创建的会话都可以利用L2缓存(他比任何会话级L1缓存都要长寿)中已有的数据的好处。
L1缓存是一直启用的而且是由Hibernate内部实现的,而L2缓存是可选的而且有很多的可插拔的实现。Ignite可以作为L2缓存的实现非常容易地嵌入,而且可以用于所有的访问模式(READ_ONLY
,READ_WRITE
,NONSTRICT_READ_WRITE
和TRANSACTIONAL
),支持广泛的相关特性:
复制
和分区
如果要将Ignite作为Hibernate的二级缓存,需要简单的三个步骤:
本章节的后面会详细介绍这些步骤的细节。
要将Ignite配置为Hibernate的二级缓存,不需要修改已有的Hibernate代码,只需要:
应用中可能会用到的部分Hibernate类需要设置一个特定的BinaryTypeConfiguration
,比如,org.hibernate.cache.spi.CacheKey
已经添加了该配置,大致如下:
BinaryConfiguration bCfg = new BinaryConfiguration();
// Setting specific binary configuration for a Hibernate class.
BinaryTypeConfiguration btCfg = new BinaryTypeConfiguration();
// Setting the class name.
btCfg.setTypeName(org.hibernate.cache.spi.CacheKey.class.getName());
// Setting special binary identity resolver.
btCfg.setIdentityResolver(new BinaryAbstractIdentityResolver() {
@Override protected int hashCode0(BinaryObject obj) {
return return obj.field("key").hashCode();
}
@Override protected boolean equals0(BinaryObject o1, BinaryObject o2) {
Object obj0 = o1.field("key");
Object obj1 = o2.field("key");
return Objects.equals(obj0, obj1);
}
});
// Applying the configuration.
bCfg.setTypeConfigurations(Collections.singleton(btCfg));
IgniteConfiguration igniteConfiguration = new IgniteConfiguration()
.setBinaryConfiguration(bCfg);
Maven配置
Maven依赖
为了开启Ignite的hibernate集成,需要在工程里面添加ignite-hibernate的依赖,或者在从命令行启动之前,从libs/optional中将ignite-hibernate模块拷贝到libs文件夹。
要在项目中添加Ignite-hibernate集成,需要将下面的依赖加入POM文件:
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-hibernate</artifactId>
<version>RELEASE</version>
</dependency>
Hibernate配置示例
一个用Ignite配置Hibernate二级缓存的典型例子看上去像下面这样:
<hibernate-configuration>
<session-factory>
...
<!-- Enable L2 cache. -->
<property name="cache.use_second_level_cache">true</property>
<!-- Generate L2 cache statistics. -->
<property name="generate_statistics">true</property>
<!-- Specify Ignite as L2 cache provider. -->
<property name="cache.region.factory_class">org.apache.ignite.cache.hibernate.HibernateRegionFactory</property>
<!-- Specify the name of the grid, that will be used for second level caching. -->
<property name="org.apache.ignite.hibernate.grid_name">hibernate-grid</property>
<!-- Set default L2 cache access type. -->
<property name="org.apache.ignite.hibernate.default_access_type">READ_ONLY</property>
<!-- Specify the entity classes for mapping. -->
<mapping class="com.mycompany.MyEntity1"/>
<mapping class="com.mycompany.MyEntity2"/>
<!-- Per-class L2 cache settings. -->
<class-cache class="com.mycompany.MyEntity1" usage="read-only"/>
<class-cache class="com.mycompany.MyEntity2" usage="read-only"/>
<collection-cache collection="com.mycompany.MyEntity1.children" usage="read-only"/>
...
</session-factory>
</hibernate-configuration>
这里,我们做了如下工作:
Ignite配置示例
一个典型的支持Hibernate二级缓存的Ignite配置,像下面这样:
<!-- Basic configuration for atomic cache. -->
<bean id="atomic-cache" class="org.apache.ignite.configuration.CacheConfiguration" abstract="true">
<property name="cacheMode" value="PARTITIONED"/>
<property name="atomicityMode" value="ATOMIC"/>
<property name="writeSynchronizationMode" value="FULL_SYNC"/>
</bean>
<!-- Basic configuration for transactional cache. -->
<bean id="transactional-cache" class="org.apache.ignite.configuration.CacheConfiguration" abstract="true">
<property name="cacheMode" value="PARTITIONED"/>
<property name="atomicityMode" value="TRANSACTIONAL"/>
<property name="writeSynchronizationMode" value="FULL_SYNC"/>
</bean>
<bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
<!--
Specify the name of the caching grid (should correspond to the
one in Hibernate configuration).
-->
<property name="gridName" value="hibernate-grid"/>
...
<!--
Specify cache configuration for each L2 cache region (which corresponds
to a full class name or a full association name).
-->
<property name="cacheConfiguration">
<list>
<!--
Configurations for entity caches.
-->
<bean parent="transactional-cache">
<property name="name" value="com.mycompany.MyEntity1"/>
</bean>
<bean parent="transactional-cache">
<property name="name" value="com.mycompany.MyEntity2"/>
</bean>
<bean parent="transactional-cache">
<property name="name" value="com.mycompany.MyEntity1.children"/>
</bean>
<!-- Configuration for update timestamps cache. -->
<bean parent="atomic-cache">
<property name="name" value="org.hibernate.cache.spi.UpdateTimestampsCache"/>
</bean>
<!-- Configuration for query result cache. -->
<bean parent="atomic-cache">
<property name="name" value="org.hibernate.cache.internal.StandardQueryCache"/>
</bean>
</list>
</property>
...
</bean>
上面的代码为每个二级缓存区域指定了缓存的配置:
分区
缓存在缓存节点间拆分数据,其他的策略也可以选择复制
模式,这样的话就在所有缓存节点上复制完整的数据集,可以参照相关的章节以了解更多的信息。事务
原子化模式来利用缓存事务的优势FULL_SYNC
模式保持备份节点的完全同步另外,指定了一个缓存来更新时间戳,它可以是原子化
的,因为性能好。
配置完Ignite缓存节点后,可以通过如下方式在节点内启动他:
Ignition.start("my-config-folder/my-ignite-configuration.xml");
上述代码执行完毕后,内部的节点就启动了然后准备缓存数据,也可以从控制台执行如下命令来启动额外的独立的节点:
$IGNITE_HOME/bin/ignite.sh my-config-folder/my-ignite-configuration.xml
对于Windows,可以执行同一文件夹下的.bat
脚本。
节点也可以在其他主机上启动,以形成一个分布式的缓存集群,一定要确保在配置文件中指定正确的网络参数。
除了二级缓存,Hibernate还提供了查询缓存,这个缓存存储了通过指定参数集进行查询的结果(或者是HQL或者是Criteria),因此,当重复用同样的参数集进行查询时,他会命中缓存而不会去访问数据库。
查询缓存对于反复用同样的参数集进行查询时是有用的。像二级缓存的场景一样,Hibernate依赖于一个第三方的缓存实现,Ignite也可以这样用。
要考虑Ignite中对于基于SQL的内存内查询的支持,他会比通过Hibernate性能更好。
上面的配置信息完全适用于查询缓存,但是额外的配置和代码变更还是必要的。
Hibernate配置
要在Hibernate种启用查询缓存,只需要在配置文件中添加额外的一行:
<!-- Enable query cache. -->
<property name="cache.use_query_cache">true</property>
然后,需要对代码做出修改,对于要缓存的每一个查询,都需要通过调用setCacheable(true)
来开启cacheable
标志。
Session ses = ...;
// Create Criteria query.
Criteria criteria = ses.createCriteria(cls);
// Enable cacheable flag.
criteria.setCacheable(true);
...
这个完成之后,查询结果就会被缓存了。
Ignite配置
要在Ignite中开启Hibernate查询缓存,需要指定一个额外的缓存配置:
<property name="cacheConfiguration">
<list>
...
<!-- Query cache (refers to atomic cache defined in above example). -->
<bean parent="atomic-cache">
<property name="name" value="org.hibernate.cache.internal.StandardQueryCache"/>
</bean>
</list>
</property>
注意为了更好的性能缓存配置为原子化
的。
从1.5版本开始,Ignite可以作为MyBatis的二级缓存使用,可以提高MyBatis的性能。
如果是一个Maven用户,可以简单地在pom.xml中添加如下的依赖:
<dependencies>
...
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ignite</artifactId>
<version>1.0.3</version>
</dependency>
...
</dependencies>
或者,也可以下载zip包,解压缩之后将jar文件加入类路径。
然后,只需要像下面这样在映射XML文件中指定即可:
<mapper namespace="org.acme.FooMapper">
<cache type="org.mybatis.caches.ignite.IgniteCacheAdapter" />
</mapper>
然后在config/default-config.xml中配置Ignite缓存(可以简单地参考下github中的配置)。
当前的实现中,EvictionPolicy,CacheLoaderFactoryCacheWriterFactory在config/default-config.xml中无法启用。
要了解MyBatis缓存特性的更多信息,可以参考MyBatis手册。
Ignite提供了一个SpringCacheManager
-一个Spring缓存抽象的实现。他提供了基于注解的方式来启用Java方法的缓存,这样方法的执行结果就会存储在Ignite缓存中。如果之后同一个方法通过同样的参数集被调用,结果会直接从缓存中获得而不是实际执行这个方法。
Spring缓存抽象文档
关于如何使用Spring缓存抽象的更多信息,包括可用的注解,可以参照这个文档页面:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html.
只需要两个简单的步骤就可以将Ignite缓存嵌入基于Spring的应用:
SpringCacheManager
作为缓存管理器。嵌入式节点可以通过SpringCacheManager
自己启动,这种情况下需要分别通过configurationPath
或者configuration
属性提供一个Ignite配置文件的路径或者IgniteConfiguration
Bean(看下面的例子)。注意同时设置两个属性是非法的以及抛出IllegalArgumentException
。
配置路径:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- Provide configuration file path. -->
<bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager">
<property name="configurationPath" value="examples/config/spring-cache.xml"/>
</bean>
<!-- Enable annotation-driven caching. -->
<cache:annotation-driven/>
</beans>
配置Bean:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<-- Provide configuration bean. -->
<bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager">
<property name="configuration">
<bean class="org.apache.ignite.configuration.IgniteConfiguration">
...
</bean>
</property>
</bean>
<-- Enable annotation-driven caching. -->
<cache:annotation-driven/>
</beans>
当缓存管理器初始化时也可能已经有一个Ignite节点正在运行(比如已经通过ServletContextListenerStartup
启动了)。这时只需要简单地通过gridName
属性提供网格名字就可以了。注意如果不设置网格名字,缓存管理器会试图使用默认的Ignite实例(名字为空的),下面是一个示例:
使用已启动的Ignite实例:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- Provide grid name. -->
<bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager">
<property name="gridName" value="myGrid"/>
</bean>
<!-- Enable annotation-driven caching. -->
<cache:annotation-driven/>
</beans>
远程节点
注意应用内部启动的节点只是希望连接的网络的一个入口,可以根据需要通过Ignite发行版提供的bin/ignite.{sh|bat}
脚本启动尽可能多的远程独立节点,所有这些节点都会参与缓存数据。
虽然通过Ignite配置文件可以获得所有必要的缓存,但是这不是必要的。如果Spring要使用一个不存在的缓存时,SpringCacheManager
会自动创建它。
如果不指定,会使用默认值创建一个新的缓存。要定制的话,可以通过dynamicCacheConfiguration
属性提供一个配置模板,比如,如果希望使用复制
缓存而不是分区
缓存,可以像下面这样配置SpringCacheManager
:
<bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager">
...
<property name="dynamicCacheConfiguration">
<bean class="org.apache.ignite.configuration.CacheConfiguration">
<property name="cacheMode" value="REPLICATED"/>
</bean>
</property>
</bean>
也可以在客户端侧使用近缓存,要做到这一点只需要简单地通过dynamicNearCacheConfiguration
属性提供一个近缓存配置即可。默认的话近缓存是不启用的,下面是一个例子:
<bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager">
...
<property name="dynamicNearCacheConfiguration">
<bean class="org.apache.ignite.configuration.NearCacheConfiguration">
<property name="nearStartSize" value="1000"/>
</bean>
</property>
</bean>
一旦在Spring应用上下文中加入了SpringCacheManager
,就可以通过简单地加上注解为任意的java方法启用缓存。
通常为很重的操作使用缓存,比如数据库访问。比如,假设有个Dao类有一个averageSalary(...)
方法,他计算一个组织内的所有雇员的平均工资,那么可以通过@Cacheable
注解来开启这个方法的缓存。
private JdbcTemplate jdbc;
@Cacheable("averageSalary")
public long averageSalary(int organizationId) {
String sql =
"SELECT AVG(e.salary) " +
"FROM Employee e " +
"WHERE e.organizationId = ?";
return jdbc.queryForObject(sql, Long.class, organizationId);
}
当这个方法第一次被调用时,SpringCacheManager
会自动创建一个averageSalary
缓存,他也会在缓存中查找事先计算好的平均值然后如果存在的话就会直接返回,如果这个组织的平均值还没有被计算过,那么这个方法就会被调用然后将结果保存在缓存中,因此下一次请求这个组织的平均值,就不需要访问数据库了。
缓存键
因为organizationId
是唯一的方法参数,所以他会自动作为缓存键。
如果一个雇员的工资发生变化,可能希望从缓存中删除这个雇员所属组织的平均值,否则averageSalary(...)
方法会返回过时的缓存结果。这个可以通过将@CacheEvict
注解加到一个方法上来更新雇员的工资:
private JdbcTemplate jdbc;
@CacheEvict(value = "averageSalary", key = "#e.organizationId")
public void updateSalary(Employee e) {
String sql =
"UPDATE Employee " +
"SET salary = ? " +
"WHERE id = ?";
jdbc.update(sql, e.getSalary(), e.getId());
}
在这个方法被调用之后,这个雇员所属组织的平均值就会被从averageSalary
缓存中踢出,这会强迫averageSalary(...)
方法在下一次调用时重新计算。
Spring表达式语言(SpEL)
注意这个方法是以雇员为参数的,而平均值是通过组织的Id将平均值存储在缓存中的。为了明确地指定什么作为缓存键,可以使用注解的key
参数和Spring表达式语言。
#e.organizationId
表达式的意思是从e变量中获取organizationId
属性的值。本质上会在提供的雇员对象上调用getOrganizationId()
方法,以及将返回的值作为缓存键。
Ignite具有缓存所有兼容Java Servlet3.0规范的Java Servlet容器的Web Session的能力。包括Apache Tomcat,Eclipse Jetty,Oracle WebLogic以及其他的。
缓存Web会话对于运行一个应用服务器集群时是有用的。当在一个Servlet容器中运行一个Web应用时,可能面临性能和可扩展性的问题,一个单独的应用服务器通常可能无法自己处理很大的流量,一个常规的解决方案就是跨越多个集群实例扩展Web应用。
在上面的架构中,高可用代理(负载平衡器)在多个应用服务器实例之间分发请求(应用服务器1,应用服务器2……),来降低每个实例的负载以及提供在任意实例故障时的服务可用性,这里的问题就是Web会话的可用性。Web会话通过Cookie保持请求之间的中间逻辑状态,并且通常绑定到一个特定的应用实例。通常这是由粘性连接来处理,来确保来自同一个用户的请求被同一个应用服务器实例处理。然而,如果该实例故障,会话就丢失了,所有当前未保存的状态也丢失了,然后用户会重新创建它。
这里的一个解决方案就是用Ignite来缓存Web会话-维护每个创建的会话的拷贝的分布式缓存,在所有的实例中共享。如果任何一个应用实例故障,Ignite会马上从分布式缓存中恢复故障实例所属的会话,而不管下一个请求会被转发到哪个应用服务器。这样的话,随着Web会话被缓存粘性连接就变得不那么重要,因为会话可以用于请求被路由到的任何应用服务器。
这个章节给出了一个Ignite的Web会话缓存功能的主要架构概况以及介绍了如何配置Web应用来启用Web会话缓存。
要用Ignite配置一个分布式Web会话缓存,通常需要将应用启动为一个Ignite节点(嵌入式模式),当多个应用服务器实例启动后,所有的Ignite节点会连姐在一起形成一个分布式缓存。
注意并不是所有的Ignite缓存节点都需要运行在应用服务器内部,也可以启动额外的,独立的Ignite节点,然后将他们加入集群。
当将会话存储在Ignite中时有几个复制策略可供选择,复制策略是在缓存的备份设定中定义的,本章节将主要覆盖最常用的配置。
全复制缓存
这个策略保存每个Ignite节点上的所有会话的拷贝,提供了最大的可用性。然而这个方法缓存的会话的数量必须匹配单个服务器的内存大小,另外,性能也会变差因为现在Web会话状态的每一次改变都必须复制到集群中所有的其他节点。
要启用全复制策略,设置缓存的cacheMode为REPLICATED
:
<bean class="org.apache.ignite.configuration.CacheConfiguration">
<!-- Cache mode. -->
<property name="cacheMode" value="REPLICATED"/>
...
</bean>
有备份的分区缓存
在分区模式,Web会话会被拆分为区,每个节点只负责缓存分配给该节点的分区的数据,这个方法中如果有更多的节点,就可以缓存更多的数据,新的节点也可以动态地加入以增加更多的内存。
分区
模式中,冗余是通过为每个缓存的Web会话配置一定数量的备份实现的。
要开启分区策略,设置缓存的cacheMode为PARTITIONED
以及通过CacheConfiguration
的backups
属性来配置备份的数量。
<bean class="org.apache.ignite.configuration.CacheConfiguration">
<!-- Cache mode. -->
<property name="cacheMode" value="PARTITIONED"/>
<property name="backups" value="1"/>
</bean>
可以参照
3.3.缓存模型
来了解关于Ignite不同复制策略的更多信息。
当会话过期后会被缓存自动清理。然而,如果创建了大量的长期存活的会话,当缓存达到一个特定的限值时,为了节省内存可能需要将不必要的缓存退出。这个可以通过设置缓存的退出策略以及指定缓存中可以存储的会话的最大值来实现。比如,要启用基于LRU算法的自动退出以及10000个会话的限制,可以通过如下的缓存配置来实现:
<bean class="org.apache.ignite.configuration.CacheConfiguration">
<!-- Cache name. -->
<property name="name" value="session-cache"/>
<!-- Set up LRU eviction policy with 10000 sessions limit. -->
<property name="evictionPolicy">
<bean class="org.apache.ignite.cache.eviction.lru.LruEvictionPolicy">
<property name="maxSize" value="10000"/>
</bean>
</property>
...
</bean>
要了解各个退出策略的更多信息,可以参照
3.15.退出策略
章节。
要在应用中通过Ignite开启Web会话缓存,需要:
添加Ignite的jar包,下载Ignite然后将如下的jar包加入应用的类路径(WEB-INF/lib目录);
或者,如果是一个基于Maven的工程,可以将下面的片段加入应用的pom.xml:
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-core</artifactId>
<version> ${ignite.version}</version>
</dependency>
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-web</artifactId>
<version> ${ignite.version}</version>
</dependency>
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-log4j</artifactId>
<version>${ignite.version}</version>
</dependency>
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-spring</artifactId>
<version>${ignite.version}</version>
</dependency>
确保将${ignite.version}替换为实际的Ignite版本。
2. 配置缓存模式,配置Ignite的缓存,要么是分区
模式,要么是复制
模式(可以看上面的例子);
3. 更新web.xml,在web.xml中声明一个ContextListener和一个WebSessionsFilter:
...
<listener>
<listener-class>org.apache.ignite.startup.servlet.ServletContextListenerStartup</listener-class>
</listener>
<filter>
<filter-name>IgniteWebSessionsFilter</filter-name>
<filter-class>org.apache.ignite.cache.websession.WebSessionFilter</filter-class>
</filter>
<!-- You can also specify a custom URL pattern. -->
<filter-mapping>
<filter-name>IgniteWebSessionsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Specify Ignite configuration (relative to META-INF folder or Ignite_HOME). -->
<context-param>
<param-name>IgniteConfigurationFilePath</param-name>
<param-value>config/default-config.xml </param-value>
</context-param>
<!-- Specify the name of Ignite cache for web sessions. -->
<context-param>
<param-name>IgniteWebSessionsCacheName</param-name>
<param-value>partitioned</param-value>
</context-param>
...
在应用启动时,监听器会在应用中启动一个Ignite节点,他会连接网络中的其他节点以形成一个分布式缓存。
4. 设置退出策略(可选),为缓存中的旧数据设置退出策略(可以看上面的例子)。
配置参数
ServletContextListenerStartup
有如下的配置参数:
参数名 | 描述 | 默认值 |
---|---|---|
IgniteConfigurationFilePath |
Ignite配置文件的路径(相对于META-INF 文件夹或者IGNITE_HOME ) |
/config/default-config.xml |
WebSessionFilter有如下的配置参数:
参数名 | 描述 | 默认值 |
---|---|---|
IgniteWebSessionsGridName |
启动Ignite节点的网格名,可以参照配置文件的grid部分(如果配置文件中指定了网格名) | 无 |
IgniteWebSessionsCacheName |
Web会话缓存的缓存名 | 无 |
IgniteWebSessionsMaximumRetriesOnFail |
只对ATOMIC 缓存有效,指定了当主节点故障时的重试次数 |
3 |
Ignite官方测试了如下的应用服务器: