百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程文章 > 正文

Feign : 优雅的调用 API

qiyuwang 2024-11-26 08:02 8 浏览 0 评论


前言

在项目开发中,系统间会存在接口调用,在微服务架构中可以使用 Dubbo 来实现服务间的 RPC 调用,也可以使用 Spring Could 完成 API 调用,这些框架屏蔽了 API 调用中的像服务建连、通信协议、序列化等等细节,让开发者可以很轻松发起一个接口调用,但是当需要调用一个外部系统时,由于通信协议、技术框架、网络隔离、安全性等因素,一般会采用原生 HTTP 协议进行接口调用

URLConnection

在 Java 中,JDK 提供了 HTTP 的工具类 URLConnection,通过 URLConnection 可以发起 HTTP 请求,如:

HttpURLConnection connection = null;
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
try {
    connection = (HttpURLConnection)(new URL("http://www.api.com/get")).openConnection();
    connection.connect();
    // 读取接口返回内容
    inputStream = connection.getInputStream();
    inputStreamReader = new InputStreamReader(inputStream);
    bufferedReader = new BufferedReader(inputStreamReader);
    String line = bufferedReader.readLine();
    StringBuffer lines = new StringBuffer();
    while(line != null) {
        line = bufferedReader.readLine();
        lines.append(line);
    }
   
    // 反序列化成 json
    JSONObject jsonObject = JSON.parseObject(EntityUtils.toString(response.getEntity()));
    // TODO
    // jsonObject.get('xx')
    // ……
    
} catch(Exception e) {
    e.printStackTrace();
} finally {
    connection.disconnect();
    if(inputStream != null) inputStream.close();
    if(inputStreamReader != null) inputStreamReader.close();
    if(bufferedReader != null) bufferedReader.close();
}

在不借助任何框架的前提下使用 JDK 自带的类库来发起 HTTP 调用

HttpClient

在实际项目中一般不会使用 JDK 自带的 URLConnection,原因是性能不高,没有连接池,代码也很臃肿,所以一般会使用 HttpClient 来代替 URLConnection,这也是大家比较常见的使用方式,如:

SchemeRegistry schreg = new SchemeRegistry();
schreg.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
PoolingClientConnectionManager conMgr = new PoolingClientConnectionManager(schreg);
conMgr.setMaxTotal(200);// 连接池最大数
conMgr.setDefaultMaxPerRoute(conMgr.getMaxTotal());

// 发起 API 调用
DefaultHttpClient defaultHttpClient = new DefaultHttpClient(conMgr);
HttpGet get = new HttpGet("http://www.api.com/get");
CloseableHttpResponse response = defaultHttpClient.execute(get);

// 反序列化成 json
JSONObject jsonObject = JSON.parseObject(EntityUtils.toString(response.getEntity()));
// TODO
// jsonObject.get('xx')
// ……

EntityUtils.consume(response.getEntity());

一般来说 HttpClient 可以满足日常的开发了,如果是使用 json 作为序列化协议的话,则需要在调用接口和获取接口返回值时分别进行序列化和反序列化操作,这样看似没有问题,但假如当系统中需要调用100个不同的 HTTP 接口,每个接口都需要处理序列化、反序列化以及上面的 HttpClient 相关的代码,代码可读性就非常差,系统的可维护性就低,当然你可以封装 HttpClient 的相关方法,对序列化、反序列化进行泛化封装,提高可读性,此外也有另一种更好的处理方法,使用 Feign 框架,像调用本地方法一样调用 API。

Feign

引入依赖

<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-core</artifactId>
  <version>10.10.1</version>
</dependency>

使用 Feign 调用接口跟调用 RPC 一样,都是先定义 API 的接口

interface API {
    @RequestLine("GET /get")
    @Headers("Content-Type: application/json")
    Response get(Request request);
}

API 接口声明了一个 get 方法,接口的地址为 /get ,同时在 header 头中加入了 Content-Type,Request 作为参数是 get 接口的参数,Response 作为返回值是 get 接口的返回值,Feign 会自动将 Request 对象序列化成 /get 的参数,同时把接口返回值序列化成 Response 对象。

