Apache Dubbo做为一款高性能的Java RPC框架,在国内服务化体系的演进过程当中扮演了一个很是重要的角色,被大量公司普遍使用。面试
临近年关,或许有小伙伴有着寻找新机会的想法,那么在面试过程当中极可能会经常见到这样一个问题:apache
你了解Dubbo吗 ? 能不能讲一讲它的调用流程。bash
对于不了解的盆友而言,无疑会下降印象分;若是仅仅会使用,其实也不太够,最起码咱们要了解它的基本原理。网络
本文试图从Dubbo使用者的角度上,结合流程图和关键代码把相应知识点串联起来,回答咱们上面的问题。app
从程序开发者的角度来看,咱们要先有服务提供者。一般,咱们在具体接口的实现上标注Dubbo的Service
注解。负载均衡
package com.viewscenes.producer.dubbo;
import org.apache.dubbo.config.annotation.Service;
import com.viewscenes.common.service.DubboUserService;
@Service
public class DubboUserServiceImpl implements DubboUserService {
}
复制代码
这个实现类在Dubbo中对应的解析类为ServiceBean
,它负责将这个实现对外暴露成一个服务。过程以下:框架
结合上图来看,咱们能够说在提供者端,暴露一个服务的过程以下:post
首先,ServiceConfig
类引用对外提供服务的实现类ref (如DubboUserServiceImpl
) , 而后经过ProxyFactoty
接口的扩展实现类的getInvoker()
方法使用ref生成一个AbstractProxyInvoker
实例,到此就完成了具体服务到Invoker
的转化。性能
接下来,经过Dubbo协议的export()
方法,将Invoker
转化为Exporter
。那么在这里,就会先启动Netty Server
的监听,而后将服务注册到服务注册中心。ui
在这里,咱们必需要注意的是,做为服务提供者端,已经经过Netty开启了TCP端口的监听。那么,当消费者调用的时候,经过一系列Netty Handler处理器,就会调用到DubboProtocol > ExchangeHandler.reply()
。
在这个方法里,就是一个反推的过程。经过要调用的服务接口名称,找到Exporter
,而后再获取到Invoker
对象。
Invoker<?> getInvoker(Channel channel, Invocation inv) throws RemotingException {
int port = channel.getLocalAddress().getPort();
String path = inv.getAttachments().get(PATH_KEY);
String serviceKey = serviceKey(port, path, inv.getAttachments().get(VERSION_KEY), inv.getAttachments().get(GROUP_KEY));
DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey);
return exporter.getInvoker();
}
复制代码
从上面的分析中咱们已经知道,这里的Invoker
对象是根据服务实现类生成的一个AbstractProxyInvoker
实例。它最终会调用到wrapper.invokeMethod()
方法。这里的wrapper
类是经过Javassist
生成的,在内存中的类,它的核心方法长这样:
Dubbo
会给每一个服务提供者的实现生成一个Wrapper
类。当接收到消费方的请求后,根据传递的方法名和参数,Wrapper
类调用服务提供者的接口类实现便可。这样作的目的主要是为了减小反射的调用。
在服务消费者端,咱们直接引用一个接口便可。
@Reference
DubboUserService userService;
复制代码
或许你可能要问,为啥只注入了这么一个普通的接口,就能够调用到远端的服务呢 ?
咱们想一想在 Mybatis中的Dao接口和XML文件里的SQL是如何创建关系的? 这个问题中,它们是怎么关联起来的呢 ?
说穿了仍是Spring
的功劳,或者说是Spring FactoryBean
的功劳。
在Dubbo
中,标注了@Reference
的接口,都会被当成一个Factory Bean
,这个Bean通常都会返回一个代理对象,来屏蔽底层一些复杂的操做。好比Mybatis
里的mapper接口和xml文件关联,Dubbo
中的网络通讯等。
咱们仍是先来经过一张图看看消费者端的具体过程:
结合上图来看,咱们总结下服务引用的过程:
Reference
注解标注的Dubbo
接口,会被注册成FactoryBean
,并最终返回一个代理对象。
在建立代理的过程当中,会调用其余方法构建以及合并 Invoker
实例。
首先,调用DubboProtocol
的refer方法,返回DubboInvoker
对象。在这里,比较重要的是获取客户端实例。好比NettyClient
,Dubbo
要依靠它来进行网络通讯。
而后,还须要将多个服务提供者实例合并成一个,这是集群容错机制的实现。
最后,经过JavassistProxyFactory
建立代理并返回。在这里,它的处理器是InvokerInvocationHandler
,这就意味着,当咱们在消费者端调用一个Dubbo
接口的时候,实际上会调用到InvokerInvocationHandler.invoke()
方法,在这里面Dubbo
完成了譬如集群容错、负载均衡、调用远程方法的一系列动做。
Dubbo消费者发送请求的时候,最终会调用到DubboInvoker
中的方法。在这里,会完成具体的请求逻辑,好比发送请求数据。
final class HeaderExchangeChannel implements ExchangeChannel {
//建立请求消息对象
Request req = new Request();
req.setVersion(Version.getProtocolVersion());
req.setTwoWay(true);
req.setData(request);
//建立Future,用于获取返回结果
DefaultFuture future = DefaultFuture.newFuture(this.channel, req, timeout, executor);
try {
//经过Netty客户端发送数据
this.channel.send(req);
return future;
} catch (RemotingException var7) {
future.cancel();
throw var7;
}
}
复制代码