QFS MetaServer处理ChunkServer请求流程

148 阅读4分钟
原文链接: zhuanlan.zhihu.com

在上一篇博客中我们详细描述了MetaServer处理客户端请求流程。承接上篇,今天主要来描述MetaServer如何处理ChunkServer请求。

因为MetaServer处理ChunkServer和处理客户端请求流程极其相似,所以可以先浏览前一篇文章,帮组应该会很大。两者主要区别在于在处理ChunkServer请求时,MetaServer并未采取类似处理客户端请求的Master + worker线程的模式,所有ChunkServer请求都在Master进程中处理,之所以这样做我想是因为ChunkServer的请求处理逻辑相对比较简单,不会阻塞。

数据结构

ChunkServerFactory

其角色可简单类比ClientManager。负责监听来自ChunkServer的新连接请求,并在收到连接时触发预先设置的回调函数,话不多说,先上代码:

class ChunkServerFactory : public IAcceptorOwner
{
    ...
private:
    Acceptor* mAcceptor;
};

根据我们之前的分析,由于继承了IAcceptorOwner,因此,当新连接到来时便会触发回调函数CreateKfsCallbackObj。

ChunkServer

class ChunkServer :
    public KfsCallbackObj,
    public CSMapServerInfo,
    private SslFilterVerifyPeer,
    public boost::enable_shared_from_this<ChunkServer>
{
    ...
    int HandleMsg(IOBuffer *iobuf, int msgLen);
​
    /// Handlers for the 3 types of messages we could get:
    /// 1. Hello message from a chunkserver
    /// 2. An RPC from a chunkserver
    /// 3. A reply to an RPC that we have sent previously.
​
    int HandleHelloMsg(IOBuffer *iobuf, int msgLen);
    int HandleCmd(IOBuffer *iobuf, int msgLen);
    int HandleReply(IOBuffer *iobuf, int msgLen);
​
    /// Send a response message to the MetaRequest we got.
    bool SendResponse(MetaRequest *op);
    ...
}

ChunkServer可类比前文提到的ClientSM。MetaServer每收到一个来自新ChunkServer的连接便会创建一个该对象。由于MetaServer处理ChunkServer没有采用Master + worker线程的机制,因此,每个新连接上的Request都是由主进程来处理。而chunkServer则主要是维护该连接对应的ChunkServer状态。

核心流程

接收ChunkServer新连接

Master进程负责接收客户端上的新的连接。ChunkServerFactory内负责监听网络端口以及接收新连接的是成员Acceptor。关键的API如下:

bool ChunkServerFactory::Bind()
​
bool ChunkServerFactory::StartAcceptor()

当Acceptor接收到一个新的连接时,会触发Acceptor的回调函数:Acceptor::RecvConnection。在这里会调用其拥有者(也即ChunkServerFactory对象)的回调:mAcceptorOwner->CreateKfsCallbackObj(conn)

Acceptor::Acceptor(
    NetManager&           netManager,
    const ServerLocation& location,
    bool                  ipV6OnlyFlag,
    IAcceptorOwner*       owner,
    bool                  bindOnlyFlag)
    : mLocation(location),
      mIpV6OnlyFlag(ipV6OnlyFlag),
      mAcceptorOwner(owner),
      mConn(),
      mNetManager(netManager)
{
    SET_HANDLER(this, &Acceptor::RecvConnection);
    Acceptor::Bind();
    if (! bindOnlyFlag) {
        Acceptor::StartListening();
    }
}
​
int
Acceptor::RecvConnection(int code, void* data)
{
    switch (code) {
        case EVENT_NEW_CONNECTION:
        break;
        ......
    }
    NetConnectionPtr& conn = *reinterpret_cast<NetConnectionPtr*>(data);
    KfsCallbackObj* const obj = mAcceptorOwner->CreateKfsCallbackObj(conn);
    if (conn) {
        if (obj) {
            conn->SetOwningKfsCallbackObj(obj);
            mNetManager.AddConnection(conn);
        } else {
            conn->Close();
        }
    }
    return 0;
}