定义好接口后,就可以通过 Feign 来生成 "http://www.api.com" 接口的代理类

API api = Feign.builder()
                .decoder(new JacksonDecoder())
                .encoder(new JacksonEncoder())
                .client(new ApacheHttpClient(HttpClientBuilder.create().setMaxConnPerRoute(100).setMaxConnTotal(500).build()))
                .logger(new Slf4jLogger())
                .addCapability(new Metrics5Capability())
                .retryer(Retryer.NEVER_RETRY)
                .target(Oudianyun.class, "http://www.api.com");

通过 Feign.builder() 为每个域名生成一个 API 代理类,其中序列化协议使用了 json;HTTP 框架使用了 Apache HttpClient,在 Feign 内部最终还是通过 HttpClient 来发送 HTTP 请求,也可以只用 OkHttp;日志使用了 Slf4j

通过 Feign.builder 得到 API 的代理类后,就可以发起接口调用了

// 构建参数
Request request = new Request();
request.setId("abc");
// 调用 get 接口
Response response = api.get(request);

发现没有,使用 Feign 后根据不用关心 HTTP 请求中需要处理的序列化、请求头等硬编码,这些都是 Feign 帮我们实现了,使用时只需要根据不同的域名通过 Feign 来生成代理类,然后跟调用本地方法一样直接调用接口

上面完成简单的 Feign 的使用,在实际开发中这还是远不够的,上面的 API 对象是通过硬编码的方式来创建的,而实际项目中对象一般都是交给 Spring 来管理的,因此最佳的方式是把 Feign 和 Spring 整合

Spring Cloud Feign

Spring Cloud 整合了 Feign,并对其进行改造并将整合内容放到了 spring-cloud-openfeign 模块中,相比原生的 Feign 来说 Spring 容器管理了所有的对象,并对其进行了增强,加入了 fallback机制、负载均衡等特性。

引入依赖

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
  <version>2.2.6.RELEASE</version>
</dependency>

跟 Feign 一样,Spring Cloud Feign 也是先定义 API 的接口

@FeignClient(value = "SpringCloudAPI", url ="http://www.api.com", fallback = SpringCloudAPIFallback.class)
interface SpringCloudAPI {
    
    @GetMapping("/get")
    Response get(Request request);
    
}

使用 @FeignClient 注解来声明 url 以及 fallback 降级处理类,当接口出现异常时会调用 SpringCloudAPIFallback 类中的降级方法,降级类如下:

@Component
class SpringCloudAPIFallback implements SpringCloudAPI{
    public Response get(Request request) {
        return "接口出现异常,使用 mock 数据";
    }
}

通过在 Spring Boot 中新增 @EnableFeignClients 开启 Spring Cloud Feign 功能

@SpringBootApplication(scanBasePackages = "feign")
@EnableFeignClients(basePackages = "feign")
public class SpringCloudFeignDemo {
    public static void main(String[] args) {
        new SpringApplication(SpringCloudFeignDemo.class).run(args);
    }
}

Spring Boot 中启用 Feign HttpClient 的参数配置如下:

feign.httpclient.enabled=true
feign.httpclient.connection-timeout=2000
feign.httpclient.connection-timer-repeat=3000
feign.httpclient.max-connections=500
feign.httpclient.max-connections-per-route=500
feign.httpclient.time-to-live=900
feign.httpclient.time-to-live-unit=SECONDS

Spring Could Feign 的 Fallback 是依赖 hystrix 来实现降级的,由于 hystrix 已经不维护了,因此推荐使用 Alibaba 的 Sentinel 来代替 hystrix,加入以下配置启用 Sentinel

引入依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.2.3.RELEASE</version>
</dependency>

开启配置

feign.sentinel.enabled=true

相关推荐

PayPal严重漏洞可通过不安全的JAVA反序列化对象

在2015年12月,我在PayPal商业网站(manager.paypal.com)中发现了一个严重的漏洞,这个漏洞的存在,使得我可以通过不安全的JAVA反序列化对象,在PayPal的网站服务器上远程...

提醒:Apache Dubbo存在反序列化漏洞

背景:近日监测到ApacheDubbo存在反序列化漏洞(CVE-2019-17564),此漏洞可导致远程代码执行。ApacheDubbo是一款应用广泛的高性能轻量级的JavaRPC分布式服务框架...

【预警通报】关于WebLogicT3存在反序列化高危漏洞的预警通报

近日,我中心技术支撑单位监测到WebLogicT3存在反序列化0day高危漏洞,攻击者可利用T3协议进行反序列化漏洞实现远程代码执行。...

Apache dubbo 反序列化漏洞(CVE-2023-23638)分析及利用探索

在对Apachedubbo的CVE-2023-23638漏洞分析的过程中,通过对师傅们对这个漏洞的学习和整理,再结合了一些新学的技巧运用,从而把这个漏洞的利用向前推了一步。整个过程中的研究思路以及...

案例|WebLogic反序列化漏洞攻击分析

目前网络攻击种类越来越多,黑客的攻击手段也变得层出不穷,常规的防护手段通常是对特征进行识别,一旦黑客进行绕过等操作,安全设备很难发现及防御。通过科来网络回溯分析系统可以全景还原各类异常网络行为,记录所...

【预警通报】关于ApacheOFBizRMI反序列化远程代码 执行高危漏洞的预警通报

近日,我中心技术支撑单位监测发现ApacheOFBiz官方发布安全更新,修复了一处远程代码执行漏洞。成功利用该漏洞的攻击者可造成任意代码执行,控制服务器。该漏洞编号:CVE-2021-26295,安...

关于OracleWebLogic wls9-async组件存在反序列化远程命令执行高危漏洞的预警通报

近日,国家信息安全漏洞共享平台(CNVD)公布了OracleWebLogicwls9-async反序列化远程命令执行漏洞。攻击者利用该漏洞,可在未授权的情况下远程执行命令。该漏洞安全级别为“高危”。现...

Rust语言从入门到精通系列 - Serde序列化/反序列化模块入门指北

Serde是一个用于序列化和反序列化Rust数据结构的库。它支持JSON、BSON、YAML等多种格式,并且可以自定义序列化和反序列化方式。Serde的特点是代码简洁、易于使用、性能高效。...

Java反序列化漏洞详解(java反序列化漏洞利用)

Java反序列化漏洞从爆出到现在快2个月了,已有白帽子实现了jenkins,weblogic,jboss等的代码执行利用工具。本文对于Java反序列化的漏洞简述后,并对于Java反序列化的Poc进行详...

关于Oracle WebLogic Server存在反序列化远程代码执行漏洞的安全公告

安全公告编号:CNTA-2018-00222018年7月18日,国家信息安全漏洞共享平台(CNVD)收录了OracleWebLogicServer反序列化远程代码执行漏洞(CNVD-2018-13...

CVE-2020-9484 Apache Tomcat反序列化漏洞浅析

本文是i春秋论坛作家「Ybwh」表哥原创的一篇技术文章,浅析CVE-2020-9484ApacheTomcat反序列化漏洞。01漏洞概述这次是因为错误配置和org.apache.catalina....

告别脚本小子系列丨JAVA安全(8)——反序列化利用链(下)

0x01前言...

关于WebLogic反序列化高危漏洞的紧急预警通报

近日,WebLogic官方发布WebLogic反序列化漏洞的紧急预警通告,利用该漏洞可造成远程代码执行并直接控制Weblogic服务器,危害极大。该漏洞编号为:CVE-2019-2890,安全级别为“...

高危!Fastjson反序列化漏洞风险通告

漏洞描述...

学习Vulhub的Java RMI Registry 反序列化漏洞

这个实验,我们先通过dnslog演示命令执行,然后通过反弹shell获得root权限。JavaRemoteMethodInvocation用于在Java中进行远程调用。RMI存在远程bind的...

取消回复欢迎 发表评论: