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

Simple-Spring-Memcached使用Protobuf序列化Java对象

2013-04-30 15:18 976 查看
本文提供一段代码示例,演示如何在Simple-Spring-Memcached中使用Protobuf序列化Java对象。主要解决的问题有:

Protobuf序列化Map对象
实现SSM中CacheTranscoder接口的Protobuf实现
在SSM中为需要序列化的对象配置相应的Transcoder


Protobuf序列化Java对象

在项目中使用Protobuf序列化时,需要编写.proto消息定义文件,然后使用protoc编译出相应的java对象。其中的Java对象可以作为一个Bean在系统中使用,但是并不推荐。我们目前采用的策略如下:

项目中使用独立的Domain对象,在类中提供一个通过Probobuf生成类为参数的构造函数
Protobuf生成的Java对象只用于序列化,不允许出现在其它场合
每个项目模块维护一份.proto消息定义文件,放在domain/protobuf目录下,protoc编译生成的Java对象,也放在该目录下。结构如下:




首先编辑.proto文件,按ProtoBuf的规则,添加需要的字段。如下:


ssm.proto

package org.colorfuldays.ssm.domain.protobuf;

option java_package = "org.colorfuldays.ssm.domain.protobuf";

message MapEntity{
optional string key = 1;
optional string value = 2;
}

message Session{
optional int64 sessionId = 1;
repeated MapEntity attributes = 2;
}



编辑完成后,使用protoc编译出Java对象的源码


执行下面命令即可编译中上图中的Ssm类源码,上面定义的Session,MapEntity生成的源码以Ssm的内部类形式放在Ssm.java文件中。
protoc --java_out=/Users/star/Workspace/github/ssm-demo/src/main/java ssm.proto


注:–java_out必须是绝对路径,在maven的项目中,需要定位在java目录。

Protobuf序列化Session对象


在生成的Ssm.java对象中包含了为Session,MapEntity做序列化操作的对象。

序列化

通过生成代码中各对象对应Builder类来构建对象,代码如下:

Ssm.Session.Builder builder = Ssm.Session.newBuilder();
Iterator<String> iterator = session.getAttributeKeySet().iterator();
int index = 0;
while (iterator.hasNext()) {
Ssm.MapEntity.Builder entityBuilder = Ssm.MapEntity.newBuilder();
entityBuilder.setKey(iterator.next()).setValue(session.getAttribute(iterator.next()));
builder.setAttributes(index, entityBuilder.build());
}
Ssm.Session = builder.build();


反序列化

反序列化比较简单,通过parseFrom方法即可,如下:

try {
Ssm.Session session = Ssm.Session.parseFrom(data.getData());
return new Session(session);
} catch (InvalidProtocolBufferException e) {
LOG.error("parse session from protobuf error", e);
}


其中new Session(session) 使用的是Ssm.Session对象为参数的构造方法。

Map在ProtoBuf中的序列化


Protobuf并不提供原生Map类型,因此在序列化Java的Map对象时,需要自己想办法。在示例中,先定义了一个MapEntity,然后将其作为一个repeated域定义在Session中,在生在的代码中Session中存在一个包含MapEntity的List,类似于Java Api中Map的内部实现。通过这种变通的方式实现Map的序列化时,在序列化与反序列化时,需要手动作数据转换。


Protobuf序列化与SSM框架结合

SSM框架中提供了用户定义序列化方法的扩展点。要定制序列化方式首先需要实现CacheTranscoder接口,之后在配置文件中添加上相应的配置。

SSM配置的自定义Transcoder粒度非常细,可以针对每一个需要序列化的对象配置相应的Transcoder实现类。

实现CacheTranscoder接口


CacheTranscoder接口只提供两个方法:
public CachedObject encode(T t);
public T decode(CachedObject data);


其中encode为序列化就走,decode为反序列化方法。在这个demo示例的实现代码如下:

SessionPBTranscoder

public class SessionPBTranscoder implements CacheTranscoder<Session> {
private static final Logger LOG = LoggerFactory.getLogger(SessionPBTranscoder.class);
@Override
public Session decode(CachedObject data) {
try { Ssm.Session session = Ssm.Session.parseFrom(data.getData()); return new Session(session); } catch (InvalidProtocolBufferException e) { LOG.error("parse session from protobuf error", e); }
return null;
}

@Override
public CachedObject encode(Session session) {
Ssm.Session.Builder builder = Ssm.Session.newBuilder();
Iterator<String> iterator = session.getAttributeKeySet().iterator();
int index = 0;
while (iterator.hasNext()) {
Ssm.MapEntity.Builder entityBuilder = Ssm.MapEntity.newBuilder();
entityBuilder.setKey(iterator.next()).setValue(session.getAttribute(iterator.next()));
builder.setAttributes(index, entityBuilder.build());
}

return new CachedObjectImpl(PROTOBUF_SERIALIZED,builder.build().toByteArray());
}

private static final int PROTOBUF_SERIALIZED = 9;
}



CacheTranscoder配置


CacheTranscoder的配置是在SSM选用的memcached client中配置的。在这里使用的是XMemcached客户端,配置如下:
<bean name="defaultMemcachedClient" class="com.google.code.ssm.CacheFactory">
<property name="cacheClientFactory">
<bean class="com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl"/>
</property>
<property name="addressProvider">
<bean class="com.google.code.ssm.config.DefaultAddressProvider">
<property name="address" value="127.0.0.1:11211"/>
</bean>
</property>
<property name="configuration">
<bean class="com.google.code.ssm.providers.CacheConfiguration">
<property name="consistentHashing" value="true"/>
</bean>
</property>
<property name="cacheTranscoders">
<map>
<entry key="org.colorfuldays.ssm.domain.BookDO" value-ref="jsonTranscoder"/>
<entry key="org.colorfuldays.ssm.domain.UserDO" value-ref="userProtobufTranscoder"/>
<entry key="org.colorfuldays.ssm.domain.Session" value-ref="sessionPBTranscoder"/>
</map>
</property>
</bean>

<bean name="jsonTranscoder" class="com.google.code.ssm.transcoders.JsonTranscoder">
<constructor-arg index="0"  value="org.colorfuldays.ssm.domain.BookDO"/>
<constructor-arg index="1">
<ref bean="JsonObjectMapper"/>
</constructor-arg>
<constructor-arg index="2">
<ref bean="longToStringTranscoder"/>
</constructor-arg>
</bean>

<bean name="userProtobufTranscoder" class="org.colorfuldays.ssm.transcoders.ProtobufTranscoder"/>
<bean name="sessionPBTranscoder" class="org.colorfuldays.ssm.transcoders.SessionPBTranscoder"/>
<bean name="longToStringTranscoder" class="com.google.code.ssm.transcoders.LongToStringTranscoder"/>
<bean name="JsonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper"/>


注:上述cacheTranscoders是一个Map<Class,CacheTranscoder>对象,其中Class作为Map的key,在Spring配置中只需要配置Class的全名即可。

完成上述配置后,即整个过程就全部完成了。具体使用的例子请参考github里面的源码 https://github.com/iamxhu/ssm-demo

[update]:测试发现,在使用自定义的CacheTranscoder时必须给AOP拦截的方法加上 @UseJson注解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: