dubbo学习笔记

2020-05-21  本文已影响0人  7d972d5e05e8

一、消费者启动--到底干了些啥

  1. 消费者启动过程,其实只是给消费者初始化invokers。纯粹只是把消费者所关心服务的提供者列表,给初始化好。连分组信息其实也是消费者在本地去匹配的,因为消费者会把服务所有的提供者全部初始化成invoker列表。
  2. 在消费者初始化invoker的时候,会提前建立到提供者ip的connection。这里为啥这么早就创建连接呢,假如连接建立了,这个服务其实业务根本就没去调用过,岂不是浪费。dubbo为啥不用懒加载呢?

我猜测:1. 如果是懒加载,那么在服务调用的时候有可能连接建立不上,这个时候影响可能比较大,违背尽量让错误提前暴露的原则。2. 提前建立连接,其实并不浪费。tcp连接毕竟不像mysql数据连接池的连接那么宝贵,提供者就算多一个tcp连接在那维持着,用了netty这种NIO,其实是消耗不了多少资源的。3. 提前建立连接,不至于让第一次调用很慢,期望到达所有请求都很稳定。

  1. 提供者的invokers列表有了,具体调用哪个invoker是由服务调用正在发起的时候确定。启动时,消费者只要提供服务列表出来,其他就不管了。
  2. 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看接口实现总是搞错。这两个方法类和方法都太像了。。。

上一篇 下一篇

猜你喜欢

热点阅读