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流程分析中,比较重要的参数是
,决定orchagent应用于syncd引用的交互模式。
-z
SaiHelper::initSaiApi() – 初始化的重点,初始化了sairedis api,对于orchagent应用来说,是实际调用sai的入口
: 根据
sai_api_initialize()
内include的
saihelper.cpp
文件,在项目目录下寻找对应的c文件,推断在
"sai.h"
文件中实现。
src/sonic-sairedis/sonic-sairedis/lib/sai_redis_interfacequery.cpp
最终调用的是全局变量
的
redis_sai
方法,
initialize()
是一个指向
redis_sai
对象空间的共享指针,是实现sairedis抽象层的主要类,目的是与syncd应用建立通信通道
ClientServerSai
: 查询如acl、switch、vlan等对应的sairedis api,并赋值给对应的指针对象如
sai_api_query()
、
sai_switch_api
–>保存的是如create、remove、update等操作的调用方法,通过sairedis建立的通信通道实际调用sai–最终会调用到SDK中的方法
sai_acl_api等
: 设置每个类型的sai api的日志级别
sai_log_set()
– 初始化sairedis的一些配置信息
SaiHelper::initSaiRedis()
根据获取的参数设置
的日志记录配置,包括存储位置与日志名称 – 发送消息给
sairedis
根据获取的参数设置
sairedis
的日志使能使能
sairedis
的
redis
功能如果是
Pipeline
平台,设置从
mellanox
得到响应的超时时间
Syncd
初始化日志参数,如果日志使能,检查日志文件是否能够被正常打开
实例化数据库连接:
、
APPL_DB
、
CONFIG_DB
STATE_DB
设置交换机类型:查询
库中的
CONFIG_DB
表,有一条
DEVICE_METADATA
的记录,如下图所示,不存在
key=localhost
字段,所以交换机类型设置为
"switch_type"
,并且设置mac信息
"switch"
设置同步模式: 根据参数可以选择同步还是异步。根据目前交换机上的信息将交互模式设置为:
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
时,指定这个属性(必须作为列表上的最后一个属性传递),它将决定与哪个上下文通信。Context是一个syncd实例。此外,这个值被内部编码到每个对象ID中,因此每个API调用将从内部知道syncd的哪个实例发送API请求。
switch
设置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_SWITCH_ATTR_SWITCH_HARDWARE_INFO: 硬件信息
SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC
向
发送创建
redis
实例的操作,属性信息为上述设置的attributes
switch
获取默认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
初始化各个守护进程: OrchDaemon::init() – 重点,主要是订阅通知
启动各个守护进程: OrchDaemon::start() – 重点,开始循环获取事件通知进行处理
其中比较重要的步骤是3、18、19步骤。接下来展开叙述这三个步骤。
2. SaiHelper::initSaiApi()
初始化步骤分为三步:
、
sai_api_initialize()
、
sai_api_query()
,前两个初始化动作很重要,后面sai api的调用都是基于这两个操作。
sai_log_set()
根据
内
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
对象空间的共享指针,是实现sairedis抽象层的主要类,目的是与syncd应用建立通信通道。
ClientServerSai
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 初始化过程
是否已经初始化过,如果是,输出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()
的值 – 这里假定该值不存在,因为在orchagent应用代码中没有找到其他ClientServer的初始化过程因为disable client,使用子类
SAI_REDIS_KEY_ENABLE_CLIENT
初始化成员变量
ServerSai
调用
std::shared_ptr<SaiInterface> m_sai
,这里是子类
m_sai->initialize(flags, service_method_table)
的
ServerSai
第7步初始化成功则设置对象的初始化标志位为true
initialize()
2.1.2.1 ServerSai::initialize()
看上述第7步的初始化过程:ServerSai::initialize()
是否已经初始化过,如果是,输出LOG,返回
flag是否等于0,如果不等于0,输出LOG,返回
SAI_STATUS_FAILURE
SAI_STATUS_INVALID_PARAMETER
及其成员是否存在某一个是NULL,如果存在则输出LOG,返回
service_method_table
将
SAI_STATUS_INVALID_PARAMETER
复制给成员变量使用子类
service_method_table
初始化成员变量
Sai
调用
std::shared_ptr<SaiInterface> m_sai
,这里是子类
m_sai->initialize(flags, service_method_table)
的
Sai
第vi步初始化成功:
initialize()
获取
profile_get_value()
的值 – 这里假定该值不存在,这是一个sairedis server配置文件根据上一步得到的参数获取配置信息,配置ZeroMQ的端点地址,不存在则获取默认参数
SAI_REDIS_SERVER_CONFIG
m_zmqEndpoint("ipc:///tmp/saiServer"),
m_zmqNtfEndpoint("ipc:///tmp/saiServerNtf")
使用上一步的默认值
初始化成员变量
"ipc:///tmp/saiServer"
: 创建ZeroMQ连接,启动循环子线程获取zmq socket的事件状态,然后notify – ???启动子线程循环获取zmq socket的事件的详细信息,并进行处理设置对象的初始化标志位为true
std::shared_ptr<SelectableChannel> m_selectableChannel
2.1.2.2 Sai::initialize()
继续分析上述第6步的初始化过程:Sai::initialize()
是否已经初始化过,如果是,输出LOG,返回
SAI_STATUS_FAILURE
flag是否等于0,如果不等于0,输出LOG,返回
SAI_STATUS_INVALID_PARAMETER
及其成员是否存在某一个是NULL,如果存在则输出LOG,返回
service_method_table
SAI_STATUS_INVALID_PARAMETER
将
复制给成员变量
service_method_table
初始化成员
– 日志类
std::shared_ptr<Recorder> m_recorder
获取
profile_get_value()
的值 – 这里假定该值不存在,文件形式为:
SAI_REDIS_KEY_CONTEXT_CONFIG
加载第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步得到的配置信息初始化成员变量
,一个存放Context类的map对象
m_contextMap
设置对象的初始化标志位为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
,表示与syncd应用的通信模式,存在三种:
SAI_REDIS_COMMUNICATION_MODE_REDIS_ASYNC
: 通过redis机制进行异步通信,不等待最终结果,每一个流程最终到写入数据库即为成功
SAI_REDIS_COMMUNICATION_MODE_REDIS_ASYNC
: 通过redis的同步通信,需要阻塞等待来自syncd应用向redis写入的事件通知
SAI_REDIS_COMMUNICATION_MODE_REDIS_SYNC
: 通过ZeroMQ机制直接与syncd应用建立通信channel,只进行同步通信,等待syncd应用的response
SAI_REDIS_COMMUNICATION_MODE_ZMQ_SYNC
表示当前的上下文配置,默认为:
m_contextConfig
是否已经初始化过,如果是,输出LOG,返回
{"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"}
初始化成员变量
SAI_STATUS_FAILURE
: 在get命令时,管理哪些可忽略属性不写入记录初始化一些标志位,比较重要的是
std::shared_ptr<SkipRecordAttrContainer> m_skipRecordAttrContainer
以及
m_syncMode
m_redisCommunicationMode
: 初始化为false
m_syncMode
: 初始化为
m_redisCommunicationMode
,表示当前通信模式为基于redis的异步模式 初始化成员
SAI_REDIS_COMMUNICATION_MODE_REDIS_ASYNC
,是建立通信通道的具体操作:
std::shared_ptr<Channel> m_communicationChannel
如果
中
m_contextConfig
为true,使用子类
m_zmqEnable
初始化,设置成员
ZeroMQChannel
为
m_syncMode
如果
true
中
m_contextConfig
为false,使用子类
m_zmqEnable
初始化:
RedisChannel
初始化asic_db数据库的连接初始化ASIC_STATE表的连接,初始化为一个生产者类
对象,初始化发布通知功能的lua脚本 – 使用
ProducerTable
命令调用脚本通过redis发布通知,通过redis列表与syncd应用交互初始化GETRESPONSE表的连接,初始化为一个消费者类
EVALSHA
对象,初始化接收并处理通知的lua脚本类,并且监控redis列表以及订阅频道
ConsumerTable
WATCH ASIC_STATE_KEY_VALUE_OP_QUEUE
初始化asic_db的连接,初始化为一个通知消费者类
SUBSCRIBE ASIC_STATE_CHANNEL@asic_db_index
对象,订阅频道
NotificationConsumer
启动notification处理线程: 即对
NOTIFICATIONS
频道订阅的通知处理 获取通信通道的响应等待时间: 默认为
NOTIFICATIONS
– 60s创建与数据库asic_db的连接对象初始化成员
SAI_REDIS_DEFAULT_SYNC_OPERATION_RESPONSE_TIMEOUT
: 是一个vid生成器 – vid具体表示什么??ru’guo
std::shared_ptr<RedisVidIndexGenerator> m_redisVidIndexGenerator
:
clear_local_state()
初始化成员
: 一个管理交换机的对象初始化成员
std::shared_ptr<SwitchContainer> m_switchContainer
: 一个vid的管理器,传入上下文、
std::shared_ptr<VirtualObjectIdManager> m_virtualObjectIdManager
以及
m_switchContainer
作为构造参数如果
m_redisVidIndexGenerator
成功,则调用
m_meta->lock()
,清除在应用层面保存的所有object信息 – Meta类管理应用层面的Object 设置对象的初始化标志位为true
m_meta->meta_init_db()
2.2. sai_api_query
2.2.1 sai_api_query()
实际上是给上述定义的全局指针变量赋值,初始化所有crud操作方法的入口,下面的代码是重点代码:
sai_api_query()
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
包含进来,与SDK中的
#include "saimetadata.h"
是对应的:
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,
};
是一个工具,检查当前
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
赋值
实际上是根据
将数组
sai_api_id
中对应的值赋给每一个指向每种类型操作方法的全局指针。如下图所示,是数组
redis_apis
的定义,使用
redis_apis
构造对应的全局变量:
API
#define API(api) .api ## _api = const_cast<sai_ ## api ## _api_t*>(&redis_ ## api ## _api)
如
实际上是
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
每一个指针变量指向的是每种类型的操作方法,如针对acl的操作对应指针定义是:
,该指针指向的对象包含了acl所有支持的crud操作的方法:
sai_acl_api_t* sai_acl_api;
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()
是一个宏定义,对应acl每种类型的crud方法:
REDIS_GENERIC_QUAD_API()
#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
,在sai_api_initialize()章节中已经初始化。
std::shared_ptr<SaiInterface> redis_sai = std::make_shared<ClientServerSai>();
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
类对象:创建一个epoll实例,管理订阅的事件通知
Select
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
创建订阅对象,使用模式匹配向redis订阅相关表获取订阅的表的所有记录及其字段与值,并存储在变量中(
COUNTERS_DB:CRM
)
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针对每一个守护进程对象,执行
,遍历对象初始化时创建的consumer对象:
bake()
取出所有events – 这里的event指的是通过epoll从内核取出来的,在
操作中会不断循环从内核取事件通知同步events信息到
start()
(multimap对象) – 事件处理队列,守护进程处理的是这个map中订阅对象的events
m_toSync
如果map中不存在有关于对应table的操作,则直接插入如果存在,且是一条DEL操作的通知,擦除map中原有记录,插入新的如果存在,且是一条SET操作的通知,找到map中对应记录且是SET操作的数据,使用新的通知中表字段信息取代原来的信息,且保留新的信息中不存在但是原来信息中就有的字段信息 返回events数量 三次迭代,调用每个守护进程对象的
方法,该方法是处理events的 – 为什么要迭代三次??单独调用MirrorOrch以及AclOrch的
doTask()
方法 – ??恢复验证 – ????更新交换机属性
doTask()
– 通过switch sai接口入口
SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD
通过port sai接口入口
sai_switch_api
获取并且更新端口状态更新orchagent应用的热重启状态为”RECONCILED”
sai_port_api
3.3 start()
遍历
,针对每一个对象,将对象初始化时创建的消费者通过
m_orchlist
添加到内核。创建消费者实际上是创建与订阅的表的连接,得到连接的
epoll_ctl
,将
fd
描述符通过
fd
添加到内核进行监听。无限循环,每次处理一个订阅对象的events,轮询处理不同订阅对象 –
epoll_ctl
:
Select::select()
epoll_wait查询当前存在的连接是否有events遍历得到的fd,针对每个fd,读取内核中events存入一个deque将fd存入一个有序集合
,该集合表示当前已经获取到events的连接,按照连接上次使用的时间戳进行排序,时间戳小排在前面 – 即处理等待时间长的events遍历该有序集合:获取集合最前面的订阅对象
m_ready
从集合中删除,并且更新时间戳如果该订阅对象没有events,继续循环如果该订阅对象有events,返回该对象作为待处理的events对象;如果不只一条event,重新将该fd插入到有序集合中更新订阅对象中待处理的消息数量,退出循环 在第4步得到的订阅对象中,将events同步到事件处理队列
中,并且执行一次
m_toSync
操作,处理队列中所有events确保当前交换机可以进行热重启,并且orchagent进程进行休眠,等待热重启
doTask()