sonic守护进程管理

内容分享11小时前发布
0 0 0

orchagent是一个守护进程集合,监听redis数据库的变化并针对变化进行处理。

1. orchagent应用初始化

应用启动:
orchagent [-h] [-r record_type] [-d record_location] [-f swss_rec_filename] [-j sairedis_rec_filename] [-b batch_size] [-m MAC] [-i INST_ID] [-s] [-z mode] [-k bulk_size]
,位于
main.cpp
中:

WarmStart:

初始化相关数据库连接(STATE_DB/CONFIG_DB)与表连接(STATE_DB:WARM_RESTART_ENABLE_TABLE,STATE_DB:WARM_RESTART_TABLE,CONFIG_DB:WARM_RESTART)检查swss container是否具有热启动能力:从相关表中获取swss容器的相关热重启信息以判断

解析
main()
方法参数:
b:m:r:f:j:d:i:hsz:k:

-h: display this message-r record_type: record orchagent logs with type (default 3)
Bit 0: sairedis.rec, Bit 1: swss.rec, Bit 2: responsepublisher.rec.value example: 0: do not record logs; 1: record SAI call sequence as sairedis.rec; 2: record SwSS task sequence as swss.rec; 3: enable both above two records; 7: enable sairedis.rec, swss.rec and responsepublisher.rec -d record_location: set record logs folder location (default .)-b batch_size: set consumer table pop operation batch size (default 128)-m MAC: set switch MAC address-i INST_ID: set the ASIC instance_id in multi-asic platform-s enable synchronous mode (deprecated, use -z)-z redis communication mode (redis_async|redis_sync|zmq_sync), default: redis_async-f swss_rec_filename: swss record log filename(default ‘swss.rec’)-j sairedis_rec_filename: sairedis record log filename(default sairedis.rec)-k max bulk size in bulk mode (default 1000)
在acl流程分析中,比较重要的参数是
-z
,决定orchagent应用于syncd引用的交互模式。

SaiHelper::initSaiApi() – 初始化的重点,初始化了sairedis api,对于orchagent应用来说,是实际调用sai的入口


sai_api_initialize()
: 根据
saihelper.cpp
内include的
"sai.h"
文件,在项目目录下寻找对应的c文件,推断在
src/sonic-sairedis/sonic-sairedis/lib/sai_redis_interfacequery.cpp
文件中实现。
最终调用的是全局变量
redis_sai

initialize()
方法,
redis_sai
是一个指向
ClientServerSai
对象空间的共享指针,是实现sairedis抽象层的主要类,目的是与syncd应用建立通信通道
sai_api_query()
: 查询如acl、switch、vlan等对应的sairedis api,并赋值给对应的指针对象如
sai_switch_api

sai_acl_api等
–>保存的是如create、remove、update等操作的调用方法,通过sairedis建立的通信通道实际调用sai–最终会调用到SDK中的方法
sai_log_set()
: 设置每个类型的sai api的日志级别


SaiHelper::initSaiRedis()
– 初始化sairedis的一些配置信息

根据获取的参数设置
sairedis
的日志记录配置,包括存储位置与日志名称 – 发送消息给
sairedis
根据获取的参数设置
sairedis
的日志使能使能
redis

Pipeline
功能如果是
mellanox
平台,设置从
Syncd
得到响应的超时时间

初始化日志参数,如果日志使能,检查日志文件是否能够被正常打开

实例化数据库连接:
APPL_DB

CONFIG_DB

STATE_DB

设置交换机类型:查询
CONFIG_DB
库中的
DEVICE_METADATA
表,有一条
key=localhost
的记录,如下图所示,不存在
"switch_type"
字段,所以交换机类型设置为
"switch"
,并且设置mac信息
sonic守护进程管理

设置同步模式: 根据参数可以选择同步还是异步。根据目前交换机上的信息将交互模式设置为:
SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC

如果使用了
-i
,设置
SAI_SWITCH_ATTR_SWITCH_HARDWARE_INFO
属性,配置最大字符串长度

