主备冗余
当部署的服务镜像对可靠性和可用性有较高的要求时,您可以使用 ECSM 提供的主备冗余功能。主备冗余是指主设备上的服务承担主要的工作,而备设备上的服务在主设备正常工作时处于备份状态,当主设备出现故障或者需要进行维护时,备份设备将接管主设备的工作,以确保业务的连续运行和数据的完整性。通过主备冗余的方式可以减少因设备故障或维护导致的业务中断时间。
操作须知
ECSM 实现的主备冗余需要配置负载均衡中的主备模式,并指定主用容器和备用容器。本章节中通过主动停止主服务镜像的方式来模拟故障场景,当主服务镜像停止运行后,业务会立即切换到备服务镜像,具体实现请参考操作步骤。
前提条件
在学习本章节前需要完成如下功能的学习,本章节在操作步骤中只做关键步骤说明:
代码示例
本章节主备冗余功能服务端模拟陀螺仪传感器场景,客户端订阅陀螺仪位置数据。通过查看客户端接收数据的情况来体现主备冗余功能。
服务端的代码示例 server 模拟陀螺仪位置信息,当客户端订阅成功后,发送陀螺仪的位置信息。
客户端代码示例 client 只订阅数据并在终端输出。
#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 <unistd.h> #include <errno.h> #ifdef SYLIXOS #include <sys/vproc.h> #endif #include "vsoa_cliauto.h" /* My server password */ #define MY_SERVER_PASSWD "123456" /* 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); } }
操作步骤
部署容器镜像
在容器部署页面,单击服务列表左上方的新建,进入新建容器部署页面的基本信息界面。
单击下一步,进入选择节点界面。
根据页面提示选择节点。
在资源配置界面,设置容器的资源配置参数,并单击部署,进入完成界面。
完成容器部署,新部署的容器会显示在容器部署页面的服务列表中。
通过相同的方式部署备用服务镜像 server_slave。
配置主备模式
在左侧导航栏单击服务治理,进入服务治理页面。
在服务治理页面,选择需要配置主备冗余的微服务,单击其操作栏的负载均衡,打开负载均衡策略窗口。
选择主备,并分别设置主备服务。
运行客户端,发起服务订阅,客户端开始接收主用设备发布的数据。
触发主备切换
选择 server_master 服务镜像,单击停止按钮用于模拟主设备服务故障场景。
查看客户端结果输出,业务短暂中断后立即切换到备用设备服务。
说明:
- 主用服务设备发生故障之前,客户端不断地接收主用设备发布的数据。
- 主用服务设备发生故障之后,连接会被短暂地断开,接着客户端迅速连接到备用设备并继续接收其发布的数据。