1. Overview
TerraCotta
通过将POJO对象在群集内下的共享,让POJO不再局限于SNA(Share Nothing Architect)的架构,比较透明的支持了集群模式,可谓POJO开发模型的最后一块拼图。
其实它的原理很简单,本身是一个中央式的Cache服务器。在应用启动命令中添加Terracotta参数,Classloader就会根据配置文件在JVM级以AOP方式修改ByteCode,用户透明地将对象存储于中央服务器。
为了性能,它以对象属性而不是整个对象为存储单位;为了可用性,它本身也支持主备的集群。
Book
《The Definitive Guide to Terracotta》
Articles
参考手册
2. TC DSO配置
Root and Instrumentation Class
指定一个或多个根属性,以某个类的某个属性(比如某个HashMap)作为起点,然后属性又属性下来所有可到达的对象都会纳入共享范围。
在Instrumentation节点指出上面的对象图里所需的Class,如果懒就写成类似<class-expression>org.springside..*</class-expression>
指定为Root的属性不可更改指针,但可更改里面的内容可改。
共享对象的属性中,一部分为原始类型,一部分为Reference其他对象,透明的被置为该对象在TC中的ID。
当带有根属性的对象构造时,构造函数中的根属性值将被忽略,而被注入为TC服务器中的属性。
并发锁控制
在集群环境下,所有的程序都是并发执行的。因此,首先需要改造共享对象的代码为线程安全的。
比如将属性设为线程安全的ConcurrentHashMap 、AtomicInteger,或在访问共享对象属性的代码中加入synchronized控制。
TC暂时不自动为Collections.synchronizedMap()的返回加锁。
然后在配置文件中用auto-locks将锁扩展到集群范围,设定以锁为边界的批量更新属性的事务。采用AOP的语法配置,最懒就写成类似<method-expression>* org.springside..*.*(..)</method-expression>
默认的级别是write,另外有 synchronous-write, read, and concurrent 三个级别。
对于无法修改synchronized的第三方代码,可以用name lock来实现粗粒度的锁。
反向理解TC的CTO同志关于调优的讲话,锁没搞好的话对性能影响挺大。
Transients Field
有些对象关乎本地资源,是不能共享的,如Loggers, File Handlers, Sockets, java.io., java.net., java.nio., java.sql.Connection,java.lang.Thread。
可以修改代码,在属性定义中注明transient,并在Instrumentation时设定 <honor-transient>true</honor-transient>
更少侵入的做法是完全在tc-config.xml中设定。(推荐)
<transient-fields>
<field-name>com.mycompany.pkga.MyClassOne.fieldA</field-name>
</transient-fields>
最大麻烦就是这些transient的属性,在另一个JVM中会被强制设为Null。这时候,有三种不算很好的解决方法:
- 修改代码,内部全部用getter函数访问属性,getter函数中如果为null则初始化。
- 在tc-config.xml 中定义<on-load>节点的<execute>子节点,在里面编写Beanshell初始化代码。
- 在tc-config.xml 中编写Beanshell代码虽然较少侵入,但beanshell语法让人头痛。更简单的方式是在类里多写一个初始化函数如tc_init(),然后在tc-config.xml的<on-load>节点的<method>子节点定义.(推荐)
另外关于logger, 好像TC已智能完成,不需要自己设定。
Session Configure 工具 、 Eclipse插件和工作流程
很喜欢这种"前商业项目",一般都会有不错的工具。
- Sessions Configurator 。以Debug模式将tc-confg.xml运行在一个预配置的双机集群下,让你观察共享对象的数值变化,出现运行时错误时,提示配置文件缺漏错误的修正。
- Eclipse插件。通过对着任意的类、属性、函数点右键来设定tc-config.xml。
建议的流程:
- 检查程序的synchronized
- Eclipse插件定义忽略Transients Field, 并设置项目的全部类和全部方法为Instrumentation Class和Autolock
- 用Session Configruer运行调试
- 用Eclipse插件细化Instrumentation Class和Autolock, 再次用Session Configruer调试。
3. 高阶配置
TC 对分布式Cache方案的增强
- 透明的,非API式调用。
- 更少的访问原始数据源取得Cache。
只要其中一台Server获得Cache即可存储在TC Server,不用每台Server都去访问数据源获得一次。
另外TC活得比应用长,应用重启后可以立刻从TC Server获得Cache。
TC Cluster
TC承担了实现POJO集群的功能,但TC Server本身就存在单点故障的危险,需要配成Cluster模式。
TC的Persistent HA Cluster模式中,所有数据会Persist到磁盘,Cluster里永远只有一个Active Node,其他节点作为Passive Node。Active Node的失效切换与Client的重连都是透明的。
Passive与Active Node可以使用同一块支持文件锁的磁盘空间,也可以让Active Node将所有变化通过网络同步到Passive Node上。一般采用后者。
分布式方法
可以在所有JVM中调用相同的方法。能用于维护不可共享的本地资源。
TC Spring
配置更简单,只需要定义context文件和context中需要共享的bean。
如果设了non-distributed-field,就会从本地IOC注入,如DataSource。如果没有特别声明instrumentation class,就不用写locks,只要保证写好synchronized。
现有Modules
- Hibernate 有使用Ehcache 2级缓存共享,也有已经Detached的对象的共享。