如果交换机类型是
voq
,需要写入额外属性 –
SONiC VOQ System
,当前交换机不是,暂不做理解

设置全局上下文:
SAI_REDIS_SWITCH_ATTR_CONTEXT
,设置为
SAI_NULL_OBJECT_ID
。创建
switch
时,指定这个属性(必须作为列表上的最后一个属性传递),它将决定与哪个上下文通信。Context是一个syncd实例。此外,这个值被内部编码到每个对象ID中,因此每个API调用将从内部知道syncd的哪个实例发送API请求。

设置sai attributes

SAI_SWITCH_ATTR_INIT_SWITCH: true – 初始化switch、sdk??SAI_SWITCH_ATTR_FDB_EVENT_NOTIFY: on_fdb_event – fdb events的回调函数SAI_SWITCH_ATTR_PORT_STATE_CHANGE_NOTIFY: on_port_state_change – port state change events的回调函数SAI_SWITCH_ATTR_SHUTDOWN_REQUEST_NOTIFY(SAI_SWITCH_ATTR_SWITCH_SHUTDOWN_REQUEST_NOTIFY): on_switch_shutdown_request – 关机请求事件通知的回调函数SAI_SWITCH_ATTR_SRC_MAC_ADDRESS: 如果参数制定了mac_address,设置该属性SAI_REDIS_SWITCH_ATTR_REDIS_COMMUNICATION_MODE: 设置通信模式,同步还是异步,
SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC
SAI_SWITCH_ATTR_SWITCH_HARDWARE_INFO: 硬件信息


redis
发送创建
switch
实例的操作,属性信息为上述设置的attributes
sonic守护进程管理

获取默认virtual router ID –
SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID

获取NAT支持信息,默认支持 –
SAI_SWITCH_ATTR_AVAILABLE_SNAT_ENTRY

创建回环路由接口

gearbox: 判断平台是否支持该功能,首先
/usr/share/sonic/hwsku/gearbox_config.json
文件要存在,当前交换机不存在,则不支持;其次
APPL_DB
库中的
_GEARBOX_TABLE
表要存在一条
key=GearboxConfigDone
的记录。
sonic守护进程管理

初始化各个守护进程: OrchDaemon::init() – 重点,主要是订阅通知

启动各个守护进程: OrchDaemon::start() – 重点,开始循环获取事件通知进行处理

其中比较重要的步骤是3、18、19步骤。接下来展开叙述这三个步骤。

2. SaiHelper::initSaiApi()

初始化步骤分为三步:
sai_api_initialize()

sai_api_query()

sai_log_set()
,前两个初始化动作很重要,后面sai api的调用都是基于这两个操作。

根据
saihelper.cpp

include

"sai.h"
文件,在项目目录下寻找对应的文件,推断对应的是
src/sonic-sairedis/sonic-sairedis/lib/sai_redis_interfacequery.cpp
文件。

2.1 sai_api_initialize()


sai_redis_interfacequery.cpp
文件中最终调用的是全局变量
redis_sai

initialize()
方法,
redis_sai
是一个指向
ClientServerSai
对象空间的共享指针,是实现sairedis抽象层的主要类,目的是与syncd应用建立通信通道。

2.1.1 参数传递


sai_status_t ClientServerSai::initialize(
        _In_ uint64_t flags, //只能为0
        _In_ const sai_service_method_table_t *service_method_table);


service_method_table
传递的是两个方法:


typedef struct _sai_service_method_table_t
{
    /**
     * @brief Get variable value given its name
     */
    sai_profile_get_value_fn        profile_get_value;

    /**
     * @brief Enumerate all the K/V pairs in a profile.
     *
     * Pointer to NULL passed as variable restarts enumeration. Function
     * returns 0 if next value exists, -1 at the end of the list.
     */
    sai_profile_get_next_value_fn   profile_get_next_value;

} sai_service_method_table_t;


profile_get_value
: 获取
gProfileMap
变量中对应的
value

profile_get_next_value
: 获取
gProfileMap
变量是否还有第二个
key-value

2.1.2 初始化过程

sonic守护进程管理

