文章目录
- 前言
- 一、服务暴露流程简述:
- 二、暴露源码分析,从ServiceConfig的export开始
- 三、injvm暴露过程:
- 1、我们先看一下proxFactory自适应类的配置
- 2、在看javassistProxyFactory如何生成ref的代理类
- 3、生成的wrapper类如下:
- 4、此时的invoker对象层次是AbstractProxyInvoker(wrapper(ref)),通过字节码生成的invoker对象,负责ref实例具体方法的调用
- 5、扩展,此时我们可以看看jdk是如果代理我们的ref实例的:
- 6、我们再看protocol.export(proxyFactory.getInvoker(this.ref, this.interfaceClass, local));此时protocol也是Protocol接口的自适应对象;
- 7、包装完毕后到injvmProtocol进行暴露
- 四、进行远程暴露:回到ServiceConfig的doExportUrlsFor1Protocol方法远程暴露逻辑
- 五、此时经过doLoclExport方法在DubboProtocol中的export服务成功;得到了DubboExport(invoker)对象 并 开启web监听
- 六、得到的exporter的封装整体情况如下:
- 后言
前言
服务暴露源码全流程分析
一、服务暴露流程简述:
1、生成接口实例代理Invoker调用对象(invoker代理ref的调用)
2、包装invoker对象,比如:exportListenr(暴露成功后回调监听者)、filter(进行方法调用的时候各种过滤器)
3、进行本地暴露(生成一个new InjvmExporter(invoker)对象)
4、远程暴露(生成一个new DubboExporter(invoker),并开启web服务,监听端口)
5、注册服务和订阅服务(把provider url注册到zk,同时监听zk状态重连重注册、同时订阅configurators,重置export)
二、暴露源码分析,从ServiceConfig的export开始
三、injvm暴露过程:
1、我们先看一下proxFactory自适应类的配置
2、在看javassistProxyFactory如何生成ref的代理类
3、生成的wrapper类如下:
实现了父类Wrapper的所有方法;被包装的类的方法调用,由wrapper进行转发调用;没有什么其它逻辑;就是额外获取了被包装的类属性
public class com.alibaba.dubbo.common.bytecode.Wrapper0 implements com.alibaba.dubbo.common.bytecode.Wrapper{
public static String[] pns;//被包装类字段名
public static java.util.Map pts;//被包装类 <字段名,字段类型>
public static String[] mns;//被包装类的 所有方法名集合
public static String[] dmns;//被包装类的 直系方法名集合
public static Class[] mts; //被包装类的 方法参数类型
com.alibaba.dubbo.common.bytecode.Wrapper0(){}
public Object invokeMethod(Object instance, String methodName, Class[] paramTypes, Object[] paramValues) throws java.lang.reflect.InvocationTargetException {
kd.bos.service.DispatchService w;
try {
w = ((kd.bos.service.DispatchService) $1);
} catch (Throwable e) {
throw new IllegalArgumentException(e);
}
try {
if ("invoke".equals($2) && $3.length == 4) {
return ($w) w.invoke((java.lang.String) $4[0], (java.lang.String) $4[1], (java.lang.String) $4[2], (java.lang.Object[]) $4[3]);
}
} catch (Throwable e) {
throw new java.lang.reflect.InvocationTargetException(e);
}
throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + $2 + "\" in class kd.bos.service.DispatchService.");
}
public String[] getPropertyNames() {
return pns;
}public boolean hasProperty(String n) {
return pts.containsKey($1);
}
public Class getPropertyType(String n) {
return (Class) pts.get($1);
}
public String[] getMethodNames() {
return mns;
}
public String[] getDeclaredMethodNames() {
return dmns;
}
public void setPropertyValue(Object o, String n, Object v) {
kd.bos.service.DispatchService w;
try {
w = ((kd.bos.service.DispatchService) $1);
} catch (Throwable e) {
throw new IllegalArgumentException(e);
}
throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + $2 + "\" filed or setter method in class kd.bos.service.DispatchService.");
}
public Object getPropertyValue(Object o, String n) {
kd.bos.service.DispatchService w;
try {
w = ((kd.bos.service.DispatchService) $1);
} catch (Throwable e) {
throw new IllegalArgumentException(e);
}
throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + $2 + "\" filed or setter method in class kd.bos.service.DispatchService.");
}
}
4、此时的invoker对象层次是AbstractProxyInvoker(wrapper(ref)),通过字节码生成的invoker对象,负责ref实例具体方法的调用
5、扩展,此时我们可以看看jdk是如果代理我们的ref实例的:
6、我们再看protocol.export(proxyFactory.getInvoker(this.ref, this.interfaceClass, local));此时protocol也是Protocol接口的自适应对象;
public class ProtocolListenerWrapper implements Protocol {
……
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
return (Exporter)("registry".equals(invoker.getUrl().getProtocol()) ? this.protocol.export(invoker) : new ListenerExporterWrapper(this.protocol.export(invoker), Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class).getActivateExtension(invoker.getUrl(), "exporter.listener"))));
}//通过dubbo spi获取ExporterListener接口的实现类;
//假设registry即注册中心的暴露则不包装直接调用实现类继续暴露;是rpc的暴露则包装到ListenerExporterWrapper,(作用暴露成功通知)
……
}
public class ProtocolFilterWrapper implements Protocol {
……
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
return "registry".equals(invoker.getUrl().getProtocol()) ? this.protocol.export(invoker) : this.protocol.export(buildInvokerChain(invoker, "service.filter", "provider"));
}
//假设假设registry即注册中心的暴露则不包装直接调用实现类继续暴露; 是rpc的暴露则把invoker包装一层filters(作用服务调用先进行filter)
……
}
7、包装完毕后到injvmProtocol进行暴露
四、进行远程暴露:回到ServiceConfig的doExportUrlsFor1Protocol方法远程暴露逻辑
Invoker<?> invoker = proxyFactory.getInvoker(this.ref, this.interfaceClass, registryURL.addParameterAndEncoded("export", url.toFullString()));
Exporter<?> exporter = protocol.export(invoker);
1、 此处代理ref的invoker对象和上述调用一致
不过protocol.export中invoker父类中的url是registryURL:registry://172.18.1.204:2182/com.alibaba.dubbo.registry.RegistryService?application=mservice-tlx&dubbo=2.8.4&export=dubbo……
所以export暴露的时候,经过wrapper类的判断"registry".equals(invoker.getUrl().getProtocol()) ? this.protocol.export(invoker) 会直接走到RegistryProtocol进行暴露
2、根据协议类型进行远程暴露
2、根据协议类型进行远程暴露
3、同理rpc协议暴露,会经过exportListener和filter 包装;最后调用到rpc协议实现类去;默认是dubbo协议;所以此时到DubboProtocol进行export
4、我们先分析下requestHandler如何处理消息的
5、再看如何创建netty server的创建
五、此时经过doLoclExport方法在DubboProtocol中的export服务成功;得到了DubboExport(invoker)对象 并 开启web监听
final RegistryProtocol.ExporterChangeableWrapper<T> exporter = this.doLocalExport(originInvoker);
final Registry registry = this.getRegistry(originInvoker);
final URL registedProviderUrl = this.getRegistedProviderUrl(originInvoker);
registry.register(registedProviderUrl);
final URL overrideSubscribeUrl = this.getSubscribedOverrideUrl(registedProviderUrl);
final RegistryProtocol.OverrideListener overrideSubscribeListener = new RegistryProtocol.OverrideListener(overrideSubscribeUrl);
this.overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
return new Export(){……}
我们再回到RegistryProtocol的export的后续方法
1、得到注册对象的流程
看看ZookeeperRegistry的父类做了什么:
2、得到注册对象后,registry.register(registedProviderUrl) 注册providerUrl
3、注册provider后,会注册configurator节点并订阅其孩子 registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
4、此时已经远程暴露成功得到exporter对象,也启动web监听;也注册provider了,也订阅了configurators; 我们再看看订阅configurators后,收到变化后,如何重置exporter的
可以看看isMatch如何判断被监听到的url是否匹配
在看看配置url如何合并原始provider的
现在配置url重写原始providerUrl得到了新的providerUrl;再看如何重置exporter的
configurator url: 示例:
override://10.20.153.10/com.foo.BarService?category=configurators&dynamic=false&disbaled=true
禁用此ip的barservice的提供者
override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null
服务降级
到此服务暴露大体流程;远程暴露、服务注册、订阅和通知动作,都已经讲完 ;
六、得到的exporter的封装整体情况如下:
时序图如下:
后言
暴露的结果就是,监听消费者调用,缓存Exporter对象,用来调用接口实现类;