sonic中syncd容器与redis容器通信源码解析
总结1.syncd初始化配置2.连接数据库并监听3.数据处理
摘要
整个功能实现基本由以下步骤组成
1.syncd初始化配置
2.连接数据库并监听
3.数据处理
背景
sonic-buildimage的git commit为774778,将在此版本上进行分析,github地址如下:
https://github.com/Azure/sonic-buildimage/tree/77477857b47b114fde18afc33985e1a76c464c09
进入src目录,可以看到对应的代码如下图:
用到的代码为sairedis(在syncd上容器运行,负责与redis数据库通信以及调用厂家提供的api及sdk,编译后的可执行文件名字为syncd)与swss-common,swss以后有机会的话再讲吧。
saireids地址:Azure/sonic-sairedis at 13474d17435d3876e7bd6b50133d25bb11dd3c54 (github.com)
在sonic中,database容器中运行的是redis数据库,而在syncd容器中使用了hiredis(redis数据库的c接口)与redis进行通信,hiredis的github地址如下:
https://github.com/search?q=hiredis
1.m_contextConfig初始化
在sonic-sairedis\syncd\Syncd.cpp的syncd构造函数中调用了如下函数,对容器的上下文进行初始化:
auto ccc = sairedis::ContextConfigContainer::loadFromFile(m_commandLineOptions->m_contextConfig.c_str());
m_contextConfig = ccc->get(m_commandLineOptions->m_globalContext);
m_commandLineOptions->m_contextConfig.c_str()是启动syncd时指定的命令行参数,如果不指定,将使用默认配置,代码不展开,直接给出默认情况下一些关键变量的值,若有兴趣可自行查看代码
class ContextConfig | m_contextConfig |
uint32_t m_guid | 0 |
std::string m_name | "syncd" |
std::string m_dbAsic | "ASIC_DB" |
std::string m_dbCounters | "COUNTERS_DB" |
std::string m_dbFlex | "FLEX_COUNTER_DB" |
std::string m_dbState | "STATE_DB" |
bool m_zmqEnable | FALSE |
2.连接redis,切换到指定数据库
m_dbAsic = std::make_shared<swss::DBConnector>(m_contextConfig->m_dbAsic, 0);
调用swss::DBConnector的构造函数,该 class 定义如下:
explicit DBConnector(const DBConnector &other);
DBConnector(int dbId, const RedisContext &ctx);
DBConnector(int dbId, const std::string &hostname, int port, unsigned int timeout);
DBConnector(int dbId, const std::string &unixPath, unsigned int timeout);
DBConnector(const std::string &dbName, unsigned int timeout, bool isTcpConn = false);
DBConnector(const std::string &dbName, unsigned int timeout, bool isTcpConn, const std::string &netns);
可以看到,这是一个多态的构造函数,根据传参可知,调用了第三个构造函数,并且bool isTcpConn = false。代码如下:
#define EMPTY_NAMESPACE std::string()//在别处定义
DBConnector::DBConnector(const string& dbName, unsigned int timeout, bool isTcpConn, const string& netns)
: m_dbId(SonicDBConfig::getDbId(dbName, netns))
, m_dbName(dbName)
, m_namespace(netns)
{
struct timeval tv = {0, (suseconds_t)timeout * 1000};
struct timeval *ptv = timeout ? &tv : NULL;
if (isTcpConn)
{
initContext(SonicDBConfig::getDbHostname(dbName, netns).c_str(), SonicDBConfig::getDbPort(dbName, netns), ptv);
}
else
{
initContext(SonicDBConfig::getDbSock(dbName, netns).c_str(), ptv);
}
select(this);
}
DBConnector::DBConnector(const string& dbName, unsigned int timeout, bool isTcpConn)
: DBConnector(dbName, timeout, isTcpConn, EMPTY_NAMESPACE)
{
// Empty constructor
}
timeout为0,所以*ptv为NULL。
由于isTcpConn = false,所以执行else分支下的代码。先来看下initContext函数:
void RedisContext::initContext(const char *path, const timeval *tv)
{
if (tv)
{
m_conn = redisConnectUnixWithTimeout(path, *tv);
}
else
{
m_conn = redisConnectUnix(path);
}
if (m_conn->err)
throw system_error(make_error_code(errc::address_not_available),
"Unable to connect to redis (unix-socket)");
}
redisConnectUnix(path)为hiredis提供的api,用以连接redis,*path为SonicDBConfig::getDbSock(dbName, netns).c_str(),dbname为"ASIC_DB",netns为空字符串。
看一下getDbSock,代码如下:
string SonicDBConfig::getDbSock(const string &dbName, const string &netns)
{
return getRedisInfo(dbName, netns).unixSocketPath;
}
RedisInstInfo& SonicDBConfig::getRedisInfo(const std::string &dbName, const std::string &netns)
{
std::lock_guard<std::recursive_mutex> guard(m_db_info_mutex);
SWSS_LOG_ENTER();
if (!m_init)//值为false
initialize(DEFAULT_SONIC_DB_CONFIG_FILE);
//余下部分省略
}
DEFAULT_SONIC_DB_CONFIG_FILE为宏定义,值在SonicDBConfig类中定义:
static constexpr const char *DEFAULT_SONIC_DB_CONFIG_FILE = "/var/run/
redis/sonic-db/database_config.json";
该路径是sonic系统中的路径,编译过程出会将sonic-swss-common\common\database_config.json放入sonic系统下的/var/run/
redis下,文件如下:
{
"INSTANCES": {
"redis":{
"hostname" : "127.0.0.1",
"port" : 6379,
"unix_socket_path" : "/var/run/redis/redis.sock"
},
"redis_chassis":{
"hostname" : "redis_chassis.server",
"port" : 6380,
"unix_socket_path" : "/var/run/redis/redis_chassis.sock"
}
},
"DATABASES" : {
"APPL_DB" : {
"id" : 0,
"separator": ":",
"instance" : "redis"
},
"ASIC_DB" : {
"id" : 1,
"separator": ":",
"instance" : "redis"
},
"COUNTERS_DB" : {
"id" : 2,
"separator": ":",
"instance" : "redis"
},
"LOGLEVEL_DB" : {
"id" : 3,
"separator": ":",
"instance" : "redis"
},
"CONFIG_DB" : {
"id" : 4,
"separator": "|",
"instance" : "redis"
},
"PFC_WD_DB" : {
"id" : 5,
"separator": ":",
"instance" : "redis"
},
"FLEX_COUNTER_DB" : {
"id" : 5,
"separator": ":",
"instance" : "redis"
},
"STATE_DB" : {
"id" : 6,
"separator": "|",
"instance" : "redis"
},
"SNMP_OVERLAY_DB" : {
"id" : 7,
"separator": "|",
"instance" : "redis"
},
"RESTAPI_DB" : {
"id" : 8,
"separator": "|",
"instance" : "redis"
},
"GB_ASIC_DB" : {
"id" : 9,
"separator": "|",
"instance" : "redis"
},
"GB_COUNTERS_DB" : {
"id" : 10,
"separator": "|",
"instance" : "redis"
},
"GB_FLEX_COUNTER_DB" : {
"id" : 11,
"separator": "|",
"instance" : "redis"
},
"CHASSIS_APP_DB" : {
"id" : 12,
"separator": "|",
"instance" : "redis_chassis"
},
"CHASSIS_STATE_DB" : {
"id" : 13,
"separator": "|",
"instance" : "redis_chassis"
}
},
"VERSION" : "1.0"
}
综上所述,initContext函数连接了/var/run/redis/redis.sock,接下来看select(this),代码如下
void DBConnector::select(DBConnector *db)
{
string select("SELECT ");
select += to_string(db->getDbId());
RedisReply r(db, select, REDIS_REPLY_STATUS);
r.checkStatusOK();
}
顾名思义,改函数执行了redis命令中的SELECT命令,切换到ASIC数据库。RedisReply封装了hiredis中的api,此处不展开。
更多推荐
所有评论(0)