1.Transport层实现解析
根据Dubbo框架分层,Transporter与Serializase层主要用于真正的网络通信传输与通信内容的序列化,主要是现在remoting/transport包中,经过对Exchanger层的实现讲解,可以知道网络通信都可以抽象为Server/Client/Channel/ChannelHandler几个主要概念,在transport中使用各种Abstract抽象类针对不同的通信框架的封装,通信框架如netty/grizzly/mina等。其中传输的接口设计如下所示,这是一个扩展点,默认实现为NettyTransporter类。在Exchange层次初始化时会使用Transporters类,这里返回NettyClient和NettyServer对象。
public interface Transporter {
@Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException;
@Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
Client connect(URL url, ChannelHandler handler) throws RemotingException;
}
public class Transporters {
...
public static RemotingServer bind(String url, ChannelHandler... handler) {
return bind(URL.valueOf(url), handler);
}
public static Client connect(URL url, ChannelHandler... handlers) {
return getTransporter().connect(url, handler);
}
public static Transporter getTransporter() {
//加载Transporter的扩展实现类
return ExtensionLoader.getExtensionLoader(Transporter.class)
.getAdaptiveExtension();
}
...
}
public class NettyTransporter implements Transporter {
@Override
public RemotingServer bind(URL url, ChannelHandler handler)
throws RemotingException {
//返回Netty实现的服务器实例
return new NettyServer(url,g handler);
}
@Override
public Client connect(URL url, ChannelHandler handler)
throws RemotingException {
//返回Netty实现的客户端实例
return new NettyClient(url, handler);
}
}
1.1 服务器与客户端设计解析
服务端和客户端都采用了模版设计方法,提供了AbstractServer,AbstractClient,分别指定了服务端和客户端实现的一个基本框架,具体实类逻辑如下所示,底层通过Channel进行实际网络通信。创建服务端对象实例,具体实现类实现抽象方法doOpen,完成实际服务端实例创建。
public abstract class AbstractServer extends AbstractEndpoint
implements RemotingServer {
...
public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
...
try {
doOpen();
...
} catch (Throwable t) {
...
}
executor = executorRepository.createExecutorIfAbsent(url);
}
//抽象方法,留给继承的子类实现
protected abstract void doOpen() throws Throwable;
}
服务端接收连接,调用实现类中的getChannels方法判断是否超过了最大可接收连接数,然后在调用AbstractPeer#connected来处理Channel创建/断开逻辑。
public abstract class AbstractServer extends AbstractEndpoint
implements RemotingServer {
@Override
public void connected(Channel ch) throws RemotingException {
...
Collection<Channel> channels = getChannels();
if (accepts > 0 && channels.size() > accepts) {
...
ch.close();
return;
}
super.connected(ch);
}
}
public abstract class AbstractPeer implements Endpoint, ChannelHandler {
@Override
public void connected(Channel ch) throws RemotingException
//这里是构造好的处理器实例
handler.connected(ch);
}
}
在客户端实现中,可以查看AbstractClient的相关方法。这里在初始化时通过connectLock重入锁实现线程并发控制,抽象方法doConnect由具体实现类实现特定的连接逻辑。
public abstract class AbstractClient extends AbstractEndpoint implements Client {
public AbstractClient(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
...
try {
//调用抽象方法
doOpen();
} catch (Throwable t) {
close();
}
try {
connect();
} catch (RemotingException t) {
...
} catch (Throwable t) {
...
}
}
protected void connect() throws RemotingException {
//上锁,避免线程同步问题
connectLock.lock();
try {
if (isConnected()) {
return;
}
doConnect();
} catch (RemotingException e) {
throw e;
} catch (Throwable e) {
...
} finally {
//释放锁
connectLock.unlock();
}
}
protected abstract void doConnect() throws Throwable;
}
客户端在发送信息时,回先判断客户端是否已连接上服务端,如果没有则进行连接,如果有则调用子类具体的send方法发送信息。
public abstract class AbstractClient extends AbstractEndpoint implements Client {
@Override
public void send(Object message, boolean sent) throws RemotingException {
//判断是否需要连接
if (needReconnect && !isConnected()) {
connect();
}
//通过子类返回实现的信道
Channel channel = getChannel();
//发送信息
channel.send(message, sent);
}
protected abstract Channel getChannel();
}
以Netty实现的服务端与客户端为例,在netty信道初始化时会将编解码器和处理类设置上去,这样每条信道的处理都会经过这些编解码和处理类去处理。
public class NettyClient extends AbstractClient {
public NettyClient(final URL url, final ChannelHandler handler)
throws RemotingException {
//进行处理链初始化
super(url, wrapChannelHandler(url, handler));
}
@Override
protected void doOpen() throws Throwable {
final NettyClientHandler nettyClientHandler = new NettyClientHandler(getUrl(), this);
...
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//初始化编解码器
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
//初始化pipeline,设置编解码器和处理类
ch.pipeline()
.addLast("logging",new LoggingHandler(LogLevel.INFO))
.addLast("decoder", adapter.getDecoder())
.addLast("encoder", adapter.getEncoder())
.addLast("handler", nettyClientHandler);
}
});
...
}
}
public class NettyServer extends AbstractServer implements RemotingServer {
...
@Override
protected void doOpen() throws Throwable {
bootstrap.group(bossGroup, workerGroup)
.channel(NettyEventLoopFactory.serverSocketChannelClass())
.option(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
.childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
....
//初始化pipeline,设置编解码器和处理类
ch.pipeline()
.addLast("decoder", adapter.getDecoder())
.addLast("encoder", adapter.getEncoder())
.addLast("server-idle-handler",
new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
.addLast("handler", nettyServerHandler);
}
});
}
}
1.2 信道处理实现解析
在网络通信中,双方建立连接后,在Dubbo框架中抽象为ChannelHandler,来处理通信信道的创建/断开/消息发送/消息接收/信道异常等情况,Dubbo中提供了大量的Handler去承载特性和扩展,这些Handler最终会和底层通信框架做关联,如netty等。一次完整的RPC调用贯穿了一系列的Handler,如果直接挂载到底层框架中,会造成处理链路过程,并会触发大量链式查找和事件,不仅低效,而且浪费资源。
@SPI
public interface ChannelHandler {
void connected(Channel channel) throws RemotingException;
void disconnected(Channel channel) throws RemotingException;
void sent(Channel channel, Object message) throws RemotingException;
void received(Channel channel, Object message) throws RemotingException;
void caught(Channel channel, Throwable exception) throws RemotingException;
}
一些常用处理类如下所示。
- ExchangeHandlerAdapter: 用于查找微服务方法调用;
- HeaderExchangeHandler:用于封装处理Request和Response
- DecodeHandler: 支持在Dubbo线程池中做解码
- ChannelHandlerDisptcher: 封装多Handler广播调用
- AllChannelHandler: 支持Dubbo线程池调用业务方法
- HeartbeatHandler: 支持心跳处理
- MultiMessageHandler: 支持流中多消息报文批处理
- ConnnectionOrderedChannelHandler: 单独线程池处理TCP的连接和断开
- MessageOnlyChannelHandler: 仅在线程池处理接收报文,其他事件在I/O线程处理
- WrappedChannelHandler: 基于内存key-value存储封装和共享线程池能力,比如记录线程池等
- NettyServerHandler: 封装Netty服务端事件,处理连接、断开、读取、写入和异常等
- NettyClientHandler: 封装Netty客户端事件,处理连接、断开、读取、写入和异常等
HeaderExchangeHandler处理类, HeaderExchangeHandler的下一个处理节点是ExchangeHandlerAdapter处理类。通过代理类的包装后,就可以以责任链处理的方式将这些处理器连接起来。
public interface ChannelHandlerDelegate extends ChannelHandler {
ChannelHandler getHandler();
}
//Handler代理抽象类,可设置责任链下一个节点
public abstract class AbstractChannelHandlerDelegate
implements ChannelHandlerDelegate {
protected ChannelHandler handler;
protected AbstractChannelHandlerDelegate(ChannelHandler handler) {
this.handler = handler;
}
@Override
public ChannelHandler getHandler() {
if (handler instanceof ChannelHandlerDelegate) {
return ((ChannelHandlerDelegate) handler).getHandler();
}
return handler;
}
}
最终效果如下所示,在ChannelPipeline里面将这个处理链路设置上去,在通信内容入站和出战的时候都经过这些处理类,进行依次处理。
public class ChannelHandlers {
public static ChannelHandler wrap(ChannelHandler handler, URL url) {
return ChannelHandlers.getInstance().wrapInternal(handler, url);
}
protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {
return new MultiMessageHandler(new HeartbeatHandler
(ExtensionLoader.getExtensionLoader(Dispatcher.class)
.getAdaptiveExtension().dispatch(handler, url)));
}
}
Dispatcher是线程池分发器,可以理解为拥有线程池分发能力的ChannelHandler,比如AllChannelHandler、 MessageOnlyChannelHandler和ExcutionChannelHandler等,其本身并不具备线程派发能力。Dispatcher属于Dubbo的扩展点,用来生成动态的Handler,以满足不同的使用场景,其中Dubbo支持以下几种分发策略。
@SPI(AllDispatcher.NAME)
public interface Dispatcher {
@Adaptive({Constants.DISPATCHER_KEY, "dispather", "channel.handler"})
ChannelHandler dispatch(ChannelHandler handler, URL url);
}
- AllDispatcher: 将所有I/O事件交给Dubbo线程池处理,Dubbo默认启用;
- Connection: 单独线程池处理连接断开事件,和Dubbo线程池分开;
- DirectDispatcher: 所有方法调用和事件处理都在I/O线程中;
- MessageOnlyChannelHandler: 只在线程池处理请求和响应事件,其他都在I/O线程池中;
- MockDispatcher: 默认返回.
与线程池分发紧密相关的是线程池的实现,Dubbo框架中线程池ThreadPool也是一个SPI扩展点,有FixedThreadPool/LimitedThreadPool等多种具体实现,可以根据URL的相关参数决定使用具体的线程池实现。在一个应用中可以有多个线程池的,这里使用了DefaultExecutorRepository存储,在AbstractServer/AbstractClient中会调用DefaultExecutorRepository#createExecutorIfAbsent来创建线程池。
@SPI("fixed")
public interface ThreadPool {
//根据URL上的threadpool参数决定
@Adaptive({THREADPOOL_KEY})
Executor getExecutor(URL url);
}
public class DefaultExecutorRepository implements ExecutorRepository {
public synchronized ExecutorService createExecutorIfAbsent(URL url) {
String componentKey = EXECUTOR_SERVICE_COMPONENT_KEY;
if (CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY))) {
componentKey = CONSUMER_SIDE;
}
ExecutorService executor =
executors.computeIfAbsent(portKey, k -> createExecutor(url));
return executor;
}
//根据URL创建线程池
private ExecutorService createExecutor(URL url) {
return (ExecutorService) ExtensionLoader
.getExtensionLoader(ThreadPool.class).getAdaptiveExtension()
.getExecutor(url);
}
}
public abstract class AbstractServer extends
AbstractEndpoint implements RemotingServer {
public AbstractServer(URL url, ChannelHandler handler)
throws RemotingException {
...
//初始化服务端线程池
executor = executorRepository.createExecutorIfAbsent(url);
}
Dubbo框架提供了几种不同线程池实现:
- FixedThreadPool:创建一个复用固定个数线程的线程池;
- LimitedThreadPool:创建一个线程池,这个线程池中线程个数随着需要量动态增加,但是数量不超过配置的阈值的个数,另外空闲线程不会被回收,会一直存在;
- EagerThreadPool :创建一个线程池,这个线程池当所有核心线程都处于忙碌状态时候,创建新的线程来执行新任务,而不是把任务放入线程池阻塞队列;
- CachedThreadPool:创建一个自适应线程池,当线程处于空闲1分钟时候,线程会被回收,当有新请求到来时候会创建新线程。
2. 协议解析和编解码实现原理
2.1 Dubbo协议解析
高效精巧的通信协议设计直接决定了框架通信的性能、稳定性与扩展性等。Dubbo协议设计参考了现有的TCP/IP协议,主要可分为协议头和协议体两部分。
- 报文头: 长度为16字节,其中包括了魔数(0xdabb)用来处理分割黏包处理,当前请求报文是Request、Response、心跳和事件的信息,当前报文体内的序列化协议编号,请求状态,请求唯一标识ID,报文体长度信息。
- 协议体: 包括dubbo版本、服务名、服务版本、方法名、方法参数类型、方法参数等内容。
2.2 编码实现解析
Codec2中已经定义了I/O的编解码过程,主要方法是encode和decode定义如下所示。
@SPI
public interface Codec2 {
@Adaptive({Constants.CODEC_KEY})
void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException;
@Adaptive({Constants.CODEC_KEY})
Object decode(Channel channel, ChannelBuffer buffer) throws IOException;
}
其中主要的实现类有以下几个,其中AbstractCodec主要提供基础能力,比如校验报文长度和查找具体编解码等,DubboCodec继承ExchangeCodec,而ExchangeCodec又继承了TelnetCodec实现。
Dubbo的RPC请求构造中,编码器主要对Java对象编码编程字节流返回到处于上层的客户端,包括了构造报文的头部,然后对消息体进行序列化。所有的报文头的处理的实现都在ExchangeCodec#encode中,包括了请求和回复两种编码处理。
public class ExchangeCodec extends TelnetCodec {
@Override
public void encode(Channel channel, ChannelBuffer buffer, Obj ect msg) throws IOException {
if (msg instanceof Request) {
encodeRequest(channel, buffer, (Request) msg);
} else if (msg instanceof Response) {
encodeResponse(channel, buffer, (Response) msg);
} else {
super.encode(channel, buffer, msg);
}
}
protected void encodeRequest(Channel channel,
ChannelBuffer buffer, Request req) throws IOException {
//获取指定或默认的序列化协议
Serialization serialization = getSerialization(channel);
//构造16字节头
byte[] header = new byte[HEADER_LENGTH];
//占用两个字节的魔法数,用来处理半包黏包
Bytes.short2bytes(MAGIC, header);
//在第三个字节
header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());
....
//设置请求唯一标识
Bytes.long2bytes(req.getId(), header, 4);
int savedWriteIndex = buffer.writerIndex();
//跳过报文头的16个字节
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);
ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
//进行序列化
ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
if (req.isEvent()) {
encodeEventData(channel, out, req.getData());
} else {
//交给子类重写方法去实现
encodeRequestData(channel, out, req.getData(), req.getVersion());
}
...
int len = bos.writtenBytes();
//检查是否超过默认8MB大小
checkPayload(channel, len);
//将消息长度写入头部的第12个字节的位置
Bytes.int2bytes(len, header, 12);
buffer.writerIndex(savedWriteIndex);
//写入完整的报文头
buffer.writeBytes(header);
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len);
}
protected void encodeRequestData(Channel channel, ObjectOutput out, Object data, String version) throws IOException {
//定义了继承方法,子类可重写该方法,通过DubboCodec重写实现
encodeRequestData(out, data);
}
}
public class DubboCodec extends ExchangeCodec {
protected void encodeRequestData(Channel channel, ObjectOutput out,
Object data, String version) throws IOException {
//每一次远程调用都封装成RpcInvocation,能拿到
RpcInvocation inv = (RpcInvocation) data;
//写入框架版本
out.writeUTF(version);
String serviceName = inv.getAttachment(INTERFACE_KEY);
if (serviceName == null) {
serviceName = inv.getAttachment(PATH_KEY);
}
//写入接口服务名
out.writeUTF(serviceName);
//写入服务版本
out.writeUTF(inv.getAttachment(VERSION_KEY));
//写入调用方法名
out.writeUTF(inv.getMethodName());
//写入方法参数类型描述
out.writeUTF(inv.getParameterTypesDesc());
Object[] args = inv.getArguments();
if (args != null) {
//依次写入方法参数值
for (int i = 0; i < args.length; i++) {
out.writeObject(encodeInvocationArgument(channel, inv, i));
}
}
//写入隐式参数
out.writeAttachments(inv.getObjectAttachments());
}
在处理完编码请求后,可以看下响应的编码在ExchangeCodec#encodeResponse中实现,具体响应体编码在DubboCodec#encodeResponseData中实现。
protected void encodeResponse(Channel channel, ChannelBuffer buffer, Response res) throws IOException {
int savedWriteIndex = buffer.writerIndex();
try {
//获取指定或默认的序列化协议(默认为Hessian2)
Serialization serialization = getSerialization(channel);
//构造16字节报文头
byte[] header = new byte[HEADER_LENGTH];
//占用2个字节存储魔数
Bytes.short2bytes(MAGIC, header);
//在第三个的存储响应标志
byte status = res.getStatus();
header[3] = status;
//设置请求唯一标识
Bytes.long2bytes(res.getId(), header, 4);
//空出16字节头部用于存储响应报文
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);
ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
if (status == Response.OK) {
if (res.isHeartbeat()) {
encodeEventData(channel, out, res.getResult());
} else {
//交给子类覆盖方法实现
encodeResponseData(channel, out, res.getResult(), res.getVersion());
}
} else {
out.writeUTF(res.getErrorMessage());
}
...
int len = bos.writtenBytes();
//检查是否超出了80M大小限制
checkPayload(channel, len);
Bytes.int2bytes(len, header, 12);
buffer.writerIndex(savedWriteIndex);
buffer.writeBytes(header);
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len);
} catch (Throwable t) {
buffer.writerIndex(savedWriteIndex);
if (!res.isEvent() && res.getStatus() != Response.BAD_RESPONSE) {
Response r = new Response(res.getId(), res.getVersion());
r.setStatus(Response.BAD_RESPONSE);
if (t instanceof ExceedPayloadLimitException) {
try {
//这里告知是超出限制异常
r.setErrorMessage(t.getMessage());
channel.send(r);
return;
} catch (RemotingException e) {
}
} else {
...
}
}
....
}
}
public class DubboCodec extends ExchangeCodec {
...
protected void encodeResponseData(Channel channel, ObjectOutput out, Object data, String version) throws IOException {
Result result = (Result) data;
//判断客户端请求的版本是否支持服务端返回
boolean attach = Version.isSupportResponseAttachment(version);
Throwable th = result.getException();
if (th == null) {
//如果没有抛出异常则获取正常结果
Object ret = result.getValue();
if (ret == null) {
out.writeByte(attach ? RESPONSE_NULL_VALUE_WITH_ATTACHMENTS
: RESPONSE_NULL_VALUE);
} else {
out.writeByte(attach ? RESPONSE_VALUE_WITH_ATTACHMENTS
: RESPONSE_VALUE);
out.writeObject(ret);
}
} else {
//如果有抛出错误或异常,标记调用异常,序列化异常信息
out.writeByte(attach ? RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS
: RESPONSE_WITH_EXCEPTION);
out.writeThrowable(th);
}
}
...
}
跟编码类似,这里也分为两部分,首先分解报文的头部(16字节),然后解码报文体携带的内容 的,以及如何将报文体的内容转换成一个RpcInvocation对象,当服务器端进行解码时,会触发ExchangeCodec#decode方法。
public class ExchangeCodec extends TelnetCodec {
@Override
public Object decode(Channel channel, ChannelBuffer buffer) throws IOException {
int readable = buffer.readableBytes();
byte[] header = new byte[Math.min(readable, HEADER_LENGTH)];
//先读16字节的报文头
buffer.readBytes(header);
return decode(channel, buffer, readable, header);
}
@Override
protected Object decode(Channel channel, ChannelBuffer buffer, int readable, byte[] header) throws IOException {
//如果处理的字节流的起始处不是Dubbo魔数时
if (readable > 0 && header[0] != MAGIC_HIGH
|| readable > 1 && header[1] != MAGIC_LOW) {
int length = header.length;
//网络流中还有可读取的数据
if (header.length < readable) {
header = Bytes.copyOf(header, readable);
buffer.readBytes(header, length, readable - length);
}
for (int i = 1; i < header.length - 1; i++) {
if (header[i] == MAGIC_HIGH && header[i + 1] == MAGIC_LOW) {
buffer.readerIndex(buffer.readerIndex() - header.length + i);
header = Bytes.copyOf(header, i);
break;
}
}
return super.decode(channel, buffer, readable, header);
}
//网络流数据不够16个字节,则期待更多数据
if (readable < HEADER_LENGTH) {
return DecodeResult.NEED_MORE_INPUT;
}
//提取头部存储的报文长度,并校验是否超出限制
int len = Bytes.bytes2int(header, 12);
checkPayload(channel, len);
//如果不是完整的Dubbo报文,则等待更多的数据
int tt = len + HEADER_LENGTH;
if (readable < tt) {
return DecodeResult.NEED_MORE_INPUT;
}
ChannelBufferInputStream is = new ChannelBufferInputStream(buffer, len);
try {
//解码消息体,完整的RPC调用报文
return decodeBody(channel, is, header);
} finally {
//如果解码过程发生问题,则跳过这次RPC调用报文
if (is.available() > 0) {
try {
StreamUtils.skipUnusedStream(is);
} catch (IOException e) {
...
}
}
}
}
protected Object decodeBody(Channel channel, InputStream is, byte[] header)
throws IOException {
byte flag = header[2],
proto = (byte) (flag & SERIALIZATION_MASK);
long id = Bytes.bytes2long(header, 4);
//如果是响应回复
if ((flag & FLAG_REQUEST) == 0) {
Response res = new Response(id);
if ((flag & FLAG_EVENT) != 0) {
res.setEvent(true);
}
byte status = header[3];
res.setStatus(status);
try {
//进行反序列化
ObjectInput in = CodecSupport.deserialize(channel.getUrl(), is, proto);
if (status == Response.OK) {
Object data;
if (res.isHeartbeat()) {
data = decodeHeartbeatData(channel, in);
} else if (res.isEvent()) {
data = decodeEventData(channel, in);
} else {
data = decodeResponseData(channel, in, getRequestData(id));
}
res.setResult(data);
} else {
res.setErrorMessage(in.readUTF());
}
} catch (Throwable t) {
res.setStatus(Response.CLIENT_ERROR);
res.setErrorMessage(StringUtils.toString(t));
}
return res;
} else {
//如果是请求
Request req = new Request(id);
req.setVersion(Version.getProtocolVersion());
req.setTwoWay((flag & FLAG_TWOWAY) != 0);
if ((flag & FLAG_EVENT) != 0) {
req.setEvent(true);
}
try {
//反序列化
ObjectInput in = CodecSupport.deserialize(channel.getUrl(), is, proto);
Object data;
if (req.isHeartbeat()) {
data = decodeHeartbeatData(channel, in);
} else if (req.isEvent()) {
data = decodeEventData(channel, in);
} else {
data = decodeRequestData(channel, in);
}
req.setData(data);
} catch (Throwable t) {
req.setBroken(true);
req.setData(t);
}
return req;
}
}
}
3.总结
本篇主要讲解Dubbo网络通信层的实现原理,介绍了Client和Server的初始化流程,信道处理类的相关实现,线程分发策略以及线程池的实现,还讲解了Dubbo通信协议的设计以及编解码的实现原理。
参考文献
zhuanlan.zhihu.com/p/98562180 Dubbo协议详解
www.jianshu.com/p/abfa29c01… CompetableFutrue深度解