Apache Druid源码导读--Google guice DI框架

2023-05-16

文章目录

  • 缘起
  • Google Guice介绍
    • 与Spring的对比
    • Example
    • 覆盖已有绑定关系
    • 默认绑定
  • Apache Druid中Guice模块
    • guice-lifecycle
    • guice-jsonconfig
    • guice-jersey-jetty

缘起

在大数据应用组件中,有两款OLAP引擎应用广泛,一款是偏离线处理的Kylin,另一个是偏实时的Druid。Kylin是一款国人开源的优秀离线OLAP引擎,基本上是Hadoop领域离线OLAP事实标准,在离线报表,指标分析领域应用广泛。而Apache Druid则在实时OLAP领域独领风骚,优异的性能、高可用、易扩展。Kylin的实现细节网上资料很多,而Druid很少,最近打算研究下源码,写几篇阅读导读,记录于此。

阅读Druid源码的第一个障碍莫过于Google Guice这个小巧的DI框架了。不了解Guice很难阅读Druid源码,而Guice在国内应用偏少,文章也少,无疑加大了难度。我把Druid中使用的guice扩展单独抽出来,放在了https://github.com/Skycrab/guice-module下,对druid源码注释放在了https://github.com/Skycrab/druid-comment。

本文主要介绍下Google Guice以及Druid中实现的guice扩展模块。

Google Guice介绍

Guice是Google开源的一个小巧的依赖注入框架。
下面主要介绍下与Spring的对比,以及guice几个核心的能力。

与Spring的对比

Guice与Spring没有直接竞争关系,Spring是复杂的技术栈,而Guice只专注于依赖注入。

Guice与Spring的表现方式也稍微有所区别。Guice觉得基于xml的方式过于隐晦,而自动注入(AutoWired)又过于灵活,所以Guice基于代码绑定实现,较为克制。

而基于Module的方式让Guice获得了巨大的灵活性与可复用性,可以简单理解为多个xml装配,但更加强大,可复用。

Example

看下面这个例子(git地址)

@Slf4j
public class GuiceExample {
    public static void main( String[] args ) throws Exception {
        Injector injector = Guice.createInjector(new Module() {
            @Override
            public void configure(Binder binder) {
                binder.bind(ProduceService.class).to(KafkaPrduceService.class);
                binder.bind(String.class).annotatedWith(Names.named("server")).toInstance("localhost:9002");
                binder.bind(String.class).annotatedWith(Names.named("topic")).toInstance("test");
            }
        });

        ProduceService produce = injector.getInstance(ProduceService.class);
        produce.produce("hello guice");
    }

    public interface ProduceService {
        void produce(Object msg);
    }

    @Singleton
    public static class KafkaPrduceService implements ProduceService {
        private String server;
        private String topic;

        @Inject
        public KafkaPrduceService(@Named("server") String server, @Named("topic") String topic) {
            this.server = server;
            this.topic = topic;
        }
        @Override
        public void produce(Object msg) {
            log.info("produce {}-{}-{}", server, topic, msg);
        }
    }
}

我们看到guice中的绑定关系是在Module中维护的,可以简单当做是spring的xml文件。Singleton代表该服务是单例的,通过@Inject注入需要的bean,如果需要的bean没有绑定,会通过默认构造函数实例化。
其它基本介绍可参考guice文档

覆盖已有绑定关系

Apache Druid好多模块是可以自定义替换的,一方面通过spi机制+ClassLoader加载扩展模块实现模块热插拔,另一方面通过Guice覆盖绑定关系将新实现注入到框架。下面要介绍的就是guice的覆盖绑定关系能力。
看下面这个例子

/**
 * Created by yihaibo on 2019-09-29.
 * Guice模块绑定覆盖
 */
public class GuiceOverrideExample {
  public static void main(String[] args) {
    List<Module> builtIns = ImmutableList.of(binder -> {
      binder.bind(Service.class).to(BuiltinService.class);
    });
//    List<Module> customs = ImmutableList.of();
    List<Module> customs = ImmutableList.of(binder -> {
      binder.bind(Service.class).to(CustomService.class);
    });
    Injector injector = Guice.createInjector(Modules.override(builtIns).with(customs));
    FrameWork frameWork = injector.getInstance(FrameWork.class);
    frameWork.start();
  }
  
  public static class FrameWork {
    private Service service;
    @Inject
    public FrameWork(Service service) {
      this.service = service;
    }
    public void start() {
      this.service.run();
    }
  }
  
  public interface Service {
    void run();
  }
  
  public static class BuiltinService implements Service {
    @Override
    public void run() {
      System.out.println("BuiltinService");
    }
  }

  public static class CustomService implements Service {
    @Override
    public void run() {
      System.out.println("CustomService");
    }
  }
}

我们看到框架本来绑定的是BuiltinService,现在我们需要替换成CustomService,只需要Modules.override即可覆盖绑定关系。

默认绑定

在做基础库的时候,有时会依赖一些服务,但这些服务很可能被用户自定义,这时可以使用guice的默认绑定功能。
看下面代码

@Slf4j
public class GuiceOptionalBinderExample {
public static void main(String[] args) {
  Injector injector = Guice.createInjector(new FrameWorkModule(), new Module() {
    @Override
    public void configure(Binder binder) {
      //覆盖框架默认实现
      //如果需要传递参数,可以1.Inject 2.使用Provider
      OptionalBinder.newOptionalBinder(binder, Emit.class).setBinding().to(kafkaEmit.class);
    }
  });

  TestService testService = injector.getInstance(TestService.class);
  testService.test();
}

public static class TestService {
  private Emit emit;
  @Inject
  public TestService(Emit emit) {
    this.emit = emit;
  }
  public void test() {
    this.emit.emit("start TestService");
  }
}

//-------应用代码
public static class kafkaEmit implements Emit {
  @Override
  public void emit(Object object) {
    log.info("kafkaEmit emit");
  }
}

//-------库代码
public static class FrameWorkModule implements Module {
  @Override
  public void configure(Binder binder) {
    //库默认实现
    OptionalBinder.newOptionalBinder(binder, Emit.class).setDefault().to(HttpEmit.class);
  }
}

public interface Emit {
  void emit(Object object);
}

public static class HttpEmit implements Emit {
  @Override
  public void emit(Object object) {
    log.info("HttpEmit emit");
  }
}
}

Apache Druid中Guice模块

在Druid中有几个通用的Guice扩展,不了解会对代码阅读产生影响。

模块功能
guice-lifecycle实现生命周期托管,实现服务start、stop方法
guice-jsonconfigProperties配置文件bean自动装配
guice-jersey-jetty内嵌jetty的jersey Restful

guice-lifecycle

LifecycleModule提供服务托管能力,提供了4级服务优先级,框架会自动调用start和stop方法。该功能在druid应用非常广泛,是应用启动的原点。

/**
 * Guice 生命周期管理
 */
public class GuiceLifecycleExample
{
    public static void main( String[] args ) throws Exception
    {
        final Injector injector = Guice.createInjector(new LifecycleModule());
        final Bootstrap bootstrap = injector.getInstance(Bootstrap.class);
        bootstrap.run();
    }

    public static class Bootstrap{
        // 必须主动注入ManageLifecycle,否则需要通过Lifecycle.addHandler主动注册
        private PrintLifecycle printLifecycle;
        private Lifecycle lifecycle;
        @Inject
        public Bootstrap(Lifecycle lifecycle, PrintLifecycle printLifecycle) {
            this.lifecycle = lifecycle;
            this.printLifecycle = printLifecycle;
        }
        public void run() throws Exception {
            System.out.println("Bootstrap run");
            lifecycle.start();
            lifecycle.join();
        }
    }
    
    @ManageLifecycle
    @Slf4j
    public static class PrintLifecycle {
        @LifecycleStart
        public void start() {
            System.out.println("PrintLifecycle start");
        }

        @LifecycleStop
        public void stop()
        {
            System.out.println("PrintLifecycle stop");
        }
    }
}

guice-jsonconfig

jsonconfig提供了配置文件bean自动装配,并支持validation注解校验。Druid中读取配置功能都使用了该功能。

public class GuiceJsonConfigExample {
  public static void main( String[] args ) {
    Injector injector = Guice.createInjector(new JsonConfigModule(), new Module() {
      @Override
      public void configure(Binder binder) {
        JsonConfigProvider.bind(binder, "druid.server", DruidServerConfig.class);
      }

      @Provides
      @Singleton
      /**
       * Properties代码注入需注意必须为string
       */
      Properties provideProperties() {
        Properties props = new Properties();
        props.put("druid.server.hostname", "0.0.0.0");
        props.put("druid.server.port", "3333");
        return props;
      }
    });

    DruidServerConfig druidServerConfig = injector.getInstance(DruidServerConfig.class);

    System.out.println(druidServerConfig.port);
    System.out.println(druidServerConfig.hostname);
  }

  public static class DruidServerConfig {
    @JsonProperty
    @NotNull
    public String hostname = null;
    @JsonProperty @Min(1025) public int port = 8080;
  }
}

guice-jersey-jetty

jersey-jetty提供了内嵌jetty的jersey Restful。Druid中http接口都由jersey支持,由Jetty充当servlet容器。

/**
 * Created by yihaibo on 2019-10-08.
 * jersey restful简单列子
 */
@Slf4j
public class GuiceJerseryJettyExample {
  public static void main(String[] args) throws Exception {
    Injector injector = Guice.createInjector(new JsonConfigModule(), new JettyServerModule(), new Module() {
      @Override
      public void configure(Binder binder) {
        Jerseys.addResource(binder, IndexResource.class);
      }

      @Provides @Singleton
      Properties provideProperties() {
        Properties props = new Properties();
        props.put("server.http.host", "0.0.0.0");
        props.put("server.http.port", "9000");
        return props;
      }
    });

    JerseyJettyServer jerseyJettyServer = injector.getInstance(JerseyJettyServer.class);
    jerseyJettyServer.start();
    Thread.currentThread().join();
  }

  @Singleton
  @Path("/index")
  public static class IndexResource {
    private ServerConfig serverConfig;
    @Inject
    public IndexResource(ServerConfig serverConfig) {
      this.serverConfig = serverConfig;
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public ServerConfig doGet(@Context final HttpServletRequest req) {
      return serverConfig;
    }
  }
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Apache Druid源码导读--Google guice DI框架 的相关文章

  • Apache HTTP localhost 在 macOS Monterey 上随机花费 5 秒,但在 HTTPS 上速度很快

    我尽可能地等待 但最终不得不从莫哈韦升级 我知道有些东西会不必要地损坏 当然确实如此 较大的文件 超过 100k 随机需要 5 秒才能加载 它并不总是相同的文件 也不是每次都相同 并且它只发生在 HTTP Apache 中 尝试过端口 80
  • .htaccess HTTPS 主域和通配符 HTTP 子域 + 所有非 WWW

    Stack Overflow 上有很多类似的解决方案 例如htaccess http 到 https 带 www 不重定向子域 https stackoverflow com questions 30242495 htaccess http
  • 在Java中解析包含multipart/form-data请求体的字符串

    问题陈述 我认为标题说明了一切 我正在寻找解析 a 的方法String包含 multipart form data HTTP 请求的正文部分 IE 字符串的内容看起来像这样 xyzseparator blah Content Disposi
  • RewriteEngine On .htaccess 不起作用

