package com.techq.available.quorum;
import java.nio.ByteBuffer;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.techq.available.connector.ElectionCnxManager;
/**
*
* @author CHQ
* 2012-2-3
*/
public class Messenger {
static Logger LOG = LoggerFactory.getLogger(Messenger.class);
QuorumPeer curPeer;
LinkedBlockingQueue<Notification> sendqueue;
LinkedBlockingQueue<Notification> recvqueue;
LinkedBlockingQueue<Notification> pingQueue;
ProposalVote curVote;
WorkerSender ws;
ElectionCnxManager manager;
WorkerReceiver wr;
public Messenger(
ElectionCnxManager manager,
QuorumPeer peer,
LinkedBlockingQueue<Notification> sendqueue,
LinkedBlockingQueue<Notification> recvqueue,
LinkedBlockingQueue<Notification> agreequeue,
ProposalVote curVote) {
this.manager = manager;
curPeer = peer;
this.sendqueue = sendqueue;
this.recvqueue = recvqueue;
this.pingQueue = agreequeue;
this.curVote = curVote;
}
public void start() {
this.ws = new WorkerSender(manager);
Thread t = new Thread(this.ws, "WorkerSender[myid=" + curPeer.getId() + "]");
t.setDaemon(true);
t.start();
this.wr = new WorkerReceiver(manager);
t = new Thread(this.wr, "WorkerReceiver[myid=" + curPeer.getId() + "]");
t.setDaemon(true);
t.start();
}
void halt() {
this.ws.stop = true;
this.wr.stop = true;
}
/**
* Receives messages from instance of QuorumCnxManager on method run(),
* and processes such messages.
*/
class WorkerReceiver implements Runnable {
volatile boolean stop;
ElectionCnxManager manager;
WorkerReceiver(ElectionCnxManager manager) {
this.stop = false;
this.manager = manager;
}
public void run() {
Message response;
while (!stop) {
// Sleeps on receive
try {
response = manager.pollRecvQueue(3000, TimeUnit.MILLISECONDS);
if (response == null)
continue;
/**
* make sure it is the participant we have known
*/
if (curPeer.getVotingViews().containsKey(response.sid)) {
if (DebugConfig.debug) {
LOG.debug("#now it is debug mode");
if (response.buffer.capacity() != Message.DEBUG_DEFAULT_SIZE) {
LOG.error("Got a short response: " + response.buffer.capacity() + ", expect size:" + Message.DEBUG_DEFAULT_SIZE);
continue;
}
} else {
if (response.buffer.capacity() != Message.DEFAULT_SIZE) {
LOG.error("Got a short response: " + response.buffer.capacity() + ", expect size:" + Message.DEFAULT_SIZE);
continue;
}
}
response.buffer.clear();
Notification.mType type = Notification.mType.UNKNOW;
int val = response.buffer.getInt();
type = Notification.getTypeByInt(val);
// State of peer that sent this message
ServerState ackstate = ServerState.LOOKING;
val = response.buffer.getInt();
switch (val) {
case 0:
ackstate = ServerState.LOOKING;
break;
case 1:
ackstate = ServerState.FOLLOWING;
break;
case 2:
ackstate = ServerState.LEADING;
break;
default:
LOG.error("what's this? value:" + val);
break;
}
// Instantiate Notification and set its attributes
long leader = response.buffer.getLong();
long zxid = response.buffer.getLong();
long electionEpoch = response.buffer.getLong();
long from = response.buffer.getLong();
ServerState state = ackstate;
long sid = response.sid;
Notification n = new Notification(
type,
leader,
zxid,
electionEpoch,
state,
from,//to whom
sid//
);
/*
* Print notification info
*/
if (DebugConfig.debug) {
int seq = response.buffer.getInt();
n.setSeq(seq);
}
if (LOG.isInfoEnabled()) {
LOG.info("recv new message: myid=" + curPeer.getId() + ", remote peer id:" + n.sid + ", msg:"+ n.toString());
}
/*
* If this server is looking, then send proposed leader
*/
// if (type.equals(Notification.mType.AGREEMENT) || type.equals(Notification.mType.PING)
// || type.equals(Notification.mType.ACK) || type.equals(Notification.mType.CONFIRM)) {
// LOG.debug("recv msg, and add msg to ping queue:" + n);
// pingQueue.offer(n);
// continue;
// }
/*
* current peer is looking, then do it
*/
if (curPeer.getPeerState() == ServerState.LOOKING) {
LOG.trace("===============curPeer.getPeerState() is "
+ curPeer.getPeerState() + "============");
recvqueue.offer(n);
} else {// if current peer is not looking, must be following or leading
/*
* If this server is not looking, but the one that
* sent the ack is looking, then send back what it
* believes to be the leader.
*/
curVote = curPeer.curVote;
if (ackstate == ServerState.LOOKING && type != Notification.mType.ACCEPT) {
Notification not = new Notification(
Notification.mType.PROPOSE,
curVote.proposedLeader,
curVote.proposedZxid,
curVote.logicalclock,
curPeer.getPeerState(),
n.sid,
curPeer.getId()
);
LOG.info("response to peer[id="+n.getFrom()+"]:" + not);
sendqueue.offer(not);
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("don't send me that message: My id = "
+ curPeer.getId() + ", " + n.toString());
}
}
}
} else {
LOG.warn("where is it from ? i am " + curPeer.getId() + " its id is " + response.sid + ".");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
}
}
LOG.info("WorkerReceiver is down");
}
}
/**
* This worker simply dequeues a message to send and and queues it on
* the manager's queue.
*/
class WorkerSender implements Runnable {
volatile boolean stop;
ElectionCnxManager manager;
WorkerSender(ElectionCnxManager manager) {
this.stop = false;
this.manager = manager;
}
public void run() {
while (!stop) {
try {
Notification m = sendqueue.poll(3000, TimeUnit.MILLISECONDS);
if (m == null)
continue;
DebugConfig.msgCnt.addAndGet(1);
m.setSeq(DebugConfig.msgCnt.get());
LOG.info("send new message: myid = " + curPeer.getId() + ", msg:" + m);
process(m);
} catch (InterruptedException e) {
break;
}
}
LOG.info("WorkerSender is down");
}
/**
* Called by run() once there is a new message to send.
*
* @param m
* message to send
*/
private void process(Notification m) {
manager.toSend(m.sid, m.toBuffer());
}
}
}