Skip to content

《函数设计尽量功能单一》 #30

Description

@bingtaoli

设计一个函数时候,先想好这个方法要做哪些事情,那么这些事情组合在一起就成了这个函数,我们要求事情尽量少而精,这样函数看上去就比较清晰明了。

举个例子,我在设计我们网络项目时候的一个函数,检查是否支持cgroup网络管控,以进行服务降级。

开始的代码如下。Initialize这么重要的初始化入口内部逻辑看上去混乱,也容易出错。我这里确实就因为写得乱而把业务逻辑搞错了。b_cgroup=true时候,去创建cgroup组失败的话直接返回引擎初始化失败了,本来降级的逻辑一下子把整个模块的功能搞没了。

static bool check_cgroup()
{
    char cmd[IPTABLE_CMD_LEN] = { 0 };
    str_snprintf(cmd, IPTABLE_CMD_LEN, 
        "iptables --append OUTPUT -p tcp -m cgroup --cgroup %u -j ACCEPT", FW_CG_CLASSID);
    int exit_code = loop_exec_cmd(cmd, 3);
    if (exit_code != 0) {
        error("exe failed: %s, exit code: %d\n", cmd, exit_code);
        return false;
    } else {
        info("cmd succ:%s \n", cmd);
    }
    str_snprintf(cmd, IPTABLE_CMD_LEN, 
        "iptables --delete OUTPUT -p tcp -m cgroup --cgroup %u -j ACCEPT", FW_CG_CLASSID);
    eaio_shell_execute_timeout(cmd, RUNCMD_TIMEOUT);
    return true;
}

int CIptEngine::Initialize()
{
    // 判断系统是否支持cgroup
    b_cgroup = check_cgroup();
    if (b_cgroup) {
        info("system check cgroup succ \n");
        // 创建目录
        if (eaio_dirutils_mkdir(FW_CG_NET_LIMIT_PATH, 0755)) {
            warn("eaio_dirutils_mkdir is fail, %s.\n", FW_CG_NET_LIMIT_PATH);
            return -1;
        }
        int exit_code(0);
        char cmd[IPTABLE_CMD_LEN] = { 0 };
        str_snprintf(cmd, IPTABLE_CMD_LEN, "echo %u > %snet_cls.classid", FW_CG_CLASSID, FW_CG_NET_LIMIT_PATH);
        exit_code = eaio_shell_execute_timeout(cmd, RUNCMD_TIMEOUT);
        if (exit_code != 0) {
            error("exe failed: %s, exit code: %d\n", cmd, exit_code);
            return -1;
        } else {
            info("cmd succ:%s \n", cmd);
        }
    } else {
        warn("system check cgroup failed \n");
    }
    return 0;
}

优化后代码,首先引擎初始化中检查是否需要降级,降级不会影响初始化成功;check_cgroup检查降级代码里面有两件事情,一是检查iptables是否有xt_cgroup;二是创建cgroup组是否能成功。
这样逻辑会清晰很多,可扩展性也好了,如果要修改check_cgroup的逻辑,加一个环节就行。

/**
 * 检查cgroup规则设置是否有效
*/
static bool check_iptables_cgroup()
{
    char cmd[IPTABLE_CMD_LEN] = { 0 };
    str_snprintf(cmd, IPTABLE_CMD_LEN, 
        "iptables --append OUTPUT -p icmp -m cgroup --cgroup %u -j ACCEPT", FW_CG_CLASSID);
    int exit_code = loop_exec_cmd(cmd, 3);
    if (exit_code != 0) {
        error("exe failed: %s, exit code: %d\n", cmd, exit_code);
        return false;
    } else {
        info("cmd succ:%s \n", cmd);
    }
    str_snprintf(cmd, IPTABLE_CMD_LEN, 
        "iptables --delete OUTPUT -p icmp -m cgroup --cgroup %u -j ACCEPT", FW_CG_CLASSID);
    eaio_shell_execute_timeout(cmd, RUNCMD_TIMEOUT);

    info("check_iptables_cgroup succ\n");
    return true;
}

/**
 * 检查系统支持cgroup的net_cls流量管控
*/
static bool check_net_cls()
{
    int exit_code = 0;
    // 创建目录,如果机器禁用了net_cls的话,创建cgroup组会失败
    if (eaio_dirutils_mkdir(FW_CG_NET_LIMIT_PATH, 0755)) {
        warn("eaio_dirutils_mkdir is fail, %s.\n", FW_CG_NET_LIMIT_PATH);
        return false;
    }
    // 为刚创建的cgroup组设置id
    char cmd[IPTABLE_CMD_LEN] = { 0 };
    str_snprintf(cmd, IPTABLE_CMD_LEN, "echo %u > %snet_cls.classid", FW_CG_CLASSID, FW_CG_NET_LIMIT_PATH);
    exit_code = eaio_shell_execute_timeout(cmd, RUNCMD_TIMEOUT);
    if (exit_code != 0) {
        error("exe failed: %s, exit code: %d\n", cmd, exit_code);
        return false;
    } else {
        info("cmd succ:%s \n", cmd);
    }

    info("check_net_cls succ\n");
    return true;
}

/**
 * 检查本机是否支持iptables设置cgroup网络管控
*/
static bool check_cgroup()
{
    return check_net_cls() && check_iptables_cgroup();
}

int CIptEngine::Initialize()
{
    // 判断系统是否支持cgroup
    b_cgroup = check_cgroup();
    if (b_cgroup) {
        info("system check cgroup succ \n");
    } else {
        warn("system check cgroup failed \n");
    }
    return 0;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions