设计一个函数时候,先想好这个方法要做哪些事情,那么这些事情组合在一起就成了这个函数,我们要求事情尽量少而精,这样函数看上去就比较清晰明了。
举个例子,我在设计我们网络项目时候的一个函数,检查是否支持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;
}
设计一个函数时候,先想好这个方法要做哪些事情,那么这些事情组合在一起就成了这个函数,我们要求事情尽量少而精,这样函数看上去就比较清晰明了。
举个例子,我在设计我们网络项目时候的一个函数,检查是否支持cgroup网络管控,以进行服务降级。
开始的代码如下。Initialize这么重要的初始化入口内部逻辑看上去混乱,也容易出错。我这里确实就因为写得乱而把业务逻辑搞错了。b_cgroup=true时候,去创建cgroup组失败的话直接返回引擎初始化失败了,本来降级的逻辑一下子把整个模块的功能搞没了。
优化后代码,首先引擎初始化中检查是否需要降级,降级不会影响初始化成功;check_cgroup检查降级代码里面有两件事情,一是检查iptables是否有xt_cgroup;二是创建cgroup组是否能成功。
这样逻辑会清晰很多,可扩展性也好了,如果要修改check_cgroup的逻辑,加一个环节就行。