dubbo/dubbox 增加原生thrift及avro支持

http://www.cnblogs.com/yjmyzz/archive/2016/03/06/dubbo-pritimive-thrift-avro-support.html


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

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

Thrift2Protocal.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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的文件,内容为:

1
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节也要增加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<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种协议,做了下简单的对比测试,测试用例很简单:

1
2
3
public  String ping() {
       return  "pong" ;
   }

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

1
2
3
4
5
6
7
8
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.


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值