Posts Tagged ‘google protobuf’

选择或设计实现远程RPC调用所需要考虑的n个方面

星期三, 8月 27th, 2008

选择或设计实现远程RPC调用所需要考虑的n个方面

(前段时间记录的零散片段,却一直没时间仔细整理,仅记录在此,以做备忘。)
可参考协议(组件)为:
http,smtp,pop,ftp,dns,burlap,json,xml-rpc,xmpp,smpp,rmi,soap,hessian,thrift , protocol buffers等等
需要考虑以下方面:

必备条件:
a.多语言支持
b.强大的序列化和反序列化(xml,text,pdu,text+stream)
c.多种传输模式

1.服务端所支持的编程语言。
java,C,C++,C#,PHP等等

2.客户端支持的语言。
java,C,C++,JavaScript等

3.字节流转码。
支持GBK,UTF-8等。

4.支持的基础数据类型。
char,int,long,double,floadt,boolean,List,Map,Object(Struct)

5.同名重载方法区别

6.支持数据包的转发代理 (前端负载均衡)

7.自定义超时连接

8.数据包转换 (对象的序列化和反序列化)

9.数据类型扩展

10.类与接口实现的约束

11.是否支持负载限制 (比如超过100并发时,直接返回系统忙)

12.failover处理

13.事务控制

14.异步调用

15.数据包版本自动识别

16.TCP/UDP支持
(UDP 有1472字节限制。)

17.代码生成器(主要针对数据包的序列化)

18.与现有框架的整合难度(组件化程度)

19.大文件传输

21.数据包序列化效率

客户端连接池
应用范围

22.网络带宽约束(数据包是否支持压缩)

23.服务接口监控统计

数据包加密

SSL支持

是否需要握手消息(是需多次交互还是简单的请求应答模式)

调试抓包的方便性

是否支持双向调用(xmpp支持)

部署难易程度

参数配置难度

是否支持请求队列

请求应答的对应关系

协议可读性

支持客户端并发限制(比如同一ip每秒只能够调用10次)

断点续传和重发数据

URL设置

access.log配置与合并,自定义 logger(用于控制是否打印敏感信息)

过滤器拦截模式

接口代码可读性

常用端口选择

重启服务时的热切换

黏性会话支持(基于IP,或者基于sessionid)

安全端口

是否可以多端口

配置文件

是否能够方便获取真实客户端ip和端口
MDC支持
是否支持线程局部变量(类似webservice获取session,ICE的Current)

防雪崩(提供初始化预热加载)

Tags: google protobuf, Hessian, RPC, thrift

Related posts

cygwin环境下调试thrift的例子

星期五, 7月 18th, 2008
thift是facebook的rpc调用框架,和google protobuf相比,额外多提供了rpc server和client的框架。
1.下载

http://www.cygwin.com/setup.exe

来源 http://www/cygwin.com
2.双击setup.exe
3.按照安装提示“下一步”
4.选择从“互联网”安装:
5.选择安装的路径为d:\Cygwin,选择本地临时目录d:\Cygwindown,用于存放下载的软件;选择连接互联网的方式,选“直接连接”。
6.选择下载源,直接在URL里输入http://www.cygwin.cn/pub/,点 Add
7.根据安装提示,选择需要安装的软件包,需要选择:
  • Cygwin or MinGW
  • GNU build tools (autoconf 2.60, automake 1.10, libtool 1.5.24)
  • boost 1.33.1+
  • g++ 4.0+
  • bison 2.3-1
  • boost 1.33.1-4
  • boost-devel 1.33.1-4
  • flex 2.5.33-1
  • pkgconfig
  • libtool
(参考:http://wiki.apache.org/thrift/ThriftInstallationWin32
在devel目录下选择这些包,
另外还需要选 make (the GNU version of ‘make’ utility ),
否则在最后执行 make的时候回被提示错误:
bash: make: command not found)
完成安装。
( D:\cygwindown\http%3a%2f%2fwww.cygwin.cn%2fpub%2f\release 下面一共有以下文件夹
aalib
alternatives
apache2
apr1
aprutil1
ash
audiofile
autoconf
automake
autotrace
base-files
base-passwd
bash
binutils
bison
boost
bzip2
cmake
coreutils
cpio
crypt
curl
cygutils
cygwin
cygwin-doc
db
diffutils
docbook-xml412
docbook-xsl
e2fsprogs
editrights
esound
expat
expect
fcgi
file
findutils
flac
flex
fontconfig
freeglut
gawk
gcc
gcc-mingw
gdbm
gettext
ghostscript
glib
gmp
GNOME
gnutls
GraphicsMagick
grep
groff
gtk2-x11
guile
gzip
icu
ImageMagick
jasper
jbigkit
jpeg
lapack
lcms
less
libao
libEMF
libfpx
libgcrypt
libglade2
libgpg-error
libiconv
liblzo2
libmcrypt
libmng
libogg
libpng
libsmi
libssh2
libtasn1
libtextcat
libtool
libungif
libvorbis
libwmf
libxml2
libxslt
login
lzma
m4
make
man
ming
mingw-runtime
minires
ncurses
neon
opencdk
openjade
OpenSP
openssl
pango
patch
pcre
perl
pkg-config
pkgconfig
plotutils
popt
postgresql
pstoedit
python
qhull
readline
rebase
run
sed
speex
sqlite3
t1lib
tar
tcltk
tcp_wrappers
termcap
terminfo
tetex
texinfo
tiff
tzcode
unzip
util-linux
uw-imap
w32api
wget
which
X11
xdelta
xerces-c
zlib
_obsolete
_update-info-dir)
9.thrift-20080411p1.tar.gz
下载地址在: http://developers.facebook.com/thrift/download_thrift.php
10.解压到D:\Java\thrift-20080411p1
11.启动cygwin
$ cd d:/Java/thrift-20080411p1
转到thrift目录。
12.运行

./bootstrap.sh
运行结果为:
lizongbo@lizongbo-pc /cygdrive/d/Java/thrift-20080411p1
$ ./bootstrap.sh
configure.ac: warning: missing AC_PROG_AWK wanted by: test/Makefile:133
configure.ac: warning: missing AC_PROG_RANLIB wanted by: test/Makefile:196
configure.ac:7: installing `./install-sh'
configure.ac:7: installing `./missing'
compiler/cpp/Makefile.am: installing `./depcomp'
configure.ac: installing `./ylwrap'
lizongbo@lizongbo-pc /cygdrive/d/Java/thrift-20080411p1
$
13.运行
./configure
耐心等待命令执行完成。
14.开始make,
$ cd compiler/cpp/
lizongbo@lizongbo-pc /cygdrive/d/Java/thrift-20080411p1/compiler/cpp
$ make
(要先装make包,否则会提示make: command not found)
耐心等待编译完成。
生成编译器文件在:

D:\Java\thrift-20080411p1\compiler\cpp\thrift.exe

14.用文本编辑器打开
D:\Java\thrift-20080411p1\tutorial下的\tutorial.thrift和shared.thrift
将tutorial.thrift第一行的#!/usr/local/bin/thrift -cpp -java -py -php -xsd -perl
改成#!/cygdrive/d/Java/thrift-20080411p1/compiler/cpp/thrift.exe -cpp -java -py -php -rb -perl -erl -xsd -r
shared.thrift的第一行#!/usr/local/bin/thrift -cpp -java -py -php -xsd -perl
改成#!/cygdrive/d/Java/thrift-20080411p1/compiler/cpp/thrift.exe  -cpp -java -py -php -xsd -perl
保存文件。
15.cygwin下转到tutorial目录.
cd /cygdrive/d/Java/thrift-20080411p1/tutorial
16.分别运行
./shared.thrift
和./tutorial.thrift
生成相关代码。
17.在D:\Java\thrift-20080411p1\lib\java下面运行ant.生成libthrift.jar
18.编辑D:\Java\thrift-20080411p1\tutorial\java\build.xml。
<property name=”cpath” location=”/usr/local/lib/libthrift.jar” />
改成
<property name=”cpath” location=”../../lib/java/libthrift.jar” />
19.
D:\Java\thrift-20080411p1\tutorial\java\下运行ant。
生成class文件。
20.在D:\Java\thrift-20080411p1\tutorial\java\下面启动Server。
java -cp ./tutorial.jar;../../lib/java/libthrift.jar JavaServer
然后再启动客户端:
java -cp ./tutorial.jar;../../lib/java/libthrift.jar JavaClient
服务端可以看到输出信息:
Starting the server…
ping()
add(1,1)
calculate(1, {4,1,0})
calculate(1, {2,15,10})
getStruct(1)
客户端的输出为:
ping()
1+1=2
Invalid operation: Cannot divide by 0
15-10=5
Check log: 5
Tags: google protobuf, RPC, thrift

Related posts

扩展hessian的SerializerFactory以优化支持Protocol Buffers格式的对象

星期六, 7月 12th, 2008

