您的位置:首页 > 数据库 > Memcache

If-Memcached集成手册

2015-08-05 23:56 633 查看

1、Cache服务版本历史

版本发布日期内容注释
4.3.9

2011-Q1支持AOP,API方式

4.3.23

2011-Q4支持Hibernate

http://svn.99bill.net/opt/99billsrc/PMD/SRC/IF/build/if-memcached
4.3.24

2015-Q1增加瞬间防重功能

2、总览

2.1、支持三种接口:

a) AOP
b) API
c) Hibernate

2.2、与开源MemCached主要区别

a) 写两份读其中一份的集群方式
b) 支持客户端自动重连。
支持热部署,步骤为:

停备用服务器,改动后重启
等最大TTL时间,例等20分钟
停主服务器,改动后重启

2.3、注意事项

1)AOP方式(重载函数方式):

只适合于读频繁的应用场景,如果数据更新了,只有TTL时间后才会从数据库中读到最新的数据。
对于写频繁的数据读函数重载是危险的,例如对账户余额读函数重载。
对于公共服务的函数进行重载是危险的,万一公共服务的另一个函数的逻辑是依赖于该函数,会导致公共服务工作不正常。正确的方法应该是另写一新应用函数调用该公共函数,只重载该新应用函数。例如 com.bill99.seashell.domain.service.member.impl.MemberServiceImpl#getMemberByMemberCode。
不同应用使用不同的TTL函数,不同的应用使用不同的服务器集群。

2)建议使用Hibernate-Cache方式,或使用API方式,如果你弄不清要缓存的数据是否读频繁。

3) memcached服务本身对Key值有限制,若超过250个字符,则需要调整类/方法/参数的包名的长度;

注:com.bill99.seashell.domain.service.riskauditlist.impl.RiskauditListManagerServiceImpl#getRiskauditListDtoList#Object#Object=300只表示ttl的值对应的key,而实际存放在memcached中的

key是com.bill99.seashell.domain.service.riskauditlist.impl.RiskauditListManagerServiceImpl#getRiskauditListDtoList#参数1类名@地址#...#参数n类型@地址。

3、基础配置

3.1、ivy中要确定加入了如下jar包

<dependency org="org.spring" name="spring" rev="2.5.5" conf="zip->default" />

<dependency org="org.apache" name="commons-logging" rev="1.0.4" conf="zip->default"/>

<dependency org="org.aspectj" name="aspectjrt" rev="1.5" conf="zip->default"/>

<dependency org="org.aspectj" name="aspectjweaver" rev="1.5.3" conf="zip->default"/>

<dependency org="org.apache" name="log4j" rev="1.2.11" conf="zip->default"/>

<dependency org="org.slf4j" name="slf4j-api" rev="1.5.8" conf="zip->default"/>

<dependency org="org.slf4j" name="slf4j-log4j12" rev="1.5.8" conf="zip->default"/>

<dependency org="com.google" name="xmemcached" rev="1.3.5" conf="zip->default"/>

<dependency org="org.hibernate" name="hibernate-core" rev="3.3.2.GA" conf="zip->default"/>

<dependency org="org.hibernate" name="hibernate-memcached" rev="1.2.2" conf="zip->default"/>

<dependency org="org.dom4j" name="dom4j" rev="1.6.1" conf="compile->default"/>

<dependency org="org.apache" name="commons-collections" rev="3.1" conf="compile->default"/>

<dependency org="org.hibernate" name="hibernate-annotations" rev="3.4.0.GA" conf="zip->default"/>

<dependency org="org.hibernate" name="hibernate-commons-annotations" rev="3.2.0.Final" conf="compile->default"/>

<dependency org="org.hibernate" name="hibernate-jpa-2.0-api" rev="1.0.0.Final" conf="compile->default"/>

<dependency org="javassist" name="javassist" rev="3.11.0.GA" conf="compile->default"/>

