dubbo 之remotting (1)

本文详细解析了Dubbo框架中服务的发布与远程调用过程。包括服务器端通过Netty协议建立连接并发布服务,以及客户端如何发起远程调用,通过层层包装与解包找到真正的方法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

dubbo rpc篇中我们介绍了duboo在服务器段是如何将服务层层包装成invoker,通过协议工厂代理成exchanger发布消息出去的。我们又介绍了客户端是如何将接口动态封装成wrapper,通过协议类反向走代理链,找到真正的服务的。rpc文没有详细的将通信过程,本文将基于dubbo-remoting包具体讲解服务是怎么发布出去的。 #服务器端 上文中我们说到服务器端通过在类DubboProtocol中将Invoker转成Exporter,通过dubbo协议发布url到注册中心。接下来我从这里开始去发现服务器端并建立连接。 DubboProtocol里面的export方法将invoker转换成为了exporter对象。并调用 openServer(url);发布服务

serverMap.put(key, createServer(url));

#1、使用netty协议建立连接 ###1.1、在url里面放置netty连接需要的参数。 (channel.readonly.sent,heartbeat、server、codec) ###1.2、调用 Exchangers.bind(url, requestHandler)建立服务器实例 这个方法做了两件事情 首先:通过ExtendLoader找到url指定的协议对应的Transporter(默认是NettyTransporter)。 其次:调用Transporter的bind(URL url, ChannelHandler listener)方法新建一个对应协议连接,并将实例服务实例返回。

 public Server bind(URL url, ChannelHandler listener) throws RemotingException {
        return new NettyServer(url, listener);
    }
    
     public NettyServer(URL url, ChannelHandler handler) throws RemotingException{
        super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
    }

Exchangers.bind(url, requestHandler)的调用比较复杂。 首先是这个Exchangers类的bind方法首先要根据url配置路由到真正的实现类

 getExchanger(url).bind(url, handler);
 
  public static Exchanger getExchanger(URL url) {
        String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
        return getExchanger(type);
    }
       public static Exchanger getExchanger(String type) {
        return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
    }

最终找到了类 HeaderExchanger 调用该类的bind(url, handler)方法。

 public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
        return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
    }

该方法首先要包装Handler,将hander包装在编码处理器中。 然后,Transporters.bind方法。该方法也是有多步组成:

1、调用getTransporter获得Transporter适配器。

####2、调用适配器的bind方法时候会 感应url信息去找到真正的Transporter实现类(默认是netty)。 ####3、到真正的Transporter实现类中调用bind方法。

  public Server bind(URL url, ChannelHandler listener) throws RemotingException {
        return new NettyServer(url, listener);
    }
      public NettyServer(URL url, ChannelHandler handler) throws RemotingException{
        super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
    }

虽然表面上这里只是一个初始化的过程,其实不然,nettyServer在初始化之后通过调用doOpen()方法还建立了连接。 至此我们也修改下上文的错误,invoker并没有发布到远程,这里只是简单的进行了netty协议的链接。 接下来的内容就是netty包的内容。后文细讲。 至此,dubbo的远程连接就到这里了,我们来屡屡这个思路。首先从dubboProtocol那儿开始讲一个叫requestHandler的实例层层包装给了dubbo的handler。

#客户端 上文中我们说到客户端最后是调用DubboInvoker的doInvoker方法去发现真正的执行类。接下来从这里出发,发现客户端在远程调用的过程都做了什么。 首先会针对请求做一系列判断,是否是异步,是否是本地服务请求,会根据不同请求类型做不同处理。如果是远程请求,那么调用方法

currentClient.request(inv, timeout).get()

首先从clients里面获取ReferenceCountExchangeClient实例,然后调用该实例的request方法。

public ResponseFuture request(Object request, int timeout) throws RemotingException {
        return client.request(request, timeout);
    }

上面的client指的是 HeaderExchangeClient,她得request方法是要继续查找通道发送请求。

     public ResponseFuture request(Object request) throws RemotingException {
        return channel.request(request);
    }

找到了 HeaderExchangeChannel,它将invoker信息包装成了一个request,去找真正的通道发送。

      public ResponseFuture request(Object request, int timeout) throws RemotingException {
        if (closed) {
            throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
        }
        // 创建一个请求,将版本号,invoker信息包装成数据发送给远程。
        Request req = new Request();
        req.setVersion("2.0.0");
        req.setTwoWay(true);
        req.setData(request);
        DefaultFuture future = new DefaultFuture(channel, req, timeout);
        try{
            channel.send(req);
        }catch (RemotingException e) {
            future.cancel();
            throw e;
        }
        return future;
    }

最终找到了nettyClient,调用子类的send方法发送信息。 发送完信息,服务器端会通过netty接收到该请求。 首先、nettyHandler的方法获得该请求。 然后、调用HeaderExchangeHandler的messageReceived继续往后传递。 传递过程中汇调用requestHandler的两个逻辑。 一个是连接请求。和客户端建立连接。 一个是处理请求调用reply方法处理请求获得结果。该方法里面会调用invoke方法执行具体的逻辑

 public void connected(Channel channel) throws RemotingException {
            invoke(channel, Constants.ON_CONNECT_KEY);
        }
 public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
   return invoker.invoke(inv);
 }

找到代理类的doInvoke方法去执行


    public Result invoke(Invocation invocation) throws RpcException {
        try {
            return new RpcResult(doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()));
        } catch (InvocationTargetException e) {
            return new RpcResult(e.getTargetException());
        } catch (Throwable e) {
            throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

比如找到javasistProxyFactory则这样执行

   public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper类不能正确处理带$的类名
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName, 
                                      Class<?>[] parameterTypes, 
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

回到上文中我们交代了这个wrapper实例的强大,我们截取他getSize 方法就会发现这时候调用的就是实现类的getSize方法了,返回的结果就是实际的结果了。 最后让我们再来仔细看看请求处理器requestHandler 该匿名类主要包括几个方面的方法 1、接收到信息如何处理 如果是服务端,自然是要调用代理类找实现类。如果是客户端,那应该是应该讲结果返回给相应调用。 2、连接请求调用onconnect事务处理逻辑进行连接处理。 3、断开连接的请求调用ondisconnect处理逻辑继续断开连接处理。

 private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
        
        public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
            if (message instanceof Invocation) {
                Invocation inv = (Invocation) message;
                Invoker<?> invoker = getInvoker(channel, inv);
                //如果是callback 需要处理高版本调用低版本的问题
                if (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))){
                    String methodsStr = invoker.getUrl().getParameters().get("methods");
                    boolean hasMethod = false;
                    if (methodsStr == null || methodsStr.indexOf(",") == -1){
                        hasMethod = inv.getMethodName().equals(methodsStr);
                    } else {
                        String[] methods = methodsStr.split(",");
                        for (String method : methods){
                            if (inv.getMethodName().equals(method)){
                                hasMethod = true;
                                break;
                            }
                        }
                    }
                    if (!hasMethod){
                        logger.warn(new IllegalStateException("The methodName "+inv.getMethodName()+" not found in callback service interface ,invoke will be ignored. please update the api interface. url is:" + invoker.getUrl()) +" ,invocation is :"+inv );
                        return null;
                    }
                }
                RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
                return invoker.invoke(inv);
            }
            throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
        }

        @Override
        public void received(Channel channel, Object message) throws RemotingException {
            if (message instanceof Invocation) {
                reply((ExchangeChannel) channel, message);
            } else {
                super.received(channel, message);
            }
        }

        @Override
        public void connected(Channel channel) throws RemotingException {
            invoke(channel, Constants.ON_CONNECT_KEY);
        }

        @Override
        public void disconnected(Channel channel) throws RemotingException {
            if(logger.isInfoEnabled()){
                logger.info("disconected from "+ channel.getRemoteAddress() + ",url:" + channel.getUrl());
            }
            invoke(channel, Constants.ON_DISCONNECT_KEY);
        }
        
        private void invoke(Channel channel, String methodKey) {
            Invocation invocation = createInvocation(channel, channel.getUrl(), methodKey);
            if (invocation != null) {
                try {
                    received(channel, invocation);
                } catch (Throwable t) {
                    logger.warn("Failed to invoke event method " + invocation.getMethodName() + "(), cause: " + t.getMessage(), t);
                }
            }
        }

转载于:https://my.oschina.net/zjItLife/blog/735737

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值