基本概念

在详细分析模块加载过程之前,先了解几个核心概念。

1.模块(Module)

Module是指继承了IFloodlightModule接口的类。IFloodlightModule定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface IFloodlightModule {
//获取module包含的service
public Collection<Class<? extends IFloodlightService>> getModuleServices();
public Map<Class<? extends IFloodlightService>,
IFloodlightService> getServiceImpls();
//获取module依赖service
public Collection<Class<? extends IFloodlightService>> getModuleDependencies();
void init(FloodlightModuleContext context) throws FloodlightModuleException;
void startUp(FloodlightModuleContext context)
throws FloodlightModuleException;
}

我们知道,在Java中,一个类,可以有多个实例。但在Floodlight系统中,每个Module类只包含一个module实例。Module类通过ServiceLoader只实例化一个module实例,类似于Spring框架的功能,详见3.2.2节。

(约定:以下内容中,首字母大写的Module表示类,小写的module表示实例)

2.服务(Service)

Service是指继承了IFloodlightService接口的类。IFloodlightService定义如下,是一个空接口,保证类型安全:

1
2
3
public abstract interface IFloodlightService {
// This space is intentionally left blank....don't touch it
}

3.配置需要启动的Module

Floodlight从命令行启动的时候,可以加上配置文件参数(见第2节);如果没有添加,则启动默认的配置文件。

可以在这个配置文件中定义一个Module列表,这些Module在Floodlight运行的时候自动启动。

4.Module与Service的关系

  1. 一个Module可以包含多个Service。
  2. 一个Service也可以被多个Module包含。
  3. 但是,对于定义在配置文件(见3.2.1)中将要启动的Module,任意两个Module不能包含相同的Service。

总结一下:Module和Service可以是Many-to-Many的关系;但是对于定义在配置文件中的所有Module,是one-to-Many的关系。

5.依赖关系

一个Module可能依赖于多个Service,通过调用getModuleDependencies方法获取module实例所依赖的Service。

定义在配置文件的Module在启动前,程序会解决依赖问题。如果一个Module依赖一个Service,那么程序会加载包含这个Service的Module解决依赖。如果包含这个Service的Module有多个,那么必须选一个,显式写在配置文件中。


Main函数

main函数很简单(除去注释、空行,只有十几行),也很容易理解,主要包括以下几部分内容:

  1. 解析命令行参数
  2. 加载自定义模块
  3. 启动REST服务器
  4. 启动Controller

这几部分在接下来的几节中详细论述。


命令行参数解析

程序使用CmdLineParser解析命令参数。CmdLineSetting定义了命令行参数的格式:-cf[--configFile] FILE,用来通过命令行传入配置文件路径。

1
2
@Option(name="-cf", aliases="--configFile", metaVar="FILE", usage="Floodlight configuration file")
private String configFile = DEFAULT_CONFIG_FILE;

默认情况下,配置文件路径为“config/floodlight.properties”。


FloodlightModuleLoader

从主函数可以看到,它实例化了FloodlightModuleLoader,用来加载模块,而这个类也继承了IFloodlightService接口,所以它可以看作是一种特殊的Service,用来加载Module的Service。

在详细说明FloodlightModuleLoader功能之前,我先介绍一下它内部的几个静态成员变量:

  • serviceMap:Service类型与所属的多个modules实例的映射关系
  • moduleService:module实例与包含的Services类型的映射关系
  • moduleNameMap:Module名称与module实例的映射关系

FloodlightModuleLoader主要做了三件事(loadModulesFromConfig方法):

  1. 解析配置文件,读取其中的Module名称(用于第三步启动)
  2. 利用ServiceLoader实例化所有Module
  3. 对于2中的所有实例,只启动配置文件所定义的类型

1.解析配置文件

参见loadModulesFromConfig方法。
配置文件是Properties文件,以等号(“=”)分隔属性名和属性值,使用java.util.Properties就可以解析。

  • “floodlight.modules”属性对应的值是以逗号分隔的Module类名字符串。这表示要启动的多个模块。
  • “floodlight.confd”属性对应的值是子配置文件目录。这个目录可以包含多个子配置文件,每个文件中也可以包含“floodlight.modules”属性,对应的以逗号分隔Module名也需要被启动。

第1步:将“floodlight.modules”属性定义的多个Module类名,存放到列表中
第2步:如果有“floodlight.confd”属性,解析对应的子配置文件目录,递归解析所有的子配置文件
第3步:返回结果列表

2.实例化所有Module

这部分的内容主要包含在findAllModules方法中。

ServiceLoader是Java SE 6所提供的API,用于自动实例化继承了给定接口(或抽象类)的类,类似于Spring依赖注入的功能。
ServiceLoader的使用请参见《ServiceLoader的使用》

ServiceLoader为resource/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule文件中定义的所有类都创建了一个实例。这些类全部实现了IFloodlightModule,所以可以这么说,ServiceLoader为这个文件中所有的Module创建了一个实例。

然后更新serviceMap、moduleService和moduleNameMap这三个静态变量。

最后,检查配置文件中包含的Module类,是否存在两个以上的Module包含相同的service,否则抛出异常。

3.启动配置文件定义的Module

这部分内容主要在loadModulesFromList方法中。主要过程为:

  1. 将需启动的Module实例加入到启动列表中
  2. 将依赖的Module实例加入启动列表中(递归添加依赖Module)
  3. 启动上述列表中的所有实例

从“解析配置文件”小节中已知道,解析配置文件后,返回一个列表(Collection<String>),其中包含要启动的Module名称。接下来,循环迭代这个列表,根据Module名称从moduleNameMap中获取对应的module实例。然后把这个实例添加到启动列表中。

如果这个module实例依赖于其他一个或多个Service,那么需要解决依赖,将包含这些Service的module实例也添加到启动列表。