摘要

整个功能实现基本由以下步骤组成

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 ContextConfigm_contextConfig
uint32_t m_guid0
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_zmqEnableFALSE

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,此处不展开。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