zeroMQ初体验-28.可靠性-主从模式

来源:互联网 发布:吉利汽车出口数据 编辑:程序博客网 时间:2024/06/04 18:32
 虽然"硬盘模式"看起来已经非常靠谱了,不过,还记得前段时间"亚马逊云拓机"么,异地灾备似乎才能真正当的上'高可用',暂且抛开物理、成本上的问题,zeromq也为此提供了靠谱的支持~

官方声明:
1.这是一个直接的高可用的方案
2.足够简单,让人易于理解和使用
3.在需要时可以提供可靠的转移

比较经典也比较靠谱的模式简图:


迁移:


作为一个高可用的框架,至少得做到如下几点:
  • 为灾难性事故做准备,例如主机房失火了、地震了等。
  • 切换应该在尽可能短的时间内完成(60秒内,最好是10s内)。
  • 由主切换到从可以是自动的,不过恢复时最好手动完成。
  • 客户端应该明了这种切换机制,最好在api中给出自动方案。
  • 有明确的标识来明确主从。
  • 主从间不能有启动顺序的依赖。
  • 服务端的切换不会导致客户端崩溃(或许会重新连接)。
  • 主从状态必须可监控。
  • 主从间的连接必须靠谱,高速。


基于如此结构的一些假设:
  • 一主一从结构已经足够保险,不再需要多层次的备份。
  • 主和从都能单独负载整个需求,而不是平衡性的承载整个符合。
  • 要有足够的预算供起这么一个日常空转的从。


这些并没有被涉及到:
  • 从并非用来负载均衡。
  • 信息不持久化。
  • 主从间不会自动探测对方。
  • 主从间不会同步服务器的状态、信息。


如果要配置一对主从,需要在各自的配置中设定好对方及同步时间。

匹配的,作为客户端需要做的:
1.知道主从的地址
2.先连主,失败则试试从
3.检测失效的连接(心跳)
4.重连时遵守2
5.重新在请求的服务器上创建状态
6.当主从切换时,可以重新传递请求

当然,这些都会由客户端的api完成。这里注意:作者一再强调,主从同时只能有一个在提供服务!

服务器端:
C代码 复制代码 收藏代码
  1. //   
  2. //  Binary Star server   
  3. //   
  4. #include "czmq.h"   
  5.   
  6. //  We send state information every this often  
  7. //  If peer doesn't respond in two heartbeats, it is 'dead'  
  8. #define HEARTBEAT 1000          //  In msecs  
  9.   
  10. //  States we can be in at any point in time  
  11. typedefenum {   
  12.     STATE_PRIMARY = 1,          //  Primary, waiting for peer to connect  
  13.     STATE_BACKUP = 2,           //  Backup, waiting for peer to connect  
  14.     STATE_ACTIVE = 3,           //  Active - accepting connections  
  15.     STATE_PASSIVE = 4           //  Passive - not accepting connections  
  16. } state_t;   
  17.   
  18. //  Events, which start with the states our peer can be in  
  19. typedefenum {   
  20.     PEER_PRIMARY = 1,           //  HA peer is pending primary  
  21.     PEER_BACKUP = 2,            //  HA peer is pending backup  
  22.     PEER_ACTIVE = 3,            //  HA peer is active  
  23.     PEER_PASSIVE = 4,           //  HA peer is passive  
  24.     CLIENT_REQUEST = 5          //  Client makes request  
  25. } event_t;   
  26.   
  27. //  Our finite state machine   
  28. typedefstruct {   
  29.     state_t state;              //  Current state  
  30.     event_t event;              //  Current event  
  31.     int64_t peer_expiry;        //  When peer is considered 'dead'  
  32. } bstar_t;   
  33.   
  34. //  Execute finite state machine (apply event to state)  
  35. //  Returns TRUE if there was an exception  
  36.   
  37. static Bool   
  38. s_state_machine (bstar_t *fsm)   
  39. {   
  40.     Bool exception = FALSE;   
  41.     //  Primary server is waiting for peer to connect  
  42.     //  Accepts CLIENT_REQUEST events in this state  
  43.     if (fsm->state == STATE_PRIMARY) {   
  44.         if (fsm->event == PEER_BACKUP) {   
  45.             printf ("I: connected to backup (slave), ready as master\n");   
  46.             fsm->state = STATE_ACTIVE;   
  47.         }   
  48.         else  
  49.         if (fsm->event == PEER_ACTIVE) {   
  50.             printf ("I: connected to backup (master), ready as slave\n");   
  51.             fsm->state = STATE_PASSIVE;   
  52.         }   
  53.     }   
  54.     else  
  55.     //  Backup server is waiting for peer to connect  
  56.     //  Rejects CLIENT_REQUEST events in this state  
  57.     if (fsm->state == STATE_BACKUP) {   
  58.         if (fsm->event == PEER_ACTIVE) {   
  59.             printf ("I: connected to primary (master), ready as slave\n");   
  60.             fsm->state = STATE_PASSIVE;   
  61.         }   
  62.         else  
  63.         if (fsm->event == CLIENT_REQUEST)   
  64.             exception = TRUE;   
  65.     }   
  66.     else  
  67.     //  Server is active   
  68.     //  Accepts CLIENT_REQUEST events in this state  
  69.     if (fsm->state == STATE_ACTIVE) {   
  70.         if (fsm->event == PEER_ACTIVE) {   
  71.             //  Two masters would mean split-brain  
  72.             printf ("E: fatal error - dual masters, aborting\n");   
  73.             exception = TRUE;   
  74.         }   
  75.     }   
  76.     else  
  77.     //  Server is passive   
  78.     //  CLIENT_REQUEST events can trigger failover if peer looks dead  
  79.     if (fsm->state == STATE_PASSIVE) {   
  80.         if (fsm->event == PEER_PRIMARY) {   
  81.             //  Peer is restarting - become active, peer will go passive  
  82.             printf ("I: primary (slave) is restarting, ready as master\n");   
  83.             fsm->state = STATE_ACTIVE;   
  84.         }   
  85.         else  
  86.         if (fsm->event == PEER_BACKUP) {   
  87.             //  Peer is restarting - become active, peer will go passive  
  88.             printf ("I: backup (slave) is restarting, ready as master\n");   
  89.             fsm->state = STATE_ACTIVE;   
  90.         }   
  91.         else  
  92.         if (fsm->event == PEER_PASSIVE) {   
  93.             //  Two passives would mean cluster would be non-responsive  
  94.             printf ("E: fatal error - dual slaves, aborting\n");   
  95.             exception = TRUE;   
  96.         }   
  97.         else  
  98.         if (fsm->event == CLIENT_REQUEST) {   
  99.             //  Peer becomes master if timeout has passed  
  100.             //  It's the client request that triggers the failover  
  101.             assert (fsm->peer_expiry > 0);   
  102.             if (zclock_time () >= fsm->peer_expiry) {   
  103.                 //  If peer is dead, switch to the active state  
  104.                 printf ("I: failover successful, ready as master\n");   
  105.                 fsm->state = STATE_ACTIVE;   
  106.             }   
  107.             else  
  108.                 //  If peer is alive, reject connections  
  109.                 exception = TRUE;   
  110.         }   
  111.     }   
  112.     return exception;   
  113. }   
  114.   
  115. int main (int argc, char *argv [])   
  116. {   
  117.     //  Arguments can be either of:   
  118.     //      -p  primary server, at tcp://localhost:5001  
  119.     //      -b  backup server, at tcp://localhost:5002  
  120.     zctx_t *ctx = zctx_new ();   
  121.     void *statepub = zsocket_new (ctx, ZMQ_PUB);   
  122.     void *statesub = zsocket_new (ctx, ZMQ_SUB);   
  123.     void *frontend = zsocket_new (ctx, ZMQ_ROUTER);   
  124.     bstar_t fsm = { 0 };   
  125.   
  126.     if (argc == 2 && streq (argv [1], "-p")) {   
  127.         printf ("I: Primary master, waiting for backup (slave)\n");   
  128.         zsocket_bind (frontend, "tcp://*:5001");   
  129.         zsocket_bind (statepub, "tcp://*:5003");   
  130.         zsocket_connect (statesub, "tcp://localhost:5004");   
  131.         fsm.state = STATE_PRIMARY;   
  132.     }   
  133.     else  
  134.     if (argc == 2 && streq (argv [1], "-b")) {   
  135.         printf ("I: Backup slave, waiting for primary (master)\n");   
  136.         zsocket_bind (frontend, "tcp://*:5002");   
  137.         zsocket_bind (statepub, "tcp://*:5004");   
  138.         zsocket_connect (statesub, "tcp://localhost:5003");   
  139.         fsm.state = STATE_BACKUP;   
  140.     }   
  141.     else {   
  142.         printf ("Usage: bstarsrv { -p | -b }\n");   
  143.         zctx_destroy (&ctx);   
  144.         exit (0);   
  145.     }   
  146.     //  Set timer for next outgoing state message  
  147.     int64_t send_state_at = zclock_time () + HEARTBEAT;   
  148.   
  149.     while (!zctx_interrupted) {   
  150.         zmq_pollitem_t items [] = {   
  151.             { frontend, 0, ZMQ_POLLIN, 0 },   
  152.             { statesub, 0, ZMQ_POLLIN, 0 }   
  153.         };   
  154.         int time_left = (int) ((send_state_at - zclock_time ()));   
  155.         if (time_left < 0)   
  156.             time_left = 0;   
  157.         int rc = zmq_poll (items, 2, time_left * ZMQ_POLL_MSEC);   
  158.         if (rc == -1)   
  159.             break;              //  Context has been shut down  
  160.   
  161.         if (items [0].revents & ZMQ_POLLIN) {   
  162.             //  Have a client request   
  163.             zmsg_t *msg = zmsg_recv (frontend);   
  164.             fsm.event = CLIENT_REQUEST;   
  165.             if (s_state_machine (&fsm) == FALSE)   
  166.                 //  Answer client by echoing request back  
  167.                 zmsg_send (&msg, frontend);   
  168.             else  
  169.                 zmsg_destroy (&msg);   
  170.         }   
  171.         if (items [1].revents & ZMQ_POLLIN) {   
  172.             //  Have state from our peer, execute as event  
  173.             char *message = zstr_recv (statesub);   
  174.             fsm.event = atoi (message);   
  175.             free (message);   
  176.             if (s_state_machine (&fsm))   
  177.                 break;          //  Error, so exit  
  178.             fsm.peer_expiry = zclock_time () + 2 * HEARTBEAT;   
  179.         }   
  180.         //  If we timed-out, send state to peer  
  181.         if (zclock_time () >= send_state_at) {   
  182.             char message [2];   
  183.             sprintf (message, "%d", fsm.state);   
  184.             zstr_send (statepub, message);   
  185.             send_state_at = zclock_time () + HEARTBEAT;   
  186.         }   
  187.     }   
  188.     if (zctx_interrupted)   
  189.         printf ("W: interrupted\n");   
  190.   
  191.     //  Shutdown sockets and context   
  192.     zctx_destroy (&ctx);   
  193.     return 0;   
  194. }  


