業務與底層網絡通信分離
Server大部分主要分爲兩層:
- 網絡接收層:負責監聽端口,負責收包,編碼,解碼工作,負責將響應包回傳給客戶端。
- 業務處理層:負責接收網絡接收層完整的包,如果是RPCserver,則需要根據上下文請求(由網絡接收層構建)中指定的方法名以及參數調用指定服務(需要有完善的異常處理機制),得到響應包,通過網絡接收層編碼回傳給客戶端。
TAF server的大致組件結構
- Application:服務需要自行繼承該App,代表了一個服務應用。
- EpollServer:實現底層的Epoll網絡監聽模型以及adapter的註冊管理等工作,監聽多個端口。
- Epoller:負責Epoll底層實現細節。
- BindApapter:代表一個網絡端口的監聽,將具體的網絡底層處理委託給EpollServer,擁有自己的協議編解碼器和業務處理器。
- Protocol:是一個仿函數,表示編碼解碼具體的協議,可自定義,掛載到Adapter上面。
- ServantHandler:負責將請求交給具體的業務邏輯servant處理,通常接收編解碼後的請求包(上下文),掛載到Adapter上。
- ServantHelperManager:是一個單例類,主要用來管理servant類與名字的對應關係,管理adapterName與servant的對應關係。以供EpollServer決定將請求分發給哪一個Adapter處理。
- Servant:表示具體的服務接口,具體業務具體實現。
Application代表一個應用,我們自己實現的Server繼承自此類,自定義初始化操作,並調用main啓動,操作包括:
- 忽略PIPE信號。
- 解析應用部署時自動生成的配置文件:解析配置文件。首先根據命令行參數構建一個TC_Option,獲取參數--config獲取配置文件路徑,保存在ServerConfig :: ConfigFile變量中,利用TC_Config解析配置文件,並將配置加載到Application::conf的靜態變量中。
- 初始化客戶端部分;
- 初始化服務端部分。
- 綁定業務servant的adapter到EpollServer中。
- 初始化業務應用。
- 設置HandleGroup分組,啓動線程。
- 啓動業務處理線程。
- 恢復BUS連接。
- 動態加載配置文件
- 動態設置滾動日誌等級
- 動態設置按天日誌等級
- 查看服務狀態
- 查看當前鏈接狀態
- 查看編譯的TAF版本
- 加載配置文件中的屬性信息
- 查看服務支持的管理命令
- 設置染色信息
- 上報版本
- 發送心跳給node, 表示啓動了
- 發送給notify表示服務啓動了
- ctrl + c能夠完美結束服務
- 重定向 stdin、stdout、 stderr
- 初始化完畢後, 日誌再修改爲異步
void
Application::main(int
argc,
char
*argv[])
{
try
{
TC_Common::ignorePipe();
//解析配置文件
parseConfig(argc, argv);
//初始化Proxy部分
initializeClient();
//初始化Server部分
initializeServer();
vector<TC_EpollServer
::BindAdapterPtr
> adapters;
//綁定對象和端口
bindAdapter(adapters);
//業務應用的初始化
initialize();
//輸出所有adapter
outAllAdapter(cout);
//設置HandleGroup分組,啓動線程
for
(size_t
i = 0; i < adapters.size(); ++i)
{
string
name = adapters[i]->getName();
string
groupName = adapters[i]->getHandleGroupName();
if(name != groupName)
{
TC_EpollServer::
BindAdapterPtr
ptr = _epollServer->getBindAdapter(groupName);
if
(!ptr)
{
throw
runtime_error("[TAF][adater
`"
+ name + "` setHandle to group `"
+ groupName +
"` fail!" );
}
}
setHandle(adapters[i]);
/*
if (name == groupName)
{
setHandle(adapters[i]);
continue;
}
TC_EpollServer::BindAdapterPtr
ptr = _epollServer->getBindAdapter(groupName);
if (!
ptr)
{
throw runtime_error("[TAF][
adater `" + name + "` setHandle to group `" + groupName + "` fail!");
}
adapters[i]->setHandle(
ptr);
*/
}
//啓動業務處理線程
_epollServer->startHandle();
_epollServer->createEpoll();
//恢復BUS連接
cout <<
"begin BusConnectRecover" <<
endl;
BusConnectRecover();
cout <<
"end BusConnectRecover" <<
endl;
cout <<
"\n" <<
outfill("[initialize server] ",
'.') <<
" [Done]"
<< endl;
cout << OUT_LINE_LONG <<
endl;
//動態加載配置文件
TAF_ADD_ADMIN_CMD_PREFIX(TAF_CMD_LOAD_CONFIG,
Application::cmdLoadConfig);
//動態設置滾動日誌等級
TAF_ADD_ADMIN_CMD_PREFIX(TAF_CMD_SET_LOG_LEVEL,
Application::cmdSetLogLevel);
//動態設置按天日誌等級
TAF_ADD_ADMIN_CMD_PREFIX(TAF_CMD_SET_DAYLOG_LEVEL,
Application::cmdEnableDayLog);
//查看服務狀態
TAF_ADD_ADMIN_CMD_PREFIX(TAF_CMD_VIEW_STATUS,
Application::cmdViewStatus);
//查看當前鏈接狀態
TAF_ADD_ADMIN_CMD_PREFIX(TAF_CMD_CONNECTIONS,
Application::cmdConnections);
//查看編譯的TAF版本
TAF_ADD_ADMIN_CMD_PREFIX(TAF_CMD_VIEW_VERSION,
Application::cmdViewVersion);
//加載配置文件中的屬性信息
TAF_ADD_ADMIN_CMD_PREFIX(TAF_CMD_LOAD_PROPERTY,
Application::cmdLoadProperty);
//查看服務支持的管理命令
TAF_ADD_ADMIN_CMD_PREFIX(TAF_CMD_VIEW_ADMIN_COMMANDS,
Application::cmdViewAdminCommands);
//設置染色信息
TAF_ADD_ADMIN_CMD_PREFIX(TAF_CMD_SET_DYEING,
Application::cmdSetDyeing);
TAF_ADD_ADMIN_CMD_PREFIX(TAF_CMD_CLOSE_CORE,
Application::cmdCloseCoreDump);
//上報版本
TAF_REPORTVERSION(TAF_VERSION);
//發送心跳給node, 表示啓動了
TAF_KEEPALIVE(""
);
//發送給notify表示服務啓動了
TafRemoteNotify::getInstance()->report(
"restart");
//ctrl
+ c能夠完美結束服務
signal(SIGINT, sighandler);
if(
_conf.
get("/
taf/application/server<closecout>",
AppCache::
getInstance()->get
("closeCout"
)) !=
"0" )
{
// 重定向stdin、stdout、
stderr
int
fd =
open("/dev/null"
,
O_RDWR );
if(fd != -1)
{
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
}
else
{
close(0);
close(1);
close(2);
}
}
}
catch
(exception
&ex)
{
TafRemoteNotify::getInstance()->report(
"exit: "
+ string(ex.what
()));
cout
<< "[main exception]:"
<< ex.what() <<
endl;
terminate();
}
//初始化完畢後, 日誌再修改爲異步
TafRollLogger::getInstance()->sync(false);
}
接下來,我們分析其中的一些關鍵步驟。
(1)忽略PIPE信號。爲何需要忽略PIPE信號呢?對一個對端已經關閉的socket調用兩次write, 第二次將會生成SIGPIPE信號, 該信號默認結束進程.具體的分析可以結合TCP的"四次握手"關閉. TCP是全雙工的信道, 可以看作兩條單工信道, TCP連接兩端的兩個端點各負責一條. 當對端調用close時,
雖然本意是關閉整個兩條信道, 但本端只是收到FIN包. 按照TCP協議的語義, 表示對端只是關閉了其所負責的那一條單工信道, 仍然可以繼續接收數據. 也就是說, 因爲TCP協議的限制, 一個端點無法獲知對端的socket是調用了close還是shutdown.對一個已經收到FIN包的socket調用read方法, 如果接收緩衝已空, 則返回0, 這就是常說的表示連接關閉. 但第一次對其調用write方法時, 如果發送緩衝沒問題, 會返回正確寫入(發送).
但發送的報文會導致對端發送RST報文, 因爲對端的socket已經調用了close, 完全關閉, 既不發送, 也不接收數據. 所以, 第二次調用write方法(假設在收到RST之後), 會生成SIGPIPE信號, 導致進程退出.爲了避免進程退出, 可以捕獲SIGPIPE信號, 或者忽略它, 給它設置SIG_IGN信號處理函數:
signal(SIGPIPE, SIG_IGN);這樣, 第二次調用write方法時, 會返回-1, 同時errno置爲SIGPIPE. 程序便能知道對端已經關閉.
在linux下寫socket的程序的時候,如果嘗試send到一個disconnected socket上,就會讓底層拋出一個SIGPIPE信號。
這個信號的缺省處理方法是退出進程,大多數時候這都不是我們期望的。因此我們需要重載這個信號的處理方法。調用以下代碼,即可安全的屏蔽SIGPIPE:
signal (SIGPIPE, SIG_IGN);
對於產生信號,我們可以在產生信號前利用方法 signal(int signum, sighandler_t handler) 設置信號的處理。如果沒有調用此方法,系統就會調用默認處理方法:中止程序,顯示提示信息(就是我們經常遇到的問題)。我們可以調用系統的處理方法,也可以自定義處理方法。
系統裏邊定義了三種處理方法:
- SIG_DFL信號專用的默認動作:
(b)把掛起信號的信號動作設置成SIG_DFL,且其默認動作是忽略信號 (SIGCHLD)。
- SIG_IGN忽略信號
(b)系統不允許把SIGKILL或SIGTOP信號的動作設置爲SIG_DFL
- SIG_ERR
void
TC_Common::ignorePipe()
{
struct
sigaction
sig;
sig.sa_handler
= SIG_IGN;
sig.sa_flags
= 0;
sigemptyset
(&sig.sa_mask);
sigaction(SIGPIPE,&sig,NULL);
}
(3)中是獲取配置文件的default屬性,利用CommunicatorFactory構建communicator,初始化通信器。
(4)中將配置文件的該APP的信息都設置到ServerConfig的靜態成員屬性中。將ServerConfig::LocalIP屬性設置爲本地所有IP地址的第一個非127.0.0.1迴環地址以外的值。
初始化是否防空連接以及空連接超時等選項/taf/application/server<emptyconcheck>,/taf/application/server<emptyconntimeout>。默認爲false和3
緩存應用的數據文件,數據文件路徑爲/taf/application/server<datapath>/${servername}.tafdat路,將文件內容緩存到AppCache的_tFileCache變量中。並且設置本地日誌級別,初始化到LogServer代理,初始化到配置中心代理,初始化到Node的代理,構造一個EpollServer,這個很重要。將AdminServant的管理Servant對應的Adapter綁定到EpollServer上。EndPoint是在配置文件的/taf/application/server<local>指定的。該Adapter的handle線程只有1.將Adapter綁定到EpollServer中。
if(!ServerConfig::Local.empty())
{
ServantHelperManager::getInstance()->
addServant<AdminServant>(
"AdminObj");
ServantHelperManager::getInstance()->
setAdapterServant("AdminAdapter",
"AdminObj");
TC_EpollServer::BindAdapterPtr
lsPtr =
new
TC_EpollServer::BindAdapter(_epollServer
.get());
lsPtr->setName("AdminAdapter"
);
lsPtr->setEndpoint(ServerConfig
::Local);
lsPtr->setMaxConns(TC_EpollServer
::BindAdapter
::DEFAULT_MAX_CONN);
lsPtr->setQueueCapacity(TC_EpollServer
::BindAdapter
::DEFAULT_QUEUE_CAP);
lsPtr->setQueueTimeout(TC_EpollServer
::BindAdapter
::DEFAULT_QUEUE_TIMEOUT);
lsPtr->setProtocolName("
taf");
lsPtr->setProtocol(AppProtocol
::parse);
lsPtr->setHandleGroupName("AdminAdapter"
);
lsPtr->setHandleNum(1);
lsPtr->setHandle<
ServantHandle>();
_epollServer->bind(lsPtr);
}
採用的協議爲AppProtocol::parse,採用的處理器爲默認的Servanthandler。Servant對應的Obj名稱爲AdminObj,Adapter的名稱爲AdminAdapter。其中缺省的數據值爲
enum
{
DEFAULT_QUEUE_CAP = 10*1024,
/**流量*/
MIN_QUEUE_TIMEOUT = 3*1000,
/**隊列最小超時時間(
ms)*/
DEFAULT_MAX_CONN = 1024,
/**缺省最大連接數*/
DEFAULT_QUEUE_TIMEOUT = 60*1000,
/**缺省的隊列超時時間(
ms)*/
};
(5)中主要將配置文件中的業務Servant以及參數配置生成Adapter綁定到EpollServer中。主要是幾點值得注意
Servant的Obj名稱必須符合App.ServerName.的格式,ServantHelperManager會把Servant的Obj名稱與AdapterName對應管理起來,而Obj與Servant類對應起來,需要Application的initialize的addServant<XXXXServant>來做。另外不同於AdminServant綁定在LocalIP的EndPoint上,這裏需要在Servant中指定EndPoint。如果該Servant的配置中指定Protocol爲taf,則使用AppProtocol::parse來進行協議解析。此處並沒有設置handler。將各種Adapter綁定到EpollServer中。並且Application::adapters會維護除了AdminAdapter以外的所有業務Adapter。
void
Application::bindAdapter(vector<TC_EpollServer
::BindAdapterPtr
>& adapters)
{
string
sPrefix =
ServerConfig
::Application
+
"."
+
ServerConfig::
ServerName
+
".";
vector
<string>
adapterName;
map
<string,
ServantHandle*> servantHandles;
if
(_conf.getDomainVector("/
taf/application/server",
adapterName))
{
for
(size_t
i = 0; i < adapterName.
size(); i++)
{
string
servant = _conf.
get("/
taf/application/server/"
+
adapterName[i] +
"<servant>");
checkServantNameValid(servant, sPrefix);
//TC_EpollServer::BindAdapterPtr bindAdapter = bind("/taf/application/server", adapterName[i],
servant);
ServantHelperManager::getInstance()->
setAdapterServant(adapterName
[i], servant);
TC_EpollServer::
BindAdapterPtr
bindAdapter =
new
TC_EpollServer::BindAdapter(_epollServer
.get());
string
sLastPath = "/taf/application/server/"
+
adapterName[i];
bindAdapter->
setName(adapterName
[i]);
bindAdapter->
setEndpoint(_conf[sLastPath
+ "<endpoint>
"]);
bindAdapter->setMaxConns(TC_Common::strto<int>(_conf.get(sLastPath
+ "
<maxconns>",
"128"
)));
bindAdapter->
setOrder(parseOrder
(_conf
.get(sLastPath +
"<order>","allow,deny"
)));
bindAdapter->setAllow(TC_Common::sepstr<string>(_conf[sLastPath
+ "<allow>"],
";,"
,
false));
bindAdapter->setDeny(TC_Common::sepstr<string>(_conf.get
(sLastPath +
"<deny>"
,""
),
";,"
,
false));
bindAdapter->setQueueCapacity(TC_Common::strto<int>(_conf.get(sLastPath
+ "
<queuecap>",
"1024"
)));
bindAdapter->setQueueTimeout(TC_Common::strto<int>(_conf.get(sLastPath
+ "
<queuetimeout>",
"10000"
)));
bindAdapter->
setProtocolName(_conf.get(sLastPath
+ "<protocol>",
"taf"));
if
(bindAdapter->isTafProtocol())
{
bindAdapter->
setProtocol(AppProtocol::parse);
}
bindAdapter->
setHandleGroupName(_conf.get(sLastPath
+ "<handlegroup>
",
adapterName[i]));
bindAdapter->setHandleNum(TC_Common::strto<int>(_conf.get(sLastPath
+ "<threads>"
,
"0"
)));
bindAdapter->setShm(TC_Common::strto<int>(_conf.get(sLastPath
+ "<shmkey>
",
"0")),
TC_Common::strto<int>(_conf.get(sLastPath
+ "<shmcap>
",
"0")));
bindAdapter->
setBusCommuPath(_conf.get(sLastPath
+ "<buscommupath>
",
ServerConfig::DataPath));
bindAdapter->setBusCommuSize(TC_Common::strto<size_t>(_conf.get
(sLastPath +
" <buscommucap>",
"1051648"
)));
bindAdapter->
setBusCommuEnable(_conf.get(sLastPath
+ "<buscommuenable>
",
"N") ==
"Y");
bindAdapter->setLoadFactor(TC_Common::strto<size_t>(_conf.get
("/taf/application/server<loadfactor>
",
"100")));
bindAdapter->initialize();
_epollServer->bind(bindAdapter);
adapters.
push_back(bindAdapter);
//隊列取平均值
if(!
_communicator->
getProperty("property").empty())
{
_communicator->getStatReport()->createPropertyReport(bindAdapter->getName()
+ ".queue",
PropertyReport::
avg());
_communicator->getStatReport()->createPropertyReport(bindAdapter->getName()
+ ".connectRate",
PropertyReport::
avg());
}
}
}
}
如果使用了共享內存隊列
bindAdapter->setShm(
TC_Common::
strto<int
>(
_conf.
get(sLastPath + "<shmkey>
",
"0")),
TC_Common::
strto<int
>(
_conf.
get(sLastPath + "<shmcap>
",
"0")));
void
TC_EpollServer::BindAdapter::setShm(int
iKey,
int
iSize)
{
if
(iKey > 0 && iSize > 0)
{
_shmQueue.setShmKey(static_cast<key_t
>(iKey));
_shmQueue.setShmSize(static_cast<size_t
>(iSize));
_bUseShm
=
true;
}
}
則在Adapter的initialize方法中,會對共享內存隊列進行初始化。
void
TC_EpollServer::BindAdapter::initialize()
{
if
(_bUseShm)
{
_shmQueue.initialize();
}
}
(6)初始化業務應用中,會執行我們重載的Application的initialize方法,主要包括addServant<XXServant>往ServantHelperManager通過模版泛型指定Servant類型來建立起Obj與ServantCreationPolicy對應關係。
(7)中設置HandleGroup分組,啓動線程。在(5)中我們爲業務線程並沒有指定Handler,這裏統一指定,我們獲取adapter配置中groupName,該groupName必須有對應的AdpaterName的Adapter存在,爲每個Adapter設置Handler爲ServantHandler。
for
(size_t
i = 0; i <
adapters .size(); ++i)
{
string
name = adapters
[i]->getName();
string
groupName =
adapters[i]->getHandleGroupName();
if(name != groupName)
{
TC_EpollServer::
BindAdapterPtr
ptr =
_epollServer->getBindAdapter(groupName);
if
(!ptr)
{
throw
runtime_error("[TAF][adater
`"
+ name +
"` setHandle to group `"
+ groupName +
"` fail!"
);
}
}
setHandle(adapters
[i]);
void
Application::setHandle(
TC_EpollServer::
BindAdapterPtr&
adapter)
{
adapter
->setHandle<ServantHandle
>();
}
而Adapter調用setHandler方法,又會連同handleGroupName與handleNum一齊委託給EpollServer的setHandleGroup方法,EpollServer會維護一個groupName與HandlerGroup的KV map。每次當有新的HandleGroupName時,會構建HandleGroup掛載到相應的Adapter上去,並將至放入EpollServer的map中管理起來,並生成對應數目的Handler(此處爲ServantHander)放入該HandleGroup中。每個handler都是繼承線程的。所以其實HanderGroup就是一個線程池。如果該name的HandleGroup已經存在了,則將adapter將如到該HandleGroup中來,這個Adapter與其他Adapter共享一個HandleGroup(此處的疑問,就是handler的數目並不累加)。
struct
HandleGroup
: public
TC_HandleBase
{
string
name;
TC_ThreadLock
monitor;
vector<HandlePtr
>
handles;
map<string,
BindAdapterPtr>
adapters;
};
template<class
T>
void
setHandleGroup(const
string
& groupName,
int32_t
handleNum,
BindAdapterPtr
adapter)
{
map<string,
HandleGroupPtr
>::iterator
it = _handleGroups.find(groupName);
if
(it ==
_handleGroups.end())
{
HandleGroupPtr
hg =
new
HandleGroup();
hg->
name
= groupName;
adapter->
_handleGroup
= hg;
for
(
int32_t
i = 0; i < handleNum; ++i)
{
HandlePtr
handle =
new
T();
handle->setEpollServer(
this);
handle->setHandleGroup(hg);
hg->
handles.
push_back(handle);
}
_handleGroups[groupName] = hg;
it =
_handleGroups.
find(groupName);
}
it->second->adapters[adapter->getName()]
= adapter;
adapter->
_handleGroup
= it->
second;
}
(8)委託EpollServer啓動業務處理線程
_epollServer->startHandle()
首先判斷並且設置_handleStarted狀態,避免重複啓動handler線程。遍歷當前EpollServer中的所有HandleGroup,將HandleGroup中的每一個Handler都start起來。
void
TC_EpollServer::startHandle()
{
if
(!_handleStarted)
{
_handleStarted
=
true;
map<string,
HandleGroupPtr
>::iterator
it;
for
(it =
_handleGroups.begin();
it != _handleGroups.end();
++it)
{
vector<HandlePtr>&
hds = it-> second->handles
;
for
(
uint32_t
i = 0; i < hds.
size(); ++i)
{
if
(!hds[i]->
isAlive())
{
hds[i]->
start();
}
}
}
}
}
ServantHandler繼承與Handle,Handle的run如下:
void
TC_EpollServer::Handle::run()
{
initialize();
handleImp
();
}
首先會實現我們業務Servant的initialize方法。所以Servant的initialize方法是我們配置了多少個handleNum就會執行多少次。並且會執行一系列的handle方法,我們後續分析ServantHandler的時候進一步詳細分析。
然後啓動Epoll,開始監聽所有Adapter的ServerSock:
_epollServer->createEpoll();
在Application的waitForShutdown方法中:
void
Application::waitForShutdown()
{
_epollServer->waitForShutdown();
destroyApp();
TafRemoteNotify::getInstance()->report(
"stop",
true);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
初始化EpollServer,初始化工作如下:
- 設置一系列的狀態
- 標記未構建底層Epoller。
- 初始化conn list。
- 不妨空連接攻擊。
- 默認空連接超時時間2000
- udp連接時接收包緩存大小,針對所有udp接收緩存有效64K
- 初始化Socket管道(用於關閉服務)
- 初始化Socket管道(用於通知有數據需要發送就
TC_EpollServer::TC_EpollServer()
:
_bTerminate
(false)
,
_handleStarted
(false)
,
_createEpoll
(false)
,
_list
(this)
,
_pLocalLogger
(NULL)
,_bEmptyConnAttackCheck
(false)
,_iEmptyCheckTimeout
(MIN_EMPTY_CONN_TIMEOUT)
,_nUdpRecvBufferSize
(DEFAULT_RECV_BUFFERSIZE)
{
_shutdown.createSocket();
_notify.createSocket();
}
EpollServer的綁定Adapter,我們可以把Adapter綁定到該EpollServer中,每個EpollServer都包括一個BindAdapter的Map,用於維護與管理。每次綁定新的Adapter的時候,都會根據AdapterName來進行衝突檢測,保證Adapter的唯一性。如果存在重名,則會拋出異常,然後根據Adapter中指定的EndPoint綁定到該Adapter包含的TC_Socket上面去。綁定後listeners將保存該ServerSock的描述符與Adapter的KV關係。
int
TC_EpollServer::bind(BindAdapterPtr
&lsPtr)
{
map<
int,
BindAdapterPtr>::iterator
it =
_listeners.begin();
while
(it !=
_listeners.end())
{
if(it->second->getName()
== lsPtr->getName())
{
throw
TC_Exception(
"bind name '"
+ lsPtr->getName() +
"' conflicts.");
}
++it;
}
const
TC_Endpoint
&ep = lsPtr->getEndpoint();
TC_Socket& s = lsPtr->getSocket();
bind(ep, s);
_listeners[s.getfd()] = lsPtr;
return
s.getfd();
}
創建響應EndPoint的ServerSock的時候,會對ServerSock做一些響應的設置,如果是TCP的話,listenerSize設置爲1024,sockopt會設置爲alive連接,關閉Nagle實現tcpnodelay,以及SO_LINGER和非阻塞。
void
TC_EpollServer::bind(
const
TC_Endpoint
&ep,
TC_Socket
&s)
{
int
type = ep.isUnixLocal()?
AF_LOCAL:AF_INET
;
if(ep.isTcp())
{
s.createSocket(SOCK_STREAM,
type);
}
else
{
s.createSocket(SOCK_DGRAM,
type);
}
//設置網絡qos的
dscp標誌
if(0 != ep.getQos())
{
int
iQos=ep.getQos();
s.setSockOpt(IP_TOS,&iQos,
sizeof(iQos),
SOL_IP);
}
if(ep.isUnixLocal())
{
s.bind(ep.getHost().c_str());
}
else
{
s.bind(ep.getHost(), ep.getPort());
}
if(ep.isTcp() && !ep.isUnixLocal())
{
s.listen(1024);
s.setKeepAlive();
s.setTcpNoDelay();
//不要設置close wait否則http
服務回包主動關閉連接會有問題
s.setNoCloseWait();
}
s.setblock(false);
}
EpollServer啓動業務線程,首先判斷——handlerStarted的狀態並設置,避免重啓啓動,遍歷
void
TC_EpollServer::startHandle()
{
if
(!_handleStarted)
{
_handleStarted
=
true;
map<string,
HandleGroupPtr
>::iterator
it;
for
(it =
_handleGroups.begin(); it !=
_handleGroups.end(); ++it)
{
vector<HandlePtr>& hds = it->
second->handles
;
for
(
uint32_t i = 0; i < hds.
size(); ++i)
{
if
(!hds[i]->
isAlive())
{
hds[i]->
start();
}
}
}
}
}
EpollServer負責建立對應的Epoller底層處理器,默認處理鏈接數目爲10240最大的連接數目。
- 將shutdown與notify的套接字加入到Epoll中。
- 遍歷註冊到EpollServer中的BindAdapter。
- 根據所有Adapter設置的MaxConn的累加計算出系統可以總共可以管理的最大連接數目(如果是UDP,則算一個連接),並初始化連接隊列ConnectionList。
- 如果存在UDP連接,則找出相應的Adapter,並建立Connection。
void
TC_EpollServer::createEpoll()
{
if(!_createEpoll)
{
_createEpoll
=
true;
//創建epoll
_epoller.
create(10240);
_epoller.
add(_shutdown
.getfd(), H64(ET_CLOSE),
EPOLLIN);
_epoller.
add(_notify
.getfd(), H64(ET_NOTIFY),
EPOLLIN);
size_t
maxAllConn = 0;
bool hasUdp =
false;
//監聽socket
map<int
,
BindAdapterPtr>::iterator
it =
_listeners.
begin();
while(it !=
_listeners.
end())
{
if(it->
second->getEndpoint
().isTcp())
{
//獲取最大連接數
maxAllConn += it->
second->getMaxConns
();
_epoller.
add(it->first
, H64(ET_LISTEN
) | it->first,
EPOLLIN);
}
else
{
maxAllConn++;
hasUdp =
true;
}
++it;
}
//初始化連接管理鏈表
_list.
init(maxAllConn);
if(hasUdp)
{
//監聽socket
map<int
,
BindAdapterPtr>::iterator
it =
_listeners.
begin();
while(it !=
_listeners.
end())
{
if(!it->
second->getEndpoint
().isTcp())
{
Connection
*cPtr =
new
Connection(it->second.get(),
it-> first);
//udp分配接收buffer
cPtr->
setRecvBuffer(_nUdpRecvBufferSize);
addUdpConnection(cPtr);
}
++it;
}
}
}
}
void
TC_EpollServer::ConnectionList::init(uint32_t
size)
{
_lastTimeoutTime
=
TC_TimeProvider::getInstance()->getNow();
_total
= size;
_free_size = 0;
//初始化鏈接鏈表
if(_vConn)
delete[]
_vConn;
//分配total+1個空間(多分配一個空間, 第一個空間其實無效)
_vConn
=
new
list_data[_total+1];
_iConnectionMagic = ((
uint32_t)
_lastTimeoutTime) << 20;
//free從1開始分配, 這個值爲uid, 0保留爲管道用,
epollwait根據0判斷是否是管道消息
for(uint32_t
i = 1; i <=
_total; i++)
{
_vConn[i].first
= NULL;
_free.push_back(i);
++_free_size;
}
}
我們具體看一下連接是如何被listen,accept,以及具體到read,write,close等過程的。在EpollServer構建的時候,會創建兩個SOCK
_shutdown.createSocket
(AF_INET,SOCK_STREAM);
_notify.createSocket
(AF_INET,SOCK_STREAM);
註冊感興趣的EPOLLIN,表示接收信號。
_epoller.
add(_shutdown
.getfd(), H64(
ET_CLOSE),
EPOLLIN);
_epoller.
add(
_notify.getfd(), H64(
ET_NOTIFY),
EPOLLIN);
H64表示把一個int32爲的數據轉化爲int64位,然後把低端32位移到高端32位。
H64(x) (((uint64_t)x) << 32)
然後EpollServer會遍歷listenter中的所有fd
_epoller.add(it->
first, H64(
ET_LISTEN) | it->first
,
EPOLLIN);
可見epoll_event data聲明如下:
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
我們這裏選擇在把u64的高端32位設定爲事件類型
enum
{
ET_LISTEN
= 1,
ET_CLOSE = 2,
ET_NOTIFY
= 3,
ET_NET = 0,
};
進入輪詢響應請求階段
最後系統會調用EpollServer會waitShutdown,進行Epollwait操作。經典的死循環,每次EpollWait之前都進行超時檢測,調用Epoller進行wait獲取有事件激活的sock(2s超時),返回事件激活的sock的數目,並進行遍歷得到epoll_event,根據epoll_event中的data,得到該事件的關聯信息,進行處理。
void
TC_EpollServer::waitForShutdown()
{
startHandle();
createEpoll();
//循環監聽網路連接請求
while(!_bTerminate)
{
_list.
checkTimeout(TC_TimeProvider::getInstance()->getNow());
int
iEvNum =
_epoller.wait(2000);
for(int
i = 0; i < iEvNum; ++i)
{
try
{
const
epoll_event
&ev =
_epoller.get(i);
uint32_t
h = ev.
data.u64
>> 32;
switch(h)
{
case
ET_LISTEN:
{
//監聽端口有請求
map<int
,
BindAdapterPtr>::const_iterator
it =
_listeners.find(ev.data.u32);
if( it !=
_listeners.
end())
{
if(ev.
events &
EPOLLIN )
{
bool
ret;
do
{
ret =
accept(ev.data
.u32);
}
while(ret);
}
}
}
break;
case
ET_CLOSE:
//關閉請求
break;
case
ET_NOTIFY:
//發送通知
processPipe();
break;
case
ET_NET:
//網絡請求
processNet(ev);
break;
default:
assert(
true
);
}
}
catch(
exception
&ex)
{
error("waitForShutdown exception:"
+
string(ex.what
()));
}
}
}
stopThread();
}
把事件類型取出來uint32_t h = ev.
data.u64
>> 32
因爲epoll_data_t是一個union,所以直接ev.
data.u32可以得到listen的sock fd。
監聽端口有反應?客戶端來了?
當listener的接口獲取到客戶端的握手請求的時候,會accept調用來建立與客戶端的連接。在連接建立的時候,會進行白名單的判斷,如果不在白名單中,則會斷開連接。如果超出了當前Adapter允許的最大連接數,也會斷掉。如果連接建立成功,將Adapter中設置的超時時間拿出來,爲客戶端構建一個新的Connection對象,超時時間默認不短於2S。
bool
TC_EpollServer::accept(
int
fd)
{
struct
sockaddr_in
stSockAddr;
socklen_t
iSockAddrSize = sizeof
(sockaddr_in
);
TC_Socket
cs;
cs.setOwner(false);
//接收連接
TC_Socket
s;
s.init
(fd,
false
, AF_INET);
int
iRetCode = s.accept(cs, (struct
sockaddr
*) &stSockAddr, iSockAddrSize);
if
(iRetCode > 0)
{
string ip;
uint16_t
port;
char
sAddr[INET_ADDRSTRLEN] =
"\0"
;
struct
sockaddr_in
*p = (struct
sockaddr_in
*)&stSockAddr;
inet_ntop(AF_INET, &p->
sin_addr, sAddr,
sizeof(sAddr));
ip = sAddr;
port =
ntohs(p->sin_port
);
debug("accept ["
+ ip +
":"
+
TC_Common
::tostr(port) +
"] ["
+
TC_Common
::tostr(cs.getfd()) +
"]
incomming
");
if(!_listeners[fd]->isIpAllow(ip))
{
debug("accept ["
+ ip +
":"
+
TC_Common::
tostr(port) +
"] ["
+
TC_Common
::tostr(cs.getfd()) +
"] not allowed");
cs.close();
return
true;
}
if(_listeners[fd]->isLimitMaxConnection())
{
cs.close();
debug("accept ["
+ ip +
":"
+
TC_Common::
tostr(port) +
"]["
+
TC_Common
::tostr(cs.getfd()) +
"] beyond max connection:"
+
TC_Common::tostr(_listeners
[fd]->getMaxConns()));
return
true;
}
cs.setblock(
false);
cs.setKeepAlive();
cs.setTcpNoDelay();
cs.setCloseWaitDefault();
//設置網絡qos的
dscp標誌
if(0 !=
_listeners[fd]->getEndpoint().getQos())
{
int
iQos=
_listeners[fd]->
getEndpoint().getQos
();
cs.
setSockOpt(IP_TOS
,&iQos,sizeof
(iQos),SOL_IP);
info("setSockOpt ["
+ ip +
":"
+
TC_Common::
tostr(port) +
"]["
+
TC_Common::tostr(cs.getfd()) +
"] listen server
qos is:"+
TC_Common::
tostr(iQos));
}
int
timeout =
_listeners[fd]->
getEndpoint().getTimeout
()/1000;
Connection
*cPtr =
new
Connection(_listeners
[fd].get(), fd, (timeout < 2 ? 2 : timeout), cs.getfd(), ip, port);
addTcpConnection(cPtr);
//過濾連接首個數據包包頭
cPtr->setHeaderFilterLen(_listeners[fd]->getHeaderFilterLen());
return
true;
}
else
{
//直到發生EAGAIN纔不繼續accept
if(errno
== EAGAIN)
{
return
false;
}
return
true;
}
return
true;
}
在構建好Connection後,會將Connection放入到EpollServer統一的ConnectionList中進行管理。另外該Adapter的當前連接數目會+1;並且該連接會註冊到Epoll,讀寫時間都將被註冊。該Connection的ID會當作epoll_event的data被放入。因爲Connection本身ID爲int32,所以連接類型爲NET=0;
void
TC_EpollServer::addTcpConnection(
TC_EpollServer::
Connection
*cPtr)
{
uint32_t
uid =
_list.getUniqId();
cPtr->init(uid);
_list.add(cPtr,
cPtr->getTimeout() +
TC_TimeProvider::getInstance()->getNow());
cPtr->getBindAdapter
()->increaseNowConnection();
//注意epoll
add必須放在最後, 否則可能導致執行完, 才調用上面語句
_epoller.add(cPtr->getfd(),
cPtr->getId(), EPOLLIN
| EPOLLOUT);
}
上述步驟將處理了反覆接受accept事件,accept設置爲了unblock,所以當沒有新的握手請求了,反覆將邊緣觸發的事件處理完後停止接收。
客戶端和服務器果斷開始基情燃燒
如果是普通的客戶端請求的處理,則需要處理NET類型的事件。如果發生錯誤,直接斷掉請求。
void
TC_EpollServer::processNet(const
epoll_event
&ev)
{
uint32_t
uid = ev.data.u32;
Connection
*cPtr = getConnectionPtr(uid);
if(!cPtr)
{
debug("NetThread::processNet connection["
+
TC_Common::
tostr(uid) +
"] not exists.");
return;
}
if
((ev.events & EPOLLERR
|| ev.events & EPOLLHUP
) && cPtr->getType() !=
Connection::EM_BUS
)
{
delConnection(cPtr);
return;
}
if(ev.events
& EPOLLIN)
//有數據需要讀取
{
recv_queue::queue_type
vRecvData;
int
ret =
recvBuffer(cPtr, vRecvData);
if(ret < 0)
{
delConnection(cPtr);
return;
}
if(!vRecvData.empty())
{
cPtr->
insertRecvQueue(vRecvData);
}
}
if
(ev.events
& EPOLLOUT)
//有數據需要發送
{
int
ret = sendBuffer(cPtr);
if
(ret < 0)
{
delConnection(cPtr);
return;
}
}
_list.refresh(uid,
cPtr->getTimeout() +
TC_TimeProvider::getInstance()->getNow());
}
如果有數據要讀,代表客戶端發包請求,調用Connector進行數據讀取,以及數據包的解析。最後將得到的完整的數據包插入到該Adapter統一的接受隊列中來。
如果有數據需要寫,表示有rsp發送到客戶端去:會調用該Connector將sendBuff的數據都發送到客戶端去。當每次buffer遺留的數據太多了(超過了8192),則把sendBuff和_sendPos都復位一下。這樣避免總是copy
buff,也避免因爲buffer無限增大。
int
TC_EpollServer::Connection::send(const
string
& buffer,
const
string &ip,
uint16_t
port)
{
if(_lfd
== -1)
{
int
iRet =
_sock.
sendto((const
void*) buffer.c_str(),
buffer.length(), ip, port, 0);
if(iRet < 0)
{
_pBindAdapter->getEpollServer()->
error("[TC_EpollServer::Connection] send ["
+
_ip
+
":"
+
TC_Common::
tostr(_port) +
"] error");
return
-1;
}
return
0;
}
_sendbuffer
+= buffer;
size_t
pos = 0;
size_t
sendLen = _sendbuffer
.length() -
_sendPos;
const
char
*sendBegin =
_sendbuffer.
c_str() +
_sendPos;
while
(pos < sendLen )
{
int
iBytesSent = 0;
iBytesSent =
write(_sock.getfd(),
(const
void*)(
sendBegin
+ pos), sendLen - pos);
if
(iBytesSent < 0)
{
if(
errno ==
EAGAIN )
{
break;
}
else
{
_pBindAdapter->getEpollServer()->
debug("send ["
+
_ip
+
":"
+
TC_Common::
tostr(_port) +
"] close connection by peer.");
return
-1;
}
}
pos += iBytesSent;
//發送的數據小於需要發送的,break, 內核會再通知你的
if(pos < sendLen)
{
break;
}
}
if(pos > 0)
{
if(
_sendbuffer.
length() > 8192)
{
_sendbuffer
=
_sendbuffer.
substr(_sendPos
+ pos);
_sendPos
= 0;
}
else
{
_sendPos
+= pos;
}
}
//需要關閉鏈接
if(_bClose
&& ( (
_sendbuffer.
length() -
_sendPos) == 0 ) )
{
_pBindAdapter->getEpollServer()->
debug("send ["
+
_ip
+
":"
+
TC_Common
::tostr(
_port) +
"] close connection by user.");
_sendbuffer.
clear();
_sendPos
= 0;
return
-1;
}
return
0;
}
下面完了後該上面了,關於邏輯層的處理
我們來看一下業務線程都是如何處理這些我們收到的完整的數據包的。我們已經知道handleGroup裏面有很多Handle線程,並且關聯了一個或者多個Adapter,他會如何處理這些Adapter中收到的包的呢?每個Handle都會在線程run方法中調用handleImp方法,startHandle是一個鉤子函數,在處理業務邏輯之前處理回調,當該handleGroup所有掛載的Adapter都沒有接受數據的時候會掛起線程等待通知,
- 發送心跳
- 業務處理附加的自有消息遍歷所有的Servant處理doCustomMessage。
- 處理各個Adapter的回包。
void
TC_EpollServer::Handle::handleImp()
{
startHandle();
while
(!getEpollServer()->
isTerminate())
{
{
TC_ThreadLock::
Lock
lock(
_handleGroup->
monitor);
if
(allAdapterIsEmpty() && allFilterIsEmpty())
{
_handleGroup->
monitor.timedWait(
_iWaitTime);
}
}
//上報心跳
heartbeat();
//爲了實現所有主邏輯的單線程化,在每次循環中給業務處理自有消息的機會
handleAsyncResponse();
handleCustomMessage(
true);
tagRecvData* recv =
NULL;
map<string,
BindAdapterPtr>& adapters =
_handleGroup->
adapters;
for
(map<string,
BindAdapterPtr
>::iterator
it = adapters.begin(); it != adapters.end(); ++it)
{
BindAdapterPtr& adapter = it->
second;
try
{
while
(adapter->waitForRecvQueue(recv, 0))
{
//上報心跳
heartbeat();
//爲了實現所有主邏輯的單線程化,在每次循環中給業務處理自有消息的機會
handleAsyncResponse();
tagRecvData& stRecvData = *recv;
time_t
now = TC_TimeProvider::getInstance()->getNow();
stRecvData.
adapter
= adapter;
//數據已超載 overload
if
(stRecvData.
isOverload)
{
handleOverload(stRecvData);
}
//關閉連接的通知消息
else
if
(stRecvData.
isClosed)
{
handleClose(stRecvData);
}
//數據在隊列中已經超時了
else
if
( (now - stRecvData.recvTimeStamp)*1000
> adapter->getQueueTimeout())
{
handleTimeout(stRecvData);
}
else
{
handle(stRecvData);
}
handleCustomMessage(
false);
delete
recv;
recv =
NULL;
}
}
catch
(
exception &ex)
{
if(recv)
{
close(recv->
uid);
delete
recv;
recv =
NULL;
}
getEpollServer()->
error("[Handle::handleImp] error:"
+
string (ex.what()));
}
catch
(...)
{
if(recv)
{
close(recv->
uid);
delete
recv;
recv =
NULL;
}
getEpollServer()->
error("[Handle::handleImp] unknown error");
}
}
}
stopHandle();
}
主要是handle(stRecvData)方法,下面是ServantHandler的實現
void
ServantHandle::handle(const
TC_EpollServer::tagRecvData
&stRecvData)
{
JceCurrentPtr
current = createCurrent(stRecvData);
if
(!current)
return;
if
(current->getBindAdapter()->isTafProtocol())
{
handleTafProtocol(current);
}
else
{
handleNoTafProtocol(current);
}
}
將tagRecvData序列化成爲請求上下文,如果是taf協議的話,會把resvData中buffer的數據解析成爲一個RequestPacket的通用請求包,並把當前Adapter的AdapterName設置到該RequestPackage的sServantName中。
JceCurrentPtr
ServantHandle::createCurrent(
const
TC_EpollServer::tagRecvData
&stRecvData)
{
JceCurrentPtr
current =
new
JceCurrent(
this);
try
{
current->
initialize(stRecvData);
}
catch
(JceDecodeException
&ex)
{
LOG->error() <<
"[TAF]ServantHandle::handle request protocol decode error:"
<< ex.what() <<
endl;
close(stRecvData.
uid);
return
NULL;
}
//只有TAF協議才處理
if(current->getBindAdapter()->isTafProtocol())
{
time_t
now = TC_TimeProvider
::getInstance()->getNow();
//數據在隊列中的時間超過了客戶端等待的時間(TAF協議)
if
(current->
_request.
iTimeout
> 0 && (now - stRecvData.recvTimeStamp
-1 )*1000 > current->_request
.iTimeout
)
{
LOG->error() <<
"[TAF]ServantHandle::handle queue timeout:"
<< current->
_request.
sServantName
<<
"|"
<< current->
_request.
sFuncName
<<
"|"
<< stRecvData.
recvTimeStamp
<<
"|"
<< stRecvData.adapter->getQueueTimeout()
<< "|"
<< current->
_request.
iTimeout
<<
"|"
<< now <<
"|"
<< stRecvData.
ip
<<
":"
<< stRecvData.port
<<
endl;
current->sendResponse(JCESERVERQUEUETIMEOUT);
return
NULL;
}
}
return
current;
}
如果是taf協議的話,resvData buff中的數據會解析爲通用的RequestPackage
void
readFrom(taf::
JceInputStream<
ReaderT>& _is)
{
_is.read(
iVersion, 1,
true
);
_is.read(
cPacketType, 2,
true);
_is.read(
iMessageType, 3,
true);
_is.read(
iRequestId, 4,
true);
_is.read(
sServantName, 5,
true);
_is.read(
sFuncName, 6,
true);
_is.read(
sBuffer, 7,
true);
_is.read(
iTimeout, 8,
true);
_is.read(
context, 9,
true);
_is.read(
status, 10,
true);
}
如果不是taf協議,則把buffer的數據原封不動直接丟給RequestPackage
_request.sBuffer.resize(stRecvData.buffer
.length());
::memcpy(&_request
.sBuffer
[0], stRecvData.buffer.c_str(),
stRecvData. buffer.
length());
最終如果是taf協議,調用ServantHandle的void
ServantHandle::
handleTafProtocol(
const
JceCurrentPtr ¤t);
如果協議不是taf,調用ServantHandle的void
ServantHandle::
handleNoTafProtocol(
const
JceCurrentPtr ¤t);
這兩個都會要求Servant進行Dispatch處理,接下來了解一下Servant,在Servant的dispatch中,需要根據請求的元信息和業務Servant自動生成的OnDispatch方法進行方法分發(JAVA可用反射實現),並且將Rsp輸出。在這個方法中,可以進行一些系統的元方法處理,如taf_ping。如果是taf協議,直接調用了自動生成的onDispatch,如果是自定義協議,則需要調用doRequest方法,current的buffer中沒有經過協議解碼的數據,默認實現爲返回-1,rsp字節爲空。
int
Servant::dispatch(
JceCurrentPtr
current,
vector<char> &buffer)
{
int
ret = JCESERVERUNKNOWNERR;
if
(current->getFuncName() ==
"taf_ping")
{
TLOGINFO(
"[TAF][Servant::dispatch] taf_ping
ok from ["
<< current->getIp() <<
":"
<< current->getPort() <<
"]"
<<
endl);
ret = JCESERVERSUCCESS;
}
else
if
(current->getFuncName() ==
"taf_bus")
{
TC_LockT<
TC_ThreadRecMutex> lock(*
this);
ret =
doBus(current, buffer);
}
else
if
(!current->getBindAdapter()->isTafProtocol())
{
TC_LockT<TC_ThreadRecMutex>
lock(* this);
ret =
doRequest(current, buffer);
}
else
{
TC_LockT<TC_ThreadRecMutex>
lock(* this);
ret =
onDispatch(current, buffer);
}
return
ret;
}
一下是MobWIN廣告Servant自動生成的一個onDispatch方法:
int
AdService::
onDispatch(taf::JceCurrentPtr
_current, vector<
char> &_sResponseBuffer)
{
pair<
string*,
string*> r =
equal_range(__MobWin__AdService_all, __MobWin__AdService_all+2, _current->getFuncName());
if(r.
first == r.
second)
return
taf::JCESERVERNOFUNCERR;
switch(r.
first - __MobWin__AdService_all)
{
case 0:
{
taf::JceInputStream<
taf::BufferReader> _is;
_is.setBuffer
(_current->getRequestBuffer());
MobWin::
AdRequest4App req;
MobWin::
AdResponse rsp;
if (_current->
getRequestVersion() ==
WUPVERSION || _current->getRequestVersion() ==
WUPVERSION2)
{
UniAttribute<taf::BufferWriter
, taf::BufferReader>
tafAttr;
tafAttr.setVersion
(_current->getRequestVersion());
tafAttr.decode
(_current->getRequestBuffer());
tafAttr.get
("
req", req);
tafAttr.getByDefault
("
rsp", rsp, rsp);
}
else
{
_is.read
(req, 1, true
);
_is.read
(rsp, 2, false
);
}
taf::Int32 _ret =
getAdContents
(req,rsp, _current);
if(_current->
isResponse())
{
if (_current->
getRequestVersion() ==
WUPVERSION || _current->
getRequestVersion() == WUPVERSION2)
{
UniAttribute<taf::BufferWriter
, taf::BufferReader>
tafAttr;
tafAttr.setVersion
(_current->getRequestVersion());
tafAttr.put
(""
, _ret);
tafAttr.put
("
rsp", rsp);
tafAttr.encode
(_sResponseBuffer);
}
else
{
taf::JceOutputStream<
taf::BufferWriter> _os;
_os.write
(_ret, 0);
_os.write
(rsp, 2);
_os.swap
(_sResponseBuffer);
}
}
return
taf::JCESERVERSUCCESS;
}
case 1:
{
taf::JceInputStream<
taf::BufferReader> _is;
_is.setBuffer
(_current->getRequestBuffer());
std::
string inMsg;
std::
string rtnMsg;
if (_current->
getRequestVersion() ==
WUPVERSION || _current->getRequestVersion() ==
WUPVERSION2)
{
UniAttribute<taf::BufferWriter
, taf::BufferReader>
tafAttr;
tafAttr.setVersion
(_current->getRequestVersion());
tafAttr.decode
(_current->getRequestBuffer());
tafAttr.get
("inMsg"
, inMsg);
tafAttr.getByDefault
("rtnMsg"
, rtnMsg, rtnMsg);
}
else
{
_is.read
(inMsg, 1, true
);
_is.read
(rtnMsg, 2, false
);
}
taf::Int32 _ret =
testService
(inMsg,rtnMsg, _current);
if(_current->
isResponse())
{
if (_current->
getRequestVersion() ==
WUPVERSION || _current->
getRequestVersion() == WUPVERSION2)
{
UniAttribute<taf::BufferWriter
, taf::BufferReader>
tafAttr;
tafAttr.setVersion
(_current->getRequestVersion());
tafAttr.put
(""
, _ret);
tafAttr.put
("rtnMsg"
, rtnMsg);
tafAttr.encode
(_sResponseBuffer);
}
else
{
taf::JceOutputStream<
taf::BufferWriter> _os;
_os.write
(_ret, 0);
_os.write
(rtnMsg, 2);
_os.swap
(_sResponseBuffer);
}
}
return
taf::JCESERVERSUCCESS;
}
}
return
taf::JCESERVERNOFUNCERR;
}
業務Request從JCEInputStream的解析,以及Rsp的編碼,都是根據JCE文件自動生成的。這些留待分析JCE編碼的時候再分析。
該把結果發射給客戶端啦!
如果是同步請求,則必須把結果返還給客戶端
void
JceCurrent::sendResponse(
int iRet,
const
vector<
char>& buffer,
const
map<
string, string>& status,
const
string & sResultDesc)
{
_iRet = iRet;
//單向調用不需要返回
if (
_request.
cPacketType == JCEONEWAY)
{
return;
}
JceOutputStream<
BufferWriter> os;
if (
_request.
iVersion != WUPVERSION &&
_request.
iVersion != WUPVERSION2)
{
ResponsePacket response;
response.
iRequestId =
_request.
iRequestId;
response.
iMessageType =
_request.
iMessageType;
response.
cPacketType = JCENORMAL;
response.
iVersion = JCEVERSION;
response.
status = status;
response.
sBuffer = buffer;
response.
sResultDesc = sResultDesc;
response.
iRet = iRet;
if(IS_MSG_TYPE(
_request.
iMessageType, taf::JCEMESSAGETYPELOADED))
{
/**
* 如果業務已經設置負載值,則取業務的負載值,否則取服務端隊列大小
* 負載值加上調節因子,爲了提高客戶端的負載均衡性。
*/
int iLoaded = 1;
if(
_bSetLoaded)
{
iLoaded =
_iLoaded > 0?
_iLoaded:1;
}
else
{
iLoaded =
_pBindAdapter->getRecvBufferSize() +
_pBindAdapter
->getLoadFactor();
}
response.
status[
ServantProxy::
STATUS_LOADED_VALUE] =
TC_Common::
tostr(iLoaded);
}
TLOGINFO(
"[TAF]JceCurrent::sendResponse :"
<< response.
iMessageType <<
"|"
<<
_request.
sServantName <<
"|"
<<
_request.
sFuncName <<
"|"
<< response.
iRequestId <<
endl);
response.writeTo(os);
}
else
{
//wup迴應包用請求包的結構
RequestPacket response;
response.
iRequestId =
_request.
iRequestId;
response.
iMessageType =
_request.
iMessageType;
response.
cPacketType = JCENORMAL;
response.
iVersion =
_request.
iVersion;
response.
status = status;
response.
sBuffer = buffer;
response.
sServantName =
_request.
sServantName;
response.
sFuncName =
_request.
sFuncName;
//異常的情況下buffer可能爲空,要保證有一個空UniAttribute的編碼內容
if(response.
sBuffer.
size() == 0)
{
wup::
UniAttribute<> tafAttr;
tafAttr.
setVersion(
_request.
iVersion);
tafAttr.
encode(response.
sBuffer);
}
//iRet爲0時,不記錄在status裏面,和
wup客戶端協議一致,節省空間
if(iRet != 0)
{
response.
status[
ServantProxy::
STATUS_RESULT_CODE] =
TC_Common::
tostr(iRet);
}
//sResultDesc爲空時,不記錄在status裏面,和
wup客戶端協議一致,節省空間
if(!sResultDesc.
empty())
{
response.
status[
ServantProxy::
STATUS_RESULT_DESC] = sResultDesc;
}
TLOGINFO(
"[TAF]JceCurrent::sendResponse :"
<< response.
iMessageType <<
"|"
<< response.
sServantName <<
"|"
<< response.
sFuncName <<
"|"
<< response.
iRequestId <<
endl);
response.writeTo(os);
}
taf::
Int32 iHeaderLen =
htonl(
sizeof(taf::
Int32) + os.getLength());
string
s = ""
;
s.append
((const
char*)&iHeaderLen,
sizeof(taf::
Int32));
s.append
(os.getBuffer(), os.getLength());
_pServantHandle->
sendResponse(_uid
, s, _ip
, _port
);
}
最後會調用EpollServer的send方法
void
TC_EpollServer::
send(uint32_t
uid, const
string
&s, const
string &ip,
uint16_t port)
{
if(
_bTerminate)
{
return;
}
tagSendData* send =
new tagSendData();
send->
uid = uid;
send->
cmd =
's';
send->
buffer = s;
send->
ip = ip;
send->
port = port;
_sbuffer.
push_back(send);
//通知epoll響應, 有數據要發送
_epoller.
mod(_notify
.getfd(), H64(
ET_NOTIFY),
EPOLLOUT
);
}
EpollServer會構造tagSendData包,並放入自己的回包隊列中(EpollServer所有)。並且通知notify描述符(write只要註冊了該事件,寫緩存區符合低潮lowat的閥值就可以滿足激活條件),可寫。下面是notify類型的事件處理
void
TC_EpollServer::
processPipe()
{
send_queue::queue_type
deSendData;
_sbuffer.
swap(deSendData);
send_queue::queue_type::iterator
it = deSendData.begin();
send_queue::queue_type::iterator
itEnd = deSendData.end();
while(it != itEnd)
{
switch((*it)->
cmd)
{
case
'c':
{
Connection *cPtr =
getConnectionPtr((*it)->
uid);
if(cPtr)
{
if(cPtr->setClose())
{
delConnection(cPtr);
}
}
break;
}
case
's':
{
Connection *cPtr =
getConnectionPtr((*it)->
uid);
if(cPtr)
{
int ret =
sendBuffer(cPtr, (*it)->
buffer, (*it)->ip, (*it)->
port);
if(ret < 0)
{
delConnection(cPtr);
}
}
break;
}
default:
assert(
false
);
}
delete (*it);
++it;
}
}
把EpollServer中的回包交換出來(EpollServer的清空,拿出來的繼續處理),需要回包的繼續調用Connection將數據發送到客戶端去。
int
TC_EpollServer::
sendBuffer(TC_EpollServer
::Connection
*cPtr, const
string &buffer,
const
string &ip,
uint16_t
port)
{
return cPtr->
send(buffer, ip, port);
}
如果應用終止(_bTerminate)最後關閉線程,遍歷EpollServer中的所有handleGroup。
void
TC_EpollServer::stopThread()
{
map<string,
HandleGroupPtr>::iterator
it;
for
(it =
_handleGroups.begin();
it != _handleGroups
.end(); ++it)
{
{
TC_ThreadLock::
Lock
lock(it->
second->monitor
);
it->
second->monitor
.notifyAll();
}
vector<HandlePtr
>& hds = it->second
->handles;
for
(uint32_t
i = 0; i < hds.size(); ++i)
{
if
(hds[i]->
isAlive())
{
hds[i]->
getThreadControl().join
();
}
}
}
}