是否已经初始化过,如果是,输出LOG,返回SAI_STATUS_FAILUREflag是否等于0,如果不等于0,输出LOG,返回SAI_STATUS_INVALID_PARAMETERservice_method_table及其成员是否存在某一个是NULL,如果存在则输出LOG,返回SAI_STATUS_INVALID_PARAMETER将service_method_table复制给成员变量
profile_get_value()
获取
SAI_REDIS_KEY_ENABLE_CLIENT
的值 – 这里假定该值不存在,因为在orchagent应用代码中没有找到其他ClientServer的初始化过程因为disable client,使用子类
ServerSai
初始化成员变量
std::shared_ptr<SaiInterface> m_sai
调用
m_sai->initialize(flags, service_method_table)
,这里是子类
ServerSai

initialize()
第7步初始化成功则设置对象的初始化标志位为true

2.1.2.1 ServerSai::initialize()

看上述第7步的初始化过程:ServerSai::initialize()

是否已经初始化过,如果是,输出LOG,返回
SAI_STATUS_FAILURE
flag是否等于0,如果不等于0,输出LOG,返回
SAI_STATUS_INVALID_PARAMETER

service_method_table
及其成员是否存在某一个是NULL,如果存在则输出LOG,返回
SAI_STATUS_INVALID_PARAMETER

service_method_table
复制给成员变量使用子类
Sai
初始化成员变量
std::shared_ptr<SaiInterface> m_sai
调用
m_sai->initialize(flags, service_method_table)
,这里是子类
Sai

initialize()
第vi步初始化成功:

profile_get_value()
获取
SAI_REDIS_SERVER_CONFIG
的值 – 这里假定该值不存在,这是一个sairedis server配置文件根据上一步得到的参数获取配置信息,配置ZeroMQ的端点地址,不存在则获取默认参数


m_zmqEndpoint("ipc:///tmp/saiServer"),
m_zmqNtfEndpoint("ipc:///tmp/saiServerNtf")

使用上一步的默认值
"ipc:///tmp/saiServer"
初始化成员变量
std::shared_ptr<SelectableChannel> m_selectableChannel
: 创建ZeroMQ连接,启动循环子线程获取zmq socket的事件状态,然后notify – ???启动子线程循环获取zmq socket的事件的详细信息,并进行处理设置对象的初始化标志位为true

2.1.2.2 Sai::initialize()

继续分析上述第6步的初始化过程:Sai::initialize()

是否已经初始化过,如果是,输出LOG,返回
SAI_STATUS_FAILURE

flag是否等于0,如果不等于0,输出LOG,返回
SAI_STATUS_INVALID_PARAMETER


service_method_table
及其成员是否存在某一个是NULL,如果存在则输出LOG,返回
SAI_STATUS_INVALID_PARAMETER


service_method_table
复制给成员变量

初始化成员
std::shared_ptr<Recorder> m_recorder
– 日志类


profile_get_value()
获取
SAI_REDIS_KEY_CONTEXT_CONFIG
的值 – 这里假定该值不存在,文件形式为:
sonic守护进程管理

加载第6步中的context_config.json文件,为空则加载默认值:

{"guid":0,"name":"syncd","dbAsic":"ASIC_DB","dbCounters":"COUNTERS_DB","dbFlex":"FLEX_COUNTER_DB","dbState":"STATE_DB","zmqEnable":"false","zmqEndpoint":"ipc:///tmp/zmq_ep","zmqNtfEndpoint":"ipc:///tmp/zmq_ntf_ep"}

使用第7步得到的配置信息初始化成员变量
m_contextMap
,一个存放Context类的map对象

设置对象的初始化标志位为true

2.1.2.3 Context::Context()

继续分析上述第8步的Context类初始化过程:Context::Context()

初始化成员变量
std::shared_ptr<RedisRemoteSaiInterface> m_redisSai
– 是构建通信通道的具体实现初始化成员变量
std::shared_ptr<saimeta::Meta> m_meta

m_redisSai->setMeta(m_meta)

2.1.2.4 RedisRemoteSaiInterface

