滚动升级
滚动升级是指在不中断服务的情况下,对应用程序进行升级,是 ECSM 中的一种重要功能。ECSM 会根据用户的要求,逐步将旧版本的微服务逐步替换为新版本的微服务,直到所有的微服务都已经被更新为止。通过滚动升级的方式可以减少服务中断的时间,同时也保证了应用程序的稳定性。
操作须知
ECSM 实现的滚动升级需要配置负载均衡中的轮询模式。负载均衡可以保证在服务升级过程中,业务不中断。当升级其中一个节点上的服务时,业务切换到另一个节点服务上。服务升级完成后,开始升级另一个节点上服务,业务重新切换到已经升级完成的节点服务上。
前提条件
在学习本章节前需要完成如下功能的学习,本章节在操作步骤中只做关键步骤说明:
代码示例
本章节滚动升级功能服务端模拟陀螺仪传感器场景,客户端订阅陀螺仪位置数据。通过查看客户端在服务端升级前后接收的数据格式对比,展示滚动升级的功能。
服务端提供两个版本的代码示例 axisSer1.0.0 和 axisSer2.0.0 分别使用普通发布接口和快速发布接口,同时发布内容也有区别,具体差异请查看代码和最终运行结果。
客户端代码示例 axisCli 只订阅数据并在终端输出。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <netinet/in.h> #include <arpa/inet.h> #include "vsoa_server.h" #include "vsoa_platform.h" #define MY_SERVER_PASSWD "123456" #define AXIS_SER_BUF_LEN 100 #define AXIS_SER_PORT 3002 #define AXIS_SER_AUTO_PORT "VSOA_AUTO_PORT=" #ifndef TRUE #define TRUE 1 #endif static vsoa_server_t *axisServer; /* * Publish axis thread */ static void *publish_axis_thread (void *arg) { vsoa_url_t url; int roll, pitch,yaw; vsoa_payload_t payload; char param[AXIS_SER_BUF_LEN + 1]; url.url = "/axis"; url.url_len = strlen(url.url); payload.data = NULL; payload.data_len = 0; payload.param = param; roll = 1; pitch = 1; yaw = 1; while (TRUE) { sleep(1); if (!vsoa_server_is_subscribed(axisServer, &url)) { continue; } payload.param_len = snprintf(param, AXIS_SER_BUF_LEN, "{\"roll\": %d, \"pitch\": %d, \"yaw\": %d}", roll++, pitch++, yaw++); vsoa_server_publish(axisServer, &url, &payload); } return (NULL); } int main (int argc, char **argv) { uint16_t server_port = AXIS_SER_PORT; struct sockaddr_in addr; char *axis_serv_auto_port = getenv(AXIS_SER_AUTO_PORT); if (axis_serv_auto_port != NULL) { fprintf(stdout, "axis_ser port is %s .\n", axis_serv_auto_port); server_port = atoi(axis_serv_auto_port); if (server_port == 0) { server_port = AXIS_SER_PORT; } } memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_port = htons(server_port); addr.sin_addr.s_addr = INADDR_ANY; #ifdef VSOA_HAS_SIN_LEN addr.sin_len = sizeof(struct sockaddr_in); #endif /* * Initialize server */ axisServer = vsoa_server_create("{\"name\":\"axis_server\"}"); if (!axisServer) { fprintf(stderr, "Can not create VSOA server!\n"); return (-1); } /* * If need password */ vsoa_server_passwd(axisServer, MY_SERVER_PASSWD); /* * Start server */ if (!vsoa_server_start(axisServer, (struct sockaddr *)&addr, sizeof(struct sockaddr_in))) { vsoa_server_close(axisServer); fprintf(stderr, "Can not start VSOA server!\n"); return (-1); } fprintf(stderr, " start VSOA server success!\n"); /* * Create publish thread */ pthread_t pubThreadid; int threadRet = pthread_create(&pubThreadid, NULL, publish_axis_thread, NULL); if (threadRet) { fprintf(stderr, " create publish thread fail errno is %d!\n", errno); return (-1); } /* * set 1s timeout */ struct timespec timeout = {1, 0}; int cnt, max_fd; fd_set fds; while (TRUE) { FD_ZERO(&fds); max_fd = vsoa_server_fds(axisServer, &fds); cnt = pselect(max_fd + 1, &fds, NULL, NULL, &timeout, NULL); if (cnt > 0) { vsoa_server_input_fds(axisServer, &fds); } } return (0); }
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <netinet/in.h> #include <arpa/inet.h> #include "vsoa_server.h" #include "vsoa_platform.h" #define MY_SERVER_PASSWD "123456" #define AXIS_SER_BUF_LEN 100 #define AXIS_SER_PORT 3002 #define AXIS_SER_AUTO_PORT "VSOA_AUTO_PORT=" #ifndef TRUE #define TRUE 1 #endif static vsoa_server_t *axisServer; /* * Publish axis thread */ static void *publish_axis_thread (void *arg) { vsoa_url_t url; int roll, pitch,yaw; vsoa_payload_t payload; char param[AXIS_SER_BUF_LEN + 1]; url.url = "/axis"; url.url_len = strlen(url.url); payload.data = NULL; payload.data_len = 0; payload.param = param; roll = 1; pitch = 1; yaw = 1; while (TRUE) { sleep(1); if (!vsoa_server_is_subscribed(axisServer, &url)) { continue; } payload.param_len = snprintf(param, AXIS_SER_BUF_LEN, "{\"ROLL\": %d, \"PITCH\": %d, \"YAW\": %d}", roll++, pitch++, yaw++); roll += 10; pitch += 10; yaw += 10; vsoa_server_quick_publish(axisServer, &url, &payload); } return (NULL); } int main (int argc, char **argv) { uint16_t server_port = AXIS_SER_PORT; struct sockaddr_in addr; char *axis_serv_auto_port = getenv(AXIS_SER_AUTO_PORT); if (axis_serv_auto_port != NULL) { fprintf(stdout, "axis_ser port is %s .\n", axis_serv_auto_port); server_port = atoi(axis_serv_auto_port); if (server_port == 0) { server_port = AXIS_SER_PORT; } } memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_port = htons(server_port); addr.sin_addr.s_addr = INADDR_ANY; #ifdef VSOA_HAS_SIN_LEN addr.sin_len = sizeof(struct sockaddr_in); #endif /* * Initialize server */ axisServer = vsoa_server_create("{\"name\":\"axis_server\"}"); if (!axisServer) { fprintf(stderr, "Can not create VSOA server!\n"); return (-1); } /* * If need password */ vsoa_server_passwd(axisServer, MY_SERVER_PASSWD); /* * Start server */ if (!vsoa_server_start(axisServer, (struct sockaddr *)&addr, sizeof(struct sockaddr_in))) { vsoa_server_close(axisServer); fprintf(stderr, "Can not start VSOA server!\n"); return (-1); } fprintf(stderr, " start VSOA server success!\n"); /* * Create publish thread */ pthread_t pub_threadid; int threadRet = pthread_create(&pub_threadid, NULL, publish_axis_thread, NULL); if (threadRet) { fprintf(stderr, " create publish thread fail errno is %d!\n", errno); return (-1); } /* * set 1s timeout */ struct timespec timeout = {1, 0}; int cnt, max_fd; fd_set fds; while (TRUE) { FD_ZERO(&fds); max_fd = vsoa_server_fds(axisServer, &fds); cnt = pselect(max_fd + 1, &fds, NULL, NULL, &timeout, NULL); if (cnt > 0) { vsoa_server_input_fds(axisServer, &fds); } } return (0); }
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #ifdef SYLIXOS #include <sys/vproc.h> #endif #include "vsoa_cliauto.h" /* My server password */ #define MY_SERVER_PASSWD "123456" #ifndef TRUE #define TRUE 1 #endif /* My client */ static vsoa_client_t *client; /* My client auto */ static vsoa_client_auto_t *cliauto; /* My subscribe (string pointer array) */ static char * const sub_urls[] = { "/axis" }; static void onconnect (void *arg, vsoa_client_auto_t *cliauto, bool connect, const char *info) { printf("On connect, connect: %s, info: %s\n", (connect == true) ? "connected" : "disconnected", info); } /* * On subscribed messages received */ static void onmessage (void *arg, struct vsoa_client *client, vsoa_url_t *url, vsoa_payload_t *payload, bool quick) { printf("On message, URL: %.*s payload: %.*s\n", (int)url->url_len, url->url, (int)payload->param_len, payload->param); } /* * main function */ int main (int argc, char **argv) { #ifdef SYLIXOS vprocExitModeSet(getpid(), LW_VPROC_EXIT_FORCE); #endif /* * Create client auto robot */ cliauto = vsoa_client_auto_create(onmessage, NULL); client = vsoa_client_auto_handle(cliauto); if (!vsoa_client_auto_setup(cliauto, onconnect, NULL)) { vsoa_client_auto_delete(cliauto); fprintf(stderr, "Cannot register connect callback: %s (%d)\n", strerror(errno), errno); return -1; } /* * Client auto robot start * The robot will automatically connect to the specified server and maintain the connection. * At this time, the developer only needs to focus on the business. */ vsoa_client_auto_start(cliauto, "vsoa://axis_server", MY_SERVER_PASSWD, sub_urls, 1, 1000, 1000, 1000); while (TRUE) { sleep(1); } }
操作步骤
部署容器镜像
在容器部署页面,单击服务列表左上方的新建,进入新建容器部署页面的基本信息界面。
单击下一步,进入选择节点界面。
根据页面提示选择节点。
在资源配置界面,设置容器的资源配置参数,并单击部署,进入完成界面。
完成容器部署,新部署的容器会显示在容器部署页面的服务列表中。
通过相同的方式部署冗余服务镜像。
运行客户端程序,发起服务订阅。
配置负载均衡
服务治理页面,单击负载均衡,进入负载均衡模式配置页面。
点击轮询,完成负载均衡配置。
升级容器镜像
步骤 1:升级 axis_ser2 容器镜像
在容器部署页面,点击需要升级服务的右侧详情,进入详情页面。
点击修改,在容器部署的基本信息界面,选择新版本容器镜像。
根据提示信息,完成容器部署。
查看客户端回显页面,服务没有中断。
步骤 2:升级 axis_ser1 容器镜像
参考步骤 1 的升级方式,升级下一个服务,最终完成服务的滚动升级。
查看客户端回显页面,客户端已经订阅到新版本数据。