【metabase二次开发 • 三】Clojure运行解析

一、程序启动

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

    1. lein ring server

    1. 加载 ring 环境,包括 exclude-testsinclude-all-driverslein-ring

      include-all-drivers 会对所有数据库驱动进行加载,并在终端输出进度,耗时较长;即使在环境中注释掉include-all-drivers,后续init时依然会加载,即注释掉不会产生影响;

    2. 加载metabase.core引用的命名空间,耗时较长。某些命名空间在加载过程中会输出到终端;

      示例:2020-12-11 18:21:38,879 INFO metabase.util :: Maximum memory available to JVM: 3.6 GB

    3. 加载 metabase.core,加载过程中有输出

      示例:Copyright © 2020 Metabase, Inc.

    4. 运行 metabase.core/init!
    5. 启动ring服务,监听端口(默认3000),并将收到的请求交给handler处理

    2. lein run

    1. 加载 run 环境,包括 exclude-testsinclude-all-drivers
    2. 加载 :main 指定的入口命名空间,此处为metabase.core
    3. 运行 metabase.core/-main,如果带有cmd指令会进入 metabase.cmd/run-cmd 运行相应指令,否则进入 metabase.core/start-normally 启动程序
    4. start-normally 中,会运行 metabase.core/init!并通过jetty启动服务器,相关运行配置在 metabase.config中配置

    3. lein repl

    • 打开一个交互服务器,可以执行输入的代码并实时返回结果。
    • 启动后命名空间为user,即不会主动加载项目代码,需要手动 require 以加载环境
    • 在project.clj中配置 :repl-options { :init-ns test.core }, 可以指定默认加载空间

二、API请求

  • API官方文档可参阅 API Document

  • 如需追踪工作流,可以在 resources/log4j2.xml 中调低相关日志等级

    1. 统一API请求入口

    1. 收到API请求后,统一调用 metabase.handler/app 处理
    2. 调用 metabase.routes/routes 进行请求分流,最核心的是处理数据请求的 /api 接口,其它接口主要是辅助功能
    3. 对于api请求,进入 metabase.api.routes/routes 对于不同的接口进行二次分流
    4. 回到metabase.handler/app,对结果进行一系列的封装,如捕获异常、输出日志、转JSON等等

    2. 数据查询接口

    • 临时查询基本都走/api/dataset接口,保存的图表会有不同的接口入口,但是都会从card表中查SQL,然后通过qp/process-query-and-save-execution!执行

      GET /api/pulse/preview_card_info/4

      • 在pulse中查看数据预览时调用该接口
      • GET接口,有效参数仅card_id,后端通过数据库获取query
      • 调用 metabase.api.pulseGET "/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.cardPOST "/: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.datasetPOST "/" 路由;
        • 增加中间件 :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 执行并组装返回结果

    3. 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,里面运行了两个最关键的函数:
      1. qp.reducible/combine-middleware,这个函数会先用 qp.reducible/pivot 生成一个基础执行器,然后用 qp/userland-middleware 中定义的40个中间件逐个去包装它,最后返回包装好的执行器
      2. 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操作。

    4. 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!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值