(facebook) thrift / (hadoop) avro / (google) probuf(grpc)是近几年来比较抢眼的高效序列化/rpc框架,dubbo框架虽然有thrift的支持,但是依赖的版本较早,只支持0.8.0,而且还对协议做一些扩展,并非原生的thrift协议。

github上虽然也有朋友对dubbo做了扩展支持原生thrift,但是代码实在太多了,只需要一个类即可:

Thrift2Protocal.java:

package com.alibaba.dubbo.rpc.protocol.thrift2;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.protocol.AbstractProxyProtocol;
import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.server.TNonblockingServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport; import java.lang.reflect.Constructor; /**
* 为dubbo-rpc添加"原生thrift"支持
* by 杨俊明(http://yjmyzz.cnblogs.com/)
*/
public class Thrift2Protocol extends AbstractProxyProtocol {
public static final int DEFAULT_PORT = 33208;
private static final Logger logger = LoggerFactory.getLogger(Thrift2Protocol.class); public int getDefaultPort() {
return DEFAULT_PORT;
} @Override
protected <T> Runnable doExport(T impl, Class<T> type, URL url)
throws RpcException { logger.info("impl => " + impl.getClass());
logger.info("type => " + type.getName());
logger.info("url => " + url); TProcessor tprocessor;
TNonblockingServer.Args tArgs = null;
String iFace = "$Iface";
String processor = "$Processor";
String typeName = type.getName();
TNonblockingServerSocket transport;
if (typeName.endsWith(iFace)) {
String processorClsName = typeName.substring(0, typeName.indexOf(iFace)) + processor;
try {
Class<?> clazz = Class.forName(processorClsName);
Constructor constructor = clazz.getConstructor(type);
try {
tprocessor = (TProcessor) constructor.newInstance(impl);
transport = new TNonblockingServerSocket(url.getPort());
tArgs = new TNonblockingServer.Args(transport);
tArgs.processor(tprocessor);
tArgs.transportFactory(new TFramedTransport.Factory());
tArgs.protocolFactory(new TCompactProtocol.Factory());
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new RpcException("Fail to create thrift server(" + url + ") : " + e.getMessage(), e);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new RpcException("Fail to create thrift server(" + url + ") : " + e.getMessage(), e);
}
} if (tArgs == null) {
logger.error("Fail to create thrift server(" + url + ") due to null args");
throw new RpcException("Fail to create thrift server(" + url + ") due to null args");
}
final TServer thriftServer = new TNonblockingServer(tArgs); new Thread(new Runnable() {
public void run() {
logger.info("Start Thrift Server");
thriftServer.serve();
logger.info("Thrift server started.");
}
}).start(); return new Runnable() {
public void run() {
try {
logger.info("Close Thrift Server");
thriftServer.stop();
} catch (Throwable e) {
logger.warn(e.getMessage(), e);
}
}
};
} @Override
protected <T> T doRefer(Class<T> type, URL url) throws RpcException { logger.info("type => " + type.getName());
logger.info("url => " + url); try {
TSocket tSocket;
TTransport transport;
TProtocol protocol;
T thriftClient = null;
String iFace = "$Iface";
String client = "$Client"; String typeName = type.getName();
if (typeName.endsWith(iFace)) {
String clientClsName = typeName.substring(0, typeName.indexOf(iFace)) + client;
Class<?> clazz = Class.forName(clientClsName);
Constructor constructor = clazz.getConstructor(TProtocol.class);
try {
tSocket = new TSocket(url.getHost(), url.getPort());
transport = new TFramedTransport(tSocket);
protocol = new TCompactProtocol(transport);
thriftClient = (T) constructor.newInstance(protocol);
transport.open();
logger.info("thrift client opened for service(" + url + ")");
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new RpcException("Fail to create remoting client:" + e.getMessage(), e);
}
}
return thriftClient;
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e);
}
} }

重写父类AbstractProxyProtocol的二个抽象方法doExport及doRefer即可,doExport用于对外暴露RPC服务,在这个方法里启动thrift server,dubbo service provider在启动时会调用该方法。而doRefer用于dubbo service consumer发现服务后,获取对应的rpc-client。

参考这个思路,avro也很容易集成进来:

AvroProtocol.java

package com.alibaba.dubbo.rpc.protocol.avro;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.protocol.AbstractProxyProtocol;
import org.apache.avro.ipc.NettyServer;
import org.apache.avro.ipc.NettyTransceiver;
import org.apache.avro.ipc.Server;
import org.apache.avro.ipc.reflect.ReflectRequestor;
import org.apache.avro.ipc.reflect.ReflectResponder; import java.net.InetSocketAddress; /**
* 为dubbo-rpc添加avro支持
* by 杨俊明(http://yjmyzz.cnblogs.com/)
*/
public class AvroProtocol extends AbstractProxyProtocol {
public static final int DEFAULT_PORT = 40881;
private static final Logger logger = LoggerFactory.getLogger(AvroProtocol.class); public int getDefaultPort() {
return DEFAULT_PORT;
} @Override
protected <T> Runnable doExport(T impl, Class<T> type, URL url)
throws RpcException { logger.info("impl => " + impl.getClass());
logger.info("type => " + type.getName());
logger.info("url => " + url); final Server server = new NettyServer(new ReflectResponder(type, impl),
new InetSocketAddress(url.getHost(), url.getPort()));
server.start(); return new Runnable() {
public void run() {
try {
logger.info("Close Avro Server");
server.close();
} catch (Throwable e) {
logger.warn(e.getMessage(), e);
}
}
};
} @Override
protected <T> T doRefer(Class<T> type, URL url) throws RpcException { logger.info("type => " + type.getName());
logger.info("url => " + url); try {
NettyTransceiver client = new NettyTransceiver(new InetSocketAddress(url.getHost(), url.getPort()));
T ref = ReflectRequestor.getClient(type, client);
logger.info("Create Avro Client");
return ref;
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e);
}
} }

不要忘记在META-INF/dubbo/internal下添加名为com.alibaba.dubbo.rpc.Protocal的文件,内容为:

avro=com.alibaba.dubbo.rpc.protocol.avro.AvroProtocol

接下来谈谈如何打包到dubbo的jar里:  

dubbo-rpc/pom.xml里,把二个新增的项目加进来:

    <modules>
...
<module>dubbo-rpc-avro</module>
...
<module>dubbo-rpc-thrift2</module>
... </modules>

然后dubbo/pom.xml里:

   <artifactSet>
<includes>
...
<include>com.alibaba:dubbo-rpc-api</include>
<include>com.alibaba:dubbo-rpc-avro</include>
...
<include>com.alibaba:dubbo-rpc-thrift2</include>
...
</includes>
</artifactSet>

