EOS插件通信机制
在上一篇 EOS应用程序框架appbase已经提到appbase给插件提供了插件之间的通信接口channel和method, 使插件的之间的通信耦合性更加低,一般我们把method看成是函数调用接口一般为1对1,channel为广播一般有多个收听者,不管是method还是channel它们的实现都是基于boost::signals2::signal(类似QT中的信号槽)
接口的注册
接口的注册其实就是定义一个method或者channel接口说明类型,说明调用的参数跟返回值,然后任何插件可以实现该接口并使用,eos中chain注册的接口:
namespace eosio { namespace chain { namespace plugin_interface {
using namespace eosio::chain;
using namespace appbase;
template<typename T>
using next_function = std::function<void(const fc::static_variant<fc::exception_ptr, T>&)>;
struct chain_plugin_interface;
namespace channels {
using pre_accepted_block = channel_decl<struct pre_accepted_block_tag, signed_block_ptr>;
using rejected_block = channel_decl<struct rejected_block_tag, signed_block_ptr>;
using accepted_block_header = channel_decl<struct accepted_block_header_tag, block_state_ptr>;
using accepted_block = channel_decl<struct accepted_block_tag, block_state_ptr>;
using irreversible_block = channel_decl<struct irreversible_block_tag, block_state_ptr>;
using accepted_transaction = channel_decl<struct accepted_transaction_tag, transaction_metadata_ptr>;
using applied_transaction = channel_decl<struct applied_transaction_tag, transaction_trace_ptr>;
using accepted_confirmation = channel_decl<struct accepted_confirmation_tag, header_confirmation>;
}
namespace methods {
using get_block_by_number = method_decl<chain_plugin_interface, signed_block_ptr(uint32_t block_num)>;
using get_block_by_id = method_decl<chain_plugin_interface, signed_block_ptr(const block_id_type& block_id)>;
using get_head_block_id = method_decl<chain_plugin_interface, block_id_type ()>;
using get_lib_block_id = method_decl<chain_plugin_interface, block_id_type ()>;
using get_last_irreversible_block_number = method_decl<chain_plugin_interface, uint32_t ()>;
}
namespace incoming {
namespace channels {
using block = channel_decl<struct block_tag, signed_block_ptr>;
using transaction = channel_decl<struct transaction_tag, packed_transaction_ptr>;
}
namespace methods {
// synchronously push a block/trx to a single provider
using block_sync = method_decl<chain_plugin_interface, void(const signed_block_ptr&), first_provider_policy>;
using transaction_async = method_decl<chain_plugin_interface, void(const packed_transaction_ptr&, bool, next_function<transaction_trace_ptr>), first_provider_policy>;
}
}
namespace compat {
namespace channels {
using transaction_ack = channel_decl<struct accepted_transaction_tag, std::pair<fc::exception_ptr, packed_transaction_ptr>>;
}
}
} } }
1)channel接口using accepted_block_header = channel_decl<struct accepted_block_header_tag, block_state_ptr>; 这里就定义了一个channel类型的说明接口channel_decl<>,其中第一个类型accepted_block_header_tag这个我们可以不用管因为没什么用,第二个类型block_state_ptr表示该channel接口的参数为block_state_ptr,因为channel没有返回值所以这里不需要定义返回值
2)method接口using transaction_async = method_decl<chain_plugin_interface, void(const packed_transaction_ptr&, bool, next_function<transaction_trace_ptr>), first_provider_policy>; 这里就定义了一个method类型的说明接口method_decl<>, 这是一个处理http接收到的trx的一个异步方法,其中第一个类型chain_plugin_interface这个我们可以不用管因为没什么用,第二个参数void(const packed_transaction_ptr&, bool, next_function<transaction_trace_ptr>), first_provider_policy>是一个接口参数跟返回类型的定义,该接口的返回类型为void,参数为一共有3个
接口的使用
接口注册完成之后可以通过app().get_method<...>(),app().get_channel<...>()来获取接口对象,例如get_method:
template<typename MethodDecl>
auto get_method() -> std::enable_if_t<is_method_decl<MethodDecl>::value, typename MethodDecl::method_type&>
{
using method_type = typename MethodDecl::method_type;
auto key = std::type_index(typeid(MethodDecl));
auto itr = methods.find(key);
if(itr != methods.end()) {
return *method_type::get_method(itr->second);
} else {
methods.emplace(std::make_pair(key, method_type::make_unique()));
return *method_type::get_method(methods.at(key));
}
}
上述get_method()如果该接口类型已经存在直接返回反之则创建,所以具体的某种类型的接口只会被创建一次后面返回的都是同一个接口对象,返回类型 std::enable_if_t<is_method_decl<MethodDecl>::value, typename MethodDecl::method_type&>表示如果MethodDecl该接口类型已经注册则返回MethodDecl::method_type类型对象,MethodDecl::method_type定义
template< typename Tag, typename FunctionSig, template <typename> class DispatchPolicy = first_success_policy>
struct method_decl {
using method_type = method<FunctionSig, DispatchPolicy<FunctionSig>>;
using tag_type = Tag;
};
获取接口对象
using eosio::chain::plugin_interface::incoming::methods
auto transaction_async_method = app().get_method<transaction_async>();
1)接口的调用方
上述获取到接口对象之后直接调用
transaction_async_method (pretty_input, true, [this, next](const fc::static_variant<fc::exception_ptr, transaction_trace_ptr>& result) -> void{
...
});
2)接口的被调用方(接口的实现)
如果没有被调用方,调用方调用接口之后会直接返回,接口的被调用方可以通过register_provider实现接口
transaction_async_method.register_provider([this](const packed_transaction_ptr& trx, bool persist_until_expired, next_function<transaction_trace_ptr> next) -> void {
return my->on_incoming_transaction_async(trx, persist_until_expired, next );
});
channel接口的使用跟上述method大同小异,channel接口的接收方/被调用方通过channel对象的subscribe方法注册,目前method的调用为同步,channel的调用为异步。