zeroMQ初体验-28.可靠性-主从模式
来源:互联网 发布:吉利汽车出口数据 编辑:程序博客网 时间:2024/06/04 18:32
虽然"硬盘模式"看起来已经非常靠谱了,不过,还记得前段时间"亚马逊云拓机"么,异地灾备似乎才能真正当的上'高可用',暂且抛开物理、成本上的问题,zeromq也为此提供了靠谱的支持~
官方声明:
1.这是一个直接的高可用的方案
2.足够简单,让人易于理解和使用
3.在需要时可以提供可靠的转移
比较经典也比较靠谱的模式简图:
迁移:
作为一个高可用的框架,至少得做到如下几点:
基于如此结构的一些假设:
这些并没有被涉及到:
如果要配置一对主从,需要在各自的配置中设定好对方及同步时间。
匹配的,作为客户端需要做的:
1.知道主从的地址
2.先连主,失败则试试从
3.检测失效的连接(心跳)
4.重连时遵守2
5.重新在请求的服务器上创建状态
6.当主从切换时,可以重新传递请求
当然,这些都会由客户端的api完成。这里注意:作者一再强调,主从同时只能有一个在提供服务!
服务器端:
客户端:
主从间的状态图:
官方声明:
1.这是一个直接的高可用的方案
2.足够简单,让人易于理解和使用
3.在需要时可以提供可靠的转移
比较经典也比较靠谱的模式简图:
迁移:
作为一个高可用的框架,至少得做到如下几点:
- 为灾难性事故做准备,例如主机房失火了、地震了等。
- 切换应该在尽可能短的时间内完成(60秒内,最好是10s内)。
- 由主切换到从可以是自动的,不过恢复时最好手动完成。
- 客户端应该明了这种切换机制,最好在api中给出自动方案。
- 有明确的标识来明确主从。
- 主从间不能有启动顺序的依赖。
- 服务端的切换不会导致客户端崩溃(或许会重新连接)。
- 主从状态必须可监控。
- 主从间的连接必须靠谱,高速。
基于如此结构的一些假设:
- 一主一从结构已经足够保险,不再需要多层次的备份。
- 主和从都能单独负载整个需求,而不是平衡性的承载整个符合。
- 要有足够的预算供起这么一个日常空转的从。
这些并没有被涉及到:
- 从并非用来负载均衡。
- 信息不持久化。
- 主从间不会自动探测对方。
- 主从间不会同步服务器的状态、信息。
如果要配置一对主从,需要在各自的配置中设定好对方及同步时间。
匹配的,作为客户端需要做的:
1.知道主从的地址
2.先连主,失败则试试从
3.检测失效的连接(心跳)
4.重连时遵守2
5.重新在请求的服务器上创建状态
6.当主从切换时,可以重新传递请求
当然,这些都会由客户端的api完成。这里注意:作者一再强调,主从同时只能有一个在提供服务!
服务器端:
- //
- // Binary Star server
- //
- #include "czmq.h"
- // We send state information every this often
- // If peer doesn't respond in two heartbeats, it is 'dead'
- #define HEARTBEAT 1000 // In msecs
- // States we can be in at any point in time
- typedef enum {
- STATE_PRIMARY = 1, // Primary, waiting for peer to connect
- STATE_BACKUP = 2, // Backup, waiting for peer to connect
- STATE_ACTIVE = 3, // Active - accepting connections
- STATE_PASSIVE = 4 // Passive - not accepting connections
- } state_t;
- // Events, which start with the states our peer can be in
- typedef enum {
- PEER_PRIMARY = 1, // HA peer is pending primary
- PEER_BACKUP = 2, // HA peer is pending backup
- PEER_ACTIVE = 3, // HA peer is active
- PEER_PASSIVE = 4, // HA peer is passive
- CLIENT_REQUEST = 5 // Client makes request
- } event_t;
- // Our finite state machine
- typedef struct {
- state_t state; // Current state
- event_t event; // Current event
- int64_t peer_expiry; // When peer is considered 'dead'
- } bstar_t;
- // Execute finite state machine (apply event to state)
- // Returns TRUE if there was an exception
- static Bool
- s_state_machine (bstar_t *fsm)
- {
- Bool exception = FALSE;
- // Primary server is waiting for peer to connect
- // Accepts CLIENT_REQUEST events in this state
- if (fsm->state == STATE_PRIMARY) {
- if (fsm->event == PEER_BACKUP) {
- printf ("I: connected to backup (slave), ready as master\n");
- fsm->state = STATE_ACTIVE;
- }
- else
- if (fsm->event == PEER_ACTIVE) {
- printf ("I: connected to backup (master), ready as slave\n");
- fsm->state = STATE_PASSIVE;
- }
- }
- else
- // Backup server is waiting for peer to connect
- // Rejects CLIENT_REQUEST events in this state
- if (fsm->state == STATE_BACKUP) {
- if (fsm->event == PEER_ACTIVE) {
- printf ("I: connected to primary (master), ready as slave\n");
- fsm->state = STATE_PASSIVE;
- }
- else
- if (fsm->event == CLIENT_REQUEST)
- exception = TRUE;
- }
- else
- // Server is active
- // Accepts CLIENT_REQUEST events in this state
- if (fsm->state == STATE_ACTIVE) {
- if (fsm->event == PEER_ACTIVE) {
- // Two masters would mean split-brain
- printf ("E: fatal error - dual masters, aborting\n");
- exception = TRUE;
- }
- }
- else
- // Server is passive
- // CLIENT_REQUEST events can trigger failover if peer looks dead
- if (fsm->state == STATE_PASSIVE) {
- if (fsm->event == PEER_PRIMARY) {
- // Peer is restarting - become active, peer will go passive
- printf ("I: primary (slave) is restarting, ready as master\n");
- fsm->state = STATE_ACTIVE;
- }
- else
- if (fsm->event == PEER_BACKUP) {
- // Peer is restarting - become active, peer will go passive
- printf ("I: backup (slave) is restarting, ready as master\n");
- fsm->state = STATE_ACTIVE;
- }
- else
- if (fsm->event == PEER_PASSIVE) {
- // Two passives would mean cluster would be non-responsive
- printf ("E: fatal error - dual slaves, aborting\n");
- exception = TRUE;
- }
- else
- if (fsm->event == CLIENT_REQUEST) {
- // Peer becomes master if timeout has passed
- // It's the client request that triggers the failover
- assert (fsm->peer_expiry > 0);
- if (zclock_time () >= fsm->peer_expiry) {
- // If peer is dead, switch to the active state
- printf ("I: failover successful, ready as master\n");
- fsm->state = STATE_ACTIVE;
- }
- else
- // If peer is alive, reject connections
- exception = TRUE;
- }
- }
- return exception;
- }
- int main (int argc, char *argv [])
- {
- // Arguments can be either of:
- // -p primary server, at tcp://localhost:5001
- // -b backup server, at tcp://localhost:5002
- zctx_t *ctx = zctx_new ();
- void *statepub = zsocket_new (ctx, ZMQ_PUB);
- void *statesub = zsocket_new (ctx, ZMQ_SUB);
- void *frontend = zsocket_new (ctx, ZMQ_ROUTER);
- bstar_t fsm = { 0 };
- if (argc == 2 && streq (argv [1], "-p")) {
- printf ("I: Primary master, waiting for backup (slave)\n");
- zsocket_bind (frontend, "tcp://*:5001");
- zsocket_bind (statepub, "tcp://*:5003");
- zsocket_connect (statesub, "tcp://localhost:5004");
- fsm.state = STATE_PRIMARY;
- }
- else
- if (argc == 2 && streq (argv [1], "-b")) {
- printf ("I: Backup slave, waiting for primary (master)\n");
- zsocket_bind (frontend, "tcp://*:5002");
- zsocket_bind (statepub, "tcp://*:5004");
- zsocket_connect (statesub, "tcp://localhost:5003");
- fsm.state = STATE_BACKUP;
- }
- else {
- printf ("Usage: bstarsrv { -p | -b }\n");
- zctx_destroy (&ctx);
- exit (0);
- }
- // Set timer for next outgoing state message
- int64_t send_state_at = zclock_time () + HEARTBEAT;
- while (!zctx_interrupted) {
- zmq_pollitem_t items [] = {
- { frontend, 0, ZMQ_POLLIN, 0 },
- { statesub, 0, ZMQ_POLLIN, 0 }
- };
- int time_left = (int) ((send_state_at - zclock_time ()));
- if (time_left < 0)
- time_left = 0;
- int rc = zmq_poll (items, 2, time_left * ZMQ_POLL_MSEC);
- if (rc == -1)
- break; // Context has been shut down
- if (items [0].revents & ZMQ_POLLIN) {
- // Have a client request
- zmsg_t *msg = zmsg_recv (frontend);
- fsm.event = CLIENT_REQUEST;
- if (s_state_machine (&fsm) == FALSE)
- // Answer client by echoing request back
- zmsg_send (&msg, frontend);
- else
- zmsg_destroy (&msg);
- }
- if (items [1].revents & ZMQ_POLLIN) {
- // Have state from our peer, execute as event
- char *message = zstr_recv (statesub);
- fsm.event = atoi (message);
- free (message);
- if (s_state_machine (&fsm))
- break; // Error, so exit
- fsm.peer_expiry = zclock_time () + 2 * HEARTBEAT;
- }
- // If we timed-out, send state to peer
- if (zclock_time () >= send_state_at) {
- char message [2];
- sprintf (message, "%d", fsm.state);
- zstr_send (statepub, message);
- send_state_at = zclock_time () + HEARTBEAT;
- }
- }
- if (zctx_interrupted)
- printf ("W: interrupted\n");
- // Shutdown sockets and context
- zctx_destroy (&ctx);
- return 0;
- }
//// Binary Star server//#include "czmq.h"// We send state information every this often// If peer doesn't respond in two heartbeats, it is 'dead'#define HEARTBEAT 1000 // In msecs// States we can be in at any point in timetypedef enum { STATE_PRIMARY = 1, // Primary, waiting for peer to connect STATE_BACKUP = 2, // Backup, waiting for peer to connect STATE_ACTIVE = 3, // Active - accepting connections STATE_PASSIVE = 4 // Passive - not accepting connections} state_t;// Events, which start with the states our peer can be intypedef enum { PEER_PRIMARY = 1, // HA peer is pending primary PEER_BACKUP = 2, // HA peer is pending backup PEER_ACTIVE = 3, // HA peer is active PEER_PASSIVE = 4, // HA peer is passive CLIENT_REQUEST = 5 // Client makes request} event_t;// Our finite state machinetypedef struct { state_t state; // Current state event_t event; // Current event int64_t peer_expiry; // When peer is considered 'dead'} bstar_t;// Execute finite state machine (apply event to state)// Returns TRUE if there was an exceptionstatic Bools_state_machine (bstar_t *fsm){ Bool exception = FALSE; // Primary server is waiting for peer to connect // Accepts CLIENT_REQUEST events in this state if (fsm->state == STATE_PRIMARY) { if (fsm->event == PEER_BACKUP) { printf ("I: connected to backup (slave), ready as master\n"); fsm->state = STATE_ACTIVE; } else if (fsm->event == PEER_ACTIVE) { printf ("I: connected to backup (master), ready as slave\n"); fsm->state = STATE_PASSIVE; } } else // Backup server is waiting for peer to connect // Rejects CLIENT_REQUEST events in this state if (fsm->state == STATE_BACKUP) { if (fsm->event == PEER_ACTIVE) { printf ("I: connected to primary (master), ready as slave\n"); fsm->state = STATE_PASSIVE; } else if (fsm->event == CLIENT_REQUEST) exception = TRUE; } else // Server is active // Accepts CLIENT_REQUEST events in this state if (fsm->state == STATE_ACTIVE) { if (fsm->event == PEER_ACTIVE) { // Two masters would mean split-brain printf ("E: fatal error - dual masters, aborting\n"); exception = TRUE; } } else // Server is passive // CLIENT_REQUEST events can trigger failover if peer looks dead if (fsm->state == STATE_PASSIVE) { if (fsm->event == PEER_PRIMARY) { // Peer is restarting - become active, peer will go passive printf ("I: primary (slave) is restarting, ready as master\n"); fsm->state = STATE_ACTIVE; } else if (fsm->event == PEER_BACKUP) { // Peer is restarting - become active, peer will go passive printf ("I: backup (slave) is restarting, ready as master\n"); fsm->state = STATE_ACTIVE; } else if (fsm->event == PEER_PASSIVE) { // Two passives would mean cluster would be non-responsive printf ("E: fatal error - dual slaves, aborting\n"); exception = TRUE; } else if (fsm->event == CLIENT_REQUEST) { // Peer becomes master if timeout has passed // It's the client request that triggers the failover assert (fsm->peer_expiry > 0); if (zclock_time () >= fsm->peer_expiry) { // If peer is dead, switch to the active state printf ("I: failover successful, ready as master\n"); fsm->state = STATE_ACTIVE; } else // If peer is alive, reject connections exception = TRUE; } } return exception;}int main (int argc, char *argv []){ // Arguments can be either of: // -p primary server, at tcp://localhost:5001 // -b backup server, at tcp://localhost:5002 zctx_t *ctx = zctx_new (); void *statepub = zsocket_new (ctx, ZMQ_PUB); void *statesub = zsocket_new (ctx, ZMQ_SUB); void *frontend = zsocket_new (ctx, ZMQ_ROUTER); bstar_t fsm = { 0 }; if (argc == 2 && streq (argv [1], "-p")) { printf ("I: Primary master, waiting for backup (slave)\n"); zsocket_bind (frontend, "tcp://*:5001"); zsocket_bind (statepub, "tcp://*:5003"); zsocket_connect (statesub, "tcp://localhost:5004"); fsm.state = STATE_PRIMARY; } else if (argc == 2 && streq (argv [1], "-b")) { printf ("I: Backup slave, waiting for primary (master)\n"); zsocket_bind (frontend, "tcp://*:5002"); zsocket_bind (statepub, "tcp://*:5004"); zsocket_connect (statesub, "tcp://localhost:5003"); fsm.state = STATE_BACKUP; } else { printf ("Usage: bstarsrv { -p | -b }\n"); zctx_destroy (&ctx); exit (0); } // Set timer for next outgoing state message int64_t send_state_at = zclock_time () + HEARTBEAT; while (!zctx_interrupted) { zmq_pollitem_t items [] = { { frontend, 0, ZMQ_POLLIN, 0 }, { statesub, 0, ZMQ_POLLIN, 0 } }; int time_left = (int) ((send_state_at - zclock_time ())); if (time_left < 0) time_left = 0; int rc = zmq_poll (items, 2, time_left * ZMQ_POLL_MSEC); if (rc == -1) break; // Context has been shut down if (items [0].revents & ZMQ_POLLIN) { // Have a client request zmsg_t *msg = zmsg_recv (frontend); fsm.event = CLIENT_REQUEST; if (s_state_machine (&fsm) == FALSE) // Answer client by echoing request back zmsg_send (&msg, frontend); else zmsg_destroy (&msg); } if (items [1].revents & ZMQ_POLLIN) { // Have state from our peer, execute as event char *message = zstr_recv (statesub); fsm.event = atoi (message); free (message); if (s_state_machine (&fsm)) break; // Error, so exit fsm.peer_expiry = zclock_time () + 2 * HEARTBEAT; } // If we timed-out, send state to peer if (zclock_time () >= send_state_at) { char message [2]; sprintf (message, "%d", fsm.state); zstr_send (statepub, message); send_state_at = zclock_time () + HEARTBEAT; } } if (zctx_interrupted) printf ("W: interrupted\n"); // Shutdown sockets and context zctx_destroy (&ctx); return 0;}
客户端:
- //
- // Binary Star client
- //
- #include "czmq.h"
- #define REQUEST_TIMEOUT 1000 // msecs
- #define SETTLE_DELAY 2000 // Before failing over
- int main (void)
- {
- zctx_t *ctx = zctx_new ();
- char *server [] = { "tcp://localhost:5001", "tcp://localhost:5002" };
- uint server_nbr = 0;
- printf ("I: connecting to server at %s…\n", server [server_nbr]);
- void *client = zsocket_new (ctx, ZMQ_REQ);
- zsocket_connect (client, server [server_nbr]);
- int sequence = 0;
- while (!zctx_interrupted) {
- // We send a request, then we work to get a reply
- char request [10];
- sprintf (request, "%d", ++sequence);
- zstr_send (client, request);
- int expect_reply = 1;
- while (expect_reply) {
- // Poll socket for a reply, with timeout
- zmq_pollitem_t items [] = { { client, 0, ZMQ_POLLIN, 0 } };
- int rc = zmq_poll (items, 1, REQUEST_TIMEOUT * ZMQ_POLL_MSEC);
- if (rc == -1)
- break; // Interrupted
- // If we got a reply, process it
- if (items [0].revents & ZMQ_POLLIN) {
- // We got a reply from the server, must match sequence
- char *reply = zstr_recv (client);
- if (atoi (reply) == sequence) {
- printf ("I: server replied OK (%s)\n", reply);
- expect_reply = 0;
- sleep (1); // One request per second
- }
- else {
- printf ("E: malformed reply from server: %s\n",
- reply);
- }
- free (reply);
- }
- else {
- printf ("W: no response from server, failing over\n");
- // Old socket is confused; close it and open a new one
- zsocket_destroy (ctx, client);
- server_nbr = (server_nbr + 1) % 2;
- zclock_sleep (SETTLE_DELAY);
- printf ("I: connecting to server at %s…\n",
- server [server_nbr]);
- client = zsocket_new (ctx, ZMQ_REQ);
- zsocket_connect (client, server [server_nbr]);
- // Send request again, on new socket
- zstr_send (client, request);
- }
- }
- }
- zctx_destroy (&ctx);
- return 0;
- }
//// Binary Star client//#include "czmq.h"#define REQUEST_TIMEOUT 1000 // msecs#define SETTLE_DELAY 2000 // Before failing overint main (void){ zctx_t *ctx = zctx_new (); char *server [] = { "tcp://localhost:5001", "tcp://localhost:5002" }; uint server_nbr = 0; printf ("I: connecting to server at %s…\n", server [server_nbr]); void *client = zsocket_new (ctx, ZMQ_REQ); zsocket_connect (client, server [server_nbr]); int sequence = 0; while (!zctx_interrupted) { // We send a request, then we work to get a reply char request [10]; sprintf (request, "%d", ++sequence); zstr_send (client, request); int expect_reply = 1; while (expect_reply) { // Poll socket for a reply, with timeout zmq_pollitem_t items [] = { { client, 0, ZMQ_POLLIN, 0 } }; int rc = zmq_poll (items, 1, REQUEST_TIMEOUT * ZMQ_POLL_MSEC); if (rc == -1) break; // Interrupted // If we got a reply, process it if (items [0].revents & ZMQ_POLLIN) { // We got a reply from the server, must match sequence char *reply = zstr_recv (client); if (atoi (reply) == sequence) { printf ("I: server replied OK (%s)\n", reply); expect_reply = 0; sleep (1); // One request per second } else { printf ("E: malformed reply from server: %s\n", reply); } free (reply); } else { printf ("W: no response from server, failing over\n"); // Old socket is confused; close it and open a new one zsocket_destroy (ctx, client); server_nbr = (server_nbr + 1) % 2; zclock_sleep (SETTLE_DELAY); printf ("I: connecting to server at %s…\n", server [server_nbr]); client = zsocket_new (ctx, ZMQ_REQ); zsocket_connect (client, server [server_nbr]); // Send request again, on new socket zstr_send (client, request); } } } zctx_destroy (&ctx); return 0;}
主从间的状态图:
- zeroMQ初体验-28.可靠性-主从模式
- zeroMQ初体验-26.可靠性-管家模式
- zeroMQ初体验-27.可靠性-硬盘模式
- zeroMQ初体验-29.可靠性-自由模式
- zeroMQ初体验-23.可靠性-懒惰的海盗模式
- zeroMQ初体验-24.可靠性-简单的海盗模式
- zeroMQ初体验-25.可靠性-偏执的海盗模式
- zeroMQ初体验-22.可靠性-总览
- zeroMQ初体验-1.简介及C/S模式
- zeroMQ初体验-2.发布订阅模式(pub/sub)
- zeroMQ初体验-3.分而治之模式(push/pull)
- zeroMQ初体验-9.优雅的扩展(代理模式)
- zeroMQ初体验-13.发布/订阅模式 进阶
- zeroMQ初体验-21.应答模式进阶(七)-云计算
- 初探ZeroMQ(五) 请求-响应模式的各种可靠性设计
- zeroMQ初体验-20.应答模式进阶(六)-多对多路由模式
- zeroMQ初体验-32.发布/订阅模式进阶-克隆模式-上
- zeroMQ初体验-33.发布/订阅模式进阶-克隆模式-中
- Android的设置界面及Preference使用
- df
- zeroMQ初体验-27.可靠性-硬盘模式
- C++操作数据库
- 用mysqldump和mysqlbinlog的mysql数据恢复实验
- zeroMQ初体验-28.可靠性-主从模式
- iOS开发之Objective-C与JavaScript的交互
- CEikEdwin 助手
- sqlite3在windows平台下编译
- C/C++ 各种计时函数总结
- Java反射检查/使用类
- zeroMQ初体验-29.可靠性-自由模式
- 自定义评分器Similarity,提高搜索体验
- Window.ShowModalDialog使用