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...

   

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM