dubbo Main启动服务浅析

Dubbo 采用全 Spring 配置方式,官方推荐使用内置 Main 启动,并提供了JDK 的 ShutdownHook 优雅停机。这里看的是dubbo 2.6.2版本的代码

 

贴出dubbo提供的Main启动类

  1 /*
  2  * Licensed to the Apache Software Foundation (ASF) under one or more
  3  * contributor license agreements.  See the NOTICE file distributed with
  4  * this work for additional information regarding copyright ownership.
  5  * The ASF licenses this file to You under the Apache License, Version 2.0
  6  * (the "License"); you may not use this file except in compliance with
  7  * the License.  You may obtain a copy of the License at
  8  *
  9  *     http://www.apache.org/licenses/LICENSE-2.0
 10  *
 11  * Unless required by applicable law or agreed to in writing, software
 12  * distributed under the License is distributed on an "AS IS" BASIS,
 13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  * See the License for the specific language governing permissions and
 15  * limitations under the License.
 16  */
 17 package com.alibaba.dubbo.container;
 18 
 19 import com.alibaba.dubbo.common.Constants;
 20 import com.alibaba.dubbo.common.extension.ExtensionLoader;
 21 import com.alibaba.dubbo.common.logger.Logger;
 22 import com.alibaba.dubbo.common.logger.LoggerFactory;
 23 import com.alibaba.dubbo.common.utils.ConfigUtils;
 24 
 25 import java.text.SimpleDateFormat;
 26 import java.util.ArrayList;
 27 import java.util.Arrays;
 28 import java.util.Date;
 29 import java.util.List;
 30 import java.util.concurrent.locks.Condition;
 31 import java.util.concurrent.locks.ReentrantLock;
 32 
 33 /**
 34  * Main. (API, Static, ThreadSafe)
 35  */
 36 public class Main {
 37 
 38     public static final String CONTAINER_KEY = "dubbo.container";
 39 
 40     public static final String SHUTDOWN_HOOK_KEY = "dubbo.shutdown.hook";
 41 
 42     private static final Logger logger = LoggerFactory.getLogger(Main.class);
 43 
 44     private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
 45 
 46     private static final ReentrantLock LOCK = new ReentrantLock();
 47 
 48     private static final Condition STOP = LOCK.newCondition();
 49 
 50     public static void main(String[] args) {
 51         try {
 52             if (args == null || args.length == 0) {
 53                 String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
 54                 args = Constants.COMMA_SPLIT_PATTERN.split(config);
 55             }
 56 
 57             final List<Container> containers = new ArrayList<Container>();
 58             for (int i = 0; i < args.length; i++) {
 59                 containers.add(loader.getExtension(args[i]));
 60             }
 61             logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce.");
 62 
 63             if ("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY))) {
 64                 Runtime.getRuntime().addShutdownHook(new Thread() {
 65                     @Override
 66                     public void run() {
 67                         for (Container container : containers) {
 68                             try {
 69                                 container.stop();
 70                                 logger.info("Dubbo " + container.getClass().getSimpleName() + " stopped!");
 71                             } catch (Throwable t) {
 72                                 logger.error(t.getMessage(), t);
 73                             }
 74                             try {
 75                                 LOCK.lock();
 76                                 STOP.signal();
 77                             } finally {
 78                                 LOCK.unlock();
 79                             }
 80                         }
 81                     }
 82                 });
 83             }
 84 
 85             for (Container container : containers) {
 86                 container.start();
 87                 logger.info("Dubbo " + container.getClass().getSimpleName() + " started!");
 88             }
 89             System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " Dubbo service server started!");
 90         } catch (RuntimeException e) {
 91             e.printStackTrace();
 92             logger.error(e.getMessage(), e);
 93             System.exit(1);
 94         }
 95         try {
 96             LOCK.lock();
 97             STOP.await();
 98         } catch (InterruptedException e) {
 99             logger.warn("Dubbo service server stopped, interrupted by other thread!", e);
100         } finally {
101             LOCK.unlock();
102         }
103     }
104 
105 }
View Code

分析main方法 main方法的参数传入的是要启动的容器类(Container),dubbo提供了三个实现,分别是SpringContainer、Log4jContainer、LogbackContainer,并在这里配置好

分析 

