• 注册
    • 登录
    • 版块
    • 热门
    • Metabase官网
    • 社区汉化版
    • 达之云

    Metabase二次开发教程-03-Clojure运行解析

    Metabase二次开发教程
    1
    1
    1045
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • D
      dazdata 最后由 dazdata 编辑

      一、程序启动

      Clojure有多种启动方式,推荐使用 lein run

      • lein ring server
        • 加载 ring 环境,包括 exclude-tests、include-all-drivers、lein-ring
          include-all-drivers 会对所有数据库驱动进行加载,并在终端输出进度,耗时较长;
          即使在环境中注释掉include-all-drivers,后续init时依然会加载,即注释掉不会产生影响;
          
        • 加载metabase.core引用的命名空间,耗时较长。某些命名空间在加载过程中会输出到终端;
          示例:2020-12-11 18:21:38,879 INFO metabase.util :: Maximum memory available to JVM: 3.6 GB
          
        • 加载 metabase.core,加载过程中有输出
          示例:Copyright © 2020 Metabase, Inc.
          
        • 运行 metabase.core/init!
        • 启动ring服务,监听端口(默认3000),并将收到的请求交给handler处理
      • lein run
        • 加载 run 环境,包括 exclude-tests、include-all-drivers
        • 加载 :main 指定的入口命名空间,此处为metabase.core
        • 运行 metabase.core/-main,如果带有cmd指令会进入 metabase.cmd/run-cmd 运行相应指令,否则进入metabase.core/start-normally 启动程序
        • 在 start-normally 中,会运行 metabase.core/init!并通过jetty启动服务器,相关运行配置在 metabase.config中配置
      • lein repl
        • 打开一个交互服务器,可以执行输入的代码并实时返回结果。
        • 启动后命名空间为user,即不会主动加载项目代码,需要手动 require 以加载环境
        • 在project.clj中配置 :repl-options { :init-ns test.core }, 可以指定默认加载空间

      二、API请求

      API官方文档可参阅 API Document
      如需追踪工作流,可以在 resources/log4j2.xml 中调低相关日志等级

      • 统一API请求入口
        • 收到API请求后,统一调用 metabase.handler/app 处理
        • 调用 metabase.routes/routes 进行请求分流,最核心的是处理数据请求的 /api 接口,其它接口主要是辅助功能
        • 对于api请求,进入 metabase.api.routes/routes 对于不同的接口进行二次分流
        • 回到metabase.handler/app,对结果进行一系列的封装,如捕获异常、输出日志、转JSON等等
      • 数据查询接口
        临时查询基本都走/api/dataset接口,保存的图表会有不同的接口入口,但是都会从card表中查SQL,然后通过qp/process-query-and-save-execution!执行
        • GET /api/pulse/preview_card_info/4
          • 在pulse中查看数据预览时调用该接口
          • GET接口,有效参数仅card_id,后端通过数据库获取query
          调用 metabase.api.pulse 中 GET "/preview_card_info/:id" 路由;
          将card_id传入 api/read-check 函数,查询card信息(包括sql语句)
          将card传入 pulse-card-query-results 函数,获取数据;
          调用 qp/process-query-and-save-execution! 执行查询
          将数据直接转换成HTML格式的图表,放在 pulse_card_html中返回给前端;
          前端直接渲染,无需调用图表方法(邮件中也可以直接渲染)。
          
        • POST /api/card/1/query
          • 打开已保存的数据看板/Question时调用该接口,但如果对card条件进行更改,刷新数据时会调用dataset
          • 接口调用上传参数为空,有效参数仅card_id,后端通过数据库获取query
          调用 metabase.api.card 中 POST "/:card-id/query" 路由;
          将card_id传入 run-query-for-card-async 函数,获取数据;
          将card_id传入 api/read-check,查询card信息(包括sql语句)
          调用 qp/process-query-and-save-execution! 执行查询
          
        • POST /api/dataset
          • 所有其它调用都使用该接口,包括x-ray,ask question, SQL取数等
          调用 metabase.api.dataset 中 POST "/" 路由;
          增加中间件 :js-int-to-string?
          调用 qp/process-query-and-save-with-max-results-constraints! 组装SQL语句
          增加中间件 :add-default-userland-constraints?
          调用 qp/process-query-and-save-execution! 组装SQL语句
          调用 qp.streaming/streaming-response 执行并组装返回结果
          
      • DP流程
        • qp/process-query-and-save-execution!之后进入DP流程,以下为流程详解
        qp/process-query-and-save-execution!,此时收到的query可能为SQL语句,也可能是HoneySQL
        调用qp/process-userland-query,在此区分同步和异步查询,但之后其实都是调异步查询函数qp/process-userland-query-async,只是同步查询会在这里先加上一个 wait-for-async-result 方法
        调用 qp/base-qp,这里定义并调用了一个内部函数qp,里面运行了两个最关键的函数:
        qp.reducible/combine-middleware,这个函数会先用 qp.reducible/pivot 生成一个基础执行器,然后用 qp/userland-middleware 中定义的40个中间件逐个去包装它,最后返回包装好的执行器
        qp.reducible/async-qp, 这里定义了一个内部函数 thunk 用于把query放入执行器执行,并捕获错误,并且可以另起一个线程来运行执行器。
        
        • 出于二次开发需要,需进一步了解执行器的流程,展开如下:
        当qp.reducible/async-qp调用执行器之后,会将原始query传递给qp.reducible/pivot,
        44个中间件对执行器进行加工
        调用qp.context/runf,然后再调用qp.context/executef进行取数,此时的query已经被组装为可执行的SQL
        由于数据可能存在不同的数据源中,因此实际取数要调用driver去执行,主程序中只调用了REST接口,然后等待数据返回
        主程序中虽然引入了toucan,但只用于读取元数据,并不用于业务取数,因此主流程中找不到toucan操作。
        
      • Middleware
        • 一个普通的查询会遍历44个middleware,先遍历3个userland附加的middleware,如下表从下往上执行:
        metabase.query-processor.middleware.constraints/add-default-userland-constraints
        # 如果参数中 :add-default-userland-constraints? 为 true,则在query中添加 :constraints {:max-results 10000, :max-results-bare-rows 2000}
        metabase.query-processor.middleware.process-userland-query/process-userland-query
        # 对于面向用户的查询(userland query),记录查询日志并包装返回的结果
        metabase.query-processor.middleware.catch-exceptions/catch-exceptions
        # 捕获qp过程中的异常
        
        • 然后是41个 process-query 默认的middleware, 从下往上执行
        #'metabase.query-processor.middleware.mbql-to-native/mbql->native
        #'metabase.query-processor.middleware.check-features/check-features
        #'metabase.query-processor.middleware.optimize-datetime-filters/optimize-datetime-filters
        #'metabase.query-processor.middleware.auto-parse-filter-values/auto-parse-filter-values
        #'metabase.query-processor.middleware.wrap-value-literals/wrap-value-literals
        #'metabase.query-processor.middleware.annotate/add-column-info
        #'metabase.query-processor.middleware.permissions/check-query-permissions
        #'metabase.query-processor.middleware.pre-alias-aggregations/pre-alias-aggregations
        #'metabase.query-processor.middleware.cumulative-aggregations/handle-cumulative-aggregations
        #'metabase-enterprise.sandbox.query-processor.middleware.row-level-restrictions/apply-row-level-permissions
        #'metabase.query-processor.middleware.resolve-joined-fields/resolve-joined-fields
        #'metabase.query-processor.middleware.resolve-joins/resolve-joins
        #'metabase.query-processor.middleware.add-implicit-joins/add-implicit-joins
        #'metabase.query-processor.middleware.large-int-id/convert-id-to-string
        #'metabase.query-processor.middleware.limit/limit
        #'metabase.query-processor.middleware.format-rows/format-rows
        #'metabase.query-processor.middleware.desugar/desugar
        #'metabase.query-processor.middleware.binning/update-binning-strategy
        #'metabase.query-processor.middleware.resolve-fields/resolve-fields
        #'metabase.query-processor.middleware.add-dimension-projections/add-remapping
        #'metabase.query-processor.middleware.add-implicit-clauses/add-implicit-clauses
        #'metabase-enterprise.sandbox.query-processor.middleware.row-level-restrictions/apply-row-level-permissions
        #'metabase.query-processor.middleware.add-source-metadata/add-source-metadata-for-source-queries
        #'metabase-enterprise.sandbox.query-processor.middleware.column-level-perms-check/maybe-apply-column-level-perms-check
        #'metabase.query-processor.middleware.reconcile-breakout-and-order-by-bucketing/reconcile-breakout-and-order-by-bucketing
        #'metabase.query-processor.middleware.auto-bucket-datetimes/auto-bucket-datetimes
        #'metabase.query-processor.middleware.resolve-source-table/resolve-source-tables
        #'metabase.query-processor.middleware.parameters/substitute-parameters
        #'metabase.query-processor.middleware.resolve-referenced/resolve-referenced-card-resources
        #'metabase.query-processor.middleware.expand-macros/expand-macros
        #'metabase.query-processor.middleware.add-timezone-info/add-timezone-info
        #'metabase.query-processor.middleware.splice-params-in-response/splice-params-in-response
        #'metabase.query-processor.middleware.resolve-database-and-driver/resolve-database-and-driver
        #'metabase.query-processor.middleware.fetch-source-query/resolve-card-id-source-tables
        #'metabase.query-processor.middleware.store/initialize-store
        #'metabase.query-processor.middleware.cache/maybe-return-cached-results
        #'metabase.query-processor.middleware.validate/validate-query
        #'metabase.query-processor.middleware.normalize-query/normalize
        #'metabase.query-processor.middleware.add-rows-truncated/add-rows-truncated
        #'metabase-enterprise.audit.query-processor.middleware.handle-audit-queries/handle-internal-queries
        #'metabase.query-processor.middleware.results-metadata/record-and-return-metadata
        

      转载自:https://blog.csdn.net/weixin_43821438/article/details/111051486

      1 条回复 最后回复 回复 引用 0
      • First post
        Last post

      Powered by MetabaseCN | 提供专业Metabase培训 | QQ群:799286435 微信:MetabaseCN

      鄂ICP备20010758号