SDN控制器Floodlight源码学习(一)-模块加载

**本文通过在代码中注释的方式对Floodlight源码(Version 1.2)进行学习,同时若遇到关键点,会对关键点进行单独的详细分析.
Floodlight是一款开源的SDN控制器,首先先从模块加载开始学习,以下为加载模块的关键代码:**
首先奉上启动流程图,图中对启动过程中关键点进行了描述:
Floodlight启动流程图
分析1:main函数

public static void main(String[] args) throws FloodlightModuleException {
        try {
            // Setup logger
            System.setProperty("org.restlet.engine.loggerFacadeClass", 
                    "org.restlet.ext.slf4j.Slf4jLoggerFacade");
            //实例化一个CmdLineSettings类,该类用于设置配置文件,用户可以用-cf命令指定自定义的配置文件,若没有指定则使用默认字段
            CmdLineSettings settings = new CmdLineSettings();
            //实例化一个CmdLineParser类,该类来自于arg4j,arg4j是一款用于解析命令行参数的插件
            CmdLineParser parser = new CmdLineParser(settings);
            try {
                //解析命令行参数,并设置到settings当中
                parser.parseArgument(args);
            } catch (CmdLineException e) {
                parser.printUsage(System.out);
                System.exit(1);
            }
            // Load modules
            //创建一个Floodlight的模块加载器
            FloodlightModuleLoader fml = new FloodlightModuleLoader();
            try {
                //调用模块加载器的loadModulesFromConfig函数,入参为用户的配置文件,详细分析参见2
                IFloodlightModuleContext moduleContext = fml.loadModulesFromConfig(settings.getModuleFile());
                //从模块上线文环境获取RestApi的服务,并启动
                IRestApiService restApi = moduleContext.getServiceImpl(IRestApiService.class);
                restApi.run(); 
            } catch (FloodlightModuleConfigFileNotFoundException e) {
                // we really want to log the message, not the stack trace
                logger.error("Could not read config file: {}", e.getMessage());
                System.exit(1);
            }
            try {
                //启动controller和其它模块,详见分析6
                fml.runModules(); // run the controller module and all modules
            } catch (FloodlightModuleException e) {
                logger.error("Failed to run controller modules", e);
                System.exit(1);
            }
        } catch (Exception e) {
            logger.error("Exception in main", e);
            System.exit(1);
        }
    }

分析2:loadModulesFromConfig函数

public IFloodlightModuleContext loadModulesFromConfig(String fName)
            throws FloodlightModuleException {
        //实例化一个Properties类用于解析配置文档中非模块配置的属性
        Properties prop = new Properties();
        //创建一个String类型的集合用于存放配置文件中配置的模块
        Collection<String> configMods = new ArrayList<>();

        //若入参的配置文件为null,则加载COMPILED_CONF_FILE,这是一个默认的配置文件
        if (fName == null) {
            logger.info("Loading default modules");
            InputStream is = this.getClass().getClassLoader().
                                    getResourceAsStream(COMPILED_CONF_FILE);
            //这个函数用于将配置文件解析到prop和configMods当中
            mergeProperties(is, null, configMods, prop);
        //若入参的配置文件不为空,则解析配置文件或者目录下面的Properties文件
        } else {
            File confFile = new File(fName);
            if (! confFile.exists())
                throw new FloodlightModuleConfigFileNotFoundException(fName);
            logger.info("Loading modules from {}", confFile.getPath());
            if (confFile.isFile()) {
                mergeProperties(null, confFile,
                                configMods, prop);
            } else {
                File[] files = confFile.listFiles();
                Arrays.sort(files);
                for (File f : files) {
                    logger.debug("Loading conf.d file {}", f.getPath());

                    if (f.isFile() &&
                        f.getName().matches(".*\\.properties$")) {
                        mergeProperties(null, f, configMods, prop);
                    }
                }
            }
        }
        //最后调用loadModulesFromList函数,详见分析3
        return loadModulesFromList(configMods, prop);
    }

分析3:loadModulesFromList函数

public synchronized IFloodlightModuleContext
            loadModulesFromList(Collection<String> configMods,
                                Properties prop)
                                        throws FloodlightModuleException {
        logger.debug("Starting module loader");

        //将configMods解析到模块加载器的三个成员变量中,三个成员变量分别为:serviceMap,moduleServiceMap,moduleNameMap.详见分析4
        findAllModules(configMods);

        //创建一个IFloodlightModule类型的集合,用于存放需要加载的模块
        ArrayList<IFloodlightModule> moduleList = new ArrayList<>();
        Map<Class<? extends IFloodlightService>, IFloodlightModule> moduleMap =
                new HashMap<>();
        //创建一个set用于存在访问过的模块名称
        HashSet<String> modsVisited = new HashSet<>();
        //将configMods转换为一个队列
        ArrayDeque<String> modsToLoad = new ArrayDeque<>(configMods);
        //循环从队列中取出模块,解析模块之间的依赖关系,同时按照顺序的放入moduleList
        while (!modsToLoad.isEmpty()) {
            String moduleName = modsToLoad.removeFirst();
            //该函数的功能是保证模块之间的依赖关系,并按照顺序不重复的放入moduleList
            traverseDeps(moduleName, modsToLoad,
                         moduleList, moduleMap, modsVisited);
        }

        //将配置属性解析并放入到模块的上下文环境中floodlightModuleContext
        parseConfigParameters(prop);

        loadedModuleList = moduleList;
        //初始化模块,详见分析5
        initModules(moduleList);
        //查看startupModules状态,如果为true则调用模块实现的startUp方法,因为在FloodlightModuleLoader初始化时候,已经将startupModules置为true
        if(startupModules)
            startupModules(moduleList);
        //返回模块的上线文环境中
        return floodlightModuleContext;
    }