客户端:
C代码 复制代码 收藏代码
  1. //   
  2. //  Binary Star client   
  3. //   
  4. #include "czmq.h"   
  5.   
  6. #define REQUEST_TIMEOUT     1000    //  msecs  
  7. #define SETTLE_DELAY        2000    //  Before failing over  
  8.   
  9. int main (void)   
  10. {   
  11.     zctx_t *ctx = zctx_new ();   
  12.   
  13.     char *server [] = { "tcp://localhost:5001", "tcp://localhost:5002" };   
  14.     uint server_nbr = 0;   
  15.   
  16.     printf ("I: connecting to server at %s…\n", server [server_nbr]);   
  17.     void *client = zsocket_new (ctx, ZMQ_REQ);   
  18.     zsocket_connect (client, server [server_nbr]);   
  19.   
  20.     int sequence = 0;   
  21.     while (!zctx_interrupted) {   
  22.         //  We send a request, then we work to get a reply  
  23.         char request [10];   
  24.         sprintf (request, "%d", ++sequence);   
  25.         zstr_send (client, request);   
  26.   
  27.         int expect_reply = 1;   
  28.         while (expect_reply) {   
  29.             //  Poll socket for a reply, with timeout  
  30.             zmq_pollitem_t items [] = { { client, 0, ZMQ_POLLIN, 0 } };   
  31.             int rc = zmq_poll (items, 1, REQUEST_TIMEOUT * ZMQ_POLL_MSEC);   
  32.             if (rc == -1)   
  33.                 break;          //  Interrupted  
  34.   
  35.             //  If we got a reply, process it  
  36.             if (items [0].revents & ZMQ_POLLIN) {   
  37.                 //  We got a reply from the server, must match sequence  
  38.                 char *reply = zstr_recv (client);   
  39.                 if (atoi (reply) == sequence) {   
  40.                     printf ("I: server replied OK (%s)\n", reply);   
  41.                     expect_reply = 0;   
  42.                     sleep (1);  //  One request per second  
  43.                 }   
  44.                 else {   
  45.                     printf ("E: malformed reply from server: %s\n",   
  46.                         reply);   
  47.                 }   
  48.                 free (reply);   
  49.             }   
  50.             else {   
  51.                 printf ("W: no response from server, failing over\n");   
  52.                 //  Old socket is confused; close it and open a new one  
  53.                 zsocket_destroy (ctx, client);   
  54.                 server_nbr = (server_nbr + 1) % 2;   
  55.                 zclock_sleep (SETTLE_DELAY);   
  56.                 printf ("I: connecting to server at %s…\n",   
  57.                         server [server_nbr]);   
  58.                 client = zsocket_new (ctx, ZMQ_REQ);   
  59.                 zsocket_connect (client, server [server_nbr]);   
  60.   
  61.                 //  Send request again, on new socket  
  62.                 zstr_send (client, request);   
  63.             }   
  64.         }   
  65.     }   
  66.     zctx_destroy (&ctx);   
  67.     return 0;   
  68. }  


主从间的状态图: