github链接(更新中)
https://github.com/pourtheworld/mydb
大纲(更新中)
(0)mydb的架构设想
本期任务
- 建立EDU(Engine Dispatchable Unit)引擎调度单元类。
- 建立EDUEvent(EDU event)EDU事件类。
- 定义并实现EDU各个状态和转换事件。
- 建立EDUMgr(EDU Manager)线程池。
- 定义TCPListener、Agent类型EDU的入口函数。
- 对服务端的主函数以及客户端进行整合,用假insert模拟。
- 进行简单C/S模拟。
EDU(Engine Dispatchable Unit)引擎调度单元类
- EDU类型。
- TCPListener、Agent的处理流程。
- TCPListener的入口函数。
- EDU的控制封装类。
除了主线程以外,其他的线程我们都称为EDU。
我们会先抽象出EDU的类型:
//EDU类型,实际上只有2种:TCP监听以及Agent代理
enum EDU_TYPES
{
EDU_TYPE_TCPLISTENER=0,
EDU_TYPE_AGENT,
EDU_TYPE_UNKNOWN,
EDU_TYPE_MAXIMUM=EDU_TYPE_UNKNOWN
};
处理流程:
首先由TCPListener的EDU会调用TCPListener的入口函数,负责监听客户端的TCP请求;
TCPListener得到一个客户端请求后,创建一个Agent类型的EDU,并把接受到的套接字交给这个Agent EDU;
之后客户端会直接与Agent EDU交流,调用Agent的入口函数,而不会再走Listener了。
//TCP监听入口
//先从内核获取服务名,并转换成端口号
//用该端口号给当前EDU的sock,作为Bind_listen,将当前EDU设置为run
//再开一个循环用于accept,每听到一个,start一个新agentEDU,把sock赋给它
//如果从accept出来了,说明出错,将当前EDU设置为wait
int pmdTcpListenerEntryPoint(pmdEDUCB *cb,void *arg)
{
int rc = EDB_OK ;
pmdEDUMgr * eduMgr = cb->getEDUMgr() ;
EDUID myEDUID = cb->getID() ;
unsigned int retry = 0 ;
EDUID agentEDU = PMD_INVALID_EDUID ;
char svcName[OSS_MAX_SERVICENAME+1] ;//服务名
//每次循环检查 重试次数是否到达上限;数据库是否已经关闭
while ( retry <= PMD_TCPLISTENER_RETRY && !EDB_IS_DB_DOWN )
{
retry ++ ;
//从内核获得服务名
strcpy( svcName, pmdGetKRCB()->getSvcName() ) ;
PD_LOG ( PDEVENT, "Listening on port_test %s\n", svcName ) ;
int port = 0 ;
int len = strlen ( svcName ) ;
//将服务名转换成端口
for ( int i = 0; i<len; ++i )
{
if ( svcName[i] >= '0' && svcName[i] <= '9' )
{
port = port * 10 ;
port += int( svcName[i] - '0' ) ;
}
else
{
PD_LOG ( PDERROR, "service name error!\n" ) ;
}
}
//将端口赋予sock,此sock用于bind_listen
ossSocket sock ( port ) ;
rc = sock.initSocket () ;
EDB_VALIDATE_GOTOERROR ( EDB_OK==rc, rc, "Failed initialize socket" )
rc = sock.bind_listen () ;
EDB_VALIDATE_GOTOERROR ( EDB_OK==rc, rc,
"Failed to bind/listen socket");
// 监听成功后,该EDU转换成run
if ( EDB_OK != ( rc = eduMgr->activateEDU ( myEDUID )) )
{
goto error ;
}
//这个循环用于监听
while ( !EDB_IS_DB_DOWN )
{
int s ;
rc = sock.accept ( &s, NULL, NULL ) ;
//每次10ms,超时continue
if ( EDB_TIMEOUT == rc )
{
rc = EDB_OK ;
continue ;
}
//有错误并且数据库关闭则退出循环
if ( rc && EDB_IS_DB_DOWN )
{
rc = EDB_OK ;
goto done ;
}
else if ( rc )
{
PD_LOG ( PDERROR, "Failed to accept socket in TcpListener" ) ;
PD_LOG ( PDEVENT, "Restarting socket to listen" ) ;
break ;
}
// 把接收到的句柄赋给pData
void *pData = NULL ;
*((int *) &pData) = s ;
//让线程池再启动一个agent的EDU,赋予它刚才的sock
rc = eduMgr->startEDU ( EDU_TYPE_AGENT, pData, &agentEDU ) ;
if ( rc )
{
if ( rc == EDB_QUIESCED )
{
PD_LOG ( PDWARNING, "Reject new connection due to quiesced database" ) ;
}
else
{
PD_LOG ( PDERROR, "Failed to start EDU agent" ) ;
}
//如果刚才分配新的thread失败了,只能将这个新的sock关闭
ossSocket newsock ( &s ) ;
newsock.close () ;
continue ;
}
}
//讲道理accept是一直循环的,到这里说明出问题了,那么将状态设置成wait
if ( EDB_OK != ( rc = eduMgr->waitEDU ( myEDUID )) )
{
goto error ;
}
}
done :
return rc;
error :
switch ( rc )
{
case EDB_SYS :
PD_LOG ( PDSEVERE, "System error occured" ) ;
break ;
default :
PD_LOG ( PDSEVERE, "Internal error" ) ;
}
goto done ;
}
接下来让我们看看EDU具体的封装EDU control block类,其私有成员包含了EDU的类型、EDU的ID、EDU的状态、以及一个EDUEvent也就是事件的队列。:
class pmdEDUCB
{
public:
pmdEDUCB(pmdEDUMgr *mgr,EDU_TYPES type);
inline EDUID getID(){
return _id; }
//将待处理事件push到队列中,等待EDU处理
inline void postEvent(pmdEDUEvent const &data){
_queue.push(data); }
bool waitEvent(pmdEDUEvent &data,long long millsec)
{
bool waitMsg=false;//是否得到了队列中pop出的事件
//如果当前EDU状态不为空闲,将其状态设置为等待
if(PMD_EDU_IDLE!=_status) _status=PMD_EDU_WAITING;
if(0>millsec)
{
//如果当前设置时间小于0,则无限等待,从事件队列里pop出一个事件给data
_queue.wait_and_pop(data);
waitMsg=true;
}//给定超时时间,在超时时间内是否pop出事件
else waitMsg=_queue.timed_wait_and_pop(data,millsec);
if(waitMsg)//如果pop出了事件
{
//事件为终止,则断开连接
if(data._eventType==PMD_EDU_EVENT_TERM) _isDisconnected=true;
else _status=PMD_EDU_RUNNING;//否则,状态调整为运行
}
return waitMsg;
}
inline void force(){
_isForced=true; }//一个线程迫使其它线程退出的标志
inline void disconnect(){
_isDisconnected=true;}
inline EDU_TYPES getType(){
return _type; }
inline EDU_STATUS getStatus(){
return _status; }
inline void setType(EDU_TYPES type){
_type=type;}
inline void setID(EDUID id){
_id=id;}
inline void setStatus(EDU_STATUS status){
_status=status;}
inline bool isForced(){
return _isForced;}
inline pmdEDUMgr* getEDUMgr(){
return _mgr;}
private:
EDU_TYPES _type;
pmdEDUMgr *_mgr;
EDU_STATUS _status;
EDUID _id;
bool _isForced;
bool _isDisconnected;
ossQueue<pmdEDUEvent> _queue;
};
EDUEvent(EDU event)EDU事件类
- EDU事件类型。
- 事件的post以及wait函数。
首先是EDU事件类型:
enum pmdEDUEventTypes
{
PMD_EDU_EVENT_NONE=0, //置空
PMD_EDU_EVENT_TERM, //终止EDU
PMD_EDU_EVENT_RESUME, //恢复一个EDU,数据为开启EDU的参数
PMD_EDU_EVENT_ACTIVE,
PMD_EDU_EVENT_DEACTIVE,
PMD_EDU_EVENT_MSG,
PMD_EDU_EVENT_TIMEOUT,
PMD_EDU_EVENT_LOCKWAKEUP
};
事件的post以及wait函数,作为成员函数存放在EDUCB类中:
//将待处理事件push到队列中,等待EDU处理
inline void postEvent(pmdEDUEvent const &data){
_queue.push(data); }
bool waitEvent(pmdEDUEvent &data,long long millsec)
{
bool waitMsg=false;//是否得到了队列中pop出的事件
//如果当前EDU状态不为空闲,将其状态设置为等待
if(PMD_EDU_IDLE!=_status) _status=PMD_EDU_WAITING;
if(0>millsec)
{
//如果当前设置时间小于0,则无限等待,从事件队列里pop出一个事件给data
_queue.wait_and_pop(data);
waitMsg=true;
}//给定超时时间,在超时时间内是否pop出事件
else waitMsg=_queue.timed_wait_and_pop(data,millsec);
if(waitMsg)//如果pop出了事件
{
//事件为终止,则断开连接
if(data._eventType==PMD_EDU_EVENT_TERM)