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 简介
- HttpServer2 提供了 Builder 工具类快速构建 HttpServer2 实例
- HttpServer2 提供了默认的 Servlet 服务 :
- StackServlet :查询当前服务 Java statck 信息 http://localhost:50070/stacks
- LogLevel.Servlet :可动态设置当前服务日志级别 http://localhost:50070/logLevel
- JMXJsonServlet :查询当前 jmx 服务信息 http://localhost:50070/jmx
- ConfServlet :查询当前服务的配置信息 http://localhost:50070/conf
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);
}
- setupServlets 方法中通过调用 httpServer2.addInternalServle 注册自定义 Servlet
- StartupProgressServlet :在 NameNode 启动时查看 fsimage 加载情况
- FsckServlet :查询块副本状态
- ImageServlet :提供下载 fsimage 的功能
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 接口服务开发风格也没有很好的统一