dubbo Main啓動服務淺析

Dubbo 採用全 Spring 配置方式,官方推薦使用內置 Main 啓動,並提供了JDK 的 ShutdownHook 優雅停機。這裏看的是dubbo 2.6.2版本的代碼java

 

貼出dubbo提供的Main啓動類spring

  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,並在這裏配置好express

分析 apache

首先判斷是否傳入了啓動容器,若是沒有傳入則從-D參數數或者dubbo.properties文件中獲取key dubbo.container的值,若是都沒獲取到那麼就默認使用默認容器啓動,默認容器是哪一個呢? 查看 getDefaultExtensionName 方法能夠看到調用的loadExtensionClasses()方法中有獲取默認容器的代碼  以下 app

 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容器啓動less

而後繼續往下看ide

會循環獲取到的啓動類列表獲取啓動類實例,就是在META-INF/dubbo.internal/{接口名稱} 對應的文件會有鍵值對,就是對應的實現類,詳情請看 loader.getExtension 這裏就不展開。ui

繼續往下, 若是能獲取到 dubbo.shutdown.hook 配置而且是true 則加入 addShutdownHook 鉤子 ,這個方法會在程序正常中止時候被調用,dubbo就是使用這個實現的優雅停機,看裏面的代碼,循環拿到啓動的容器類調用stop() 方法 來中止容器,這裏還用到了鎖通知,用來喚醒Main的主程序。this

再往下 是啓動類的啓動方法 , 會循環獲取到的容器實例並調用start()方法,各個實現類的啓動方法能夠查看實現類的實現代碼,這裏貼出SpringContainer的實現spa

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

相關文章
相關標籤/搜索