(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. 原生JS封装Ajax插件(同域&&jsonp跨域)

    抛出一个问题,其实所谓的熟悉原生JS,怎样的程度才是熟悉呢? 最近都在做原生JS熟悉的练习... 用原生Js封装了一个Ajax插件,引入一般的项目,传传数据,感觉还是可行的...简单说说思路,如有不正 ...

  2. 【NLP】揭秘马尔可夫模型神秘面纱系列文章(二)

    马尔可夫模型与隐马尔可夫模型 作者:白宁超 2016年7月11日15:31:11 摘要:最早接触马尔可夫模型的定义源于吴军先生<数学之美>一书,起初觉得深奥难懂且无什么用场.直到学习自然语 ...

  3. 行为驱动开发(BDD)实践示例

    引言 BDD是对TDD理念的扩展.BDD强调有利害关系的技术团体和非技术团队都要参与到软件开发过程中.可以把它看成一种强调团体间合作的敏捷方法.大多数采用某种敏捷方法的团队最终都会遵循BDD的许多原则 ...

  4. 基于轻量型Web服务器Raspkate的RESTful API的实现

    在上一篇文章中,我们已经了解了Raspkate这一轻量型Web服务器,今天,我们再一起了解下如何基于Raspkate实现简单的RESTful API. 模块 首先让我们了解一下"模块&quo ...

  5. Coding4Fun Toolkit支持本地化解决办法

    在项目中需要使用Coding4Fun Toolkit中的TimePicker控件, 1. 但是在中文系统下显示的却是英文: 2. 最后发现,需要在源代码中添加中文资源,并重新编译出包含中文语言的dll ...

  6. C#多态“说来也说”——逻辑层BLL中的多态使用

    本文版权归博客园和作者吴双本人共同所有.欢迎转载,转载和爬虫请注明原文地址 http://www.cnblogs.com/tdws/p/5861842.html 昨天晚上,有个朋友说学了好久,依然没搞 ...

  7. asp.net MVC5 学习笔记(一)

    Html.ActionLink("linkText","actionName") 该重载的第一个参数是该链接要显示的文字,第二个参数是对应的控制器的方法,默认控 ...

  8. The method getJspApplicationContext(ServletContext) is undefined for the type JspFactory

    The method getJspApplicationContext(ServletContext) is undefined for the type JspFactory 这是由于项目里面的一些 ...

  9. 我的屌丝giser成长记-研二篇

    之前有提到过的,本来按照计划中,研一结束就该去深圳中科院研究所实习的,之前跟里面师兄说好了的,奈何导师又接到一个新的科研研究项目,跟学院的几个其他老师一起合作的,主要是关于土地流转系统,而且是一个挺大 ...

  10. 【代码笔记】iOS-旋转的风扇

    一,效果图. 二,工程图. 三,代码. AppDelegate.m #import "AppDelegate.h" //加入头文件 #import "RoundDiskV ...