EOSIO源码分析 - API调用及处理过程

前言

EOSIO对外提供了可以访问和操作的接口,调用如下

http://123.207.175.74:8888/v1/chain/get_info

成功返回如下结果

{
    
    
    "server_version": "134df9f6",
    "chain_id": "d0505f66cb656524ccdbd3c6ce1b23a126b378f5d7eb8ee7246c1cc38db6730b",
    "head_block_num": 15139606,
    "last_irreversible_block_num": 15139605,
    "last_irreversible_block_id": "00e70315b2bfd5cf47c0759ea7fbbe7d61b96116b5848ff6f7dd72cdf0e1cb87"
}

EOSIO所有的API列表可以参考如下链接:EOSIO远程HTTP接口

接口API分类

在EOSIO中,链相关接口按照功能分类(同时更可以参考代码结构),可以将接口简单分为以下几类(只简单说明最常用最核心的API)

链接口(chain_api)
  • v1/chain/get_info:返回链最基本的信息,如链ID,块高,生产者等信息
  • v1/chain/get_block:根据块号,或者块信息获取这个块的详细信息
  • v1/chain/get_account:根据账户名,返回此账户信息
  • v1/chain/get_code:获取某个账户下的合约内容
  • v1/chain/get_code_hash:获取某个账户下的合约hash,用作比较
  • v1/chain/get_abi:获取账户下合约关联的API
  • v1/chain/get_table_rows:查询表信息
  • v1/chain/get_currency_balance:获取代币余额
  • v1/chain/get_currency_stats:获取所有代币统计信息
  • v1/chain/get_producers:获取生产者信息
  • v1/chain/abi_json_to_bin:利用ABI将一段json数据转换成二进制数据,一般用作action参数序列化
  • v1/chain/abi_bin_to_json:利用ABI将一段二进制数据转换成json格式的数据
  • v1/chain/get_required_keys:鉴权相关,返回可用的第一个公钥key,用于接下来的签名操作
  • v1/chain/push_transaction:发送交易
  • v1/chain/send_transaction:发送交易
生产接口(producer_api)
  • v1/producer/pause:暂停生产
  • v1/producer/resume:激活生产
  • v1/producer/paused:返回链是否属于暂停状态
  • v1/producer/create_snapshot:创建数据快照
网络接口(net_api)
  • v1/net/connect:链接P2P到另外一个节点
  • v1/net/disconnect:断一个链接
  • v1/net/status:返回当前节点的网络链接状态
  • v1/net/connections:返货当前节点所有的链接信息
数据接口(db_size_api)
  • v1/db_size/get:返回当前节点的chainbase数据库的使用情况,主要用于运营

接口API的注册

在EOSIO中,所有的对外的http接口,都是通过依托http_plugin插件来实现的,http_plugin封装了http调用的相关操作,如接口注册,调用处理,结果返回等等

如何注册一个API
   auto& _http_plugin = app().get_plugin<http_plugin>();
   ro_api.set_shorten_abi_errors( !_http_plugin.verbose_errors() );
   _http_plugin.add_api({
    
    
      CHAIN_RO_CALL(get_info, 200, http_params_types::no_params_required)}, appbase::priority::medium_high);

调用http_plugin类的add_api方法,向插件注册了一个和url绑定的回调函数,最终保存在变量url_handlers中,函数形势如下:

 using url_handler = std::function<void(string,string,url_response_callback)>;
Http API 请求处理

首先在插件启动的时候,会做如下的操作与处理

  my->thread_pool.emplace( "http", my->thread_pool_size );
  if(my->listen_endpoint) {
    
    
     try {
    
    
        my->create_server_for_endpoint(*my->listen_endpoint, my->server);

        fc_ilog( logger, "start listening for http requests" );
        my->server.listen(*my->listen_endpoint);
        my->server.start_accept();
     } catch ( ... ){
    
    
        ...
     }
  }
  • 创建工作线程池thread_pool,用以处理最终的api业务信息
  • 创建server服务
  • 启动端口监听,开始接受外部请求

接下来核心函数的处理流程如下

  // 创建server服务,并且设置相关的参数,最核心的设置请求处理函数handle_http_request
  template<class T>
  void create_server_for_endpoint(const tcp::endpoint& ep, websocketpp::server<detail::asio_with_stub_log<T>>& ws) {
    
    
     try {
    
    
        ws.clear_access_channels(websocketpp::log::alevel::all);
        ws.init_asio( &thread_pool->get_executor() );
        ws.set_reuse_addr(true);
        ws.set_max_http_body_size(max_body_size);
        // captures `this` & ws, my needs to live as long as server is handling requests
        ws.set_http_handler([&](connection_hdl hdl) {
    
    
           handle_http_request<detail::asio_with_stub_log<T>>(ws.get_con_from_hdl(hdl));
        });
     } catch ( ... ){
    
    
        ...
     }
  }
  // 请求处理
    void handle_http_request(detail::connection_ptr<T> con) {
    
    
    try {
    
    
       auto& req = con->get_request();
       if(!allow_host<T>(req, con))
          return;
	   // 解析http请求头        
       con->defer_http_response();
	   // 验证请求合法性
       auto abstract_conn_ptr = make_abstract_conn_ptr<T>(con, shared_from_this());
       if( !verify_max_bytes_in_flight( con ) || !verify_max_requests_in_flight( con ) ) return;
	   // 调用注册业务请求处理回调函数,响应API
       std::string resource = con->get_uri()->get_resource();
       auto handler_itr = url_handlers.find( resource );
       if( handler_itr != url_handlers.end()) {
    
    
          std::string body = con->get_request_body();
          handler_itr->second( abstract_conn_ptr, std::move( resource ), std::move( body ), make_http_response_handler<T>(abstract_conn_ptr) );
       } else {
    
    
          fc_dlog( logger, "404 - not found: ${ep}", ("ep", resource) );
          error_results results{
    
    websocketpp::http::status_code::not_found,
                                "Not Found", error_results::error_info(fc::exception( FC_LOG_MESSAGE( error, "Unknown Endpoint" )), verbose_http_errors )};
          con->set_body( fc::json::to_string( results, fc::time_point::now() + max_response_time ));
          con->set_status( websocketpp::http::status_code::not_found );
          con->send_http_response();
       }
    } catch( ... ) {
    
    
       handle_exception<T>( con );
    }
 }

最后从上面的代码中,我们可以具体的出以下结论

  • http_plugin插件只是提供了一个http请求处理的基础框架,具体的业务逻辑,依赖上层注册进来回调函数
  • 如果找不到处理的API请求,自动返回404错误
  • 如果在处理中发生异常,则调用handle_exception函数进行处理

总结

  • EOSIO中http请求的处理还是比较简单的,基础逻辑在插件中处理,记住最核心的url_handler类型
  • 如果要处理请求头相关的信息,只要修改http_plugin相关代码就可以
  • 在EOSIO代码中还有其他API处理,但都不是链相关的,剩下的主要是钱包,历史状态相关接口
  • 在链相关的接口中,chain_api是最基本的,如果是个单节点,并且业务量很小,只启动这个接口类就可以

猜你喜欢

转载自blog.csdn.net/whg1016/article/details/128625498