在对  google protobuf简单试用之后(参考: http://618119.com/archives/2008/07/08/100.html),
感觉 google protobuf对对象的序列化非常方便。

google protocolbuffers可以当作一种对对象序列化的方式整合到hessian中。
对于google ProtoBuf 格式的对象,由于没有实现java.io.Serializable,默认情况下在hessian中是无法使用的,
不过可以通过启用hessian的对非序列化对象进行支持的参数,
相关代码示例为:
[code]
Hessian2Input h2i = new Hessian2Input(bai);
h2i.findSerializerFactory().setAllowNonSerializable(true);//允许非序列化对象
Hessian2Output h2o = new Hessian2Output(bao);
h2o.findSerializerFactory().setAllowNonSerializable(true);//允许非序列化对象

[/code]

在这种情况下,虽然 google protocolbuffers所生成的java对象的构造方法是私有的,
heesian依然可以通过反射方式生成对象。
但是在这种情况下, hessian所序列化出的数据里,携带了大量的无用属性,
这些属性是google ProtoBuf 用来标识对象中的属性状态的,在远程调用的时候,
没有传输的必要。

因此,可以通过扩展hessian的SerializerFactory,使之在传输 google ProtoBuf 格式的对象时,
传输的数据占用尽量少的字节。

扩展的步骤如下:
实现ProtoBufSerializerFactory和ProtoBufDeserializer及ProtoBufSerializer
然后在HessianInput(或Hessian2Input),HessianOutput(或Hessian2Output)中调用addFactory方法。

ProtoBufSerializerFactory.java的代码为:

[code]
package com.lizongbo.hessian.protobuf;

import com.caucho.hessian.io.*;
import com.google.protobuf.*;

/**
*
* <p>Title:  google protobuf 对象的序列化工厂</p>
*
* <p>Description: 扩展对google protobuf格式的对象的序列化 </p>
*
* <p>Copyright: Copyright (c) 2008</p>
*
* <p>Company: 618119.com </p>
*
* @author lizongbo
* @version 1.0
*/
public class ProtoBufSerializerFactory extends AbstractSerializerFactory {
private static final ProtoBufSerializerFactory instance = new
ProtoBufSerializerFactory();
Serializer serializer = new ProtoBufSerializer();

public Deserializer getDeserializer(Class cl) throws
HessianProtocolException {
if (Message.class.isAssignableFrom(cl)) {
try {
return new ProtoBufDeserializer(cl);
} catch (NoSuchMethodException ex) {
ex.printStackTrace();
return null;
}
}
return null;
}

public Serializer getSerializer(Class cl) throws HessianProtocolException {
if (Message.class.isAssignableFrom(cl)) {
return serializer;
}

return null;
}

public static ProtoBufSerializerFactory getInstance() {
return instance;
}

}

[/code]
ProtoBufSerializer.java的代码为:
[code]
package com.lizongbo.hessian.protobuf;

import java.io.*;

import com.caucho.hessian.io.*;
import com.google.protobuf.Message;

public class ProtoBufSerializer extends AbstractSerializer {

public void writeObject(Object obj, AbstractHessianOutput out) throws
IOException {
if (obj == null) {
out.writeNull();
} else {
Class cl = obj.getClass();

if (out.addRef(obj)) {
return;
}

int ref = out.writeObjectBegin(cl.getName());

if (ref < -1) {
out.writeString(”lizongbo”);
out.writeBytes(((Message) obj).toByteArray());
out.writeMapEnd();
} else {
if (ref == -1) {
out.writeInt(1);
out.writeString(”lizongbo”);
out.writeObjectBegin(cl.getName());
}

out.writeBytes(((Message) obj).toByteArray());
}
}

}

}
[/code]
ProtoBufDeserializer.java的代码为:
[code]
package com.lizongbo.hessian.protobuf;

import java.io.*;

import com.caucho.hessian.io.*;
import com.google.protobuf.Message;

public class ProtoBufDeserializer extends AbstractDeserializer {
private Class _cl;
private Message.Builder _constructor;

public ProtoBufDeserializer(Class cl) throws NoSuchMethodException {
_cl = cl;
try {
_constructor = (Message.Builder) cl.getMethod(”newBuilder”).invoke(new
Object[0]);
} catch (Exception ex) {
ex.printStackTrace();
}
}

public Class getType() {
return _cl;
}

public Object readMap(AbstractHessianInput in) throws IOException {
int ref = in.addRef(null);

byte[] initValue = null;

while (!in.isEnd()) {
String key = in.readString();

if (key.equals(”lizongbo”)) {
initValue = in.readBytes();
} else {
in.readString();
}
}

in.readMapEnd();

Object value = create(initValue);

in.setRef(ref, value);

return value;
}

public Object readObject(AbstractHessianInput in, String[] fieldNames) throws
IOException {
int ref = in.addRef(null);

byte[] initValue = null;

for (int i = 0; i < fieldNames.length; i++) {
String key = fieldNames[i];

if (key.equals(”lizongbo”)) {
initValue = in.readBytes();
} else {
in.readObject();
}
}

Object value = create(initValue);

in.setRef(ref, value);

return value;
}

private Object create(byte[] initValue) throws IOException {
if (initValue == null || initValue.length < 1) {
throw new IOException(_cl.getName() + ” expects name.”);
}

try {

return _constructor.mergeFrom(initValue).build();
} catch (Exception e) {
throw new IOExceptionWrapper(e);
}
}

}

[/code]

调用的代码为:

[code]
Hessian2Input h2i = new Hessian2Input(bai);

h2i.findSerializerFactory().addFactory(ProtoBufSerializerFactory.getInstance());
h2i.findSerializerFactory().setAllowNonSerializable(true);
ByteArrayOutputStream bao = new ByteArrayOutputStream();
////OutputStream bao2 = new HessianDebugOutputStream(bao, dbg); //增加调试输出,如果不需要调试,则屏蔽该行即可。
Hessian2Output h2o = new Hessian2Output(bao);
h2o.findSerializerFactory().addFactory(ProtoBufSerializerFactory.getInstance());
h2o.findSerializerFactory().setAllowNonSerializable(true);
[/code]

在我封装的hessian数据包,传递一个QQuser对象的情况下

优化前 收到的一个数据包长:259(有其它额外信息)

优化后收到的数据包长:79(有其它额外信息)

Protocol Buffers格式的byteArray得到的字节长度,17
对比可知节约259-(79-17)=197字节。

Tags: google protobuf, Hessian, Serializable

Related posts