<dependency org="j2ee" name="jta" rev="1.1" conf="compile->default"/>

<dependency org="com.99bill" name="if-memcached" rev="4.3.23" conf="zip->default"/>

3.2、properties文件配置

连接服务相关配置(应用名-memcached-config.properties),内容如下:

#主缓存服务器地址,多台主缓存服务器用空格分开

fi-memcached-config.client1Servers=192.168.126.151:12000

#备缓存服务器地址,多台备缓存服务器用空格分开

fi-memcached-config.client2Servers=192.168.126.151:12001

#各台的权重配置,用逗号分隔

fi-memcached-config.client1Weights=1

#各台的权重配置,用逗号分隔

fi-memcached-config.client2Weights=1

#主缓存服务器,nio的连接池配置

fi-memcached-config.client1ConnectionPoolSize=5

#备缓存服务器,nio的连接池配置

fi-memcached-config.client2ConnectionPoolSize=5
缓存KEY相关的TTL配置(应用名-cache-ttl.properties), 内容如下:

#实现类完整名#方法名#参数类型#参数类型#..#参数类型=TTL值(秒)

#支持的参数类型如下,且不带包名路径前缀

#Integer, String, Double, Float, Long, Boolean, Date, Map,List,Object

#用途说明:PE Cache List As Follows

com.bill99.seashell.domain.service.currency.impl.CurrencyServiceImpl#getRMBCurrency=600

com.bill99.seashell.domain.service.currency.impl.CurrencyServiceImpl#findCurrencyNumByCode#String=600

com.bill99.seashell.domain.service.exchangerate.impl.ExchangeRateServiceImpl#findExchangeRate#String#String#Object=600

3.3、新增专门的jar包,用于获得参数值

必须将配置文件放在context目录下,并命名为 context-cache-config.xml。

并读取properties的bean也放置在 context-cache-config.xml中。不同的组,会根据加载的properties文件不同。来指向各不同的cached系统。

例如Fi的:fi-memcached-config.jar。其中只有一个配置文件。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<!--若在web.xml未加载if-memcached.jar中的context/context-cache-domain.xml,则在本地加入
<import resource="classpath:context/context-cache-domain.xml"/>
-->

<!-- 用于读取memcached服务器配置 -->

<bean id="infoPropertyConfigurer"

class="com.bill99.seashell.domain.service.cache.client.PropertyPlaceholderConfigurer">

<property name="locations">

<list>

<value>file:////nfs/envconfig/pmd/fi/fi-memcached-config.properties</value>

</list>

</property>

<property name="ignoreUnresolvablePlaceholders" value="true" />

</bean>
<!-- 新增功能,各个应用独立的ttl配置,注bean id必须是memcachedTtlPropertyConfigurer -->

<bean id="memcachedTtlPropertyConfigurer"

class="com.bill99.seashell.domain.service.cache.client.PropertyPlaceholderConfigurer">

<property name="locations">

<list>

<value>file:////nfs/envconfig/pmd/fi/fi-cache-ttl.properties</value>

</list>

</property>

<property name="ignoreUnresolvablePlaceholders" value="true" />

<property name="ignoreResourceNotFound" value="true" />

</bean>
<bean id="servers1" class="java.lang.String">

<constructor-arg>

<value>${fi-memcached-config.client1Servers}</value>

</constructor-arg>

</bean>

<bean id="weightStr1" class="java.lang.String">

<constructor-arg>

<value>${fi-memcached-config.client1Weights}</value>

</constructor-arg>

</bean>

<bean id="intConnectionPoolSize1" class="java.lang.String">

<constructor-arg>

<value>${fi-memcached-config.client1ConnectionPoolSize}</value>

</constructor-arg>

</bean>

<bean id="servers2" class="java.lang.String">

<constructor-arg>

<value>${fi-memcached-config.client2Servers}</value>

</constructor-arg>

</bean>

<bean id="weightStr2" class="java.lang.String">

<constructor-arg>

<value>${fi-memcached-config.client2Weights}</value>

</constructor-arg>

</bean>

<bean id="intConnectionPoolSize2" class="java.lang.String">

<constructor-arg>

<value>${fi-memcached-config.client2ConnectionPoolSize}</value>

</constructor-arg>

</bean>

</beans>

3.4、日志集成配置(可选)

在应用的log4j.properties中加入如下内容:

log4j.logger.com.bill99.seashell.domain.service.cache=INFO,cachelog

log4j.logger.net.rubyeye.xmemcached=INFO,stdout,cachelog

log4j.logger.com.google.code.yanf4j.core=INFO,stdout,cachelog
#文件名,可以修改

log4j.appender.cachelog=org.apache.log4j.DailyRollingFileAppender

log4j.appender.cachelog.DatePattern=yyyy-MM-dd'.log'

log4j.appender.cachelog.File=/opt/log/Memcached

log4j.appender.cachelog.layout=org.apache.log4j.PatternLayout

log4j.appender.cachelog.layout.ConversionPattern=%d %p [%c] - <%m>%n

3.5、容器初始化时加载必须的XML文件

context\context-cache-domain.xml

context\context-cache-config.xml

4、集成hibernate配置

4.1、首先完成基础配置。

4.2、然后修改hibernate.cfg.xml增加二级缓存相关配置如下。

<!-- 二级缓存区域名的前缀,建议是项目组名 -->

<property name="hibernate.cache.region_prefix">if</property>

<!-- 是否使用二级缓存 -->

<property name="hibernate.cache.use_second_level_cache">true</property>

<!-- 是否允许查询缓存 -->

<property name="hibernate.cache.use_query_cache">true</property>

<!-- 是否强制Hibernate以更人性化的格式将数据存入二级缓存 -->

<property name="hibernate.cache.use_structured_entries">true</property>

<!-- 实现CacheProvider的类名 -->

<property name="hibernate.cache.provider_class">com.googlecode.hibernate.memcached.MemcachedCacheProvider</property>

<!—-99bill memcacheClient的工厂实现 -->

<property name="hibernate.memcached.memcacheClientFactory">com.bill99.seashell.domain.service.cache.hibernate.BillXmemcachedClientFactory</property>

<!-- 全局的ttl -->

<property name="hibernate.memcached.cacheTimeSeconds">300</property>

<property name="hibernate.memcached.keyStrategy">com.googlecode.hibernate.memcached.HashCodeKeyStrategy</property>

<!-- region配置 -->

<property name="hibernate.memcached.if.test.cacheTimeSeconds">3000</property>

4.3、再后更改ormapping文件,增加cache usage配置。

具体配置何种策略如下

a) read- only:无需修改, 那么就可以对其进行只读 缓存,注意,在此策略下,如果直接修改数据库,即使能够看到前台显示效果,但是将对象修改至cache中会报error,cache不会发生作用。另:删 除记录会报错,因为不能在read-only模式的对象从cache中删除。
b)read-write:需要更新数据,那么使用读/写缓存 比较合适,前提:数据库不可以为serializable transaction isolation level(序列化事务隔离级别)
c)nonstrict-read-write:只偶尔需要更新数据(也就是说,两个事务同时更新同一记录的情况很不常见),也不需要十分严格的事务隔离,那么比较适合使用非严格读/写缓存策略。

<hibernate-mapping>

<class name="com.bill99.seashell.domain.service.cache.hibernate.bean.Customer" table="CUSTOMERS">

<cache usage="read-write" region="if.test"/>

<id name="id" column="ID" type="int">

<generator class="increment" />

</id>

<property name="name" column="NAME" type="string" not-null="true" />

<property name="password" column="PASSWORD" type="string"

not-null="true" />

</class>

</hibernate-mapping>

5、使用示例

5.1、API方式

先进行基础配置
通过spring 注入 <ref="managerClient">。
调用 managerClient.set 进行缓存入值。调用managerClient.get获取缓存值。

import com.bill99.seashell.domain.service.cache.manager.ManagerClient;
public class ApiTest {

ManagerClient client;
public void test() {

client.set("if.key.001", 300, "1");

Object cache = client.get("if.key.001");

}
}
5.1.1、 清理Region的方法
import com.bill99.seashell.domain.service.cache.manager.ManagerClient;
public class ApiTest {

ManagerClient client;
public void test() {

//清理region中的缓存,可能耗时数秒到数十秒不等

boolean b = client.clearRegion("if.test");

}

}
经过测试6w条记录删除,只需1秒左右,所以性能没问题。

5.2、AOP方式

切入点表达式(Expression)由各组根据需要设置。

<aop:config>

<aop:pointcut id="cachePointcut"

expression="execution(* com.bill99.seashell.domain.service..*Service.get*(..))

|| execution(* com.bill99.seashell.domain.service..*Service.find*(..))

|| execution(* com.bill99.seashell.domain.service..*Service.has*(..))

|| execution(* com.bill99.seashell.domain.service..*Service.query*(..))" />

<aop:aspect id="cache" ref="cacheAdvisor" order="0">

<aop:around pointcut-ref="cachePointcut" method="doAround" />

</aop:aspect>

</aop:config>

使用AOP方式的时候需各应用自行在nfs服务器上增加对应 ma-cache-ttl.properties文件。

例如为/nfs/envconfig/pmd/ma/ma-cache-ttl.properties

#MA Cache List As Follows

com.bill99.seashell.domain.service.member.impl.MemberServiceImpl#getMemberByMemberCode#String=300

com.bill99.seashell.domain.service.member.impl.MemberFacadeServiceImpl#get#String=300

com.bill99.seashell.domain.service.memberacctspec.impl.MemberAcctSpecServiceImpl#getRMBBasicAccount#String=300

5.3、Hibernate方式

public void findAll() {

Session session = sessionFactory.openSession();

try {

Query q = session.createQuery("from Customer");

q.setCacheable(true);

List<Customer> lst = q.list();

} catch (Exception e) {

e.printStackTrace();

} finally {

session.close();

}

}

6、源代码位置

http://svn.99bill.net/opt/99billsrc/PMD/SRC/FI/branches/jarset068/if-memcached

FI 集成:

http://svn.99bill.net/opt/99billsrc/PMD/SRC/FI/build/fi-memcached-config

7、FAQ

(1)、当需要缓存的方法返回的是对象时,对象对应的类必须进行序列化,设置到memcache时,无法存入,导致取数据不经过缓存,直接重新计算获取。

(2)、memcached不起作用案例分析

A、aop切面配置不正确,解决方案如下:

在web.xml文件中,加载配置文件使用通配符,且配置的切面不同于默认配置定义时,新定义不起作用的情况?

因为在使用通配符加载的情况下,对配置文件的加载顺序是不确定的;在<aop:config/>中配置的内容会被合并到一节处理,新定义的配置方式如下:

在自身应用的Spring容器的配置上下文文件中增加:

<aop:config>

<aop:pointcut id="newPointCut"

expression="execution(* mm.abc..*Service.is*(..))" />

<aop:aspect id="newCache" ref="memcacheAdvisor" order="0">

<aop:around pointcut-ref="newPointCut" method="doAround" />

</aop:aspect>

</aop:config>

B、PointCut定义的方法中,包含有复杂的对象,且传入的类型为非通用类型,由于Key生成随着对象变化而变化,导致无法缓存;

通用类型指的是定义的类中默认已经覆盖toString方法的类(如Integer/Long/String/Date等)

对应的解决办法:

在传入的参数类型中,覆盖并实现toString()方法,建议其输出的字符串为:类名(不包含包名)+@+特定的输出值,注意生成的key尽量不要包含空格等信息。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: