zoukankan      html  css  js  c++  java
  • 不停服更新二进制文件

     

    原文 

    http://www.zhaoch.top/操作系统/linux/不停服更新二进制文件.html

    不停服更新二进制文件

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

    img

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

    痛点

    • 已有的链接不中断,直至这个客户端完成业务处理
    • 使用新的程序文件处理新的请求
    • 新老进程同时存在还要监听相同的端口

    思路

    • 因为要监听相同端口所以一般都是父子进程
    • 常用的fork不能更新二进制文件,需要再通过exec重新加载文件
    • 监听相同端口可以通过把句柄作为参数或者环境变量传递给exec族函数继续使用

    参考ngx_exec_new_binary

    过程

    • 更新可执行程序
    • 向主进程发送信号(例如:USR2),立即设置全局变量
    • 事件处理循环中老进程检查到全局变量后,不再accept新的连接,已有的连接正常处理
    • fork一个子进程,子进程通过exec族函数更新二进制,将listen的句柄通过参数或者环境变量
    • 子进程开始accept,新接入的连接由新程序处理
    • 老程序持续运行,直至所有的连接都完成业务(可设置超时时间),退出
    • 此时只有子程序在运行

    示例代码

    编辑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


  • 相关阅读:
    【域控】获取域控用户
    【MongoDB】开启认证权限
    【MongoDB】 安装为windows services
    【 Quartz】使用 JobListener (任务监听器可实现) 我想在一个任务执行后在执行第二个任务怎么办呢
    【多路复用】I/O多路复用
    静态类和静态类成员
    C#
    response.redirect和server.Transfer的差别详解
    DataReader
    受管制的代码和强类型系统
  • 原文地址:https://www.cnblogs.com/GhostZCH/p/9637106.html
Copyright © 2011-2022 走看看