在阅读此文章之前,我希望阅读者对Spring 扩展机制的有一定的了解,比如:自定义标签与Spring整合, InitializingBean 接口,ApplicationContextAware,BeanNameAware,

BeanFactory 接口所起到的作用 ;从来没了解过的,请看我之前的关于Spring的博客

关于此篇文章受益于 :http://dubbo.apache.org/zh-cn/docs/source_code_guide/export-service.html   dubbo官网文档

开始正题

(一)onApplicationEvent (事件监听)

dubbo 服务导出的方法是在 com.alibaba.dubbo.config.spring.ServiceBean 类中,dubbo 服务的导出过程始于在Spring 容器发生刷新事件,那么如何感知到Spring 容器发生刷新

事件呢? ~~ 得益于Spring提供的 ApplicationListener 接口,看如下代码实现:

public void onApplicationEvent(ApplicationEvent event) {
    //在Spring bean 刷新后进行服务的导出;
if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
if (isDelay() && ! isExported() && ! isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}
}

实现该接口需要实现 onApplicationEvent 方法;事件的监听,在有event 事件发生后,Spring会自动进行触发此方法;

在Spring 容器发生刷新事件后进行导出 export();   这一步就是最先开始的地方;

(二)检查参数,组装 URL

public synchronized void export() {
if (provider != null) {
//是否进行导出的操作,用于在我们配置了<dubbo:provider export="false" />的时候,本地进行操作,不进行服务暴露的时候
if (export == null) {
export = provider.getExport();
}
if (delay == null) {
delay = provider.getDelay();
}
}
if (export != null && ! export.booleanValue()) {
return;
}
//延时导出,线程睡眠,在高版本中,此方法改变成了schedule
if (delay != null && delay > 0) {
Thread thread = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(delay);
} catch (Throwable e) {
}
doExport();
}
});
thread.setDaemon(true);
thread.setName("DelayExportServiceThread");
thread.start();
} else {
doExport();
}
}

以上的方法就是进行了<dubbo:provider> 标签属性 export 与delay 的操作判断;

我们继续进行  doExport();

doExport() 又进行了一些的判断与初始化的动作,没什么好说的,接下来就是调用 doExportUrls();

