Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

1.5_深入插件

elevenqq edited this page Sep 30, 2018 · 2 revisions

插件和Task

plugin

  • 【插件体系】
    插件体系一般由两部分组成:Framework+Plugin,DataLink中的Framework主要指【TaskRuntime】,Plugin对应的是各种类型的【TaskReader&TaskWriter】
  • 【TaskRuntime】
    提供了Task的高层抽象、Task的运行时环境和Task的插件规范
  • 【TaskReader&TaskWriter】
    一个个具体的数据同步插件,遵从Task插件规范,功能自治,和TaskRuntime完全解耦,理论上插件数量可无限扩充
  • 【Task】
    DataLink中数据同步的基本单位是Task,一个Worker进程中可以运行一批Task,一个运行中的Task由一个TaskReader和至少一个TaskWriter组成,即有:
    1> 程序运行期,同一类型的插件在一个进程中可以有多个实例,实例个数取决于有多少个Task用到了该插件
    2> 程序运行期,插件的生命周期归属于Task,在不同的生命周期阶段,依照Task的配置信息或相关指令,进行创建、初始化、运行或销毁等操作
    3> 理论上,TaskReader和TaskWriter可动态任意组合(能否组合,主要取决于待组合的TaskWriter能否适配TaskReader的Record类型)
    4> 理论上,每新增一种插件,可支持的同步场景可以成倍数的增加(具体几倍,和插件类型和当前已有的插件数量有关系),举例如下所示:

    TaskReader插件 TaskWriter插件 同步场景
    MySqlTaskReader RdbmsTaskWriter Mysql → Rdbms
    HdfsTaskWriter Mysql → Hdfs
    ElasticSearchTaskWriter Mysql → ElasticSearch
    KafkaTaskWriter(新增) Mysql → Kafka
    HbaseTaskReader


    RdbmsTaskWriter Hbase → Rdbms
    HdfsTaskWriter Hbase → Hdfs
    ElasticSearchTaskWriter Hbase → ElasticSearch
    KafkaTaskWriter(新增) Hbase → Kafka
    MongodbTaskReader
    (新增)


    RdbmsTaskWriter Mongodb → Rdbms
    HdfsTaskWriter Mongodb → Hdfs
    ElasticSearchTaskWriter Mongodb → ElasticSearch

    新增一个TaskReader,可新增的同步场景数量取决于已有TaskWriter的数量,反之亦然

插件和Domain

plugin-2

  • 【Contract】
    > Contract是针对某种类型的数据源定义的【数据模型】,是一份契约和规范,是最高层次的抽象,和编程语言无关,和具体平台无关,和DataLink也没有必然关系
    > Contract是TaskReader和TaskWriter可任意组合的关键,TaskReader输出Contract数据,TaskWriter输入Contract数据,互不感知,但都理解Contract定义的【数据模型】
    > Contract定义的【数据模型】的主要表现形式是Record,如:RdbEventRecord,HRecord
  • 【Adapt】
    > TaskReader
    负责输出Contract数据,适配模式很简单,一对一,直接把底层数据组装成对应的Record即可,如:MysqlTaskReader对应RdbEventRecord
    > TaskWriter
    负责输入Contract数据,并写入目标数据源
    两个思路:
    一种是【中心化统一适配】,即:和TaskReader类似,TaskWriter和Record也是一对一的关系(假设如:HBaseTaskWriter只能接收HRecord),由【TaskRuntime】
    负责把TaskReader输出的【数据模型】转换为TaskWriter需要的 【数据模型】;一种是【各自为政分散适配】,即:TaskWriter可以接收不同类型的【数据模型】,
    内部由不同的Handler把不同【数据模型】的数据写入目标数据源;

    adapt-1

    adapt-2
    思路对比:
    前者的思路,是在数据模型层面进行适配转换,那么TaskWriter实现起来就非常简单了,看上去很美好,但可行性非常低,不同【数据模型】之间的差异是非常大的,
    如果能这样转换,那也就没必要定义多种【数据模型】了,定义一种岂不更简单,所以Pass掉;后者是在数据操作层面进行适配转换,每个TaskWriter的第一层都是适配层,
    适配完之后再调用各个功能组件进行写入操作

  • 【Business Model】
    TaskReader和TaskWriter的正常运行,离不开各种参数和规则配置,【Business Model】对数据同步领域的常见场景常用功能进行了充分的总结和抽象,为所有插件提供
    了一套统一的规则模型,并提供了灵活的扩展机制,简化了设计和开发的难度

