polardbxengine/extra/IS/consensus/net/easyNet.cc

514 lines
13 KiB
C++

/************************************************************************
*
* Copyright (c) 2016 Alibaba.com, Inc. All Rights Reserved
* $Id: easyNet.cc,v 1.0 07/31/2016 04:53:21 PM yingqiang.zyq(yingqiang.zyq@alibaba-inc.com) $
*
************************************************************************/
/**
* @file easyNet.cc
* @author yingqiang.zyq(yingqiang.zyq@alibaba-inc.com)
* @date 07/31/2016 04:53:21 PM
* @version 1.0
* @brief
*
**/
#include "easyNet.h"
#include "service.h"
#include "paxos_server.h" //TODO no driect include
#include "paxos.h" //TODO no driect include
#include "paxos.pb.h"
namespace alisql {
easy_atomic_t easy_pool_alloc_byte = 0;
static void *easy_count_realloc(void *ptr, size_t size)
{
char *p1, *p = (char *)ptr;
if (p)
{
p -= 8;
easy_atomic_add(&easy_pool_alloc_byte, -(*((int *)p)));
}
if (size)
{
easy_atomic_add(&easy_pool_alloc_byte, size);
p1 = (char *)easy_realloc(p, size + 8);
if (p1)
{
*((int *)p1) = size;
p1 += 8;
}
return p1;
}
else if (p)
easy_free(p);
return NULL;
}
EasyNet::EasyNet(uint64_t num, const uint64_t sessionTimeout, bool memory_usage_count)
:reciveCnt_(0)
,isShutdown_(false)
,sessionTimeout_(sessionTimeout)
{
if (unlikely(memory_usage_count))
{
// set allocator for memory usage count
easy_pool_set_allocator(easy_count_realloc);
}
eio_= easy_eio_create(NULL, num);
eio_->do_signal = 0;
}
int EasyNet::init(void *ptr)
{
memset(&clientHandler_, 0, sizeof(clientHandler_));
clientHandler_.decode= EasyNet::paxosDecode;
clientHandler_.encode= EasyNet::paxosEncode;
clientHandler_.process= EasyNet::reciveProcess;
clientHandler_.on_connect= EasyNet::onConnected;
clientHandler_.on_disconnect= EasyNet::onDisconnected;
clientHandler_.cleanup= EasyNet::onClientCleanup;
clientHandler_.get_packet_id= EasyNet::getPacketId;
clientHandler_.user_data= (void *)workPool_;
clientHandler_.user_data2= ptr;
memset(&serverHandler_, 0, sizeof(serverHandler_));
serverHandler_.decode= EasyNet::paxosDecode;
serverHandler_.encode= EasyNet::paxosEncode;
serverHandler_.process= EasyNet::reciveProcess;
serverHandler_.user_data= (void *)workPool_;
serverHandler_.user_data2= ptr;
return 0;
};
int EasyNet::start(int port)
{
if (eio_ == NULL)
return -3;
if (easy_connection_add_listen(eio_, NULL, port, &serverHandler_) == NULL)
return -1;
if (easy_eio_start(eio_))
return -2;
return 0;
}
int EasyNet::shutdown()
{
lock_.lock();
isShutdown_= true;
lock_.unlock();
/*
* The follow function should not be called by hold the lock.
* Because some callback function which try to hold lock, will be called.
*/
easy_eio_shutdown(eio_);
easy_eio_stop(eio_);
easy_eio_wait(eio_);
easy_eio_destroy(eio_);
return 0;
}
int EasyNet::stop()
{
easy_eio_stop(eio_);
return 0;
}
easy_addr_t EasyNet::createConnection(const std::string &addr, NetServerRef server, uint64_t timeout, uint64_t index)
{
easy_addr_t iaddr;
if (isShutdown_)
{
iaddr.port= 0;
return iaddr;
}
iaddr= easy_inet_str_to_addr(addr.c_str(), 0);
iaddr.cidx= index;
if (server != nullptr)
setConnData(iaddr, server);
if (easy_connection_connect(eio_, iaddr, &clientHandler_, timeout, NULL, EASY_CONNECT_AUTOCONN) != EASY_OK)
//if (easy_connection_connect(eio_, iaddr, &clientHandler_, timeout, NULL, 0) != EASY_OK)
iaddr.port= 0;
/*
easy_session_t *s= easy_connection_connect_init(NULL, &clientHandler_, 200, NULL, 0, NULL);
if (iaddr.family == 0 || s == NULL)
iaddr.port= 0;
if (NULL == easy_client_send(eio_, iaddr, s))
iaddr.port= 0;
*/
return iaddr;
}
void EasyNet::disableConnection(easy_addr_t addr)
{
easy_connection_disconnect(eio_, addr);
}
int EasyNet::sendPacket(easy_addr_t addr, const char *buf, uint64_t len, uint64_t id)
{
easy_session_t *s;
NetPacket *np;
if ((np= easy_session_packet_create(NetPacket, s, len)) == NULL) {
return -1;
}
auto server= std::dynamic_pointer_cast<RemoteServer>(getConnData(addr));
if (!server || !server->isLearner || !server->paxos || server->paxos->getLocalServer()->learnerConnTimeout == 0)
easy_session_set_timeout(s, sessionTimeout_); //ms
else
easy_session_set_timeout(s, server->paxos->getLocalServer()->learnerConnTimeout * 4);
np->data= &np->buffer[0];
np->len= len;
np->packetId= id;
memcpy(np->data, buf, len);
if (easy_client_dispatch(eio_, addr, s) == EASY_ERROR) {
easy_session_destroy(s);
return -2;
}
/* TODO: onFail callback function need to be added*/
return 0;
}
int EasyNet::sendPacket(easy_addr_t addr, const std::string& buf, uint64_t id)
{
return sendPacket(addr, buf.c_str(), buf.length(), id);
}
int EasyNet::resendPacket(easy_addr_t addr, void *ptr, uint64_t id)
{
NetPacket *np= (NetPacket *)ptr;
return sendPacket(addr, np->data, np->len, id);
}
int EasyNet::setRecvPacketCallback(void *handler)
{
setWorkPool((easy_thread_pool_t *)handler);
return 0;
}
NetServerRef EasyNet::getConnData(easy_addr_t addr, bool locked)
{
std::unique_lock<std::mutex> lg(lock_, std::defer_lock);
if (!locked)
lg.lock();
auto it= connStatus_.find(getAddrKey(addr));
if (it != connStatus_.end())
return it->second;
else
return nullptr;
}
NetServerRef EasyNet::getConnDataAndSetFail(easy_connection_t *c, bool isFail)
{
auto addr= c->addr;
std::lock_guard<std::mutex> lg(lock_);
auto s= getConnData(addr, true);
auto server= std::dynamic_pointer_cast<RemoteServer>(s);
if (server == nullptr)
return s;
if (isFail)
{
if (!server->netError.load())
{
if (server->c == c || server->c == nullptr)
easy_error_log("EasyNet::onDisconnected server %ld\n", server->serverId);
else
easy_error_log("EasyNet::onDisconnected server %ld, which is the old one\n", server->serverId);
}
/* disconnect */
if (server->c != nullptr && server->c != c)
return s;
server->waitForReply= 0;
server->netError.store(isFail);
}
else
{
if (server->netError.load())
easy_error_log("EasyNet::onConnected server %ld\n", server->serverId);
/* connect */
server->c = c;
server->waitForReply= 0;
server->netError.store(isFail);
}
return s;
}
void EasyNet::setConnData(easy_addr_t addr, NetServerRef server)
{
std::lock_guard<std::mutex> lg(lock_);
connStatus_.insert(std::make_pair(getAddrKey(addr), server));
}
void EasyNet::delConnDataById(uint64_t id)
{
/* Protect the connStatus_ map */
std::lock_guard<std::mutex> lg(lock_);
for (auto it= connStatus_.begin(); it != connStatus_.end();)
{
if (std::dynamic_pointer_cast<RemoteServer>(it->second)->serverId == id)
connStatus_.erase(it++);
else
++it;
if (connStatus_.size() == 0)
break;
}
}
uint64_t EasyNet::getAddrKey(easy_addr_t addr)
{
uint64_t ret= addr.u.addr;
ret <<= 32;
ret |= addr.port;
return ret;
}
void EasyNet::tryFreeMsg(NetPacket *np)
{
if (np->msg)
{
delete static_cast<PaxosMsg *>(np->msg);
np->msg= nullptr;
}
}
int EasyNet::reciveProcess(easy_request_t *r)
{
if (r == NULL)
return EASY_ERROR;
/* TODO means send callback process here? called by easy_session_process */
if (r->ms->c == NULL)
return EASY_ERROR;
auto tp= (easy_thread_pool_t *) (r->ms->c->handler->user_data);
auto srv= (Service *) (r->ms->c->handler->user_data2);
if (srv == NULL)
return EASY_ERROR;
assert(r->user_data == NULL);
r->user_data = (void *)srv;
auto en= srv->getEasyNet();
en->incRecived();
if (tp == NULL || !srv->workPoolIsRunning())
return EASY_ERROR;
int ret= EASY_AGAIN;
if (r->ipacket == NULL)
{
/*
* timeout case.
* we push the session to thread pool.
* Service::process will handle this case.
*/
auto server= std::dynamic_pointer_cast<RemoteServer>(en->getConnData(r->ms->c->addr));
/* May already be remove. */
if (server != nullptr)
{
NetPacket *np= (NetPacket *)(r->opacket);
easy_warn_log("EasyNet::reciveProcess sendMsg timeout for serverid:%ld packet_id:0x%llx(%llu)\n", server->serverId, np->packetId, np->packetId);
}
//server->waitForReply= 0;
/* Here we should not return EASY_ERROR. */
//ret= EASY_AGAIN;
ret= EASY_ERROR;
}
if (r->ms->c->type == EASY_TYPE_SERVER)
{
auto tp2 = srv->getHeartbeatPool();
NetPacket *np = (NetPacket *)r->ipacket;
assert(np);
PaxosMsg *msg = new PaxosMsg;
if (!msg->ParseFromArray(np->data, np->len))
{
// receive a wrong msg.
easy_error_log("A msg have %ld entries!! droped!!\n", msg->entries_size());
r->opacket= (void *)NULL;
return EASY_OK;
}
assert(r->args == 0);
r->args = (void *)msg;
if (tp2 == NULL || msg->msgtype() != Consensus::AppendLog || msg->entries_size() != 0 ||
Service::running < Service::workThreadCnt ||
!((Paxos *)(srv->getConsensus()))->getOptimisticHeartbeat()) {
easy_thread_pool_push(tp, r, easy_hash_key((uint64_t)(long)r));
} else {
// set msg type to OptimisticHeartbeat to let service know it is in heartbeat pool
msg->set_msgtype(Consensus::OptimisticHeartbeat);
// msg is heartbeat and work pool is full, put in heartbeat pool
easy_thread_pool_push(tp2, r, easy_hash_key((uint64_t)(long)r));
}
}
else
{
easy_session_t *s= (easy_session_t *)r->ms;
easy_thread_pool_push_session(tp, s, easy_hash_key((uint64_t)(long)r));
}
return ret;
}
void *EasyNet::paxosDecode(easy_message_t *m)
{
NetPacket *np;
uint64_t len, datalen;
if ((len= m->input->last - m->input->pos) < NetPacketHeaderSize)
return NULL;
datalen = *((uint64_t *)m->input->pos);
if (datalen > 0x4000000)
{
easy_error_log("data_len is invalid: %llu\n", datalen);
m->status = EASY_ERROR;
return NULL;
}
len -= NetPacketHeaderSize;
if (len < datalen) {
m->next_read_len = datalen - len;
easy_debug_log("Decode a net packet fail, data len expect:%llu got:%llu", datalen, len);
return NULL;
}
if ((np= (NetPacket *)easy_pool_calloc(m->pool, sizeof(NetPacket))) == NULL)
{
m->status = EASY_ERROR;
return NULL;
}
m->input->pos += NetPacketHeaderSize;
np->type= NetPacketTypeNet;
np->data= (char *)m->input->pos;
np->len= datalen;
if (m->c->type == EASY_TYPE_CLIENT)
{
auto msg= new PaxosMsg();
msg->ParseFromArray(np->data, np->len);
np->packetId= msg->msgid();
np->msg= (void *)msg;
}
m->input->pos += datalen;
easy_debug_log("Decode a net packet success, total lens:%llu", datalen + NetPacketHeaderSize);
return (void *)np;
}
int EasyNet::paxosEncode(easy_request_t *r, void *data)
{
easy_buf_t *b;
NetPacket *np= (NetPacket *)data;
if ((b= easy_buf_create(r->ms->pool, NetPacketHeaderSize + np->len)) == NULL)
return EASY_ERROR;
*((uint64_t *)b->last)= np->len;
memcpy(b->last + NetPacketHeaderSize, np->data, np->len);
b->last += (NetPacketHeaderSize + np->len);
easy_request_addbuf(r, b);
return EASY_OK;
}
int EasyNet::onConnected(easy_connection_t *c)
{
auto srv= (Service *) (c->handler->user_data2);
if (srv == NULL)
return 0;
auto en= srv->getEasyNet();
auto server= std::dynamic_pointer_cast<RemoteServer>(en->getConnDataAndSetFail(c, false));
if (server != nullptr)
{
/* We don't start heartbeatTimer for learner, so we send heartbeat here. */
server->onConnectCb();
}
return 0;
}
int EasyNet::onDisconnected(easy_connection_t *c)
{
auto srv= (Service *) (c->handler->user_data2);
if (srv == NULL)
return 0;
auto en= srv->getEasyNet();
auto server= std::dynamic_pointer_cast<RemoteServer>(en->getConnDataAndSetFail(c, true));
if (server != nullptr)
{
// onDisconnectCB
}
return 0;
}
int EasyNet::onClientCleanup(easy_request_t *r, void *apacket)
{
NetPacket *np= nullptr;
if (r == NULL)
{
np= static_cast<NetPacket *>(apacket);
if (np && np->msg)
{
PaxosMsg *msg= static_cast<PaxosMsg *>(np->msg);
easy_warn_log("EasyNet::onClientCleanup: msgId(%llu) packet_id:%llu responce from server %llu which already be deleted for timeout.", msg->msgid(), np->packetId, msg->serverid());
delete msg;
np->msg= NULL;
}
return 0;
}
np= static_cast<NetPacket *>(r->ipacket);
if (np && np->msg)
{
PaxosMsg *msg= static_cast<PaxosMsg *>(np->msg);
easy_warn_log("EasyNet::onClientCleanup: msgId(%llu) packet_id:%llu responce from server %llu.", msg->msgid(), np->packetId, msg->serverid());
delete msg;
np->msg= NULL;
}
return 0;
}
uint64_t EasyNet::getPacketId(easy_connection_t *c, void *data)
{
NetPacket *np= static_cast<NetPacket *>(data);
return np->packetId;
}
} //namespace alisql