插件机制

插件机制

目录结构

rebar3插件目录结构如下:

dgiot
|——lib
   |-dgiot_demoplugin
               |- ebin
                   |- dgiot_demopugin.app
                    - dgiot_demoplugin.beam
                    - dgiot_demoplugin_app.beam
                    - dgiot_demoplugin_sup.beam
                    - dgiot_demonplugin_channel.beam
                    - dgiot_demonplugin_handler.beam  
   -dgiot_otherplugin
               |- ebin
                    |- dgiot_otherplugin.app
                     - dgiot_otherplugin.beam
                     - dgiot_otherplugin_app.beam
                     - dgiot_otherplugin_sup.beam
                     - dgiot_otherplugin_channel.beam
                     - dgiot_otherplugin_handler.beam 

示例代码

  %%%-------------------------------------------------------------------
  %%% @author johnliu
  %%% @copyright (C) 2021, 
  %%% @doc
  %%%
  %%% @end
  %%% Created : 24. 3月 2021 10:17
  %%%-------------------------------------------------------------------
  -module(dgiot_demonplugin_channel).
  -author("johnliu").
  -behavior(dgiot_channelx).
  -include("dgiot_demonplugin.hrl").
  -define(TYPE, <<"demonplugin">>).
  -record(state, {id}).
  %% API
  -export([
      start/2
  ]).
  
  %% Channel callback
  -export([init/3, handle_init/1, handle_event/3, handle_message/2, stop/3]).
  -channel(?TYPE).
  -channel_type(#{
      type => 1,
      title => #{
          zh => <<"demonplugin通道"/utf8>>
      },
      description => #{
          zh => <<"demonplugin通道"/utf8>>
      }
  }).
  %% 注册通道参数
  -params(#{
      <<"port">> => #{
          order => 2,
          type => integer,
          required => true,
          default => 51889,
          title => #{
              zh => <<"服务器端口"/utf8>>
          },
          description => #{
              zh => <<"服务器端口"/utf8>>
          }
      }
  }).
  
  start(ChannelId, ChannelArgs) ->
      dgiot_channelx:add(?TYPE, ChannelId, ?MODULE, ChannelArgs).
  
  %% 通道初始化
  init(?TYPE, ChannelId, Args) ->
      #{
          <<"ip">> := Ip,
          <<"port">> := Port,
          <<"total">> := Total} = Args,
      do_something(<<"hello plugin">>),
      State = #state{
          id = ChannelId
      },
      {ok, State, []}.
  
  handle_init(State) ->
      {ok, State}.
  
  %% 通道消息处理,注意:进程池调用
  handle_event(_EventId, Event, State) ->
      lager:info("channel ~p", [Event]),
      {ok, State}.
  
  handle_message(_Message, State) ->
      {ok, State}.
  
  stop(ChannelType, ChannelId, _State) ->
      
      ok.
 
do_something(Msg) ->
   io:format("Msg ~p",[Msg]).

加载机制

  • 搜索到所有已经加载的插件
    application:loaded_applications()
  • 通过App名称搜索ebin下面所有的beam文件
    file:list_dir(Dir)
  • 搜素对应beam文件内的attributes是否包含channel
    Mod:module_info(attributes)
  • 如果有channele的attributes则启动插件
    Mod:start(ChannelId, ChannelArgs)

示例代码如下:

  %%%-------------------------------------------------------------------
  %%% @author johnliu
  %%% @copyright (C) 2019, 
  %%% @doc
  %%%
  %%% @end
  %%% Created : 06. 八月 2019 18:00
  %%%-------------------------------------------------------------------
  -module(dgiot_plugin).
  -author("johnliu").

-define(SYS_APP, [
    kernel, stdlib, sasl
]).

-export([start/0]) 

start() ->
    check_module(
        fun({_App, _Vsn, Mod}, Acc) ->
            case code:is_loaded(Mod) =/= false of
                true ->
                    case lists:keyfind(channel, 1, Mod:module_info(attributes)) of
                        false ->
                            Acc;
                        {channel, ChannelTypes} ->
                            do_somthing()
                    end;
                false ->
                    Acc
            end
        end, []).

  check_module(Check, Acc0) ->
      Fun =
          fun({App, _Desc, Vsn}, Acc) ->
              Dir = lists:concat(["lib/", App, "-", Vsn, "/ebin"]),
              case not lists:member(App, ?SYS_APP) andalso file:list_dir(Dir) of
                  {ok, FS} ->
                      lists:foldl(
                          fun(FileName, Acc1) ->
                              case filename:extension(FileName) == ".beam" of
                                  true ->
                                      Mod = list_to_atom(filename:basename(FileName, ".beam")),
                                      Check({App, Vsn, Mod}, Acc1);
                                  false ->
                                      Acc1
                              end
                          end, Acc, FS);
                  _ ->
                      Acc
              end
          end,
      lists:foldl(Fun, Acc0, application:loaded_applications()).

  do_somthing() ->
    ok

开源代码下载:https://gitee.com/dgiiot/dgiot
有问反馈给我们 https://gitee.com/dgiiot/dgiot/issues

你可能感兴趣的:(插件机制)