分析
std::shared_ptr<RedisRemoteSaiInterface> m_redisSai
的初始化:

有两个比较重要的成员变量
m_contextConfig
以及
m_redisCommunicationMode
:

m_redisCommunicationMode
在构造函数中初始化为
SAI_REDIS_COMMUNICATION_MODE_REDIS_ASYNC
,表示与syncd应用的通信模式,存在三种:

SAI_REDIS_COMMUNICATION_MODE_REDIS_ASYNC
: 通过redis机制进行异步通信,不等待最终结果,每一个流程最终到写入数据库即为成功
SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC
: 通过redis的同步通信,需要阻塞等待来自syncd应用向redis写入的事件通知
SAI_REDIS_COMMUNICATION_MODE_ZMQ_SYNC
: 通过ZeroMQ机制直接与syncd应用建立通信channel,只进行同步通信,等待syncd应用的response
m_contextConfig
表示当前的上下文配置,默认为:

{"guid":0,"name":"syncd","dbAsic":"ASIC_DB","dbCounters":"COUNTERS_DB","dbFlex":"FLEX_COUNTER_DB","dbState":"STATE_DB","zmqEnable":"false","zmqEndpoint":"ipc:///tmp/zmq_ep","zmqNtfEndpoint":"ipc:///tmp/zmq_ntf_ep"}
是否已经初始化过,如果是,输出LOG,返回
SAI_STATUS_FAILURE
初始化成员变量
std::shared_ptr<SkipRecordAttrContainer> m_skipRecordAttrContainer
: 在get命令时,管理哪些可忽略属性不写入记录初始化一些标志位,比较重要的是
m_syncMode
以及
m_redisCommunicationMode


m_syncMode
: 初始化为false
m_redisCommunicationMode
: 初始化为
SAI_REDIS_COMMUNICATION_MODE_REDIS_ASYNC
,表示当前通信模式为基于redis的异步模式 初始化成员
std::shared_ptr<Channel> m_communicationChannel
,是建立通信通道的具体操作:
如果
m_contextConfig

m_zmqEnable
为true,使用子类
ZeroMQChannel
初始化,设置成员
m_syncMode

true
如果
m_contextConfig

m_zmqEnable
为false,使用子类
RedisChannel
初始化:
初始化asic_db数据库的连接初始化ASIC_STATE表的连接,初始化为一个生产者类
ProducerTable
对象,初始化发布通知功能的lua脚本 – 使用
EVALSHA
命令调用脚本通过redis发布通知,通过redis列表与syncd应用交互初始化GETRESPONSE表的连接,初始化为一个消费者类
ConsumerTable
对象,初始化接收并处理通知的lua脚本类,并且监控redis列表以及订阅频道

WATCH ASIC_STATE_KEY_VALUE_OP_QUEUE

SUBSCRIBE ASIC_STATE_CHANNEL@asic_db_index
初始化asic_db的连接,初始化为一个通知消费者类
NotificationConsumer
对象,订阅频道
NOTIFICATIONS
启动notification处理线程: 即对
NOTIFICATIONS
频道订阅的通知处理 获取通信通道的响应等待时间: 默认为
SAI_REDIS_DEFAULT_SYNC_OPERATION_RESPONSE_TIMEOUT
– 60s创建与数据库asic_db的连接对象初始化成员
std::shared_ptr<RedisVidIndexGenerator> m_redisVidIndexGenerator
: 是一个vid生成器 – vid具体表示什么??ru’guo
clear_local_state()
:
初始化成员
std::shared_ptr<SwitchContainer> m_switchContainer
: 一个管理交换机的对象初始化成员
std::shared_ptr<VirtualObjectIdManager> m_virtualObjectIdManager
: 一个vid的管理器,传入上下文、
m_switchContainer
以及
m_redisVidIndexGenerator
作为构造参数如果
m_meta->lock()
成功,则调用
m_meta->meta_init_db()
,清除在应用层面保存的所有object信息 – Meta类管理应用层面的Object 设置对象的初始化标志位为true

