Thrift (Facebook)、Avro (Hadoop)、Protobuf/gRPC (Google) 是高效序列化/RPC 三大主流選擇。Dubbo 內建 Thrift 支援但鎖定 0.8.0 版本且協議非原生;社區擴展代碼冗長。本文展示單類實現原生協議接入,並給出實測效能數據。
Dubbo 協議擴展點為 com.alibaba.dubbo.rpc.Protocol,AbstractProxyProxyProtocol 已封裝 Proxy/Invoker 轉換,僅需實現兩個模板方法:
doExport(T impl, Class<T> type, URL url):Provider 啟動時啟動原生 Server。doRefer(Class<T> type, URL url):Consumer 參考服務時建立原生 Client。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.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;
public class Thrift2Protocol extends AbstractProxyProtocol {
public static final int DEFAULT_PORT = 33208;
private static final Logger logger = LoggerFactory.getLogger(Thrift2Protocol.class);
@Override public int getDefaultPort() { return DEFAULT_PORT; }
@Override
protected <T> Runnable doExport(T impl, Class<T> type, URL url) throws RpcException {
String typeName = type.getName();
if (!typeName.endsWith("$Iface")) throw new RpcException("Thrift service must be $Iface interface");
String processorClass = typeName.substring(0, typeName.indexOf("$Iface")) + "$Processor";
try {
Class<?> clazz = Class.forName(processorClass);
Constructor<?> ctor = clazz.getConstructor(type);
TProcessor processor = (TProcessor) ctor.newInstance(impl);
TNonblockingServerSocket socket = new TNonblockingServerSocket(url.getPort());
TNonblockingServer.Args args = new TNonblockingServer.Args(socket)
.processor(processor)
.transportFactory(new TFramedTransport.Factory())
.protocolFactory(new TCompactProtocol.Factory());
TServer server = new TNonblockingServer(args);
new Thread(server::serve, "Thrift-Server-" + url.getPort()).start();
return () -> { try { server.stop(); } catch (Throwable ignored) {} };
} catch (Exception e) {
throw new RpcException("Failed to start Thrift server", e);
}
}
@Override
protected <T> T doRefer(Class<T> type, URL url) throws RpcException {
String typeName = type.getName();
if (!typeName.endsWith("$Iface")) throw new RpcException("Thrift service must be $Iface interface");
String clientClass = typeName.substring(0, typeName.indexOf("$Iface")) + "$Client";
try {
Class<?> clazz = Class.forName(clientClass);
Constructor<?> ctor = clazz.getConstructor(org.apache.thrift.protocol.TProtocol.class);
TSocket socket = new TSocket(url.getHost(), url.getPort());
TTransport transport = new TFramedTransport(socket);
org.apache.thrift.protocol.TProtocol protocol = new TCompactProtocol(transport);
transport.open();
@SuppressWarnings("unchecked") T client = (T) ctor.newInstance(protocol);
logger.info("Thrift client connected to {}", url);
return client;
} catch (Exception e) {
throw new RpcException("Failed to create Thrift client", e);
}
}
}
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;
public class AvroProtocol extends AbstractProxyProtocol {
public static final int DEFAULT_PORT = 40881;
private static final Logger logger = LoggerFactory.getLogger(AvroProtocol.class);
@Override public int getDefaultPort() { return DEFAULT_PORT; }
@Override
protected <T> Runnable doExport(T impl, Class<T> type, URL url) throws RpcException {
Server server = new NettyServer(
new ReflectResponder(type, impl),
new InetSocketAddress(url.getHost(), url.getPort())
);
server.start();
return () -> { try { server.close(); } catch (Throwable ignored) {} };
}
@Override
protected <T> T doRefer(Class<T> type, URL url) throws RpcException {
try {
NettyTransceiver client = new NettyTransceiver(
new InetSocketAddress(url.getHost(), url.getPort())
);
@SuppressWarnings("unchecked") T proxy = (T) ReflectRequestor.getClient(type, client);
logger.info("Avro client connected to {}", url);
return proxy;
} catch (Exception e) {
throw new RpcException("Failed to create Avro client", e);
}
}
}
META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol 新增兩行:thrift2=com.alibaba.dubbo.rpc.protocol.thrift2.Thrift2Protocol
avro=com.alibaba.dubbo.rpc.protocol.avro.AvroProtocol
dubbo-rpc/pom.xml 加入模組:<module>dubbo-rpc-thrift2</module>
<module>dubbo-rpc-avro</module>
dubbo/pom.xml 依賴聚合(排除傳遞依賴避免衝突):<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>
ping() -> "pong")| 協議 | 總耗時 (ms) | QPS | 特點 |
|---|---|---|---|
| Dubbo (預設) | 14,778 | 3,383 | Hessian2 序列化、Netty 傳輸 |
| Avro | 10,707 | 4,670 | Netty 傳輸、二進制編碼更緊湊 |
| Thrift (TCompactProtocol + FramedTransport) | 4,667 | 10,714 | 全鏈路 Facebook 自研、零拷貝、極致效能 |
| REST (HTTP/JSON) | 112,699 | 444 | 文本協議、解析開銷大、適合異構/防火牆穿透 |
<dubbo:protocol name="thrift2" port="33208"/> <dubbo:protocol name="rest" port="8080"/>。完整實現已提交 GitHub:yjmyzz/dubbox (版本 2.8.4a),歡迎 Star 與 PR。