首先判断是否传入了启动容器,如果没有传入则从-D参数数或者dubbo.properties文件中获取key dubbo.container的值,如果都没获取到那么就默认使用默认容器启动,默认容器是哪个呢? 查看 getDefaultExtensionName 方法可以看到调用的loadExtensionClasses()方法中有获取默认容器的代码  如下 

 1     // synchronized in getExtensionClasses
 2     private Map<String, Class<?>> loadExtensionClasses() {
 3         final SPI defaultAnnotation = type.getAnnotation(SPI.class);
 4         if (defaultAnnotation != null) {
 5             String value = defaultAnnotation.value();
 6             if ((value = value.trim()).length() > 0) {
 7                 String[] names = NAME_SEPARATOR.split(value);
 8                 if (names.length > 1) {
 9                     throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
10                             + ": " + Arrays.toString(names));
11                 }
12                 if (names.length == 1) cachedDefaultName = names[0];
13             }
14         }
15 
16         Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
17         loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
18         loadDirectory(extensionClasses, DUBBO_DIRECTORY);
19         loadDirectory(extensionClasses, SERVICES_DIRECTORY);
20         return extensionClasses;
21     }
View Code

在这里获取了Container接口上的注解SPI的value,回去看Container接口 可以看到接口上有@SPI("spring")注解,value就是spring 所以这里会默认使用spring容器启动

然后继续往下看

会循环获取到的启动类列表获取启动类实例,就是在META-INF/dubbo.internal/{接口名称} 对应的文件会有键值对,就是对应的实现类,详情请看 loader.getExtension 这里就不展开。

继续往下, 如果能获取到 dubbo.shutdown.hook 配置并且是true 则加入 addShutdownHook 钩子 ,这个方法会在程序正常停止时候被调用,dubbo就是使用这个实现的优雅停机,看里面的代码,循环拿到启动的容器类调用stop() 方法 来停止容器,这里还用到了锁通知,用来唤醒Main的主程序。

再往下 是启动类的启动方法 , 会循环获取到的容器实例并调用start()方法,各个实现类的启动方法可以查看实现类的实现代码,这里贴出SpringContainer的实现

 1 /*
 2  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  * contributor license agreements.  See the NOTICE file distributed with
 4  * this work for additional information regarding copyright ownership.
 5  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  * (the "License"); you may not use this file except in compliance with
 7  * the License.  You may obtain a copy of the License at
 8  *
 9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 package com.alibaba.dubbo.container.spring;
18 
19 import com.alibaba.dubbo.common.logger.Logger;
20 import com.alibaba.dubbo.common.logger.LoggerFactory;
21 import com.alibaba.dubbo.common.utils.ConfigUtils;
22 import com.alibaba.dubbo.container.Container;
23 
24 import org.springframework.context.support.ClassPathXmlApplicationContext;
25 
26 /**
27  * SpringContainer. (SPI, Singleton, ThreadSafe)
28  */
29 public class SpringContainer implements Container {
30 
31     public static final String SPRING_CONFIG = "dubbo.spring.config";
32     public static final String DEFAULT_SPRING_CONFIG = "classpath*:META-INF/spring/*.xml";
33     private static final Logger logger = LoggerFactory.getLogger(SpringContainer.class);
34     static ClassPathXmlApplicationContext context;
35 
36     public static ClassPathXmlApplicationContext getContext() {
37         return context;
38     }
39 
40     @Override
41     public void start() {
42         String configPath = ConfigUtils.getProperty(SPRING_CONFIG);
43         if (configPath == null || configPath.length() == 0) {
44             configPath = DEFAULT_SPRING_CONFIG;
45         }
46         context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+"));
47         context.start();
48     }
49 
50     @Override
51     public void stop() {
52         try {
53             if (context != null) {
54                 context.stop();
55                 context.close();
56                 context = null;
57             }
58         } catch (Throwable e) {
59             logger.error(e.getMessage(), e);
60         }
61     }
62 
63 }
View Code

可以看到start方法初始化了一个spring应用上下文 其实也就是spring容器,这里如果没有配置 dubbo.spring.config 则会默认扫描 classpath*:META-INF/spring/*.xml 文件来加载spring bean.

stop方法关闭spring容器。

main方法的最后使用了Lock和Condition 并且使主线程等待,其实这里应该就是起到阻塞主线程的作用。关闭的钩子里会唤醒使主线程结束。

 

over...

   

 

posted @ 2019-01-25 11:16  柴柴1226  阅读(1145)  评论(0编辑  收藏  举报