2.2. sai_api_query

2.2.1 sai_api_query()


sai_api_query()
实际上是给上述定义的全局指针变量赋值,初始化所有crud操作方法的入口,下面的代码是重点代码:


    if (sai_metadata_get_enum_value_name(&sai_metadata_enum_sai_api_t, sai_api_id)) //返回api类型的名称 -- 一个字符串,或者NULL
    {
        *api_method_table = ((void**)&redis_apis)[sai_api_id - 1];
        return SAI_STATUS_SUCCESS;
    }

检查是否支持操作类型
最终所有crud操作都需要通过sai接口下发给asic,所以首先要检查sai是否支持这一类型的接口,比如是否支持acl。


sai_metadata_enum_sai_api_t

saimetadata.c
文件中定义的一个枚举变量,通过
#include "saimetadata.h"
包含进来,与SDK中的
saimetadata.c
是对应的:


const sai_enum_metadata_t sai_metadata_enum_sai_api_t = {
    .name              = "sai_api_t",
    .valuescount       = 44,
    .values            = (const int*)sai_metadata_sai_api_t_enum_values,
    .valuesnames       = sai_metadata_sai_api_t_enum_values_names,
    .valuesshortnames  = sai_metadata_sai_api_t_enum_values_short_names,
    .containsflags     = false,
};

sonic守护进程管理


sai_metadata_get_enum_value_name()
是一个工具,检查当前
sai_api_id
是否存在于
sai_metadata_enum_sai_api_t
对象的
values
中,存在则返回对应的
valuesnames


sai_api_id
是一个
sai_api_t
枚举对象,实际上是一个整数,与
sai_metadata_enum_sai_api_t.values
是对应的。
sonic守护进程管理

赋值
实际上是根据
sai_api_id
将数组
redis_apis
中对应的值赋给每一个指向每种类型操作方法的全局指针。如下图所示,是数组
redis_apis
的定义,使用
API
构造对应的全局变量:

#define API(api) .api ## _api = const_cast<sai_ ## api ## _api_t*>(&redis_ ## api ## _api)

sonic守护进程管理


API(acl)
实际上是
.acl_api = const_cast<sai_acl_api_t*>(&redis_acl_api)
,即赋值最终为:
sai_acl_api=const_cast<sai_acl_api_t*>(&redis_acl_api)

2.2.2 sai_acl_api


SaiHelper.cpp
文件中首先定义了一组全局指针变量:
sonic守护进程管理

每一个指针变量指向的是每种类型的操作方法,如针对acl的操作对应指针定义是:
sai_acl_api_t* sai_acl_api;
,该指针指向的对象包含了acl所有支持的crud操作的方法:
sonic守护进程管理

2.2.3 redis_acl_api


redis_acl_api
也是一个全局变量,声明在文件
sonic-sairedis/lib/sai_redis.h
中:

PRIVATE extern const sai_acl_api_t redis_acl_api;


sonic-sairedis/lib/sai_redis_acl.cpp
文件中定义,通过
#include "sai_redis.h"
包含进来:


REDIS_GENERIC_QUAD(ACL_TABLE,acl_table);
REDIS_GENERIC_QUAD(ACL_ENTRY,acl_entry);
REDIS_GENERIC_QUAD(ACL_COUNTER,acl_counter);
REDIS_GENERIC_QUAD(ACL_RANGE,acl_range);
REDIS_GENERIC_QUAD(ACL_TABLE_GROUP,acl_table_group);
REDIS_GENERIC_QUAD(ACL_TABLE_GROUP_MEMBER,acl_table_group_member);
    
const sai_acl_api_t redis_acl_api = {
    REDIS_GENERIC_QUAD_API(acl_table)
    REDIS_GENERIC_QUAD_API(acl_entry)
    REDIS_GENERIC_QUAD_API(acl_counter)
    REDIS_GENERIC_QUAD_API(acl_range)
    REDIS_GENERIC_QUAD_API(acl_table_group)
    REDIS_GENERIC_QUAD_API(acl_table_group_member)
};

