一、消费者启动--到底干了些啥
- 消费者启动过程,其实只是给消费者初始化invokers。纯粹只是把消费者所关心服务的提供者列表,给初始化好。连分组信息其实也是消费者在本地去匹配的,因为消费者会把服务所有的提供者全部初始化成invoker列表。
- 在消费者初始化invoker的时候,会提前建立到提供者ip的connection。这里为啥这么早就创建连接呢,假如连接建立了,这个服务其实业务根本就没去调用过,岂不是浪费。dubbo为啥不用懒加载呢?
我猜测:1. 如果是懒加载,那么在服务调用的时候有可能连接建立不上,这个时候影响可能比较大,违背尽量让错误提前暴露的原则。2. 提前建立连接,其实并不浪费。tcp连接毕竟不像mysql数据连接池的连接那么宝贵,提供者就算多一个tcp连接在那维持着,用了netty这种NIO,其实是消耗不了多少资源的。3. 提前建立连接,不至于让第一次调用很慢,期望到达所有请求都很稳定。
- 提供者的invokers列表有了,具体调用哪个invoker是由服务调用正在发起的时候确定。启动时,消费者只要提供服务列表出来,其他就不管了。
- dubbo的服务治理提供了集群模块的抽象,作为中间层 消费者调用 -> 集群Invoker -> real provider Invoker,屏蔽服务治理的细节。比如:服务路由,调用失败恢复等等服务治理相关的功能。
服务治理好像除了服务路由和失败恢复,也没其他的功能了。
二、dubbo调用编码
编码方法:com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec#encodeRequest
protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException {
Serialization serialization = getSerialization(channel);
// header.
byte[] header = new byte[HEADER_LENGTH];
// set magic number.
Bytes.short2bytes(MAGIC, header);
// set request and serialization flag.
header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());
if (req.isTwoWay()) header[2] |= FLAG_TWOWAY;
if (req.isEvent()) header[2] |= FLAG_EVENT;
// set request id.
Bytes.long2bytes(req.getId(), header, 4);
// encode request data.
//先获取现在的buffer能够写数据的起始位置
int savedWriteIndex = buffer.writerIndex();
//重新当前buffer的起始位置,新位置=上面的位置 + 16字节。目的是为了先写body
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);
//把buffer流赋给bos。因为:buffer只接受二进制,而data是文本。需要序列化器进行序列化
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());
}
out.flushBuffer();
bos.flush();
bos.close();
//序列化器把所有data一次性全部写入到buffer中,然后得到写入的长度
int len = bos.writtenBytes();
checkPayload(channel, len);
//把长度写入到header数组中
Bytes.int2bytes(len, header, 12);
//buffer先写入数据,后写入header。这里把buffer的起始位置设置回去。起始位置还是刚开始的位置。这里很关键
buffer.writerIndex(savedWriteIndex);
//真正的把16字节的header写入到buffer正确位置上。
buffer.writeBytes(header); // write header.
//设置buffer真正的,可以接受新数据的起始位置。为下个请求用,因为buffer是通用的,只有写满后才会flush出去。
buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len);
}
三、AbstractInvoker 和 AbstractProxyInvoker的区别
AbstractInvoker全路径:com.alibaba.dubbo.rpc.protocol.AbstractInvoker
AbstractProxyInvoker全路径:com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker
AbstractInvoker是消费者Invoker的调用方法。AbstractProxyInvoker是提供者调用接口的实现。两个都是表示invoker实体,但是两个的作用完全不一样。每次遇到invoker.invoke()方法的时候,用idea看接口实现总是搞错。这两个方法类和方法都太像了。。。
网友评论