接收到一个新的Connection的关键在于mAcceptorOwner->CreateKfsCallbackObj(conn)。对于MetaServer来说,其管理ChunkServer连接的类是ChunkServerFactory,因此,mAcceptorOwner->CreateKfsCallbackObj实现是:

KfsCallbackObj *ChunkServerFactory::CreateKfsCallbackObj(NetConnectionPtr &conn)
 { return ChunkServer::Create(conn); }

可以看到,对于一个新连接,会直接创建一个chunkServer对象来管理。

/* static */ KfsCallbackObj*
ChunkServer::Create(const NetConnectionPtr& conn)
{
    ...
    return CreateSelf(conn, ServerLocation());
}
​
/* static */ ChunkServer*
ChunkServer::CreateSelf(const NetConnectionPtr& conn, const ServerLocation& loc)
{
    ...
    ChunkServer& srv = *(new ChunkServer(
        conn,
        conn->IsGood() ?  conn->GetPeerName() : string("replay"),
        ! conn->IsGood()
    ));
    ...
}
​
ChunkServer::ChunkServer(
    const NetConnectionPtr& conn,
    const string&           peerName,
    bool                    replayFlag)
    : KfsCallbackObj(),
      ...
{
    ...
    // 设置了该ChunkServer连接上请求到来时的回调函数
    SET_HANDLER(this, &ChunkServer::HandleRequest);
    // 加入至全局的chunkserver队列中
    ChunkServersList::PushBack(sChunkServersPtr, *this);
    ...
}

ChunkServer对象和Connection一一对应。且在创建该对象的时候,设置了连接的事件触发回调函数chunkServer::HandleRequest。

接收ChunkServer请求

每个ChunkServer连接上一旦有新的请求到来时便触发预先设置的回调函数ChunkServer::HandleRequest。在这里会从连接上读取并解析命令。一旦完整命令解析完毕,最终会将命令通过ChunkServer::HandleMsg提交。

int
ChunkServer::HandleRequest(int code, void *data)
{
    switch (code) {
    case EVENT_NET_READ: {
        ...
        while ((gotMsgHdr = mHelloOp || IsMsgAvail(&iobuf, &msgLen))) {
            const int retval = HandleMsg(&iobuf, msgLen);
        }
        ...
    }
    ...
}

处理请求

int
ChunkServer::HandleMsg(IOBuffer *iobuf, int msgLen)
{
    ...
    return HandleCmd(iobuf, msgLen);
}
    
int
ChunkServer::HandleCmd(IOBuffer* iobuf, int msgLen)
{
    ...
    op->clnt               = this;
    if (sMaxPendingOpsCount <= mPendingOpsCount) {
        ...
    } else {
        ...
        Submit(*op);
    }
    return 0;
}
    
inline void
ChunkServer::Submit(MetaRequest& op)
{
    submit_request(&op);
}

submit_request()流程已经在前面分析得比较透彻。与客户端请求在工作线程处理不同的是,来自ChunkServer的请求则是直接在Master进程中被处理,了解这点就可以了。

请求处理完成,返回响应

而请求一旦处理完成后,会调用MetaRequest::SubmitEnd来收尾工作。这里的收尾工作主要是要将处理结果调度返回给ChunkServer,其主要的调用流程是:

MetaRequest::SubmitEnd()
    --->NetDispatch::Dispatch()
             --->ChunkServer::HandleRequest(EVENT_CMD_DONE, r)
​
int
ChunkServer::HandleRequest(int code, void *data)
{
    ...
    switch (code) {
    ...
    case EVENT_CMD_DONE: {
        MetaRequest* const op = reinterpret_cast<MetaRequest*>(data);
        ...
        if (SendResponse(op) && deleteOpFlag) {
            MetaRequest::Release(op);
        }
        break;
    }
}

最终直接在这里面给ChunkServer返回了响应。这样,MetaServer处理来自ChunkServer请求、发送响应等流程已经非常清晰。