dubbo之网络通讯

前言

在《dubbo架构篇》中可知通信层主要有3部份组成Transporter,Exchanger和Protocol。Transporter负责netty的封装,Exchanger负责将异步同步化。Protocol 负责服务化,封装协议。

Transporter

结合《Netty概述》中介绍其API。dubbo对其进行了如下封装:

public class TransportTest {

    public static void main(String[] args) throws Exception {

        URL url = URL.valueOf("dubbo://127.0.0.1:28092/");

        RemotingServer server = Transporters.bind(url, new ChannelHandlerAdapter() {
            @Override
            public void received(Channel channel, Object message) throws RemotingException {
                System.out.println("accept msg:" + message);
                channel.send("ok");
            }

        });

        Client client =  Transporters.connect(url, new ChannelHandlerAdapter(){
            @Override
            public void received(Channel channel, Object message) throws RemotingException {
                System.out.println("client accept msg:" + message);
            }
        });

        client.send("yoyoyoy");
    }
}

通过源码了解到期主要类NettyServer

 protected void doOpen() throws Throwable {
        bootstrap = new ServerBootstrap();
        
        // 主线程
        bossGroup = NettyEventLoopFactory.eventLoopGroup(1, "NettyServerBoss");
        // io线程
        workerGroup = NettyEventLoopFactory.eventLoopGroup(
                getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
                "NettyServerWorker");
        
        // 代理handler,最终会指向
        // @see ChannelHandlers.wrapInternal 
        final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
        channels = nettyServerHandler.getChannels();

        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 {
                        int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
                        // 获取根据url参数及spi接口Codec2具体实现
                        // 解码器,编码器的封装类
                        NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
                        
                        ch.pipeline()
                                 // 解码器
                                .addLast("decoder", adapter.getDecoder())
                                // 编码器 
                                .addLast("encoder", adapter.getEncoder())
                                .addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
                                // 事件处理handler
                                .addLast("handler", nettyServerHandler);
                    }
                });
        // bind
        ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
        channelFuture.syncUninterruptibly();
        channel = channelFuture.channel();

    }

通过ChannelHandlers.wrapInternal的分析得handler链为

decoder
idle
NettyServerHandler
MultiMessageHandler
HeartbeatHandler
Dispatcher
业务Handler
Encoder

解析说明:

  1. decoder触码
  2. idle空闲监听
  3. NettyServerHandler转发
  4. MultiMessageHandler List处理
  5. HeartbeatHandler 心跳监听
  6. Dispatcher 通过spi进行任务分配
  7. 业务Handler 自己写的业务代码
  8. Encoder 编码发送

最终得Transport模型如下所示:
在这里插入图片描述

Exchanger

Exchanger在Transporter之上结合Jdk提供的CompletableFuture类,完成了异步转同步的过程。先看例子:

public class ExchangeTest {

    public static void main(String[] args) throws RemotingException, ExecutionException, InterruptedException {

        URL url = URL.valueOf("dubbo://127.0.0.1:28092/?timeout=12000");

        ExchangeServer server =  Exchangers.bind(url, new Replier(){

            @Override
            public Object reply(ExchangeChannel channel, Object request) throws RemotingException {
                System.out.println("accept msg:" + request);
                return "ok";
            }
        });

        ExchangeClient client = Exchangers.connect(url);

        CompletableFuture<Object> future = client.request("yyyyyy", Executors.newSingleThreadExecutor());

        System.out.println(future.get());

    }
}

Exchanger对上述的handler模主要做了2处改动

  1. 利用spi将解析器换成ExchangeCodec
    主要给原本的传输内容附加了header信息,来标识请求一些属性
    在这里插入图片描述
  2. 对业务Handler进行扩充成
DecodeHandler
HeaderExchangeHandler
业务Handler

解析说明:

  1. DecodeHandler 结合《dubbo之Serialization》加解码body内容
  2. HeaderExchangeHandler 同步异步处理
异步转同步过程

client.request

 public CompletableFuture<Object> request(Object request, int timeout, ExecutorService executor) throws RemotingException {
        
        // 创建Request包装类
        // 生成请求id,具体请求数据,请求版本等
        Request req = new Request();
        req.setVersion(Version.getProtocolVersion());
        req.setTwoWay(true);
        req.setData(request);
        // CompletableFuture的继承类
        // 提供 DefaultFuture getFuture(id)方法找到并设置结果
        DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout, executor);
        try {
            channel.send(req);
        } catch (RemotingException e) {
            future.cancel();
            throw e;
        }
        return future;
    }

HeaderExchangeHandler.received

// response对在Codec封装
 public static void received(Channel channel, Response response, boolean timeout) {
        try {
            // 找到请求时的future
            DefaultFuture future = FUTURES.remove(response.getId());
            if (future != null) {
                Timeout t = future.timeoutCheckTask;
                if (!timeout) {
                    // decrease Time
                    t.cancel();
                }
                // 设置结果
                future.doReceived(response);
            } else {
                // error
            }
        } finally {
           
            CHANNELS.remove(response.getId());
        }
    }

具体过程如下
在这里插入图片描述

Protocol

Protocol 负责服务化, 封装协议,弱化协议使用调用方和提供方无感知,以dubboProtocol为例子

public class ProtocolTest {
    public static void main(String[] args) throws Exception {
         
        // 数据元,描述服务的一次方法信息 
        ServiceRepository repository = ApplicationModel.getServiceRepository();
        ServiceDescriptor serviceDescriptor = repository.registerService(GreetingsService.class);
        MethodDescriptor  methodDescriptor = serviceDescriptor.getMethod("sayHi", new Class[]{ String.class});
        Method method = methodDescriptor.getMethod();
        String serviceName =  serviceDescriptor.getServiceName();

        URL url = URL.valueOf("dubbo://127.0.0.1:28092/"+serviceName+"?timeout=12000");

        Protocol protocol = new DubboProtocol();

        protocol.export(new Invoker<GreetingsService>() {
            @Override
            public URL getUrl() {
                return url;
            }

            @Override
            public boolean isAvailable() {
                return true;
            }

            @Override
            public void destroy() {}

            @Override
            public Class<GreetingsService> getInterface() {
                return GreetingsService.class;
            }

            @Override
            public Result invoke(Invocation invocation) throws RpcException {
                System.out.println("yoyoy");
                return AsyncRpcResult.newDefaultAsyncResult(invocation);
            }
        });

        Invoker invoker = protocol.refer(GreetingsService.class, url);
        invoker.invoke(new RpcInvocation(method, serviceName, new String[]{"yoyo"}));
        Thread.sleep(60000);
    }
}

其中export操作的调用过程为
在这里插入图片描述
refer操作过程为
在这里插入图片描述

以上2步骤将链路中的ExchangeCodec升级为DubboCodec
@see DubboProtocol.createServer
@see DubboProtocol.initClient

invoker.invoke(RpcInvocation)调用过程上

  1. AbstractInvoker 上下文中的一些信息放如RpcInvocation
  2. DubboInvoker url的一些信息放入RpcInvocation
  3. RpcInvocation通过DubboCodec加解码发送,接收
  4. 业务Invoker调用并返回

主要参考

源码分析Dubbo网络通讯篇概要总结
服务调用过程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值