心有多大舞台便有多大

Embrace changes, pursue excellence, share niceness.

hessian序列化协议+memcached的缓存存取

大名鼎鼎的memcached恐怕没人不知道吧!hessian是一种远程调用的机制,类似与web service,不过它是使用自己的序列化协议.
那么,为什么要把hessian的序列化协议和memcached结合起来实现缓存的读取呢?
有过使用memcached的经验的人会了解到,php+memcached的性能是最好的,java+memcached的性能比较差,其主要原因就是在于java本身的序列化机制很慢.
我做了个简单的测试,一个UserData类,有一个字符串属性,一个日期属性,一个double属性,分别用java,hessian来序列化一百万次,结果让人吃惊,不止是hessian序列化的速度要比java的快上一倍,而且hessian序列化后的字节数也要比java的少一倍.因为我在测试的时候只是做了序列化这部分的工作,并没有把序列化后的结果放到网络上传输,所以,实际中的性能hessian应该会更好!
既然hessian的序列化协议要比java本身的好,而memcached客户端的性能又在很大程度上依赖与对象的序列化.所以,我就决定把我的cache实现中序列化这部分的工作改成用hessian来实现了.
我用的memcached客户端是用的danga.MemCached包,主要是改动了MemCachedClient的get方法及set方法,在set方法中改为调用hessian的序列化:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//修改以前的序列化代码:
 //(new ObjectOutputStream( bos )).writeObject( value );
//修改后的序列化代码:
serializeByHessian(bos, value);
 val = bos.toByteArray();
serializeByHessian方法如下:

 protected void serializeByHessian(OutputStream os, Object object) throws IOException {
        AbstractHessianOutput out = new Hessian2Output(os);;
        SerializerFactory serializerFactory = getSerializerFactory();
        out.setSerializerFactory(serializerFactory);
        out.startReply();
        out.writeObject(object);     
        out.completeReply();
        out.flush();
 }
在get方法中主要是修改了这个方法调用的类ContextObjectInputStream的readObject方法:
在ContextObjectInputStream中覆盖了readObjectOverride方法:
 protected Object readObjectOverride() throws IOException,  ClassNotFoundException {
        ByteArrayInputStream is = new ByteArrayInputStream(bytes);
        ClHessian2Input in = new ClHessian2Input(is, this.mLoader);
        in.setSerializerFactory(getSerializerFactory());
        int code = in.read();//"r"
        int major = in.read();//>=2
        int minor = in.read();//0
        Object value = in.readObject();
        is.close();
        return value;
 }
因为我的框架是基于osgi的,所以我重载了Hessian2Input,把classloader作为参数传进去,否则hessian在反序列化的时候会找不到类.如果你没有用osgi框架的话, ClHessian2Input in = new ClHessian2Input(is, this.mLoader);这行代码就可以直接用: Hessian2Input in = new Hessian2Input(is);
这样修改就基本完成了.
我把memcached client的序列化协议改为hessian也有另外一个系统架构的原因,那就是因为我的服务层逻辑都是用java+spring+osgi的方式实现,而web层则是用php实现,两者之间通讯已经是采用hessian的远程调用.所以,部分缓存数据在服务层通过java设置到memcached服务器中,在php中一样可以用memcached php client读取出来.(php的memcached client我用的是memcached-client.php,而不是php扩展,所以一样可以修改memcached-client.php的序列化机制)


posted on 2008-06-18 10:04 pony 阅读(7863) 评论(11)  编辑  收藏 所属分类: Java

评论

# re: hessian序列化协议+memcached的缓存存取 2008-06-18 10:17 pony

另外附带说一句,我的经验,在使用memcached的时候,客户端在创建socket连接时最好把nagle设置为false.熟悉tcp协议的都知道setTcpNoDelay有什么作用.如果Tcp_NoDelay被设置为true,那么发送数据包时将使用nagle算法对要发送的数据进行缓存处理,只有当达到一定数量之后才把包发送出去,设置为false则立即发送包.对于memcached缓存,一般我们放进去的数据,以及发送的get命令都是很小的包,为了将数据及时传输出去,所以要禁用nagle.  回复  更多评论   

# re: hessian序列化协议+memcached的缓存存取 2008-06-18 10:35 CHINA BAIDU

不错1!  回复  更多评论   

# re: hessian序列化协议+memcached的缓存存取 2008-06-18 12:19 dennis

@pony
很好的帖子,不知道换成了hession有没有测试数据?
对于setTcpNoDelay说反了吧?setTcpNoDelay为true,就是设置TCP_NODELAY,也就是禁掉Nagle算法;默认就是false,表示启用Nagle算法。禁掉Nagle算法可以提高响应性,相应地会降低吞吐量,在这个场景中没啥必要。



  回复  更多评论   

# re: hessian序列化协议+memcached的缓存存取 2008-06-18 13:24 lizongbo

hession 的序列化效率确实不错,

不过需要序列化的对象,试过实现java.io.Externalizable接口的方式没?


  回复  更多评论   

# re: hessian序列化协议+memcached的缓存存取 2008-06-18 13:41 pony

