Hadoop http 服务是基于 jetty 框架开发,本文首先对 Jetty 概述和基本使用方法简介,然后再基于 Hadoop HttpServer2 展开讲解,阅读本文收益如下:
- 了解基于 Jetty 的 Http 服务开发
- 掌握 Hadoop Http 接口的开发方法和 Hadoop Http 的认证方式
Jetty 简介
jetty是一个轻量级的servelt容器,Jetty 的架构是基于 Handler 来实现的,主要功能的扩展基于 Handler 来实现。
Jetty 常见 Handler
1、Server 即 Jetty 的核心Handler ,负责管理和启动 HTTP 服务,并接收和处理来个各个 Connection 的请求,下发到下游的 Handler 处理
2、ServletHandler 负责 Servlet、Filter 管理与访问路径的映射
3、ResourceHandler 负责本地静态资源访问控制管理
4、SessionHandler 负责 HTTP 请求的 Session 管理
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 服务
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 接口服务开发风格也没有很好的统一