If-Memcached集成手册

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-&gt;default" />
<dependency org="org.apache" name="commons-logging" rev="1.0.4" conf="zip-&gt;default"/>
<dependency org="org.aspectj" name="aspectjrt" rev="1.5" conf="zip-&gt;default"/>
<dependency org="org.aspectj" name="aspectjweaver" rev="1.5.3" conf="zip-&gt;default"/>
<dependency org="org.apache" name="log4j" rev="1.2.11" conf="zip-&gt;default"/>
<dependency org="org.slf4j" name="slf4j-api" rev="1.5.8" conf="zip-&gt;default"/>
<dependency org="org.slf4j" name="slf4j-log4j12" rev="1.5.8" conf="zip-&gt;default"/>
<dependency org="com.google" name="xmemcached" rev="1.3.5" conf="zip-&gt;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方式
  1. 先进行基础配置
  2. 通过spring 注入 <ref="managerClient">。
  3. 调用 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尽量不要包含空格等信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值