不停服更新二進制文件
阿新 • • 發佈:2018-09-12
bee execvp unistd.h 信號 cor consola die rap acc 原文
原文
http://www.zhaoch.top/操作系統/linux/不停服更新二進制文件.html
不停服更新二進制文件
www.zhaoch.top > 操作系統 > linux
雖然目前分布式架構和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
- My github location
- View Source of this website GhostZch.github.io
- Commit issues to discuss with me and others
不停服更新二進制文件