Thrift server 端 processor 筆記

摘要apache

在 Thrift 服務端的 processor,server,protocol,transport 四層中,不只 server 的選擇, processor/processorFactory 也會影響到服務端的併發。TMultiplexedProcessor 可讓一個服務端同時提供多種服務,可是對於同一種服務,TMultiplexedProcessor 並不能提供併發時的數據安全保證。安全


本文用到的 Thrift 服務定義:多線程

service Calculate
{
    i32 add(1: i32 i1, 2: i32 i2)
}

processor 和 processorFactory

編譯上述服務定義文件,獲得 Calculate.h, 關注以下幾個類:併發

class CalculateIf
{
public:
    virtual int32_t add(const int32_t i1, const int32_t i2) = 0;
// ...
};

class CalculateIfFactory
{
public:
    virtual CalculateIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo) = 0;
    virtual void releaseHandler(CalculateIf* /* handler */) = 0;
// ...
};

/*
// from thrift/include/thrift/TDispatchProcessor.h
class TDispatchProcessor: public TProcessor
{
// ...    
};
*/

class CalculateProcessor: public ::apache::thrift::TDispachProcessor
{
public:
    CalculateProcessor(boost::shared_ptr<CalculateIf> iface);
// ...
};

class CalculateProcessorFactory: public ::apache::thrift::TProcessorFactory
{
public:
    CalculateProcessorFactory(const ::boost::shared_ptr<CalculateIfFactory>& handlerFactory);
// ...
};

CalculateIf 類是實現業務邏輯的最終載體,RPC 服務 add 函數最終由 CalculateIf (的派生類)實現:函數

class CalculateHandler: public CalculateIf
{
public:
    int32_t add(const int32_t i1, const int32_t i2)
    {
        return i1 + i2;
    }
// ...
};

隨後,就能夠實例化一個 CalculateProcessor:spa

boost::shared_ptr<CalculateHandler> handler(new CalculateHandler());
boost::shared_ptr<CalculateProcessor> CalProcessor(handler);

CalProcessor 做爲參數之一用於實例化一個 Thrift 服務端。當 Thrift 服務端提供服務時,最終將經過 CalProcessor,調用 handler,提供 add 函數服務。這裏 handler 指向一個 new 出來的 CalculateHandler 實例,因此每次提供服務時,Thrift 總會調用到 new 出來的這個 CalculateHandler。若是是在多線程環境下,就會出現多個線程訪問這同一個實例,從而產生數據競爭問題(在本例中,可能這個問題不存在)。線程

因此,若是服務端要提供多線程服務(好比 TThreadedServer, TThreadPoolServer 等),是不能使用 CalculateProcessor 的,而應該使用 CalculateProcessorFactory。CalculateProcessorFactory 的構造函數接受 CalculateIfFactory 指針,在 Thrift 須要一個 handler 提供具體服務時,經過 CalculateIfFactory::getHandler 來新建一個 handler,用完後經過 CalculateIfFactory::releaseHandler 來釋放 handler,從而保證對每一個服務對象使用不一樣的 handler,避免數據競爭。指針

class CalHandlerFactory: public CalculateIfFactory
{
public:
    CalculateIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo)
    {
        return (new CalculateHandler);
    }
    void releaseHandler(CalculateIf* handler)
    {
        delete handler;
    }
// ...
};

構造 CalculateProcessorFactory:code

boost::shared_ptr<CalHandlerFactory> handlerFactory(new CalHandlerFactory());
boost::shared_ptr<CalculateProcessorFactory> CalProcessorFactory(hanlderFactory);

關於 TMultiplexedProtocol 和 TMultiplexedProcessor

一般狀況下,一個 Thrift Server 服務端,只能夠提供一種服務。經過 TMultiplexedProcessor(用於服務端)和 TMultiplexedProtocol(用於客戶端),Thrift 可讓一個服務端,同時提供多種服務。可是,一個客戶端依然只能是使用一個服務,要使用多個服務,就要有多個不一樣的客戶端實例。server

class TMultiplexedProcessor: public TProcessor
{
public:
    void registerProcessor(const std::string& serviceName, shared_ptr<TProcessor> processor);
// ...
};

class TMultiplexedProtocol : public TProtocolDecorator
{
public:
    TMultiplexedProtocol(shared_ptr<TProtocol> _protocol, const std::string& _serviceName);
// ...
};

按照上文所述的步驟構造好 processor 後,約定一個服務名稱,好比 "Calculate",經過 TMultiplexedProcessor::registerProcessor, 向這個多工處理器註冊一個具體的服務處理,有多個服務就註冊多個,而後將 TMultiplexedProcessor 用於構造 server。

在客戶端方面,則一樣是按普通流程構造好 protocol 後,將 protocol 和此前約定的服務名稱 "Calculate" 傳入 TMultiplexedProtocol,而後再使用 TMultiplexedProtocol 做爲構造參數生成 client 實例。這樣,一個多服務的的 Thrift 服務-客戶模型就創建起來了。

雖然採用 TMultiplexedProcessor 的服務端能夠同時提供多種不一樣的服務,可是筆者認爲,對於同一種服務,TMultiplexedProcessor 不是併發安全的。緣由在於觀察 TMultiplexedProcessor::registerProcessor,發現其接受的參數是 TProcessor 而不是 TProcessorFactory。也就是說在同一個服務被併發訪問的時候,實際上多個客戶端訪問的是服務端上的同一個 handler 實例,所以 TMultiplexedProcessor 並不能避免同一個服務被訪問時的併發問題。

相關文章
相關標籤/搜索