不停服更新二进制文件

www.zhaoch.top > 操作系统 > linux

img

虽然目前分布式架构和keepalived等工具的存在,对于某些特殊的程序,仍然需要不停服更新二进制文件。 这里参考nginx的实现介绍下如何实现这个功能的

痛点

思路

参考ngx_exec_new_binary

过程

示例代码

编辑app_new.cpp 与 app_old.cpp两个文件,app_old.cpp内容如下

#include <stdlib.h>
#include <iostream>

#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>


using namespace std;
extern char **environ;

// difference form old and new in binary file
const char *TITLE = "APP-OLD ";

bool g_update = false;
bool g_stop = false;

void signal_handler(int sig)
{
    cout << "signal_handler" << sig << endl;
    if (sig == SIGUSR2) {
        g_update = true;
    }
    g_stop = true;
}

void update_binary(char* argv[], int fd)
{
    if (fork() != 0) {
        return;
    }

    char *fdstr = new char[100];
    snprintf(fdstr, 100, "FD=%d", fd);

    int n;
    for (n = 0; environ[n]; n++);

    // copy environ
    char **env = new char*[n + 1];
    for (int i = 0; i < n - 1; i++) {
        env[i] = environ[i];
    }

    // app fd into environ
    env[n - 1] = fdstr;
    env[n] = NULL;

    // importent exec app_new
    execvpe("./app_new", argv, env);
}

int main(int argc, char* argv[])
{
    int fd = -1;
    g_stop = false;
    g_update = false;

    cout << TITLE << getpid() << endl;

    char *oldfd = getenv("FD");

    if (oldfd) {
        fd = atoi(oldfd);

    } else {
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(9999);
        addr.sin_addr.s_addr = inet_addr("127.0.0.1");

        fd = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0);
        if (fd < 0) {
            return 1;
        }
        if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0 || listen(fd, 4096) < 0) {
            close(fd);
            return 1;
        }

        if (signal(SIGUSR2, signal_handler) == SIG_ERR
            || signal(SIGTERM, signal_handler) == SIG_ERR
            || signal(SIGINT, signal_handler) == SIG_ERR) {

            close(fd);
            return 1;
        }
    }

    while (!g_stop) {
        cout << TITLE << getpid() << " fd=" << fd << endl;
        sleep(1);
    }

    if (g_update) {
        update_binary(argv, fd);
    }

    // semulate waiting for the all connection diedown
    for (int i = 0; i < 10; i++) {
        cout << TITLE << getpid() << " fd=" << fd << endl;
        sleep(1);
    }

    cout << TITLE << "end" << endl;

    return 0;
}

app_new.cpp仅仅TITLE取名不同,用于区分新旧二进制文件

diff app_old.cpp app_new.cpp

< const char *TITLE = "APP-OLD ";
---
> const char *TITLE = "APP-NEW ";

编译两个文件用于对比测试

g++ app_old.cpp -o app_old
g++ app_new.cpp -o app_new

执行结果

./app_old
APP-OLD 8521
APP-OLD 8521 fd=3
APP-OLD 8521 fd=3
APP-OLD 8521 fd=3
signal_handler12  <- 执行命令 kill -USR2 8521
APP-OLD 8521 fd=3 <- 新旧并行
APP-NEW 8524
APP-NEW 8524 fd=3
APP-OLD 8521 fd=3
APP-NEW 8524 fd=3
APP-OLD 8521 fd=3
APP-NEW 8524 fd=3
APP-OLD 8521 fd=3
APP-NEW 8524 fd=3
APP-OLD 8521 fd=3
APP-NEW 8524 fd=3
APP-OLD 8521 fd=3
APP-NEW 8524 fd=3
APP-OLD 8521 fd=3
APP-NEW 8524 fd=3
APP-OLD 8521 fd=3
APP-NEW 8524 fd=3
APP-OLD 8521 fd=3
APP-NEW 8524 fd=3
APP-OLD 8521 fd=3
APP-NEW 8524 fd=3
APP-OLD end          <- 旧进程退出,只剩新进程
APP-NEW 8524 fd=3
APP-NEW 8524 fd=3
APP-NEW 8524 fd=3
APP-NEW 8524 fd=3
APP-NEW 8524 fd=3
APP-NEW 8524 fd=3

The End