@dennis
测试hessian和java序列化的代码,写的不好请见谅^_^
由于把数据存储到memcached服务器中跟网络环境,内存有很大关系,跟memcached client设置的参数也有很大关系,所以就不做memcached的存取测试了.

下面的代码在我的机器上的结果是:
serialize 1000000 users with hessian spend 8 seconds, total size:93000000
serialize 1000000 users with java spend 17 seconds, total size:190000000

/**
* Created at 2008-06-13.
* 测试比较java和hessian的序列化效率.
*/
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Date;

import com.caucho.hessian.io.AbstractHessianOutput;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;

/**
* @author pony
*
* 如果有任何对代码的修改,请按下面的格式注明修改的内容.
* 序号 时间 作者 修改内容
* 1. 2008-6-13 pony created this class.
*/
public class TestSerializerPerformanceFucntion {
//序列化次数.
private static final int COUNT = 1000000;

private SerializerFactory factory;

public static void main(String[] args) throws Exception {
new TestSerializerPerformanceFucntion().testHessianSerializer();
new TestSerializerPerformanceFucntion().testJavaSerializer();
}

public void testHessianSerializer() throws Exception {
long curr = new Date().getTime();
int size = COUNT;
int len = 0;
for (int i=0; i<size; i++) {
User user = new User();
ByteArrayOutputStream os = new ByteArrayOutputStream();
AbstractHessianOutput out = new Hessian2Output(os);;
SerializerFactory serializerFactory = getSerializerFactory();
out.setSerializerFactory(serializerFactory);
out.startReply();
out.writeObject(user);
out.completeReply();
out.flush();
os.flush();
len += os.size();
out.close();
}
long now = new Date().getTime();
System.out.println("serialize " + size + " users with hessian spend " + (now-curr)/1000 + " seconds, total size:" + len);
}

public void testJavaSerializer() throws Exception {
long curr = new Date().getTime();
int size = COUNT;
int len = 0;
for (int i=0; i<size; i++) {
User user = new User();
ByteArrayOutputStream os = new ByteArrayOutputStream();
new ObjectOutputStream(os).writeObject(user);
os.flush();
len += os.size();
os.close();
}
long now = new Date().getTime();
System.out.println("serialize " + size + " users with java spend " + (now-curr)/1000 + " seconds, total size:" + len);
}

public SerializerFactory getSerializerFactory() {
if (null == factory) {
factory = new SerializerFactory();
}
return factory;
}
}


class User implements Serializable {
/**
* serial Version UID.
*/
private static final long serialVersionUID = -4845300297590675952L;

private String name = "test name";

private Date birthday = new Date();

private int age = 10;

private double money = 1000.56;

/**
* @return the name
*/
public String getName() {
return name;
}

/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}

/**
* @return the birthday
*/
public Date getBirthday() {
return birthday;
}

/**
* @param birthday the birthday to set
*/
public void setBirthday(Date birthday) {
this.birthday = birthday;
}

/**
* @return the age
*/
public int getAge() {
return age;
}

/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
}

/**
* @return the money
*/
public double getMoney() {
return money;
}

/**
* @param money the money to set
*/
public void setMoney(double money) {
this.money = money;
}

/**
* @return the serialVersionUID
*/
public static long getSerialVersionUID() {
return serialVersionUID;
}
}  回复  更多评论   

# re: hessian序列化协议+memcached的缓存存取 2008-06-18 13:54 pony

@lizongbo
谢谢!这是个好建议!试过之后,只有惊讶!结果是比hessian的还快了!
运行结果是:
serialize 1000000 users with hessian spend 10 seconds, total size:93000000
serialize 1000000 users with java spend 9 seconds, total size:86000000

把上面代码中的Serializable 改为
Externalizable,实现下面两个方法:
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
String name = in.readUTF();
long time = in.readLong();
int age = in.readInt();
double money = in.readDouble();
this.setName(name);
this.setBirthday(new Date(time));
this.setAge(age);
this.setMoney(money);
}

public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeLong(birthday.getTime());
out.writeInt(age);
out.writeDouble(money);
}

不过因为从架构上考虑,我要用php来读取cache中的内容,所以我希望序列化后的对象能遵循一个统一的协议,所以,我暂时还是要用hessian的方式.  回复  更多评论   

# re: hessian序列化协议+memcached的缓存存取 2008-12-04 10:31 LiMengyan

博主能否来个hessian序集?讲讲hessian部署的结构?我想看看hessian这个服务模块是怎么给其他应用提供服务的?  回复  更多评论   

# re: hessian序列化协议+memcached的缓存存取 2010-09-20 11:00 thebye85

@pony
怎么我这边测试还是hessian快  回复  更多评论   

# re: hessian序列化协议+memcached的缓存存取[未登录] 2010-10-05 16:38 Jeff

好文章!  回复  更多评论   

# re: hessian序列化协议+memcached的缓存存取[未登录] 2011-06-24 12:03 HK

好文啊!  回复  更多评论   

# re: hessian序列化协议+memcached的缓存存取[未登录] 2013-12-19 15:40 sa

hessian4.0.7有一个bigdecimal序列化的bug  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航: