暴露服务的过程中,会涉及到两个Protocol
1. DubboProtocol主要是做网络通信相关初始化
2. RegistryProtocol主要是做zk的注册和订阅相关
在提供一个服务的时候,需要在配置文件里声明如下xml
<dubbo:service....
然后Spring会根据对应关系执行对应的BeanDefinitionParser,然后实例化对应的类,提供一个服务的时候会实例化ServiceBean(具体对应关系看DubboNamespaceHandler类;spring解析自定义标签可以看下spring源码关于标签的处理,这里就不说了)
ServiceBean实现了InitializingBean和ApplicationContextAware接口,所以会执行afterPropertiesSet和onApplicationEvent方法,这里就是入口,然后就会执行export方法暴露服务
一路跟下去,都是设置一下属性值,然后到了doExportUrls方法便开始主要的逻辑
private void doExportUrls() {
List<URL> registryURLs = loadRegistries(true);//获取注册中心的url
for (ProtocolConfig protocolConfig : protocols) {
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
在Zookeeper为注册中心的情况下,registryURLs值如下
[registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=test&dubbo=2.0.0&owner=william&pid=4444®istry=zookeeper×tamp=1488886235790]
进入doExportUrlsFor1Protocol方法,看下暴露服务的主要逻辑
if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
//配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
exportLocal(url);
}
//如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露远程服务)
if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
if (registryURLs != null && registryURLs.size() > 0
&& url.getParameter("register", true)) {
for (URL registryURL : registryURLs) {
url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
URL monitorUrl = loadMonitor(registryURL);
if (monitorUrl != null) {
url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
}
if (logger.isInfoEnabled()) {
logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
}
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
Exporter<?> exporter = protocol.export(invoker);
exporters.add(exporter);
}
} else {
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
Exporter<?> exporter = protocol.export(invoker);
exporters.add(exporter);
}
}
}
1.本地暴露
会先使用exportLocal暴露本地服务
private void exportLocal(URL url) {
if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
URL local = URL.valueOf(url.toFullString())
.setProtocol(Constants.LOCAL_PROTOCOL)
.setHost(NetUtils.LOCALHOST)
.setPort(0);
Exporter<?> exporter = protocol.export(
proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
exporters.add(exporter);
logger.info("Export dubbo service " + interfaceClass.getName() +" to local registry");
}
}
看了Dubbo的扩展机制会知道,ProxyFactory默认会使用接口上@Spi注解声明的服务,为了容易理解,我把注解上的@SPI设置成jdk,那么就会使用jdk对应的实现类,即JdkProxyFactory
JdkProxyFactory的getInvoker方法如下:
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
Method method = proxy.getClass().getMethod(methodName, parameterTypes);
return method.invoke(proxy, arguments);
}
};
}
返回一个AbstractProxyInvoker类的对象,这个AbstractProxyInvoker主要是接收消费方的请求后,执行本地方法的一个Invoker,其中是使用了反射机制来调用了本地方法
获取到Invoker之后,需要使用Protocol的export来暴露这个服务,在讲Dubbo扩展机制的时候,Protocol外面有两个装饰类,那么export会先调用ProtocolListenerWrapper
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
return new ListenerExporterWrapper<T>(protocol.export(invoker),
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
.getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
}
这时的url为
injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=test&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&loadbalance=roundrobin&methods=sayHello&owner=william&pid=4444&side=provider×tamp=1488887324869
对象为
那么if条件不满足,将调用ProtocolFilterWrapper的export方法
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url);
}
return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
}
If条件还是不满足,执行下面的代码
注意:这里的protocol就是InjvmProtocol了
然后看下buildInvokerChain方法,这个方法建立了一个个的filter,使用了责任链模式,一个普通的Invoker调用也会经历这些filter,每个filter都有自己特殊的功能
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker;
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (filters.size() > 0) {
for (int i = filters.size() - 1; i >= 0; i --) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
public Class<T> getInterface() {
return invoker.getInterface();
}
public URL getUrl() {
return invoker.getUrl();
}