07_apollo_aem子模块整体软件架构深入分析文档
1. 概述
Apollo AEM(Apollo Environment Manager)子模块是Apollo自动驾驶平台的环境管理工具,提供完整的开发、构建和运行环境管理功能。采用模块化架构设计,支持多环境隔离、配置管理、资源调度和环境自动化,实现开发环境一致性与可移植性,简化平台部署维护流程,为开发者提供高效可靠的开发体验。
2. 软件架构图
Apollo AEM子模块采用分层架构设计,将环境管理功能划分为多个功能明确的组件,各组件之间通过清晰的接口进行交互。整个架构遵循模块化、可扩展、可维护的设计原则,确保了环境管理的稳定性和灵活性。
graph TB
subgraph "AEM子模块"
subgraph "核心管理层"
C1[环境管理器]:::key
C2[配置管理器]:::key
C3[资源管理器]:::key
C4[命令处理器]:::key
C5[状态监控器]:::key
end
subgraph "环境隔离层"
E1[容器隔离]:::key
E2[虚拟环境]:::key
E3[网络隔离]:::key
E4[存储隔离]:::key
end
subgraph "功能模块层"
F1[环境创建]:::key
F2[环境配置]:::key
F3[环境激活]:::key
F4[环境进入]:::key
F5[环境删除]:::key
F6[环境列表]:::key
end
subgraph "工具集成层"
T1[Bazel集成]:::key
T2[Docker集成]:::key
T3[Ansible集成]:::key
T4[Shell集成]:::key
end
subgraph "扩展机制层"
X1[插件系统]:::key
X2[命令扩展]:::key
X3[配置扩展]:::key
X4[模板系统]:::key
end
end
subgraph "外部依赖"
D1[Docker引擎]:::key
D2[Ansible]:::key
D3[Shell解释器]:::key
D4[文件系统]:::key
D5[网络系统]:::key
end
subgraph "用户接口"
U1[命令行接口]:::key
U2[脚本接口]:::key
U3[自动化接口]:::key
end
C1 --> E1
C1 --> E2
C1 --> E3
C1 --> E4
C2 --> F1
C2 --> F2
C3 --> F3
C3 --> F4
C4 --> F5
C4 --> F6
C5 --> U1
C5 --> U2
C5 --> U3
E1 --> D1
E2 --> D3
E3 --> D5
E4 --> D4
F1 --> T1
F1 --> T2
F2 --> T3
F3 --> T4
F4 --> T4
F5 --> T2
F6 --> T2
T1 --> D3
T2 --> D1
T3 --> D2
T4 --> D3
X1 --> F1
X1 --> F2
X2 --> C4
X3 --> C2
X4 --> F1
U1 --> C4
U2 --> C4
U3 --> C4
classDef key fill:#e1f5fe
3. 调用流程图
Apollo AEM子模块的调用流程涵盖了从环境创建到环境删除的完整生命周期,包括环境创建、环境配置、环境激活、环境进入和环境删除等多个阶段。整个流程设计清晰,步骤明确,便于开发者理解和使用。
3.1. 环境创建流程
sequenceDiagram
participant User as 开发者
participant AEM as AEM子模块
participant Docker as Docker引擎
participant Ansible as Ansible
participant Shell as Shell解释器
User->>AEM: 执行aem-create命令
AEM->>AEM: 解析命令行参数
AEM->>AEM: 验证参数有效性
AEM->>AEM: 加载环境模板
AEM->>Docker: 创建Docker容器
Docker-->>AEM: 容器创建完成
AEM->>Ansible: 执行环境配置
Ansible-->>AEM: 环境配置完成
AEM->>Shell: 初始化环境脚本
Shell-->>AEM: 脚本初始化完成
AEM->>AEM: 保存环境配置
AEM-->>User: 环境创建完成
3.2. 环境配置流程
sequenceDiagram
participant User as 开发者
participant AEM as AEM子模块
participant Config as 配置管理器
participant Ansible as Ansible
participant Env as 环境
User->>AEM: 执行aem-profile命令
AEM->>Config: 加载环境配置
Config-->>AEM: 配置加载完成
AEM-->>User: 显示当前配置
User->>AEM: 修改配置参数
AEM->>Config: 更新环境配置
Config-->>AEM: 配置更新完成
AEM->>Ansible: 应用配置更改
Ansible->>Env: 更新环境配置
Env-->>Ansible: 配置更新完成
Ansible-->>AEM: 配置应用完成
AEM-->>User: 配置保存完成
3.3. 环境激活流程
sequenceDiagram
participant User as 开发者
participant AEM as AEM子模块
participant Shell as Shell解释器
participant Env as 环境
User->>AEM: 执行aem命令
AEM->>AEM: 解析命令行参数
AEM->>AEM: 加载环境配置
AEM->>Shell: 生成环境激活脚本
Shell->>Env: 激活环境变量
Env-->>Shell: 环境变量激活完成
Shell->>Shell: 配置命令提示符
Shell-->>User: 环境激活完成
3.4. 环境进入流程
sequenceDiagram
participant User as 开发者
participant AEM as AEM子模块
participant Docker as Docker引擎
participant Shell as Shell解释器
participant Env as 环境
User->>AEM: 执行aem-enter命令
AEM->>AEM: 解析命令行参数
AEM->>AEM: 验证环境存在
AEM->>Docker: 检查容器状态
Docker-->>AEM: 容器状态返回
alt 容器未运行
AEM->>Docker: 启动容器
Docker-->>AEM: 容器启动完成
end
AEM->>Docker: 进入容器
Docker->>Shell: 启动shell
Shell->>Env: 加载环境配置
Env-->>Shell: 环境配置加载完成
Shell-->>User: 容器终端就绪
3.5. 环境删除流程
sequenceDiagram
participant User as 开发者
participant AEM as AEM子模块
participant Docker as Docker引擎
participant Config as 配置管理器
User->>AEM: 执行aem-remove命令
AEM->>AEM: 解析命令行参数
AEM->>AEM: 验证环境存在
AEM->>Docker: 检查容器状态
alt 容器运行中
AEM->>Docker: 停止容器
Docker-->>AEM: 容器停止完成
end
AEM->>Docker: 删除容器
Docker-->>AEM: 容器删除完成
AEM->>Config: 删除环境配置
Config-->>AEM: 配置删除完成
AEM->>AEM: 清理临时文件
AEM-->>User: 环境删除完成
4. 详细UML类图
Apollo AEM子模块的UML类图展示了系统的核心组件、它们之间的关系以及各自的职责。类图设计遵循面向对象的设计原则,确保了系统的模块化、可扩展性和可维护性。
4.1. 核心模块类图
classDiagram
class AEMManager {
+initialize()
+createEnvironment(EnvironmentConfig): Environment
+configureEnvironment(string, EnvironmentConfig): bool
+activateEnvironment(string): bool
+enterEnvironment(string): bool
+deleteEnvironment(string): bool
+listEnvironments(): list<EnvironmentInfo>
+getEnvironmentInfo(string): EnvironmentInfo
-environmentManager: EnvironmentManager
-configManager: ConfigManager
-resourceManager: ResourceManager
-commandProcessor: CommandProcessor
-statusMonitor: StatusMonitor
}
class EnvironmentManager {
+create(EnvironmentConfig): Environment
+get(string): Environment
+delete(string): bool
+list(): list<EnvironmentInfo>
+exists(string): bool
-environments: map<string, Environment>
-environmentFactory: EnvironmentFactory
-configManager: ConfigManager
}
class ConfigManager {
+loadConfig(string): EnvironmentConfig
+saveConfig(string, EnvironmentConfig): bool
+deleteConfig(string): bool
+getDefaultConfig(): EnvironmentConfig
+updateConfig(string, EnvironmentConfig): bool
-configPath: string
-configCache: map<string, EnvironmentConfig>
}
class ResourceManager {
+allocateResources(Environment): bool
+releaseResources(Environment): bool
+getResourceUsage(string): ResourceUsage
+checkResourceAvailability(ResourceRequirements): bool
-resources: map<string, Resource>
-resourceMonitor: ResourceMonitor
}
class CommandProcessor {
+processCommand(string, list<string>): CommandResult
+registerCommand(Command)
+unregisterCommand(string)
-commands: map<string, Command>
-commandParser: CommandParser
}
class StatusMonitor {
+monitorEnvironment(string): bool
+getEnvironmentStatus(string): EnvironmentStatus
+getAllEnvironmentStatus(): map<string, EnvironmentStatus>
-monitors: map<string, EnvironmentMonitor>
}
class Environment {
+getName(): string
+getConfig(): EnvironmentConfig
+getStatus(): EnvironmentStatus
+start(): bool
+stop(): bool
+enter(): bool
+delete(): bool
-name: string
-config: EnvironmentConfig
-status: EnvironmentStatus
-container: Container
-shell: Shell
}
class EnvironmentConfig {
+getName(): string
+getVersion(): string
+getImage(): string
+getMounts(): list<Mount>
+getPorts(): list<Port>
+getEnvVars(): map<string, string>
+setName(string): void
+setVersion(string): void
-name: string
-version: string
-image: string
-mounts: list<Mount>
-ports: list<Port>
-envVars: map<string, string>
}
AEMManager --> EnvironmentManager
AEMManager --> ConfigManager
AEMManager --> ResourceManager
AEMManager --> CommandProcessor
AEMManager --> StatusMonitor
EnvironmentManager --> Environment
EnvironmentManager --> ConfigManager
EnvironmentManager --> EnvironmentFactory
ConfigManager --> EnvironmentConfig
ResourceManager --> Resource
ResourceManager --> ResourceMonitor
CommandProcessor --> Command
CommandProcessor --> CommandParser
StatusMonitor --> EnvironmentMonitor
Environment --> EnvironmentConfig
Environment --> EnvironmentStatus
Environment --> Container
Environment --> Shell
4.2. 命令系统类图
classDiagram
class Command {
<<abstract>>
+getName(): string
+getDescription(): string
+getUsage(): string
+execute(list<string>): CommandResult
-name: string
-description: string
-usage: string
}
class CreateCommand {
+execute(list<string>): CommandResult
-environmentManager: EnvironmentManager
-configManager: ConfigManager
}
class ProfileCommand {
+execute(list<string>): CommandResult
-environmentManager: EnvironmentManager
-configManager: ConfigManager
}
class EnterCommand {
+execute(list<string>): CommandResult
-environmentManager: EnvironmentManager
}
class RemoveCommand {
+execute(list<string>): CommandResult
-environmentManager: EnvironmentManager
}
class ListCommand {
+execute(list<string>): CommandResult
-environmentManager: EnvironmentManager
}
class StartCommand {
+execute(list<string>): CommandResult
-environmentManager: EnvironmentManager
}
class StopCommand {
+execute(list<string>): CommandResult
-environmentManager: EnvironmentManager
}
class CommandParser {
+parse(string): CommandParseResult
+validateArguments(list<string>, Command): bool
-argumentValidators: map<string, ArgumentValidator>
}
class CommandResult {
+isSuccess(): bool
+getMessage(): string
+getExitCode(): int
+setSuccess(bool): void
+setMessage(string): void
+setExitCode(int): void
-success: bool
-message: string
-exitCode: int
}
Command <|-- CreateCommand
Command <|-- ProfileCommand
Command <|-- EnterCommand
Command <|-- RemoveCommand
Command <|-- ListCommand
Command <|-- StartCommand
Command <|-- StopCommand
CreateCommand --> EnvironmentManager
CreateCommand --> ConfigManager
ProfileCommand --> EnvironmentManager
ProfileCommand --> ConfigManager
EnterCommand --> EnvironmentManager
RemoveCommand --> EnvironmentManager
ListCommand --> EnvironmentManager
StartCommand --> EnvironmentManager
StopCommand --> EnvironmentManager
CommandProcessor --> Command
CommandProcessor --> CommandParser
CommandParser --> CommandParseResult
Command --> CommandResult
4.3. 环境隔离类图
classDiagram
class Isolation {
<<abstract>>
+isolate(Environment): bool
+unisolate(Environment): bool
+getIsolationId(): string
}
class ContainerIsolation {
+isolate(Environment): bool
+unisolate(Environment): bool
+getIsolationId(): string
+createContainer(EnvironmentConfig): Container
+deleteContainer(string): bool
-dockerClient: DockerClient
-containers: map<string, Container>
}
class VirtualEnvIsolation {
+isolate(Environment): bool
+unisolate(Environment): bool
+getIsolationId(): string
+createVirtualEnv(EnvironmentConfig): VirtualEnv
+deleteVirtualEnv(string): bool
-virtualEnvManager: VirtualEnvManager
-virtualEnvs: map<string, VirtualEnv>
}
class NetworkIsolation {
+isolate(Environment): bool
+unisolate(Environment): bool
+getIsolationId(): string
+createNetwork(EnvironmentConfig): Network
+deleteNetwork(string): bool
-networkManager: NetworkManager
-networks: map<string, Network>
}
class StorageIsolation {
+isolate(Environment): bool
+unisolate(Environment): bool
+getIsolationId(): string
+createVolume(EnvironmentConfig): Volume
+deleteVolume(string): bool
-storageManager: StorageManager
-volumes: map<string, Volume>
}
class Container {
+getId(): string
+getName(): string
+start(): bool
+stop(): bool
+enter(): bool
+delete(): bool
+getIpAddress(): string
+getPorts(): list<Port>
-id: string
-name: string
-image: string
-ipAddress: string
-ports: list<Port>
}
class VirtualEnv {
+getName(): string
+activate(): bool
+deactivate(): bool
+installPackage(string): bool
+uninstallPackage(string): bool
+getInstalledPackages(): list<string>
-name: string
-path: string
-pythonVersion: string
}
class Network {
+getId(): string
+getName(): string
+create(): bool
+delete(): bool
+addContainer(Container): bool
+removeContainer(Container): bool
-id: string
-name: string
-subnet: string
-gateway: string
}
class Volume {
+getId(): string
+getName(): string
+create(): bool
+delete(): bool
+mount(Container, string): bool
+unmount(Container): bool
-id: string
-name: string
-path: string
-size: long
}
Isolation <|-- ContainerIsolation
Isolation <|-- VirtualEnvIsolation
Isolation <|-- NetworkIsolation
Isolation <|-- StorageIsolation
ContainerIsolation --> DockerClient
ContainerIsolation --> Container
VirtualEnvIsolation --> VirtualEnvManager
VirtualEnvIsolation --> VirtualEnv
NetworkIsolation --> NetworkManager
NetworkIsolation --> Network
StorageIsolation --> StorageManager
StorageIsolation --> Volume
Environment --> Isolation
Environment --> Container
Environment --> VirtualEnv
Environment --> Network
Environment --> Volume
5. 状态机
Apollo AEM子模块的状态机展示了系统在不同阶段的状态转换和处理流程,包括环境生命周期状态机、命令处理状态机和资源管理状态机等。这些状态机设计清晰,状态转换明确,便于开发者理解系统的运行机制和行为。
5.1. 环境生命周期状态机
stateDiagram-v2
[*] --> Created: 创建环境
Created --> Configured: 配置环境
Configured --> Activated: 激活环境
Activated --> Running: 启动环境
Running --> Entered: 进入环境
Entered --> Running: 退出环境
Running --> Stopped: 停止环境
Stopped --> Running: 重启环境
Stopped --> Removed: 删除环境
Removed --> [*]: 环境删除完成
Created --> Removed: 删除环境
Configured --> Removed: 删除环境
Activated --> Removed: 删除环境
state Created {
[*] --> Initializing: 初始化环境
Initializing --> [*]: 初始化完成
}
state Configured {
[*] --> LoadingConfig: 加载配置
LoadingConfig --> ValidatingConfig: 配置加载完成
ValidatingConfig --> [*]: 配置验证完成
}
state Activated {
[*] --> SettingEnvVars: 设置环境变量
SettingEnvVars --> ConfiguringShell: 环境变量设置完成
ConfiguringShell --> [*]: Shell配置完成
}
state Running {
[*] --> StartingContainer: 启动容器
StartingContainer --> InitializingServices: 容器启动完成
InitializingServices --> [*]: 服务初始化完成
}
state Entered {
[*] --> StartingShell: 启动Shell
StartingShell --> Ready: Shell启动完成
Ready --> ExecutingCommands: 执行命令
ExecutingCommands --> Ready: 命令执行完成
Ready --> [*]: 退出Shell
}
state Stopped {
[*] --> SavingState: 保存状态
SavingState --> [*]: 状态保存完成
}
state Removed {
[*] --> StoppingContainer: 停止容器
StoppingContainer --> DeletingContainer: 容器停止完成
DeletingContainer --> CleaningUp: 容器删除完成
CleaningUp --> [*]: 清理完成
}
5.2. 命令处理状态机
stateDiagram-v2
[*] --> Waiting: 等待命令
Waiting --> Parsing: 解析命令
Parsing --> Validating: 命令解析完成
Validating --> Executing: 命令验证通过
Executing --> Processing: 执行命令
Processing --> Finished: 命令执行完成
Finished --> Waiting: 等待新命令
Validating --> Error: 命令验证失败
Executing --> Error: 命令执行失败
Processing --> Error: 命令处理失败
Error --> Waiting: 返回等待状态
state Parsing {
[*] --> Tokenizing: 命令分词
Tokenizing --> Identifying: 分词完成
Identifying --> [*]: 命令识别完成
}
state Validating {
[*] --> CheckingArguments: 检查参数
CheckingArguments --> CheckingPermissions: 参数检查完成
CheckingPermissions --> [*]: 权限检查完成
}
state Executing {
[*] --> PreparingEnvironment: 准备环境
PreparingEnvironment --> RunningCommand: 环境准备完成
RunningCommand --> [*]: 命令运行完成
}
state Processing {
[*] --> HandlingOutput: 处理输出
HandlingOutput --> UpdatingStatus: 输出处理完成
UpdatingStatus --> [*]: 状态更新完成
}
state Error {
[*] --> LoggingError: 记录错误
LoggingError --> NotifyingUser: 错误记录完成
NotifyingUser --> [*]: 用户通知完成
}
5.3. 资源管理状态机
stateDiagram-v2
[*] --> Available: 资源可用
Available --> Allocated: 分配资源
Allocated --> InUse: 使用资源
InUse --> Allocated: 释放资源
Allocated --> Available: 回收资源
InUse --> Available: 资源使用完成
Available --> Error: 资源错误
Allocated --> Error: 资源错误
InUse --> Error: 资源错误
Error --> Available: 资源恢复
state Available {
[*] --> Monitoring: 监控资源
Monitoring --> [*]: 资源监控完成
}
state Allocated {
[*] --> Reserving: 保留资源
Reserving --> Assigning: 资源保留完成
Assigning --> [*]: 资源分配完成
}
state InUse {
[*] --> Tracking: 跟踪使用
Tracking --> Updating: 使用跟踪完成
Updating --> [*]: 使用更新完成
}
state Error {
[*] --> Detecting: 检测错误
Detecting --> Recovering: 错误检测完成
Recovering --> [*]: 错误恢复完成
}
6. 源码分析
6.1. 核心脚本代码分析
6.1.1. aem-bootstrap 脚本核心逻辑
#!/usr/bin/env bash
# AEM引导脚本核心逻辑
set -e
# 定义常量
AEM_ROOT_DIR="${HOME}/.aem"
AEM_CONFIG_DIR="${AEM_ROOT_DIR}/config"
AEM_ENV_DIR="${AEM_ROOT_DIR}/envs"
AEM_LOG_DIR="${AEM_ROOT_DIR}/logs"
AEM_PLUGIN_DIR="${AEM_ROOT_DIR}/plugins"
# 初始化函数
init_aem() {
# 创建必要的目录
mkdir -p "${AEM_CONFIG_DIR}"
mkdir -p "${AEM_ENV_DIR}"
mkdir -p "${AEM_LOG_DIR}"
mkdir -p "${AEM_PLUGIN_DIR}"
# 创建默认配置文件
create_default_config
# 加载AEM核心功能
source "$(dirname "$0")/funcs.sh"
# 加载命令扩展
load_commands
# 加载插件
load_plugins
}
# 创建默认配置
create_default_config() {
local config_file="${AEM_CONFIG_DIR}/default.conf"
if [ ! -f "${config_file}" ]; then
cat > "${config_file}" << EOF
# 默认AEM配置
AEM_VERSION=1.0
DEFAULT_IMAGE=apolloauto/apollo:dev-x86_64-nvidia
DEFAULT_MOUNTS=(
"${HOME}/apollo:/apollo"
"/tmp/.X11-unix:/tmp/.X11-unix"
)
DEFAULT_PORTS=(
"8888:8888"
"5555:5555"
)
DEFAULT_ENV_VARS=(
"DISPLAY=${DISPLAY}"
"AEM_ROOT_DIR=${AEM_ROOT_DIR}"
)
EOF
fi
}
# 加载命令
load_commands() {
local commands_dir="$(dirname "$0")/commands.d"
if [ -d "${commands_dir}" ]; then
for cmd_file in "${commands_dir}"/*.sh; do
if [ -f "${cmd_file}" ]; then
source "${cmd_file}"
fi
done
fi
}
# 加载插件
load_plugins() {
if [ -d "${AEM_PLUGIN_DIR}" ]; then
for plugin_dir in "${AEM_PLUGIN_DIR}"/*; do
if [ -d "${plugin_dir}" ]; then
local plugin_init="${plugin_dir}/init.sh"
if [ -f "${plugin_init}" ]; then
source "${plugin_init}"
fi
fi
done
fi
}
# 主函数
main() {
# 初始化AEM
init_aem
# 解析命令行参数
parse_args "$@"
# 执行命令
execute_command
}
# 调用主函数
main "$@"
6.1.2. funcs.sh 核心功能函数
#!/usr/bin/env bash
# AEM核心功能函数
# 日志函数
log() {
local level="$1"
local message="$2"
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
local log_file="${AEM_LOG_DIR}/aem.log"
echo "[${timestamp}] [${level}] ${message}" >> "${log_file}"
if [ "${level}" == "ERROR" ] || [ "${level}" == "WARNING" ]; then
echo "[${level}] ${message}" >&2
else
echo "[${level}] ${message}"
fi
}
# 错误处理函数
error_exit() {
local message="$1"
local exit_code="${2:-1}"
log "ERROR" "${message}"
exit "${exit_code}"
}
# 环境配置加载函数
load_env_config() {
local env_name="$1"
local config_file="${AEM_ENV_DIR}/${env_name}/config.conf"
if [ ! -f "${config_file}" ]; then
error_exit "环境配置文件不存在: ${config_file}"
fi
source "${config_file}"
}
# Docker容器管理函数
create_container() {
local env_name="$1"
local image="$2"
local mounts="$3"
local ports="$4"
local env_vars="$5"
local container_name="aem-${env_name}"
# 构建Docker命令
local docker_cmd="docker run -itd"
docker_cmd+=" --name ${container_name}"
docker_cmd+=" --privileged"
docker_cmd+=" --net host"
docker_cmd+=" --ipc host"
docker_cmd+=" --pid host"
# 添加挂载点
for mount in ${mounts[@]}; do
docker_cmd+=" -v ${mount}"
done
# 添加端口映射
for port in ${ports[@]}; do
docker_cmd+=" -p ${port}"
done
# 添加环境变量
for env_var in ${env_vars[@]}; do
docker_cmd+=" -e ${env_var}"
done
# 添加镜像名称
docker_cmd+=" ${image}"
# 执行Docker命令
log "INFO" "执行Docker命令: ${docker_cmd}"
eval "${docker_cmd}"
return $?
}
# 环境创建函数
create_environment() {
local env_name="$1"
local image="${2:-${DEFAULT_IMAGE}}"
local mounts="${3:-${DEFAULT_MOUNTS[@]}}"
local ports="${4:-${DEFAULT_PORTS[@]}}"
local env_vars="${5:-${DEFAULT_ENV_VARS[@]}}"
# 检查环境是否已存在
if [ -d "${AEM_ENV_DIR}/${env_name}" ]; then
error_exit "环境已存在: ${env_name}"
fi
# 创建环境目录
mkdir -p "${AEM_ENV_DIR}/${env_name}"
# 保存环境配置
save_env_config "${env_name}" "${image}" "${mounts}" "${ports}" "${env_vars}"
# 创建Docker容器
create_container "${env_name}" "${image}" "${mounts}" "${ports}" "${env_vars}"
if [ $? -ne 0 ]; then
error_exit "创建Docker容器失败"
fi
# 初始化环境
initialize_environment "${env_name}"
log "INFO" "环境创建完成: ${env_name}"
}
# 环境配置保存函数
save_env_config() {
local env_name="$1"
local image="$2"
local mounts="$3"
local ports="$4"
local env_vars="$5"
local config_file="${AEM_ENV_DIR}/${env_name}/config.conf"
cat > "${config_file}" << EOF
# AEM环境配置
ENV_NAME=${env_name}
IMAGE=${image}
MOUNTS=(${mounts[@]})
PORTS=(${ports[@]})
ENV_VARS=(${env_vars[@]})
EOF
}
# 环境初始化函数
initialize_environment() {
local env_name="$1"
local container_name="aem-${env_name}"
# 等待容器启动
sleep 2
# 执行初始化命令
docker exec "${container_name}" bash -c "source /apollo/scripts/apollo_base.sh"
# 创建环境激活脚本
create_activate_script "${env_name}"
}
# 创建环境激活脚本
create_activate_script() {
local env_name="$1"
local script_file="${AEM_ENV_DIR}/${env_name}/activate.sh"
cat > "${script_file}" << EOF
#!/usr/bin/env bash
# AEM环境激活脚本
export AEM_CURRENT_ENV=${env_name}
export AEM_CONTAINER_NAME=aem-${env_name}
echo "Apollo环境 ${env_name} 已激活"
echo "使用 'aem-enter' 进入环境"
EOF
chmod +x "${script_file}"
}
6.2. 命令实现代码分析
6.2.1. aem-create 命令实现
#!/usr/bin/env bash
# aem-create命令实现
# 命令帮助信息
create_help() {
echo "aem-create - 创建新的Apollo环境"
echo ""
echo "用法: aem-create [选项] <环境名称>"
echo ""
echo "选项:"
echo " -i, --image <镜像名称> 指定Docker镜像名称"
echo " -m, --mount <挂载点> 添加挂载点(可多次使用)"
echo " -p, --port <端口映射> 添加端口映射(可多次使用)"
echo " -e, --env <环境变量> 添加环境变量(可多次使用)"
echo " -h, --help 显示帮助信息"
echo ""
echo "示例:"
echo " aem-create myenv"
echo " aem-create -i apolloauto/apollo:dev-x86_64-nvidia myenv"
echo " aem-create -m ~/code:/code -p 8080:8080 myenv"
}
# 命令执行函数
create_command() {
local env_name=""
local image="${DEFAULT_IMAGE}"
local mounts=(${DEFAULT_MOUNTS[@]})
local ports=(${DEFAULT_PORTS[@]})
local env_vars=(${DEFAULT_ENV_VARS[@]})
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case "$1" in
-i|--image)
image="$2"
shift 2
;;
-m|--mount)
mounts+=($2)
shift 2
;;
-p|--port)
ports+=($2)
shift 2
;;
-e|--env)
env_vars+=($2)
shift 2
;;
-h|--help)
create_help
return 0
;;
*)
if [ -z "${env_name}" ]; then
env_name="$1"
else
error_exit "未知参数: $1"
fi
shift
;;
esac
done
# 检查环境名称
if [ -z "${env_name}" ]; then
error_exit "请指定环境名称"
fi
# 创建环境
create_environment "${env_name}" "${image}" "${mounts[@]}" "${ports[@]}" "${env_vars[@]}"
}
# 注册命令
register_command "create" create_command create_help
6.2.2. aem-enter 命令实现
#!/usr/bin/env bash
# aem-enter命令实现
# 命令帮助信息
enter_help() {
echo "aem-enter - 进入Apollo环境"
echo ""
echo "用法: aem-enter [环境名称]"
echo ""
echo "选项:"
echo " -h, --help 显示帮助信息"
echo ""
echo "示例:"
echo " aem-enter myenv"
}
# 命令执行函数
enter_command() {
local env_name="$1"
# 检查环境名称
if [ -z "${env_name}" ]; then
# 使用当前激活的环境
env_name="${AEM_CURRENT_ENV}"
fi
if [ -z "${env_name}" ]; then
error_exit "请指定环境名称或激活一个环境"
fi
# 检查环境是否存在
if [ ! -d "${AEM_ENV_DIR}/${env_name}" ]; then
error_exit "环境不存在: ${env_name}"
fi
local container_name="aem-${env_name}"
# 检查容器是否运行
if ! docker ps | grep -q "${container_name}"; then
# 启动容器
log "INFO" "启动容器: ${container_name}"
docker start "${container_name}" || error_exit "启动容器失败"
# 等待容器启动
sleep 2
fi
# 进入容器
log "INFO" "进入环境: ${env_name}"
docker exec -it "${container_name}" bash
}
# 注册命令
register_command "enter" enter_command enter_help
6.3. 配置管理代码分析
6.3.1. aem-profile 命令实现
#!/usr/bin/env bash
# aem-profile命令实现
# 命令帮助信息
profile_help() {
echo "aem-profile - 管理Apollo环境配置"
echo ""
echo "用法: aem-profile [选项] <环境名称>"
echo ""
echo "选项:"
echo " -l, --list 列出所有配置选项"
echo " -g, --get <键> 获取配置值"
echo " -s, --set <键> <值> 设置配置值"
echo " -r, --reset 重置为默认配置"
echo " -h, --help 显示帮助信息"
echo ""
echo "示例:"
echo " aem-profile myenv"
echo " aem-profile -l myenv"
echo " aem-profile -g IMAGE myenv"
echo " aem-profile -s IMAGE apolloauto/apollo:dev-x86_64-nvidia myenv"
}
# 命令执行函数
profile_command() {
local action="show"
local key=""
local value=""
local env_name=""
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case "$1" in
-l|--list)
action="list"
shift
;;
-g|--get)
action="get"
key="$2"
shift 2
;;
-s|--set)
action="set"
key="$2"
value="$3"
shift 3
;;
-r|--reset)
action="reset"
shift
;;
-h|--help)
profile_help
return 0
;;
*)
if [ -z "${env_name}" ]; then
env_name="$1"
else
error_exit "未知参数: $1"
fi
shift
;;
esac
done
# 检查环境名称
if [ -z "${env_name}" ]; then
error_exit "请指定环境名称"
fi
# 检查环境是否存在
if [ ! -d "${AEM_ENV_DIR}/${env_name}" ]; then
error_exit "环境不存在: ${env_name}"
fi
case "${action}" in
show)
show_profile "${env_name}"
;;
list)
list_profile "${env_name}"
;;
get)
get_profile "${env_name}" "${key}"
;;
set)
set_profile "${env_name}" "${key}" "${value}"
;;
reset)
reset_profile "${env_name}"
;;
*)
error_exit "未知操作: ${action}"
;;
esac
}
# 显示配置
show_profile() {
local env_name="$1"
local config_file="${AEM_ENV_DIR}/${env_name}/config.conf"
log "INFO" "环境 ${env_name} 配置:"
cat "${config_file}"
}
# 列出配置选项
list_profile() {
local env_name="$1"
local config_file="${AEM_ENV_DIR}/${env_name}/config.conf"
log "INFO" "环境 ${env_name} 配置选项:"
grep -E '^[A-Z_]+=' "${config_file}" | cut -d '=' -f 1
}
# 获取配置值
get_profile() {
local env_name="$1"
local key="$2"
local config_file="${AEM_ENV_DIR}/${env_name}/config.conf"
if [ -z "${key}" ]; then
error_exit "请指定配置键"
fi
local value=$(grep -E "^${key}=" "${config_file}" | cut -d '=' -f 2-)
if [ -z "${value}" ]; then
error_exit "配置键不存在: ${key}"
fi
echo "${value}"
}
# 设置配置值
set_profile() {
local env_name="$1"
local key="$2"
local value="$3"
local config_file="${AEM_ENV_DIR}/${env_name}/config.conf"
if [ -z "${key}" ] || [ -z "${value}" ]; then
error_exit "请指定配置键和值"
fi
# 检查配置键是否存在
if grep -q "^${key}=" "${config_file}"; then
# 更新配置值
sed -i "s/^${key}=.*/${key}=${value}/" "${config_file}"
else
# 添加新配置键
echo "${key}=${value}" >> "${config_file}"
fi
log "INFO" "配置已更新: ${key}=${value}"
}
# 重置配置
reset_profile() {
local env_name="$1"
local config_file="${AEM_ENV_DIR}/${env_name}/config.conf"
local default_config="${AEM_CONFIG_DIR}/default.conf"
# 保存当前配置备份
cp "${config_file}" "${config_file}.bak"
# 生成新配置
cat > "${config_file}" << EOF
# AEM环境配置
ENV_NAME=${env_name}
EOF
# 从默认配置复制默认值
grep -E '^(IMAGE|MOUNTS|PORTS|ENV_VARS)=' "${default_config}" >> "${config_file}"
log "INFO" "配置已重置为默认值"
}
# 注册命令
register_command "profile" profile_command profile_help
7. 设计模式
7.1. 命令模式
Apollo AEM子模块采用命令模式来实现命令的解析和执行,将命令封装为对象,提高了命令的可扩展性和可维护性。
# 命令模式的实现
# 命令注册函数
register_command() {
local name="$1"
local handler="$2"
local help_func="$3"
COMMANDS["${name}"]="${handler}"
COMMAND_HELP["${name}"]="${help_func}"
}
# 命令执行函数
execute_command() {
local cmd_name="${ARGS[0]}"
local cmd_args=(${ARGS[@]:1})
if [ -z "${cmd_name}" ]; then
# 显示帮助信息
show_help
return 0
fi
if [ -n "${COMMANDS[${cmd_name}]}" ]; then
# 执行命令
${COMMANDS[${cmd_name}]} "${cmd_args[@]}"
return $?
else
error_exit "未知命令: ${cmd_name}"
fi
}
# 命令帮助函数
show_help() {
echo "Apollo Environment Manager (AEM)"
echo ""
echo "用法: aem [命令] [选项]"
echo ""
echo "可用命令:"
for cmd_name in "${!COMMANDS[@]}"; do
echo " ${cmd_name}"
done
echo ""
echo "使用 'aem <命令> --help' 获取命令的详细帮助"
}
7.2. 工厂模式
工厂模式用于创建不同类型的环境和资源,提高了代码的可扩展性和可维护性。
# 工厂模式的实现
# 环境工厂函数
create_environment() {
local env_type="$1"
local env_name="$2"
local options="$3"
case "${env_type}" in
docker)
create_docker_environment "${env_name}" "${options}"
;;
virtualenv)
create_virtualenv_environment "${env_name}" "${options}"
;;
*)
error_exit "未知环境类型: ${env_type}"
;;
esac
}
# Docker环境创建函数
create_docker_environment() {
local env_name="$1"
local options="$2"
# Docker环境创建逻辑
log "INFO" "创建Docker环境: ${env_name}"
# ...
}
# 虚拟环境创建函数
create_virtualenv_environment() {
local env_name="$1"
local options="$2"
# 虚拟环境创建逻辑
log "INFO" "创建虚拟环境: ${env_name}"
# ...
}
7.3. 单例模式
单例模式用于管理全局的环境管理器和配置管理器,确保系统中只有一个实例。
# 单例模式的实现
# 环境管理器单例
_get_environment_manager() {
if [ -z "${_ENVIRONMENT_MANAGER}" ]; then
_ENVIRONMENT_MANAGER="$(create_environment_manager)"
fi
echo "${_ENVIRONMENT_MANAGER}"
}
# 配置管理器单例
_get_config_manager() {
if [ -z "${_CONFIG_MANAGER}" ]; then
_CONFIG_MANAGER="$(create_config_manager)"
fi
echo "${_CONFIG_MANAGER}"
}
# 资源管理器单例
_get_resource_manager() {
if [ -z "${_RESOURCE_MANAGER}" ]; then
_RESOURCE_MANAGER="$(create_resource_manager)"
fi
echo "${_RESOURCE_MANAGER}"
}
7.4. 观察者模式
观察者模式用于实现环境状态的监控和通知,当环境状态发生变化时,自动通知相关模块进行处理。
# 观察者模式的实现
# 观察者注册函数
register_observer() {
local observer_name="$1"
local observer_func="$2"
OBSERVERS["${observer_name}"]="${observer_func}"
}
# 观察者通知函数
notify_observers() {
local event="$1"
local data="$2"
for observer_name in "${!OBSERVERS[@]}"; do
local observer_func="${OBSERVERS[${observer_name}]}"
log "DEBUG" "通知观察者: ${observer_name} 事件: ${event}"
${observer_func} "${event}" "${data}"
done
}
# 环境状态变化通知函数
notify_environment_state_change() {
local env_name="$1"
local old_state="$2"
local new_state="$3"
local event="environment_state_change"
local data="env_name=${env_name} old_state=${old_state} new_state=${new_state}"
notify_observers "${event}" "${data}"
}
# 示例观察者
log_observer() {
local event="$1"
local data="$2"
log "INFO" "观察者事件: ${event} 数据: ${data}"
}
# 注册观察者
register_observer "log" "log_observer"
7.5. 模板方法模式
模板方法模式用于定义环境创建和配置的骨架,具体步骤由子类实现,提高了代码的复用性和可维护性。
# 模板方法模式的实现
# 环境创建模板方法
create_environment_template() {
local env_name="$1"
local options="$2"
# 模板方法步骤
initialize_environment "${env_name}" "${options}"
configure_environment "${env_name}" "${options}"
validate_environment "${env_name}" "${options}"
finalize_environment "${env_name}" "${options}"
}
# 环境初始化方法
initialize_environment() {
local env_name="$1"
local options="$2"
log "INFO" "初始化环境: ${env_name}"
# ...
}
# 环境配置方法
configure_environment() {
local env_name="$1"
local options="$2"
log "INFO" "配置环境: ${env_name}"
# ...
}
# 环境验证方法
validate_environment() {
local env_name="$1"
local options="$2"
log "INFO" "验证环境: ${env_name}"
# ...
}
# 环境最终化方法
finalize_environment() {
local env_name="$1"
local options="$2"
log "INFO" "最终化环境: ${env_name}"
# ...
}
8. 总结
Apollo AEM(Apollo Environment Manager)子模块是Apollo自动驾驶平台的环境管理工具,提供完整的开发、构建和运行环境管理功能。采用模块化架构设计,支持多环境隔离、配置管理、资源调度和环境自动化,实现开发环境一致性与可移植性。通过命令模式、工厂模式、单例模式、观察者模式和模板方法等多种设计模式,提高了代码的可维护性和扩展性。AEM子模块为开发者提供了高效可靠的开发体验,简化了平台部署维护流程,是Apollo平台的重要组成部分。