插件和ClassLoader

  • 【预备知识】
    > 类加载机制:http://sts.10101111.com/ucarsymphony/article/1492936004824
    >《深入理解Java虚拟机:JVM高级特性与最佳实践》,第6、7、8、9章
  • 【Why ClassLoader】
    Datalink-Worker进程中,每个类型的插件,都有自己独立的classloader和classpath
    原因很简单:class版本隔离的需要,DataLink中,可以开发任意多个插件,出现jar包冲突很正常,必须通过classloader隔离这些冲突
  • 【Datalink-ClassLoader机制简介】

    classloader-1

    classloader-2
    > BasicModules被PluginModules依赖,但打包时会各自独立,BasicModules输出到基础lib目录"../dl-worker/lib",PluginModules输出到每个插件自己独有的目录
    > BasicModules和其依赖的jar,被默认的AppClassLoader加载
    > PluginModules和其依赖的jar(不包括依赖的BasicModules),被PluginClassLoader加载
    > PluginClassLoader有两个实现类,DevPluginClassLoader和RelPluginClassLoader,前者在开发环境使用,后者在部署环境使用
    > PluginClassLoader的类加载并没有遵循【双亲委派模型】,而是优先加载插件自己的class,找不到时才委托父加载器加载
    > PluginClassLoader提供了白名单机制(目前还比较简单,不支持参数配置),白名单内的class会跳过插件目录,直接交由父加载器加载,该机制主要用来解决的问题场景是:
    BasicModules引用了A.jar,PluginModules也引用了A.jar,BasicModules用到了A.jar中的A-Class(可能是因为版本不同,或者插件有必须用自己jar包的需求),
    并且会把A-Class的对象实例传给PluginModules使用(注意:这个实例所属的Class对象是由AppClassLoader加载的),如果没有白名单机制,PluginModules
    拿到这个对象之后,其所属的PluginClassLoader会尝试从插件目录进行类加载(因为其cache中没有对应的Class对象),但加载出来的Class对象和A实例所属的
    Class对象并不匹配,会触发LinkageError

    举例:
    BasicModule引用了guava包,MysqlTaskReader插件也引用了guava包,我们有使用guava包中EventBus类的需求,EventBus首先被AppClassLoader加载和实例化,
    MysqlReader插件会拿到该EventBus对象并调用其方法,如果没有白名单机制,便会触发LinkageError异常

    > 接上文,BasicModules和PluginModules之间的共享对象应该遵循什么样的原则呢?

    share
    原则一:共享对象所属Class尽量是【JDK-Class】或【自定义Class】,这两种类型的Class插件目录中都没有,都由AppClassLoader加载,不存在LinkageError问题;
    原则二:非【JDK-Class】和非【自定义Class】类型的对象尽量不共享;
    如果实在有需求,两个解决办法:
    其一,可进行封装调用,原理如下所示:

    reason
    其二,利用白名单机制,强制使用BasicModule中的类(如果这些类,插件也有需求必须用自己jar包里的,只能自己实现一套机制了)

插件和EventBus

eventBus

  • 每个插件除了需要实现数据同步,还必须包含一个PluginListener,PluginListener负责在EventBus上注册事件监听
  • 插件有自己独立的classloader,我们无法在框架层直接调用,通过事件提供的解耦机制,我们可以让插件提供更多【增值服务】,举例如下:
    > 解决Manager端Jar包冲突的问题。
    Manager想获取各种DB的元数据,无需引入各种Jar包,直接给Worker发指令,Worker构造一个特定事件并发送到EventBus,不同类型的插件接收到事件,并判断自己是否需要处理,
    是的话把元数据返回
    > 提供状态查询
    把插件内部的一些运行详情汇报给事件发送者


Clone this wiki locally

AltStyle によって変換されたページ (->オリジナル) /