实现一个数据库(6)线程池

本文详细介绍了如何实现一个数据库中的线程池(EDUMgr),包括EDU(Engine Dispatchable Unit)调度单元的创建、状态转换和事件管理。EDU有TCPListener和Agent两种类型,分别处理监听和客户端交互。通过EDUMgr控制EDU的状态,如CREATE、START、ACTIVATE、DESTROY等,并维护RUN和IDLE队列。还展示了如何整合服务端和客户端,使用假插入操作进行C/S模拟。
摘要由CSDN通过智能技术生成

github链接(更新中)

https://github.com/pourtheworld/mydb

大纲(更新中)
(0)mydb的架构设想

本期任务

  1. 建立EDU(Engine Dispatchable Unit)引擎调度单元类。
  2. 建立EDUEvent(EDU event)EDU事件类。
  3. 定义并实现EDU各个状态和转换事件。
  4. 建立EDUMgr(EDU Manager)线程池。
  5. 定义TCPListener、Agent类型EDU的入口函数。
  6. 对服务端的主函数以及客户端进行整合,用假insert模拟。
  7. 进行简单C/S模拟。

EDU(Engine Dispatchable Unit)引擎调度单元类

  1. EDU类型。
  2. TCPListener、Agent的处理流程。
  3. TCPListener的入口函数。
  4. 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事件类

  1. EDU事件类型。
  2. 事件的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)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值