五分钟为HTTP接口提供Java/Scala SDK

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 上次构建Spark 任务发布管理程序时,正好用到了两个yarn的接口。因为不想引入Yarn的client包,所以使用了他的Http接口。那么如何调用这个HTTP接口便是一个问题了

Case描述

我现在要使用yarn的两个接口,一个是application 列表,一个是根据appId获取这个app的详情。对应的接口大约如此:
http://[dns]/ws/v1/cluster/apps
http://[dns]/ws/v1/cluster/apps/{appId}

基于HttpClient的初级封装

基于HttpClient的一个典型封装如下:
public interface HttpTransportService {

    public SResponse post(Url url, Map data);

    public SResponse post(Url url, Map data, Map<String, String> headers);

    public SResponse post(final Url url, final Map data, final int timeout);

    public SResponse post(final Url url, final Map data, final Map<String, String> headers, final int timeout);

    public SResponse get(final Url url, final int timeout);

    public SResponse get(Url url);

    public SResponse get(Url url, Map<String, String> data);

    public SResponse get(final Url url, final Map<String, String> data, final int timeout);

    public SResponse put(Url url, Map data);
更详细的代码可以参看我以前的开源项目里的实现:HttpTransportService.java
然而这种使用比较原始,不直观。
大致使用方法如下:
SResponse  response = HttpTransportService.get(new Url("http://[dns]/ws/v1/cluster/apps/"+appId))
if(response.getStatus==200){
  //parse result like JSONObject.parse(response.getContent())
}else{
//blabla
}
所以一般如果提供了HTTP 接口的项目都会给你一个SDK,方便你做调用,帮你屏蔽掉HTTP的细节。
总体而言你有如下几种选择:
  1. 直接使用上面的封装
  2. 使用第三方SDK
  3. 自己再做一些封装,转化为普通的方法调用
第三种方案一般会是这样:
def app(appName:String):YarnApplication = {
// 实现http接口请求解析逻辑
}
虽然麻烦了些,但是调用者会比较幸福。

其实可以更简单

只要三步,就可以实现第三个方案:
  1. 定义一个接口
  2. 获取这个接口的引用
  3. 尽情使用
定义一个接口:
trait YarnController {

  @At(path = Array("/ws/v1/cluster/apps"), types = Array(RestRequest.Method.GET))
  def apps(@Param("states") states: String): java.util.List[HttpTransportService.SResponse]

  @At(path = Array("/ws/v1/cluster/apps/{appId}"), types = Array(RestRequest.Method.GET))
  def app(@Param("appId") appId: String): java.util.List[HttpTransportService.SResponse]

}
At 注解定义了 path路径以及Action Method。 方法参数决定了传递的参数。
对于同一个http接口,你也可以定义多个方法。比如,
trait YarnController {

  @At(path = Array("/ws/v1/cluster/apps"), types = Array(RestRequest.Method.GET))
  def apps(@Param("states") states: String): java.util.List[HttpTransportService.SResponse]

  @At(path = Array("/ws/v1/cluster/apps"), types = Array(RestRequest.Method.GET))
  def runningApps(@Param("states") states: String="running"): java.util.List[HttpTransportService.SResponse]

}
这样你直接调用runningApps 就可以拿到特定状态的应用,而无需传递参数。如果参数较多,你还可以指定哪些参数不传,哪些传。
接着初始化 YarnController,获得对象的引用,代码如下:
val yarnRestClient: YarnController = 
AggregateRestClient.buildIfPresent[YarnController]
(hostAndPort, 
firstMeetProxyStrategy, 
RestClient.transportService)
  • hostAndPort 是yarn的地址
  • firstMeetProxyStrategy 指定如果后端有多个实例时的访问策略
  • RestClient.transportService 就是我上面的最基础的封装HttpTransportService
理论上后面两个参数可以不用传递
这个时候你就可以直接使用获得的YarnController对象了。具体使用方式如下:
val result:java.util.List[HttpTransportService.SResponse] = yarnRestClient.apps()
上面就是你要做的所有工作,系统自动帮你实现了HTTP调用。

我希望返回结果是一个Bean

前面的放回结果是个List[SResponse]对象。我希望它是个Bean对象。所以我定义一个Bean类:
class YarnApplication(val id: String,
                            var user: String,
                            var name: String,
                            var queue: String,
                            var state: String,
                            var finalStatus: String,
                            var progress: Long,
                            var trackingUI: String,
                            var trackingUrl: String,
                            var diagnostics: String,
                            var clusterId: Long,
                            var applicationType: String,
                            var applicationTags: String,
                            var startedTime: Long,
                            var finishedTime: Long,
                            var elapsedTime: Long,
                            var amContainerLogs: String,
                            var amHostHttpAddress: String,
                            var allocatedMB: Long,
                            var allocatedVCores: Long,
                            var runningContainers: Long,
                            var memorySeconds: Long,
                            var vcoreSeconds: Long,
                            var preemptedResourceMB: Long,
                            var preemptedResourceVCores: Long,
                            var numNonAMContainerPreempted: Long,
                            var numAMContainerPreempted: Long)
然后引入一个隐式转换
import ..... SReponseConvertor ._
val result:Map[Map[String,List[YarnApplication]]]= yarnRestClient.apps().extract[Map[Map[String,List[YarnApplication]]]] 
result("apps")("app") //这样就能拿到List[YarnApplication]
SReponseConvertor 给List[SReponse]对象添加了一个新的extract 方法。当然前提是List[SReponse] 里是一个JSON格式的数据。
因为yarn的接口返回的格式比较诡异,嵌套了两层,第一层是apps,第二层是app,第三层才是具体的List对象。所以有了上面的复杂形态。那我如何简化呢?每次调用都这么搞,太复杂了。
那么自己实现一个隐式转换就行了,定义一个YarnControllerE类,
object YarnControllerE {
  implicit def mapSResponseToObject(response: java.util.List[HttpTransportService.SResponse]): SResponseEnhance = {
    new SResponseEnhance(response)
  }
}

import scala.collection.JavaConversions._

class SResponseEnhance(x: java.util.List[HttpTransportService.SResponse]) {

  private def extract[T](res: String)(implicit manifest: Manifest[T]): T = {
    if (x == null || x.isEmpty || x(0).getStatus != 200) {
      return null.asInstanceOf[T]
    }
    implicit val formats = SJSon.DefaultFormats
    SJSon.parse(res).extract[T]
  }


  def list(): List[YarnApplication] = {
    val item = extract[Map[String, Map[String, List[YarnApplication]]]](x(0).getContent)
    return item("apps")("app")
  }
现在你可以很帅气这样调用了:
import ..... YarnControllerE ._
val result: List[YarnApplication] = yarnRestClient.apps().list
这样我们就可以像RPC一样访问一个HTTP接口了。

背后的机制

核心代码其实是这个:
val yarnRestClient: YarnController = 
AggregateRestClient.buildIfPresent[YarnController]
(hostAndPort, 
firstMeetProxyStrategy, 
RestClient.transportService)
AggregateRestClient 会帮你把YarnController 自动实现了。实现的机制很简单就是 JDK 的 Proxy机制。具体源码可以参看: AggrateRestClient.scala 以及 RestClientProxy.java
目录
相关文章
|
1天前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。HashSet基于哈希表实现,提供高效的元素操作;TreeSet则通过红黑树实现元素的自然排序,适合需要有序访问的场景。本文通过示例代码详细介绍了两者的特性和应用场景。
15 6
|
1天前
|
存储 Java 数据处理
Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。
【10月更文挑战第16天】Java Set:无序之美,不重复之魅!Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。通过 hashCode() 和 equals() 方法实现唯一性,适用于需要唯一性约束的数据处理。示例代码展示了如何使用 HashSet 添加和遍历元素,体现了 Set 的高效性和简洁性。
12 4
|
3天前
|
存储 Java 数据处理
Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。
Java Set:无序之美,不重复之魅!Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。它通过 hashCode() 和 equals() 方法确保元素唯一性,适用于需要唯一性约束的数据处理。示例代码展示了如何使用 HashSet 实现这一特性。
12 5
|
1天前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
7 2
|
3天前
|
存储 Java 数据处理
在Java集合框架中,Set接口以其独特的“不重复”特性脱颖而出
【10月更文挑战第14天】在Java集合框架中,Set接口以其独特的“不重复”特性脱颖而出。本文通过两个案例展示了Set的实用性和高效性:快速去重和高效查找。通过将列表转换为HashSet,可以轻松实现去重;而Set的contains方法则提供了快速的元素查找功能。这些特性使Set成为处理大量数据时的利器。
12 4
|
3天前
|
Java
Java Set 是一个不包含重复元素的集合接口,确保每个元素在集合中都是唯一的
【10月更文挑战第14天】Java Set 是一个不包含重复元素的集合接口,确保每个元素在集合中都是唯一的。本文介绍了 Set 的独特特性和两个常用实现类:基于哈希表的 HashSet 和基于红黑树的 TreeSet。通过示例代码展示了它们如何高效地处理唯一性约束的数据。
15 3
|
3天前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其独特的“不重复性”要求,彻底改变了处理唯一性约束数据的方式。
【10月更文挑战第14天】从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其独特的“不重复性”要求,彻底改变了处理唯一性约束数据的方式。本文深入探讨Set的核心理念,并通过示例代码展示了HashSet和TreeSet的特点和应用场景。
11 2
|
3天前
|
存储 Java 索引
Java 中集合框架的常见接口和类
【10月更文挑战第13天】这些只是集合框架中的一部分常见接口和类,还有其他一些如 Queue、Deque 等接口以及相关的实现类。理解和掌握这些集合的特点和用法对于高效编程非常重要。
|
4天前
|
存储 Java 数据处理
Java中的Set接口以其独特的“不重复”特性,在集合框架中占据重要地位。
【10月更文挑战第13天】Java中的Set接口以其独特的“不重复”特性,在集合框架中占据重要地位。本文通过两个案例展示了Set的实用性和高效性:快速去重和高效查找。通过将列表转换为HashSet,可以轻松实现去重;而Set的contains方法则提供了高效的元素查找功能。这些特性使Set在处理大量数据时表现出色,值得我们在日常编程中充分利用。
18 3
|
7天前
|
Java 数据处理 开发者
Java Http 接口对接太繁琐?试试 UniHttp 框架~
【10月更文挑战第10天】在企业级项目开发中,HTTP接口对接是一项常见且重要的任务。传统的编程式HTTP客户端(如HttpClient、Okhttp)虽然功能强大,但往往需要编写大量冗长且复杂的代码,这对于项目的可维护性和可读性都是一个挑战。幸运的是,UniHttp框架的出现为这一问题提供了优雅的解决方案。
13 0