    我正在尝试提出我的请求如何处理这样的文件 r login 但有像index php这样的服务器吗 r login 我正在使用的 htaccess 代码似乎不起作用 Options FollowSymLinks RewriteEngine O
  • Apache James 学习资源 [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 该计划是创建一个列表阿帕奇 詹姆斯 http james apache org学习资源 涉及从设置到使用
  • 我怎样才能发现并生存被“Slashdotted”?

    在异常高的流量高峰中生存的好方法是什么 我的想法是 在某些触发点上 我的网站应该暂时切换到 低带宽 模式 切换到基本 HTML 页面 最少的图形 禁用可能会给数据库带来不必要负载的小部件 等等 我的想法是 监控CPU使用率 监控带宽 监控请
  • 一个 Guice 就绪的安全框架?

    有没有人见过一个为与 Guice 一起工作而编写的框架 或者一个将现有安全系统 即 Acegi 与 Guice 集成的库 到目前为止我发现了以下内容 http code google com p warp security http cod
  • PHP SNMP - 找不到模块

    我已启用 SNMP 模块并尝试使用该模块中的功能 我已将 MIBDIRS 环境变量设置为我的 mib 所在的位置 但我仍然收到这些 找不到模块 警告 Cannot find module IP MIB At line 0 in none C
  • Avro 消息中的架构

    我看到 Avro 消息嵌入了架构 然后是二进制格式的数据 如果发送多条消息并且为每条消息创建新的 avro 文件 模式嵌入不是一种开销吗 那么 这是否意味着生产者批量处理消息然后写入总是很重要 因此写入一个 avro 文件的多条消息只携带一
  • Flask/Apache 提交按钮用于文件上传

    我有一个在 apache 后面运行的 Flask 应用程序 在我的 index html 页面上有一个文件上传按钮和一个提交按钮 如下所示
  • 使用 VirtualDocumentRoot 的 SSL 虚拟主机

    我在 ubuntu 16 04 VM 上进行开发工作 当我处理多个项目时 为了让我的生活更轻松 我使用 VirtualDocumentRoot 和主机文件使用 dev 域从我的主文件夹中服务器站点 在 000 default conf 我有
  • 如何从网站网址中隐藏 .html 扩展名

    我知道这个问题以前曾被问过 但有人知道隐藏 html 扩展名的好方法吗 我已经尝试了许多代码和许多答案https stackoverflow com https stackoverflow com 但我没有看到结果 那是我再问你一次 我有一
  • 如何让 mod_perl 在更改时重新加载源文件?

    我正在开发一个带有 mod 的应用程序 perl 并在每次更改代码时重新启动服务器是一个巨大的阻力 我还是想用mod perl 用于开发 因为我计划将其用于实时服务器 我在文档中没有看到有关如何执行此操作的任何内容 想法 我认为 Apach
  • CNAME 速度慢吗?

    我将 CNAME 与 S3 CloudFront 一起使用来提供一些静态文件 例如 js css 图像等 我这样做是为了使存储桶的 URL 更漂亮 因为我认为最好将所有内容都定位到我的网站 以防万一将来我想移动这些文件 更改应该是透明的 今
  • 配置tomat的server.xml文件并自动生成mod_jk.conf

    我在用apache 2 2 15 and tomcat6 6 0 24 on CentOS 6 4并希望使用 tomcat 服务器的功能 通过添加以下内容自动生成 mod jk conf 文件
  • 从 Django 运行 shell 命令

    我正在 Django 中开发一个网页 使用 apache 服务器 需要调用 shell 命令来启用 禁用一些守护进程 我尝试这样做 os system service httpd restart 1 gt HOME out 2 gt HOM
  • 使用 mod_proxy 进行反向代理,保留原始请求 URL

    我已经使用 mod proxy Apache2 配置了一个反向代理 监听 127 0 0 1 80 将所有请求代理到 127 0 0 1 8080 所以我配置了 mod proxy 如下所示 ProxyPreserveHost On Pro
  • 注入实例上的 Guice 空指针异常

    我在 Jersey2 中使用 Guice 进行 DI 我想使用它 这样我就可以使用 Google App Engine gt 不使用 HK2 我的应用程序资源 public class ApplicationResource extends
  • 无法查看神经节仪表板

    我已经使用 rpm 文件进行了 ganglia 设置 OS centOS 6 3 Server httpd gmond 和 gmetad 正在运行 当我在浏览器中加载它时收到 禁止错误 Forbidden You don t have pe
  • Apache 访问 Linux 中的 NTFS 链接文件夹

    在 Debian jessie 中使用 Apache2 PHP 当我想在 Apache 的文档文件夹 var www 中创建一个新的小节时 我只需创建一个指向我的 php 文件所在的外部文件夹的链接 然后只需更改该文件夹的所有者和权限文件夹

随机推荐

  • 内部链接与外部链接

    在说内部连接与外部连接前 xff0c 先说明一些概念 1 声明 一个声明将一个名称引入一个作用域 在c 43 43 中 xff0c 在一个作用域中重复一个声明是合法的 以下都是声明 xff1a int foo int int 函数前置声明
  • partition/stable_partition详解

    Partition 将满足条件的元素向前移动 TEMPLATE FUNCTION partition template lt class BidIt class Pr gt inline BidIt Partition BidIt Firs
  • jsoncpp解析拼装数组

    int main 数组创建与分析 例子一 string strValue 61 34 34 ldh 34 34 001 34 34 gfc 34 34 002 34 34 yyj 34 34 003 34 34 andy 34 34 005
  • 查看静态库(.lib)和动态库(.dll)的导出函数的信息

    一般情况下 xff0c 我们需要查看一个DLL或EXE中的包含的函数或是依赖的函数之类的信息 xff0c 可以使用VS自带的工具dumpbin xff1b 可以直接在命令行下输入dumpbin就可以查看他的使用说明 xff0c 如果未显示
  • do {...} while (0) 在宏定义中的作用

    http www cnblogs com lanxuezaipiao p 3535674 html 如果你是一名C程序员 xff0c 你肯定很熟悉宏 xff0c 它们非常强大 xff0c 如果正确使用可以让你的工作事半功倍 然而 xff0c
  • Nginx 代理服务器10k文件无法上传

    在我们使用Nginx作为代理服务器的时候 xff0c 在进行文件上传时 xff0c 大于10k的文件上传失败 xff0c 因为此时后台服务并没有接收到请求 xff0c 所以在Nginx配置中进行排错 xff0c 终于找到了问题所在 1 修改
  • 即插即用型设备驱动的加载过程

    现假设驱动程序已被正确安装 xff1a 1 某种PnP总线驱动发现了即插即用设备的存在 xff1a 对于热插拔设备 xff0c 则发现过程发生于插入设备的瞬间 xff1b 如果是非热插拔设备 xff0c 则发现过程发生于系统启动时 2 Pn
  • EXCEL 基于合并计算工具实现相似表格汇总和求平均值

    1 表格汇总合并 在处理大量表格时 xff0c 有时候需要将很多相思内容的表格 xff0c 合并到一张表里 xff0c 那么就需要用到 合并计算工具 了 如下表所示为某公司南京分部的BCD产品的销售额 通过下表可以知道还有海口 上海 珠海三
  • ZYNQ7020AMP使用方法总结

    本人使用的sdk版本为2015 4本人的方法适用于15 4之后的版本 Zynq开发双核分为两种方法 xff0c 第一种双核裸跑 xff0c 第二种linux 43 裸跑 双核裸跑 xff1a 先使用Debug调试器调试 xff0c 通过SD
  • 试用了5款BI分析工具,终于找到了上手最快的那一个!

    前几天 xff0c 领导甩给我一个任务 xff0c 考察几个BI工具 xff0c 下季度立项用 潜心做ETL的我 xff0c 对BI只是略懂 之前上的BO xff0c 由于开发模式不适应 人员用不惯 xff0c 再加上负责这块的同事走的走
  • RNA-seq流程——使用hisat2进行序列比对(不利用循环&利用循环)(未完待续)

    RNA seq流程 使用hisat2进行序列比对 xff08 不利用循环 xff06 利用循环 xff09 xff08 未完待续 xff09 本次使用ky老师的文件进行序列比对 xff0c 比对时使用双端比对 xff0c 1 clean f
  • JavaWeb学习jsp中,单击验证码图片进行替换

    lt td gt 验证码 lt td gt lt td class 61 34 inputs 34 gt lt input name 61 34 checkCode 34 type 61 34 text 34 id 61 34 checkC
  • Linux的FTP安装、使用和配置(FTP客户端管理工具)

    一 FTP服务介绍 1 什么是HTP服务 FTP xff08 File Transfer Protocol xff09 是一种应用非常广泛并且古老的一个互联网文件传输协议 FTP主要用户互联网中文件的双向传输 xff08 上传 下载 xff
  • 详解警告“unreferenced local variable”

    在编译C 43 43 程序时 xff0c 我们有时候遇到这样的警告 warning C4101 39 x1 39 unreferenced local variable 下面是一个会出现上述警告的简单例子 xff1a using names
  • 为Github page绑定自定义域名并实现https访问

    欢迎参观我的网站 gt Yuci s Blog 实现目标 获取自定义域名yucichueng me 将上述域名 及www域名 解析到yucichueng github ioIP地址 将域名解析服务托管于CloudFlare 获取SSL证书
  • [NFC]NFC 客户 Support 流程

    驱动部分问题 测试程序用法再还未移植上层内容前执行测试程序后 NoACK不慎移植了上层后但还未确认底层是否移植成功需要先删除移植上层所产生的内容设备节点权限海思平台的驱动问题64位平台问题想用 NXP CLK 控制 PMIC 的 CLK安全
  • win10 文件夹背景 win10教程

    韩梦飞沙 韩亚飞 313134555 64 qq com yue31313 han meng fei sha 如何修改Windows10文件夹背景色 百度经验 Windows 10 文件夹背景 xff08 资源管理器中 xff09 如何更改
  • SpringBoot集成GuavaCache实现本地缓存「区别于redis缓存实现」

    前言 好久没有写文章了 xff0c 前段时间由于公司项目比较忙 xff0c 因此耽搁了一些时间 本篇文章也是本头条号转正后发的第一篇文章 xff0c 在此跟各位看官道歉 xff0c 同时也感谢各位看官的不离不弃 希望各位看官可以关注本头条号
  • Jackson多态反序列化的使用

    缘起 最近看Apache Druid的源代码 0 5很老的版本 xff0c 印象最深的就是对Jackson的多态反序列化和注入的使用了 xff0c 这里也属于自己的知识盲点 xff0c 看着复杂的json直接反序列化为可用对象 xff0c
  • Apache Druid源码导读--Google guice DI框架

    文章目录 缘起Google Guice介绍与Spring的对比Example覆盖已有绑定关系默认绑定 Apache Druid中Guice模块guice lifecycleguice jsonconfigguice jersey jetty