摘要apache
在 Thrift 服務端的 processor,server,protocol,transport 四層中,不只 server 的選擇, processor/processorFactory 也會影響到服務端的併發。TMultiplexedProcessor 可讓一個服務端同時提供多種服務,可是對於同一種服務,TMultiplexedProcessor 並不能提供併發時的數據安全保證。安全
本文用到的 Thrift 服務定義:多線程
service Calculate { i32 add(1: i32 i1, 2: i32 i2) }
編譯上述服務定義文件,獲得 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);
一般狀況下,一個 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 並不能避免同一個服務被訪問時的併發問題。