Hadoop HTTP 服务开发浅析


Hadoop http 服务是基于 jetty 框架开发,本文首先对 Jetty 概述和基本使用方法简介,然后再基于 Hadoop HttpServer2 展开讲解,阅读本文收益如下:

  • 了解基于 Jetty 的 Http 服务开发
  • 掌握 Hadoop Http 接口的开发方法和 Hadoop Http 的认证方式

Jetty 简介

jetty是一个轻量级的servelt容器,Jetty 的架构是基于 Handler 来实现的,主要功能的扩展基于 Handler 来实现。

image.png

Jetty 常见 Handler

1、Server 即 Jetty 的核心Handler ,负责管理和启动 HTTP 服务,并接收和处理来个各个 Connection 的请求,下发到下游的 Handler 处理
2、ServletHandler 负责 Servlet、Filter 管理与访问路径的映射
3、ResourceHandler 负责本地静态资源访问控制管理
4、SessionHandler 负责 HTTP 请求的 Session 管理


image.png

Jetty 基本用法

Jetty quick start demo

1、添加 maven 依赖

<properties>
    <jetty.version>9.4.9.v20180320</jetty.version>
    <guice.version>3.0</guice.version>
    <jersey.version>1.9.1</jersey.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-server</artifactId>
        <version>${jetty.version}</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-webapp</artifactId>
        <version>${jetty.version}</version>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-maven-plugin</artifactId>
            <version>${jetty.version}</version>
        </plugin>
    </plugins>
</build>

2、编写Jetty 服务


public static void main(String[] args) throws Exception {
    // 创建 Server 初始化 http 服务配置
    Server server = new Server(8080);
    //jetty theadpool 设置
    // ThreadPool threadPool = server.getThreadPool();

    //创建 Web 上下文
    WebAppContext context = new WebAppContext();
    context.setContextPath("/");

    //设置静态资源目录
    File webResourceLocation = new File("src/main/webapp");
    context.setBaseResource(Resource.newResource(webResourceLocation));
    //设置 WebApp 首页
    context.setWelcomeFiles(new String[]{"index.html"});

    // 注册一个 Servlet
    context.addServlet(SimpleServlet.class, "/session");

    //设置 Filter
    context.addFilter(SimpleFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));


    //为 Server 设置处理子流程的 handler 既 WebAppContext
    server.setHandler(context);
    server.start();
    server.join();
  }
}

Jetty 结合 Guice 、Jersey 开发 Http 服务

引入 guice 轻量级依赖注入框架 和 RestFull 框架Jersey 结合 , 简化 Jetty Web 服务开发流程
1、添加 maven 依赖

<dependency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>3.0</version>
</dependency>
<dependency>
    <groupId>com.google.inject.extensions</groupId>
    <artifactId>guice-servlet</artifactId>
    <version>3.0</version>
</dependency>
<dependency>
    <groupId>com.sun.jersey.contribs</groupId>
    <artifactId>jersey-guice</artifactId>
    <version>${jersey.version}</version>
</dependency>  

2、编写Jetty 服务

public class JettyJerseyDemo {
  public static void main(String[] args) throws Exception {
    Server server = new Server(8080);
    
    ServletHolder servlet = new ServletHolder(ServletContainer.class);
    // 设置初始化参数
    servlet.setInitParameter("com.sun.jersey.config.property.resourceConfigClass", "com.sun.jersey.api.core.PackagesResourceConfig");
    
    //scan Jersey 注解类的包路径
    servlet.setInitParameter("com.sun.jersey.config.property.packages", "com.demo.start.Jersey");

    // 自动将对象序列化为 json 返回
    servlet.setInitParameter("com.sun.jersey.api.json.POJOMappingFeature", "true");
    ServletContextHandler handler = new ServletContextHandler(ServletContextHandler.SESSIONS);

    handler.setContextPath("/");
    handler.addServlet(servlet, "/*");
    server.setHandler(handler);
    server.start();
  }
}
@Path("/jersey")
public class JerseyController {

  @GET
  @Path("/getInfo/{id}")
  public String getIt(@PathParam("id")  String id) {
    return "Hello From Jersey: " + id;
  }
}

Hadoop Web 技术架构

  • Yarn 、HDFS、MapReduce 服务 Web UI 前端,都是基于 bootstrap 、html 前后端分离的方式实现
  • Hadoop 基于 Jetty、Guice 、Jersey 进行整合封装了 HttpServer2 类管理 Http 服务
    • 开放了本地静态资源、服务线程个数、自定义 Servlet 、自定义 Filter 等配置入口
    • 默认提供的服务有查询 conf、jmx、stack 、动态配置 logLevel 功能
    • 为 Yarn、HDFS、MapReduce 统一提供了访问 Http 服务的多种认证方案
  • Yarn 基于 HttpServer2 再次封装了 WebApps 类可为 Yarn、MapReduce 快速构建 Web 服务
  • HDFS 基于 HttpServer2 由 NameNode 与 DataNode 封装了 NameNodeHttpServer、DatanodeHttpServer 构建各自的 Web 服务


    流程图.jpg

HttpServer2 简介

protected void addDefaultServlets() {
  addServlet("stacks", "/stacks", StackServlet.class);
  addServlet("logLevel", "/logLevel", LogLevel.Servlet.class);
  addServlet("jmx", "/jmx", JMXJsonServlet.class);
  addServlet("conf", "/conf", ConfServlet.class);
}
  • 开放了 HTTP 服务核心参数配置
参数 默认值 备注
hadoop.http.max.threads -1 Http 服务处理请求线程数
hadoop.http.max.request.header.size 65536 byte Http 请求头允许的最大数据量
hadoop.http.max.response.header.size 65536 byte Http 响应头允许的最大数据量
hadoop.http.acceptor.count -1 接收 TCP/IP 新连接处理的线程个数
hadoop.http.selector.count -1 监听 TCP/IP 连接线程个数
  • HttpServer2 提供了基础的 Filter
    • 通过配置 hadoop.http.filter.initializers 可为 Http 服务增加以下几个 Filter 功能,使用 "," 分割可同时配置多个
Initializer 类 初始化的 Filter 备注
AuthenticationFilterInitializer AuthenticationFilter AuthenticationFilter 负责 Hadoop Http 访问鉴权, 默认使用 PseudoAuthenticationHandler 的鉴权方式
HttpCrossOriginFilterInitializer CrossOriginFilter CrossOriginFilter 负责 Hadoop Http 跨域访问控制,默认允许跨域访问
StaticUserWebFilter StaticUserFilter StaticUserFilter 负责设置 Hadoop Http 页面默认的 Ugi 用户,使用该用户通过页面操作 HDFS 文件,- 默认用户:dr.who
  • HttpServer2 基于 AuthenticationFilter 提供了多种 Http 访问认证方式
    • 通过 “hadoop.http.authentication.type” 指定访问认证方式
AuthenticationHandler 类 type 作用
PseudoAuthenticationHandler simple 通过 url 后缀的方式 user.name=xxx , 进行简单的访问认证
KerberosAuthenticationHandler kerberos 使用 Kerberos 的方式进行认证
LdapAuthenticationHandler ldap 使用 ldap 的方式进行认证
MultiSchemeAuthenticationHandler multi-scheme 允许开启多种认证,其中一种认证通过即可
  • HttpServer2 提供了部分接口添加自定义 Servlet、Filter、Jersey 注解类的包路径配置
    • addJerseyResourcePackage : 添加 Jersey 注解类扫描包路径
    • addServlet : 添加自定义 Servlet
    • addFilter :添加自定义 Filter

NameNodeHttpServer 如何使用 HttpServer2 构建 Http 服务

  • Start 方法作为启动 NameNodeHttpServer 服务入口
    • 准备 Server 的 SocketAddress 信息
    • 创建 Builder
    • 使用构 Builder 构建 HttpServer2
    • 初始化配置 (认证、跨域、jersey 扫描包)
    • 设置 webAppContext 上下文属性
    • 初始化部分自定义 Servlets
    • 启动服务
oid start() throws IOException {

   //1、准备 Server 的 SocketAddress  信息
  final String infoHost = bindAddress.getHostName();
  final InetSocketAddress httpAddr = bindAddress;
  final String httpsAddrString = conf.getTrimmed(
      DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_KEY,
      DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_DEFAULT);
  InetSocketAddress httpsAddr = NetUtils.createSocketAddr(httpsAddrString);
      ....
 
 //2、创建 Builder
  HttpServer2.Builder builder = DFSUtil.httpServerTemplateForNNAndJN(conf,
      httpAddr, httpsAddr, "hdfs",
      DFSConfigKeys.DFS_NAMENODE_KERBEROS_INTERNAL_SPNEGO_PRINCIPAL_KEY,
      DFSConfigKeys.DFS_NAMENODE_KEYTAB_FILE_KEY);

  
  //3、使用构 Builder 构建 HttpServer2
  httpServer = builder.build();

  
  //4、初始化配置 (认证、跨域、jersey 扫描包)
  initWebHdfs(conf, bindAddress.getHostName(), httpServer,
      NamenodeWebHdfsMethods.class.getPackage().getName());
      
  //5、设置 webAppContext 上下文属性
  httpServer.setAttribute(NAMENODE_ATTRIBUTE_KEY, nn);
  httpServer.setAttribute(JspHelper.CURRENT_CONF, conf);
  //6、初始化部分自定义 Servlets
  setupServlets(httpServer, conf);
  //7、启动服务
  httpServer.start(); 
    ....

}
  • initWebHdfs 方法中调用了 httpServer2.addJerseyResourcePackage 方法, 扫描了 HDFS 项目中基于 Jersey 注解开发的 Http 服务类包路径
    • NamenodeWebHdfsMethods 就是被扫描到的服务类
public static void initWebHdfs(Configuration conf, String hostname,
    HttpServer2 httpServer2, String jerseyResourcePackage)
    throws IOException {
       ....
  // add webhdfs packages 
  // 初始化 jersey并 扫描 org.apache.hadoop.hdfs.web.resources
  // org.apache.hadoop.hdfs.server.namenode.web.resources; 包下的注解
  httpServer2.addJerseyResourcePackage(
      jerseyResourcePackage + ";" + Param.class.getPackage().getName(),
      pathSpec);
}
private static void setupServlets(HttpServer2 httpServer, Configuration conf) {
  httpServer.addInternalServlet("startupProgress",
      StartupProgressServlet.PATH_SPEC, StartupProgressServlet.class);
  httpServer.addInternalServlet("fsck", "/fsck", FsckServlet.class,
      true);
  httpServer.addInternalServlet("imagetransfer", ImageServlet.PATH_SPEC,
      ImageServlet.class, true);
}

结语

  • 存在的问题
    • StaticUserFilter 功能应该默认是开启的,在没有开启认证的情况下导致非法用户通过 Web 页面可以随意上传文件,对 HDFS 安全造成威胁
    • 在同一个项目中 HDFS、Yarn 没有统一快速构建 Web 服务方式,对应的 Http 接口服务开发风格也没有很好的统一

参考:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容