分析4:findAllModules函数
这个函数主要的作用是将configMods解析到加载模块的三个变量中,这三个变量是:
1.serviceMap
模块提供的服务与实现模块实例的关系,模块实现的服务是通过模块中getModuleServices中获得,这里模块提供的服务我理解是模块对外提供的服务类,比如MemoryStorageSource模块提供了IStorageSourceService服务接口,这些服务接口都需要实现最基础的IFloodlightService
2.moduleServiceMap
这个是与serviceMap相反的,实模块实例和模块所提供服务接口的映射关系
3.moduleNameMap:
提供模块名称和模块实例的映射关系

protected synchronized void findAllModules(Collection<String> mList)
            throws FloodlightModuleException {
        if (serviceMap != null)
            return;
        serviceMap = new HashMap<>();
        moduleServiceMap = new HashMap<>();
        moduleNameMap = new HashMap<>();

        //动态加载目前实现了IFloodlightModule的模块,形成一个一个的实例
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        ServiceLoader<IFloodlightModule> moduleLoader =
                ServiceLoader.load(IFloodlightModule.class, cl);
       //遍历模块的实例,解析出上面提到的成员变量
        Iterator<IFloodlightModule> moduleIter = moduleLoader.iterator();
        while (moduleIter.hasNext()) {
            IFloodlightModule m = null;
            try {
                m = moduleIter.next();
            } catch (ServiceConfigurationError sce) {
                logger.error("Could not find module: {}", sce.getMessage());
                continue;
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Found module " + m.getClass().getName());
            }

            // Set up moduleNameMap
            moduleNameMap.put(m.getClass().getCanonicalName(), m);

            // Set up serviceMap
            Collection<Class<? extends IFloodlightService>> servs =
                    m.getModuleServices();
            if (servs != null) {
                moduleServiceMap.put(m, servs);
                for (Class<? extends IFloodlightService> s : servs) {
                    Collection<IFloodlightModule> mods =
                            serviceMap.get(s);
                    if (mods == null) {
                        mods = new ArrayList<IFloodlightModule>();
                        serviceMap.put(s, mods);
                    }
                    mods.add(m);
                    // Make sure they haven't specified duplicate modules in
                    // the config
                    int dupInConf = 0;
                    for (IFloodlightModule cMod : mods) {
                        if (mList.contains(cMod.getClass().getCanonicalName()))
                            dupInConf += 1;
                    }

                    if (dupInConf > 1) {
                        StringBuilder sb = new StringBuilder();
                        for (IFloodlightModule mod : mods) {
                            sb.append(mod.getClass().getCanonicalName());
                            sb.append(", ");
                        }
                        String duplicateMods = sb.toString();
                        String mess = "ERROR! The configuration file " +
                                "specifies more than one module that " +
                                "provides the service " +
                                s.getCanonicalName() +
                                ". Please specify only ONE of the " +
                                "following modules in the config file: " +
                                duplicateMods;
                        throw new FloodlightModuleException(mess);
                    }
                }
            }
        }
    }

分析5:initModules函数

protected void initModules(Collection<IFloodlightModule> moduleSet)
                                           throws FloodlightModuleException {
        for (IFloodlightModule module : moduleSet) {
            // 获取模块提供服务的实例
            Map<Class<? extends IFloodlightService>,
                IFloodlightService> simpls = module.getServiceImpls();

            // 将模块提供服务实例放入上下文环境变量中
            if (simpls != null) {
                for (Entry<Class<? extends IFloodlightService>,
                        IFloodlightService> s : simpls.entrySet()) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Setting " + s.getValue() +
                                     "  as provider for " +
                                     s.getKey().getCanonicalName());
                    }
                    if (floodlightModuleContext.getServiceImpl(s.getKey()) == null) {
                        floodlightModuleContext.addService(s.getKey(),
                                                           s.getValue());
                    } else {
                        throw new FloodlightModuleException("Cannot set "
                                                            + s.getValue()
                                                            + " as the provider for "
                                                            + s.getKey().getCanonicalName()
                                                            + " because "
                                                            + floodlightModuleContext.getServiceImpl(s.getKey())
                                                            + " already provides it");
                    }
                }
            }
        }
        //遍历需要加载的模块,并调用模块实现的init方法,该方法实现将模块依赖的服务注入到模块的引用中
        for (IFloodlightModule module : moduleSet) {
            // init the module
            if (logger.isDebugEnabled()) {
                logger.debug("Initializing " +
                             module.getClass().getCanonicalName());
            }
            module.init(floodlightModuleContext);
        }
  }

分析6:runModules方法

public void runModules() throws FloodlightModuleException {
        List<RunMethod> mainLoopMethods = Lists.newArrayList();
        //遍历需要加载的模块      
        for (IFloodlightModule m : getModuleList()) {
            for (Method method : m.getClass().getDeclaredMethods()) {
                //找到模块方法中标识了@Run注解的方法
                Run runAnnotation = method.getAnnotation(Run.class);
                if (runAnnotation != null) {
                    RunMethod runMethod = new RunMethod(m, method);
                    if(runAnnotation.mainLoop()) {
                        mainLoopMethods.add(runMethod);
                    } else {
                        //调用方法
                        runMethod.run();
                    }
                }
            }
        }
        //标注了mainLoop的方法只能有一个
        if(mainLoopMethods.size() == 1) {
            mainLoopMethods.get(0).run();
        } else if (mainLoopMethods.size() > 1) {
            throw new FloodlightModuleException("Invalid module configuration -- "
                    + "multiple run methods annotated with mainLoop detected: " + mainLoopMethods);
        }
    }

至此Floodlight启动流程中主要涉及的方法如上。

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值