我們在命令列啟動 substrate 節點,到底發生了什麼呢?本文基於 substrate 原始碼,對其啟動流程進行了簡單的分析。
命令列啟動 substrate,主要是解析命令列引數並配置服務。
主程式在substrate/node/main.rs
中,入口是main()
函式。其中的關鍵**如下:
fn main() \n\n", e, e);
std::process::exit(1)
}}
這行**呼叫的是node/cli/src/lib.rs
的run
函式。進入該run
函式,有如下**:
pub fn run(args: i, exit: e, version: cli::versioninfo) -> error::result<()> where
i: intoiterator,
t: into + clone,
e: intoexit,
", version.name);
...}
parse_and_prepare
函式(位於core/cli/src/lib.rs
中),這個函式類似於所有的區塊鏈啟動,主要是對命令列引數進行解析,並啟動相關的操作。
在parse_and_prepare
函式中,會根據不同的引數,返回不同型別的parseandprepare
。
pub fn parse_and_prepare<'a, cc, rp, i>(
version: &'a versioninfo,
impl_name: &'static str,
args: i,
) -> parseandprepare<'a, cc, rp>
where
cc: structopt + clone + getlogfilter,
rp: structopt + clone + augmentclap,
i: intoiterator,
::item: into + clone,
),...
}
pub enum coreparams
而對於列舉型別parseandprepare
,每一類結構體,均會實現各自的run
方法,解析引數生成配置,並根據配置執行服務。
pub enum parseandprepare<'a, cc, rp>
以結構體parseandpreparerun
為例,其run
函式的實現**如下:
impl<'a, rp> parseandpreparerun<'a, rp>
}
其實執行服務,具體是在run
函式的閉包函式中,**如下:
parseandprepare::run(cmd) => cmd.run(load_spec, exit, |exit, _cli_args, _custom_args, config| ", version.name);
info!(" version {}", config.full_version());
info!(" by parity technologies, 2017-2019");
info!("chain specification: {}", config.chain_spec.name());
info!("node name: {}", config.name);
info!("roles: ", config.roles);
let runtime = runtimebuilder::new().name_prefix("main-tokio-").build()
.map_err(|e| format!("", e))?;
match config.roles ", e))?,
exit
),_ => run_until_exit(
runtime,
service::factory::new_full(config).map_err(|e| format!("", e))?,
exit
),}.map_err(|e| format!("", e))
}),
fn run_until_exit(
mut runtime: runtime,
service: t,
e: e,
) -> error::result<()>
where
t: deref> + future + send + 'static,
c: substrate_service::components,
e: intoexit,
**中呼叫了core/cli/src/informant.rs
的build
函式,建立了乙個futrue
「線人」informant
。
基本上到這兒,相關的命令就全啟動了。我們看下生成全節點或輕節點服務的具體細節。
在宣告巨集construct_service_factory
的定義中,有如下**:
#[macro_export]
macro_rules! construct_service_factory ,
authoritysetup = ,
lightservice = $light_service:ty ,
...fn new_light(
config: $crate::factoryfullconfiguration
) -> $crate::result
fn new_full(
config: $crate::factoryfullconfiguration
) -> result)}
...}
在node/cli/src/service.rs
中,包含了service結合service中的對巨集的呼叫,巨集展開後,是執行的::new(config)
,**如下:
construct_service_factory! ,
...lightservice = lightcomponents
,...
}
在core/service/src/components.rs
中定義了substrate的服務元件:fullcomponents
和lightcomponents
。它們new
函式的實現均呼叫了service
的new
函式,**如下:
ok(
self
)
通過該函式建立substrate service
,它會啟動客戶端,初始化session keys
,構建網路,交易池以及rpc
,並管理他們之間的通訊,包括區塊通知,交易通知等。關鍵**如下:
let executor = nativeexecutor::new(config.default_heap_pages);
...components::runtimeservices::generate_intial_session_keys(
client.clone(),
config.dev_key_seed.clone().map(|s| vec![s]).unwrap_or_default(),
)?;...
let network_protocol = ::build_network_protocol(&config)?;
let transaction_pool = arc::new(
components::build_transaction_pool(config.transaction_pool.clone(), client.clone())?
);...
let events = client.import_notification_stream()
.map(|v| ok::<_, ()>(v)).compat()
.for_each(move |notification| )
這個有些類似於以太坊,在啟動節點時把相關的網路服務都建立好。這樣最後ok返回整個service
。
ps:原始碼分析是基於master分支(substrate 2.0)。
1. 其中對命令列引數的解析,使用了第三方庫structopt
,該庫通過結構體來解析引數,並對clap
庫進行了補充。
2. 非同步程式設計,使用了第三方庫tokio
,該庫使用reactor-executor模式,是基於事件驅動的非阻塞i/o庫。是 rust 中的非同步程式設計框架,它將複雜的非同步程式設計抽象為 futures、tasks 和 executor,並提供了 timers 等基礎設施。
spring原始碼學習 初啟
spring最重要的概念是ioc和aop,其中ioc又是spring的根基。我們來先看看spring的整體架構 spring框架是乙個分層架構,它包含一系列的功能要素,並被分為大約20個模組,如下圖所示 從看出,整個模型就是乙個容器關係,spring通過動態載入不同的模組實現我們想要的功能,而最io...
spring原始碼分析 spring原始碼分析
1.spring 執行原理 spring 啟動時讀取應用程式提供的 bean 配置資訊,並在 spring 容器中生成乙份相應的 bean 配置登錄檔,然後根據這張登錄檔例項化 bean,裝配好 bean 之間的依賴關係,為上 層應用提供準備就緒的執行環境。二 spring 原始碼分析 1.1spr...
思科VPP原始碼分析(dpo機制原始碼分析)
vpp的dpo機制跟路由緊密結合在一起。路由表查詢 ip4 lookup 的最後結果是乙個load balance t結構。該結構可以看做是乙個hash表,裡面包含了很多dpo,指向為下一步處理動作。每個dpo都是新增路由時的乙個path的結果。dpo標準型別有 dpo drop,dpo ip nu...