dependencies节也要增加:

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-rpc-thrift2</artifactId>
<version>${project.parent.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-rpc-avro</artifactId>
<version>${project.parent.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.avro</groupId>
<artifactId>avro</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.avro</groupId>
<artifactId>avro-ipc</artifactId>
</exclusion>
</exclusions>
</dependency>

这样打包出来的dubbo-xxx.jar里,就包括新增的Protocol。至于google的protobuf,目前处于3.x -beta阶段,等以后出正式版了,再看情况整合起来。

以上代码已经提交到github:https://github.com/yjmyzz/dubbox (版本号:2.8.4a)

thrift/avro协议的使用示例见:https://github.com/yjmyzz/dubbox-sample

最后,对dubbo/thrift/avro/rest这4种协议,做了下简单的对比测试,测试用例很简单:

  public String ping() {
return "pong";
}

客户端调用ping方法,服务器返回字符串"pong",在mac book pro上做5万次调用,结果如下:

dubbo RPC testing =>
50000次RPC调用(dubbo协议),共耗时14778毫秒,平均3383.407715/秒
avro RPC testing =>
50000次RPC调用(avro协议),共耗时10707毫秒,平均4669.842285/秒
thrift RPC testing =>
50000次RPC调用(thrift协议),共耗时4667毫秒,平均10713.520508/秒
REST testing =>
50000次REST调用,共耗时112699毫秒,平均443.659668/秒

这跟预期一致,REST走http协议,自然最慢,avro与dubbo底层的网络通讯都是借助netty实现,在同一个数量级,但是avro的二进制序列化效率更高,所以略快,而thrift则是从里到外,全都是facebook自己实现的,性能最优,完胜其它协议。

个人建议:对于一个服务接口,对外同时提供thrift、REST二种形式的服务实现,内部子系统之间用thrift方式调用(因为thrift跨语言,其实从外部进来的调用,也可以用thrift-rpc方式),一些不方便直接用thrift-client调用的场景,仍然走传统的REST.

dubbo/dubbox 增加原生thrift及avro支持的更多相关文章

  1. 分布式事务之——tcc-transaction分布式TCC型事务框架搭建与实战案例(基于Dubbo/Dubbox)

    转载请注明出处:http://blog.csdn.net/l1028386804/article/details/73731363 一.背景 有一定分布式开发经验的朋友都知道,产品/项目/系统最初为了 ...

  2. Dubbo 迈出云原生重要一步 - 应用级服务发现解析

    作者 | 刘军(陆龟)  Apache Dubbo PMC 概述 社区版本 Dubbo 从 2.7.5 版本开始,新引入了一种基于实例(应用)粒度的服务发现机制,这是我们为 Dubbo 适配云原生基础 ...

  3. MWeb 1.3.7 发布!增加发布到 Wordpress 等支持 MetaWeblog API 的服务,如:Wordpress 博客、新浪博客、cnblogs、oschina。

    MWeb 1.3.7 版的新功能 增加发布到 Wordpress 等支持 Metaweblog API 的服务,目前经测试过的有: Wordpress 博客.新浪博客.cnblogs.oschina. ...

  4. AndroidInject项目使用动态代理增加对网络请求的支持

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/3540427.html AndroidInject项目是我写的一 ...

  5. 在内核中增加对yaffs文件系统的支持

    自己最近在搞一些内核以及根文件系统的移植,就涉及到了需要在内核中增加对yaffs文件系统的支持.在网上找了一些文档后,自己将具体的操作过程做了一个总结,方便以后查询使用: 1.获取yaffs源码 YA ...

  6. dubbo/dubbox部署资料收集

    dubbo/dubbox部署资料收集 最近由于项目需要要部署bubbox,dubbo,在找资料过程中用的的一些网址如下,后来由于取消没有实际应用,以备今后再用 http://dubbo.io/Admi ...

  7. NGINX 加载动态模块(NGINX 1.9.11开始增加加载动态模块支持)

    NGINX 1.9.11开始增加加载动态模块支持,从此不再需要替换nginx文件即可增加第三方扩展.目前官方只有几个模块支持动态加载,第三方模块需要升级支持才可编译成模块. tinywan@tinyw ...

  8. dubbox 增加google-gprc/protobuf支持

    好久没写东西了,今年实在太忙,基本都在搞业务开发,晚上来补一篇,作为今年的收官博客.google-rpc 正式发布以来,受到了不少人的关注,这么知名的rpc框架,不集成到dubbox中有点说不过去. ...

  9. 分布式服务框架 dubbo/dubbox 入门示例

    dubbo是一个分布式的服务架构,可直接用于生产环境作为SOA服务框架. 官网首页:http://dubbo.io/ ,官方用户指南 http://dubbo.io/User+Guide-zh.htm ...

随机推荐

  1. 【分布式】Zookeeper使用--Java API

    一.前言 上一篇博客我们通过命令行来操作Zookeper的客户端和服务端并进行相应的操作,这篇主要介绍如何通过API(JAVA)来操作Zookeeper. 二.开发环境配置 首先打开Zookeeper ...

  2. Chrome DevTools – 键盘和UI快捷键参考

    Chrome DevTools有几个内置的快捷键,可以节省你的日常工作的时间. 本指南提供了Chrome DevTools中每个快捷键的快速参考.虽然一些快捷方式在全局范围内可用,但其他的快捷方式用于 ...

  3. Linux资源管理-IO优先级

    前一篇博客介绍了利用 cgroup 来控制进程的 CPU和内存使用情况, 这次补上使用 cgroup 来控制进程的IO优先级的方法. 前提条件 如果想控制进程的IO优先级, 需要内核的支持, 内核编译 ...

  4. LINQ to SQL语句(10)之Insert

    1.简单形式 说明:new一个对象,使用InsertOnSubmit方法将其加入到对应的集合中,使用SubmitChanges()提交到数据库. var newCustomer = new Custo ...

  5. 调试关于Hibernate的程序遇到的问题

    最怕的就是初学一些东西,低级的错误犯了又犯,现在总结出来以便以后不要再犯类似的错误. 一.Hibernate的延迟加载机制 在用hibernate底层访问数据库的过程忽略了延迟加载机制导致 在访问时候 ...

  6. Django Admin 录入中文错误解决办法

    如果报错....for column 'object_repr' at row 1.就找到此列所在表为django_admin_log,然后插入: ALTER TABLE django_admin_l ...

  7. canvas 制作flappy bird(像素小鸟)全流程

    flappy bird制作全流程: 一.前言 像素小鸟这个简单的游戏于2014年在网络上爆红,游戏上线一段时间内appleStore上的下载量一度达到5000万次,风靡一时, 近年来移动web的普及为 ...

  8. 函数的使用顺序---TABLES,USING,CHANGING

    SAP使用PERFORM的时候: ... [TABLES   itab1 itab2 ...]     [USING    a1 a2 ...]     [CHANGING a1 a2 ...]. E ...

  9. [Android]使用自定义JUnit Rules、annotations和Resources进行单元测试(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5795091.html 使用自定义JUnit Rules.ann ...

  10. Android 从零开始打造异步处理框架

    转载请标明出处:http://www.cnblogs.com/zhaoyanjun/p/5995752.html 本文出自[赵彦军的博客] 概述 在Android中会使用异步任务来处理耗时操作,避免出 ...