前一段代码调用
REDIS_GENERIC_QUAD()
根据
sai_redis.h
中的宏定义模板生成
redis_acl_api
中的函数,后一段是全局数组变量
redis_acl_api
的赋值。


REDIS_GENERIC_QUAD_API()


REDIS_GENERIC_QUAD_API()
是一个宏定义,对应acl每种类型的crud方法:


#define REDIS_GENERIC_QUAD_API(ot)      
    redis_create_ ## ot,                
    redis_remove_ ## ot,                
    redis_set_ ## ot ##_attribute,      
    redis_get_ ## ot ##_attribute,


REDIS_GENERIC_QUAD_API(acl_table)
被替换为:


redis_create_acl_table,
redis_remove_acl_table,
redis_set_acl_table_attribute,
redis_get_acl_table_attribute,

其中每一项都是通过
REDIS_GENERIC_QUAD()
宏定义模板生成的方法。


REDIS_GENERIC_QUAD()


#define REDIS_GENERIC_QUAD(OT,ot)  
    REDIS_CREATE(OT,ot);           
    REDIS_REMOVE(OT,ot);           
    REDIS_SET(OT,ot);              
    REDIS_GET(OT,ot);

那么
REDIS_GENERIC_QUAD(ACL_TABLE,acl_table);
,根据
REDIS_CREATE

REDIS_REMOVE

REDIS_SET

REDIS_GET
四个宏定义被替换为:


redis_create_acl_table();
redis_remove_acl_table();
redis_set_acl_table();
redis_get_acl_table();


REDIS_CREATE(OT,ot);
为例,宏定义如下:


#define REDIS_CREATE(OT,ot)                             
    static sai_status_t redis_create_ ## ot(            
            _Out_ sai_object_id_t *object_id,           
            _In_ sai_object_id_t switch_id,             
            _In_ uint32_t attr_count,                   
            _In_ const sai_attribute_t *attr_list)      
{                                                       
    SWSS_LOG_ENTER();                                   
    return redis_sai->create(                           
            (sai_object_type_t)SAI_OBJECT_TYPE_ ## OT,  
            object_id,                                  
            switch_id,                                  
            attr_count,                                 
            attr_list);                                 
}

可以看到该操作实际上调用的是
redis_sai
对象的
create()
方法,
redis_sai
是一个全局共享指针变量,指向一个
ClientServerSai
对象:
std::shared_ptr<SaiInterface> redis_sai = std::make_shared<ClientServerSai>();
,在sai_api_initialize()章节中已经初始化。

3. OrchDaemon

根据switch type选择不同的守护进程,当前交换机类型是
"switch"
,所以初始化为
OrchDaemon
对象。


    shared_ptr<OrchDaemon> orchDaemon;
    if (gMySwitchType != "fabric")
    {
        orchDaemon = make_shared<OrchDaemon>(&appl_db, &config_db, &state_db, chassis_app_db.get());
        if (gMySwitchType == "voq")
        {
            orchDaemon->setFabricEnabled(true);
        }
    }
    else
    {
        orchDaemon = make_shared<FabricOrchDaemon>(&appl_db, &config_db, &state_db, chassis_app_db.get());
    }

3.1 构造函数


OrchDaemon::OrchDaemon(DBConnector *applDb, DBConnector *configDb, DBConnector *stateDb, DBConnector *chassisAppDb) :
        m_applDb(applDb),
        m_configDb(configDb),
        m_stateDb(stateDb),
        m_chassisAppDb(chassisAppDb)
{
    SWSS_LOG_ENTER();
    m_select = new Select();
}

为数据库连接对象赋值,包括
APPL_DB

CONFIG_DB、STATE_DB

CHASSISS_APP_DB
,其中
CHASSISS_APP_DB

voq
系统才有的,当前交换机上的
redis
中不存在这个数据库。初始化
Select
类对象:创建一个epoll实例,管理订阅的事件通知