private void doExportUrls() {
List<URL> registryURLs = loadRegistries(true);
for (ProtocolConfig protocolConfig : protocols) {
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
loadRegistries() 会将一些必要信息进行排序封装,生成url地址链接如下格式:

registry://47.95.**.138:2181/com.alibaba.dubbo.registry.RegistryService?application=hm-service-cart&check=false&dubbo=2.5.3&pid=931&registry=zookeeper&timestamp=1545835371053
有多少协议,就导出多少次:

doExportUrlsFor1Protocol(protocolConfig, registryURLs);这个方法我们在第4步分析;

我们先看看Dubbo spi 机制
  

 (三) Dubbo SPI (服务发现机制)

DUBBO SPI 的实现主要依赖于。 com.alibaba.dubbo.common.extension.ExtensionLoader 这个类,这个类是dubbo 运行的容器,维护着一些组件的实例,就像Spring 容器一样;

SPI 服务发现机制;我给大家揭开它的面纱:

在源码阅读中,我们只要发现了像如下的代码,内部就进行了spi 的机制

ExtensionLoader.getExtensionLoader(XXX.class).getExtension(type);
ExtensionLoader.getExtensionLoader(XXX.class).getAdaptiveExtension();

xxx.class 是接口名字,必须带有@SPI的注解

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
//不为空检测
if (type == null)
throw new IllegalArgumentException("Extension type == null");
//必须的接口
if(!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
//接口必须有@SPI 的注解
if(!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
//实例化
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}

具体如下

1.(大家可以去看一看dubbo jar 包下 META-INF/dubbo/internal 目录的文件):以接口作为文件名称,内部是ke y = 实现类

 private static final String SERVICES_DIRECTORY = "META-INF/services/";

 private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";

 private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";

2.在 getExtension  方法执行中,主要看  createExtension(name)方法;

public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
return getDefaultExtension();
}
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
createExtension(name)方法(ioc + aop )
private T createExtension(String name) {
      //这个方法会从文件系统中获取name 所对应的value ,大家可以看看这个方法
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
        //ioc 操作
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
       //aop 包装的操作,包装类你调用我我调用你,形成了一条执行链;wrapperClasses 是包装类,在loadFile() 方法中被装配
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}

获取所有的扩展类:

    private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
 // 此方法已经getExtensionClasses方法同步过。
private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if(defaultAnnotation != null) {
String value = defaultAnnotation.value();
if(value != null && (value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if(names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if(names.length == 1) cachedDefaultName = names[0];
}
} Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
    //从META-INF/services/ META-INF/dubbo/ /META-INF/dubbo/internal/ 这三个文件夹中获取class配置
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
  //dubbo 扩展检索的文件

    private static final String SERVICES_DIRECTORY = "META-INF/services/";

    private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";

    private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";

这样对应着key 就可以实例化value,找到实现类了; 大家可以做一做小的demo进行一下测试:

/**
接口
* dubbo spi 机制,需要引用dubbo的spi 注解标签
*/
@SPI
public interface DubboSPI { public void sayHello();
} //实现类
public class DuboSPIImpl implements DubboSPI { public void sayHello(){
System.out.println("dubbo spi 机制运行......cys");
}; } //测试类
public static void main(String[] args) {
ExtensionLoader<DubboSPI> extensionLoader =
ExtensionLoader.getExtensionLoader(DubboSPI.class);
DubboSPI optimusPrime = extensionLoader.getExtension("DubboSPI");
optimusPrime.sayHello(); }

在META-INF/dubbo 下配置创建 com.baseknow.spi.dubbospi.DubboSPI (你的接口名字)文件

DubboSPI = com.baseknow.spi.dubbospi.DuboSPIImpl

关于一些常规的操作就到这里;

高级特性:(动态生成字节码文件,加载进入内存)

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

 如上:dubbo 通过 getAdaptiveExtension()进行扩展适配;通过javassist 将动态创建的类加载进内存;可以这么说,如上的protocol  proxyFactory 这两个对象是不存在的

通过dubbo 扩展 SPI Adapter 机制,将会自动生成实现该接口的一个类(这个类是自动生成的,不存在我们的文件中),通过javassiste 进行记载,然后通过创建的Adapter 类进行方法

的调度;

具体请看 ExtensionLoader 类如下两个方法(源码生成➕编译):

private Class<?> createAdaptiveExtensionClass() {
//生成源码code ,建议debug ,看一看生成的源码是什么样子的
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
//默认会进行javassist 将源码加载toclass()
return compiler.compile(code, classLoader);
} //源码生成的方法
private String createAdaptiveExtensionClassCode() {
StringBuilder codeBuidler = new StringBuilder();
Method[] methods = type.getMethods();
boolean hasAdaptiveAnnotation = false;
for(Method m : methods) {
if(m.isAnnotationPresent(Adaptive.class)) {
hasAdaptiveAnnotation = true;
break;
}
}
//........ 

如下是dubbo   通过 getAdaptiveExtension() 通过一系列判断 stringbbuilder 拼接而生成的 实现了Protocol接口的 适配器源码,根据传来的url来处理不同协议的调度

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
public void destroy() {throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
} public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
} public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {
if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
}

这是实现了ProxyFactory  接口的 适配器源码

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adpative implements com.alibaba.dubbo.rpc.ProxyFactory {
public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("proxy", "javassist");
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getProxy(arg0);
}
public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2) throws java.lang.Object {
if (arg2 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg2;
String extName = url.getParameter("proxy", "javassist");
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getInvoker(arg0, arg1, arg2);
}
}

以上两个类就是生成的源码code;

通过Javassist 进行将类进行加载成字节码 com.alibaba.dubbo.common.compiler.support.JavassistCompiler

总的来说就二步:

1.通过Dubbo 扩展机制 ExtensionLoader 生成一个接口的自定义实现类源码;
2.通过javassist 将生成的源码转换为字节码加载进入内存

关于Protocol 与 ProxyFactory 这两个动态的实现类,内部又是进行了spi机制的一些方法调用;大家看一看,因为下面的一些扩展都是基于这两个的,不然很难知道它进行调用了哪个实现类;当然还有 Transporter 接口实现等等也是该机制;

如下是Transport Adapter 源码:可以看出在没有指定传输的时候,默认使用的是netty

package com.alibaba.dubbo.remoting;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Transporter$Adpative implements com.alibaba.dubbo.remoting.Transporter {
public com.alibaba.dubbo.remoting.Client connect(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.common.URL {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("client", url.getParameter("transporter", "netty"));
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([client, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.connect(arg0, arg1);
}
public com.alibaba.dubbo.remoting.Server bind(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.common.URL {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("server", url.getParameter("transporter", "netty"));
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([server, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.bind(arg0, arg1);
}
}

关于高级特性就到这里,具体的还是要看getAdaptiveExtension() 方法实现,里面是进行了 StringBuilder  append 拼接源码生成新的类,再进行JAVAssist toClass() 操作,需要对

JAVAssist 了解,不了解,大约知道生成的新类长什么样子就好了;

 (四) Netty(mina) 服务的启动,端口绑定

紧接着第二步的步伐,我们分析 doExportUrlsFor1Protocol();我们抛开一些小细节,具体看如下Mark 红色的部分

 //如果配置不是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);
proxyFactory 与 protocol 都是动态生成类,大家可以看第四部分我贴出来的动态生成的源码,就可以知道真正的实现类是哪一个(默认是javassist 的实现类);

protocol 则是进行了aop 式的包装类,会将DubboProtocol 类 ProtocolFilterWrapper 类, ProtocolListenerWrapper类,RegistryProtocol 类的export()方法执行一边,为什么都会执行呢,请大家看 ExtensionLoader 的 createExtension 方法;如下:
private T createExtension(String name) {
      //这个方法会从文件系统中获取name 所对应的value ,大家可以看看这个方法
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
        //ioc 操作
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
       //aop 包装的操作,包装类你调用我我调用你,形成了一条执行链;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}

在调用 DubboProtocol 的export() 方法时候,会进行 openServer(url)操作;

 openServer(url);
private void openServer(URL url) {
// find server.
String key = url.getAddress();
//client 也可以暴露一个只有server可以调用的服务。
boolean isServer = url.getParameter(Constants.IS_SERVER_KEY,true);
if (isServer) {
ExchangeServer server = serverMap.get(key);
if (server == null) {
serverMap.put(key, createServer(url));
} else {
//server支持reset,配合override功能使用
server.reset(url);
}
}
}

检查是否开启过服务,没有的话,createServer(url);创建服务,这里大家就往下面点,会经过如下的过程:

会使用dubbo SPI 机制返回Transporter 传输对象,在我们不在xml 做任何配置的情况下,默认使用的是netty,我们可以看Javassist 生成的Transporter Adapter 源码:

package com.alibaba.dubbo.remoting;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Transporter$Adpative implements com.alibaba.dubbo.remoting.Transporter {
public com.alibaba.dubbo.remoting.Client connect(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.common.URL {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("client", url.getParameter("transporter", "netty"));
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([client, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.connect(arg0, arg1);
}
public com.alibaba.dubbo.remoting.Server bind(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.common.URL {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("server", url.getParameter("transporter", "netty"));
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([server, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.bind(arg0, arg1);
}
}

如果使用mina ,我们可以在xml 做如下配置:

    <!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20881" transporter="mina" />

在实例化的时候会进行doOpen() 操作了,就是正常的netty api 流程了;

public NettyServer(URL url, ChannelHandler handler) throws RemotingException{
super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
} @Override
protected void doOpen() throws Throwable {
NettyHelper.setNettyLoggerFactory();
ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
bootstrap = new ServerBootstrap(channelFactory); final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
channels = nettyHandler.getChannels();
// https://issues.jboss.org/browse/NETTY-365
// https://issues.jboss.org/browse/NETTY-379
// final Timer timer = new HashedWheelTimer(new NamedThreadFactory("NettyIdleTimer", true));
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec() ,getUrl(), NettyServer.this);
ChannelPipeline pipeline = Channels.pipeline();
/*int idleTimeout = getIdleTimeout();
if (idleTimeout > 10000) {
pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0));
}*/
pipeline.addLast("decoder", adapter.getDecoder());
pipeline.addLast("encoder", adapter.getEncoder());
pipeline.addLast("handler", nettyHandler);
return pipeline;
}
});
// bind
channel = bootstrap.bind(getBindAddress());
}
通过分析,netty 服务创建流程就是spi 的各种调用,所以大家了解dubbo 就必须了解它的spi 机制;
这样,一个netty 服务就启动了.. 

 (五) 注册中心注册服务

注册中心我们以zookeeper 为例,也是Dubbo 推荐使用的注册中心以及生产部署中常用的;

注册中心的实现是 protocol 执行链中的RegistryProtocol 类中的export 方法

实现主要是CURATOR 框架进行节点的创建以及监听

我们看节点的创建过程:

 递归创建节点,其中url地址是临时的节点,客户端断开会消失,其他的像/dubbo/接口地址 是永久的节点:如下图

RPC -dubbo 服务导出实现的更多相关文章

  1. Dubbo 服务导出-Version2.7.5

    1.源码分析 1.1分析服务导出入口 当容器为spring是dubbo会为容器注册两个监听器:DubboLifecycleComponentApplicationListener和DubboBoots ...

  2. dubbo源码阅读之服务导出

    dubbo服务导出 常见的使用dubbo的方式就是通过spring配置文件进行配置.例如下面这样 <?xml version="1.0" encoding="UTF ...

  3. Dubbo源码(三) - 服务导出(生产者)

    前言 本文基于Dubbo2.6.x版本,中文注释版源码已上传github:xiaoguyu/dubbo 在了解了Dubbo SPI后,我们来了解下Dubbo服务导出的过程. Dubbo的配置是通过Du ...

  4. Dubbo 源码分析 - 服务导出

    1.服务导出过程 本篇文章,我们来研究一下 Dubbo 导出服务的过程.Dubbo 服务导出过程始于 Spring 容器发布刷新事件,Dubbo 在接收到事件后,会立即执行服务导出逻辑.整个逻辑大致可 ...

  5. Dubbo源码阅读-服务导出

    Dubbo服务导出过程始于Spring容器发布刷新事件,Dubbo在接收到事件后,会立即执行服务导出逻辑.整个逻辑大致可分为三个部分,第一部分是前置工作,主要用于检查参数,组装URL.第二部分是导出服 ...

  6. Dubbo源码学习之-服务导出

    前言 忙的时候,会埋怨学习的时间太少,缺少个人的空间,于是会争分夺秒的工作.学习.而一旦繁忙的时候过去,有时间了之后,整个人又会不自觉的陷入一种懒散的状态中,时间也显得不那么重要了,随便就可以浪费掉几 ...

  7. 源码分析--dubbo服务端暴露

    服务暴露的入口方法是 ServiceBean 的 onApplicationEvent.onApplicationEvent 是一个事件响应方法,该方法会在收到 Spring 上下文刷新事件后执行服务 ...

  8. dubbo服务提供与消费

    一.前言 项目中用到了Dubbo,临时抱大腿,学习了dubbo的简单实用方法.现在就来总结一下dubbo如何提供服务,如何消费服务,并做了一个简单的demo作为参考. 二.Dubbo是什么 Dubbo ...

  9. 当当网开源Dubbox,扩展Dubbo服务框架支持REST风格远程调用

    当当网近日开源了Dubbox项目,可为Dubbo服务框架提供多项扩展功能,包括REST风格远程调用.Kryo/FST序列化等等. 当当网架构部和技术委员会架构师沈理向InfoQ中文站介绍了Dubbox ...

随机推荐

  1. day3-三级目录

    date = {"guangdong":{"guangzhou":{"tianhe":["tyx","zjxc ...

  2. win nginx + php bat启动/停止脚本

    启动脚本 @echo offREM Windows 下无效REM set PHP_FCGI_CHILDREN=5 REM 每个进程处理的最大请求数,或设置为 Windows 环境变量set PHP_F ...

  3. 对map集合按照value从大到小进行排序

    概述: 基本特点: 该集合存储键值对,而且要保证键的惟一性 子类: |--HashTable 底层是哈希数据表结构,不可以使用Null作为键或者值:该集合线程是同步的 |--hashMap   底层是 ...

  4. Centos7上安装mariadb

    一.查看系统是否安装了mariadb,有的话就删除掉. 1.rpm -qa | grep mariadb 2.rpm -e --nodeps mariadb-libs-5.5.56-2.el7.x86 ...

  5. 如何解决fiddler的响应显示乱码问题

    fiddler中Response出现乱码, 这是因为HTML被压缩了, 我们可以通过两种方法去解压.方法1:点击Response Raw上方的"Response is encoded any ...

  6. 加载配置文件-properties

    Properties jdbcProp = new Properties(); jdbcProp.load(getClass().getResourceAsStream("/jdbc.pro ...

  7. 注意source folder与folder是不同的,避免404错误

    在整合ssm框架的时候,程序和配置文件都没写错,tomcat也部署成功了,但在访问的时候一直404,web项目自带的index.jap却能正常访问,一直找不到原因,后来发现建立放配置文件的文件夹con ...

  8. MAIL服务器搭建

    一,邮件服务: 优    点 缺   点 应 用 sendmail 有点年代久远,稳定功能多 太过于臃肿,配置文件多且繁琐 6以前默认 postfix 优点更稳定,且交轻便 发布年限较短,市场占有率低 ...

  9. jquery调用iframe里面的方法

    $(window.parent.document).contents().find("#iframename")[0].contentWindow.iframefunction() ...

  10. 11Linux_sshd_Apache

    1.Linux系统中一切都是文件. 2.配置服务实际上就是在修改配置文件. 3.重启相应服务来加载最新的配置参数. 4.为了保证下次还生效,加入到开机启动项中.