EOS系列 - 源碼分析 - EOS線程機制

EOS有幾個線程

nodeso節點的工作線程包括:一個主線程,一個信號處理線程和四個線程池。

nodeos(10265 主線程&信號處理線程) : 進行異步io投遞 `epoll_wait` | 接收系統信號並處理
	─┬─{nodeos}(10311 controller線程池_1: 異步執行塊block_state創建,塊中交易驗證時的交易解簽名計算)
      ├─{nodeos}(10312 controller線程池_2)
      ├─{nodeos}(10314 producer_plugin線程池_1: 異步執行交易解簽名計算)
      ├─{nodeos}(10315 producer_plugin線程池_2)
      ├─{nodeos}(10317 http_plugin線程池_1: http服務)
      ├─{nodeos}(10318 http_plugin線程池_2)
      └─{nodeos}(10319 net_plugin線程池_1: p2p服務)
  • 主線程:main函數啓動線程,該線程執行完程序初始化工作後,會調用app().io_service.run(), 啓動boost::asio::io_service的異步io服務,通過異步io方式完成節點塊生產,交易處理等主要業務工作。

    void application::exec() {
    boost::asio::io_service::work work(*io_serv);
    (void)work;
    bool more = true;
    while( more || io_serv->run_one() ) {
      	while( io_serv->poll_one() ) {}
      	// execute the highest priority item
      	more = pri_queue.execute_highest();
    }
    
    shutdown(); /// perform synchronous shutdown
    io_serv.reset();
    }
    
    • eos中交易不支持並行處理, 不允許在除主線程之外的其它線程中重複執行io_serv.run()操作

      boost::asio::io_service& get_io_service() { return *io_serv; }
      
      producer_plugin::producer_plugin()
      : my(new producer_plugin_impl(app().get_io_service())){ // 在構造時傳入 app().get_io_service()
      	my->_self = this;
      }
      
      producer_plugin_impl(boost::asio::io_service& io)
      	:_timer(io)
      
  • 信號處理線程:子線程,通過異步io服務,接收系統信號並處理。

     boost::asio::io_service startup_thread_ios;     //信號處理線程
    setup_signal_handling_on_ios(startup_thread_ios);
    std::thread startup_thread([&startup_thread_ios]() {
      	startup_thread_ios.run();
    });
    
    void application::setup_signal_handling_on_ios(boost::asio::io_service& ios, bool startup) 
    
  • 線程池,線程池啓動的工作線程數可通過啓動參數配置。 (默認池內2條工作線程)

    struct controller_impl {
    ...
    boost::asio::thread_pool       thread_pool; //線程池
    }
    
    controller_impl( const controller::config& cfg, controller& s  ){
    ...
    thread_pool( cfg.thread_pool_size )
    }
    
    • controller線程池:異步執行塊block_state創建,塊中交易驗證時的交易解簽名計算。
      執行塊相關操作時,較耗時且與排序無關的動作都會投遞到controller線程池執行。

      1. 塊交易驗證時的解簽名操作 (節點收到塊)

        // 節點收到塊,會調用apply_block() 函數執行塊中交易,進行塊驗證
        void apply_block( const block_state_ptr& bsp, controller::block_status s ) {
        ...
        transaction_metadata::start_recover_keys( mtrx, thread_pool, chain_id, microseconds::maximum() ); 
        }
        
        //在線程池`thread_pool`中異步執行解簽名函數,異步解簽名的執行結果,會放入交易的 `signing_keys_future` 中
        //真正解簽名算法是 trn.get_signature_keys(),解出的公鑰放到recovered_pub_keys中。
        signing_keys_future_type transaction_metadata::start_recover_keys( const transaction_metadata_ptr& mtrx,
                                                               boost::asio::thread_pool& thread_pool,
                                                               const chain_id_type& chain_id,
                                                               fc::microseconds time_limit )
        
      2. 塊狀態block_state數據創建操作(兩輪共識計算都在這裏完成)

    • producer_plugin線程池:負責異步執行交易解簽名計算。

      void producer_plugin::plugin_initialize(const boost::program_options::variables_map& options){
      ...
      my->_thread_pool.emplace( thread_pool_size );
      }
      
      1. 爲接收到的投遞交易進行異步解簽名計算(在節點收到其它節點/客戶端廣播的交易時被調用)

        void on_incoming_transaction_async(const transaction_metadata_ptr& trx, bool persist_until_expired, next_function<transaction_trace_ptr> next) {
        	signing_keys_future_type future = transaction_metadata::start_recover_keys( trx, _thread_pool->get_executor(),
           chain.get_chain_id(), fc::microseconds( cfg.max_transaction_cpu_usage ) );
        }
        
      2. 等待解簽名計算完成,將交易投遞到主線程的異步io服務中處理。

        boost::asio::post( *_thread_pool, [self = this, future, trx, persist_until_expired, next]() {
        			if( future.valid() )
           				future.wait();
        				app().post(priority::low, [self, trx, persist_until_expired, next]() {
           				self->process_incoming_transaction_async( trx, persist_until_expired, next );
        			});
        	});
        
    • http_plugin線程池: http 的 response 修改成多線程

      optional<boost::asio::thread_pool> thread_pool

    • net_plugin線程池

      optional<boost::asio::thread_pool> thread_pool

EOS 異步框架 boost::asio

EOS 異步框架是通過實現一個任務隊列, 然後往隊列中 post 任務來實現, 而消費者不停地從中提取 task 並且執行。

boost::asio

  • 它是基於操作系統提供的異步機制( 底層封裝了 select/ epoll/ kqueue/ overlapped, 適用各種操作系統 ),採用前攝器設計模式來實現可移植的異步操作。
  • 通過 post 添加任務, run 執行任務。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章