Select::Select()
{
    m_epoll_fd = ::epoll_create(0); //内核会产生一个epoll实例数据结构并返回一个文件描述符
    if (m_epoll_fd == -1)
    {
        std::string error = std::string("Select::constructor:epoll_create1: error=("
                          + std::to_string(errno) + "}:"
                          + strerror(errno));
        throw std::runtime_error(error);
    }
}

3.2 init()

首先所有守护进程类的父类都是
Orch
类。该方法中初始化了
swss docker
需要的所有守护进程,监听
redis
中相关的
keyspace
事件,处理并且通过
redis
数据库与
syncd
同步:

获取平台信息:是Mellanox还是Barefoot还是其他

创建守护进程对象,如
SwitchOrch()、PortsOrch()、AclOrch()
等,都需要执行的是下面的步骤:

初始化需要的数据库连接以及表连接,如
COUNTERS_DB

COUNTERS_DB:CRM
创建订阅对象,使用模式匹配向redis订阅相关表获取订阅的表的所有记录及其字段与值,并存储在变量中(
m_buffer
)
主要看一下
AclOrch()
:

gAclOrch = new AclOrch(acl_table_connectors, m_stateDb, gSwitchOrch, gPortsOrch, gMirrorOrch, gNeighOrch, gRouteOrch, dtel_orch);

与switch、ports、mirror、neigh、route、dtel都相关,具体是如何相互影响的,后面再仔细看。

最后根据平台不一样,将每个平台对应需要的守护进程对象添加到私有变量
m_orchlist
中,后面启动的将会是这个列表中的守护进程。

热启动同步操作:
warmRestoreAndSyncUp()

更新orchagent应用的热重启状态为”INITIALIZED”:STATE_DB: WARM_RESTART_TABLE|orchagent针对每一个守护进程对象,执行
bake()
,遍历对象初始化时创建的consumer对象:
取出所有events – 这里的event指的是通过epoll从内核取出来的,在
start()
操作中会不断循环从内核取事件通知同步events信息到
m_toSync
(multimap对象) – 事件处理队列,守护进程处理的是这个map中订阅对象的events
如果map中不存在有关于对应table的操作,则直接插入如果存在,且是一条DEL操作的通知,擦除map中原有记录,插入新的如果存在,且是一条SET操作的通知,找到map中对应记录且是SET操作的数据,使用新的通知中表字段信息取代原来的信息,且保留新的信息中不存在但是原来信息中就有的字段信息 返回events数量 三次迭代,调用每个守护进程对象的
doTask()
方法,该方法是处理events的 – 为什么要迭代三次??单独调用MirrorOrch以及AclOrch的
doTask()
方法 – ??恢复验证 – ????更新交换机属性
SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD
– 通过switch sai接口入口
sai_switch_api
通过port sai接口入口
sai_port_api
获取并且更新端口状态更新orchagent应用的热重启状态为”RECONCILED”

3.3 start()

遍历
m_orchlist
,针对每一个对象,将对象初始化时创建的消费者通过
epoll_ctl
添加到内核。创建消费者实际上是创建与订阅的表的连接,得到连接的
fd
,将
fd
描述符通过
epoll_ctl
添加到内核进行监听。无限循环,每次处理一个订阅对象的events,轮询处理不同订阅对象 –
Select::select()

epoll_wait查询当前存在的连接是否有events遍历得到的fd,针对每个fd,读取内核中events存入一个deque将fd存入一个有序集合
m_ready
,该集合表示当前已经获取到events的连接,按照连接上次使用的时间戳进行排序,时间戳小排在前面 – 即处理等待时间长的events遍历该有序集合:获取集合最前面的订阅对象
从集合中删除,并且更新时间戳如果该订阅对象没有events,继续循环如果该订阅对象有events,返回该对象作为待处理的events对象;如果不只一条event,重新将该fd插入到有序集合中更新订阅对象中待处理的消息数量,退出循环 在第4步得到的订阅对象中,将events同步到事件处理队列
m_toSync
中,并且执行一次
doTask()
操作,处理队列中所有events确保当前交换机可以进行热重启,并且orchagent进程进行休眠,等待热重启

© 版权声明

相关文章

暂无评论

none
暂无评论...