前言

看到标题大家可能会有点疑惑吧:OpenFeign 不是挺好用的吗?尤其是微服务之间的远程调用,平时用的也挺习惯的,为啥要替换呢?

背景和原因是这样的:

  1. 部门/团队在安全性上有所考虑,即尽可能地减少/消除引入外部依赖,尽量只使用自研依赖、apache、Spring等必须的开源依赖;
  2. 而 OpenFeign 的使用则是引入了 Spring Cloud 依赖(不在安全要求范围内),所以需要考虑替换;
  3. 为以后团队的项目上 Spring 6做铺垫,Spring 6 会有 Spring 内置的 Http Interface 发起远程服务调用。

下面将从介绍 OpenFeign、常见的 Http API 以及重点介绍 Spring 自带的 RestTemplate Http 模板这3个方面展开。


一、何为OpenFeign

OpenFeign 是 Spring Cloud 在 Feign 的基础上支持了 SpringMVC 的注解,如 @RequesMapping 等,其底层默认使用的是 URLConnection 实现。

OpenFeign 的 @FeignClient 注解可以解析 SpringMVC 的@RequestMapping 注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

1.1@FeignClient注解

只要是使用 OpenFeign 那么这个注解是一定会使用到的,该注解的主要属性如下:

  • url:可以手动指定 @FeignClient 调用的远程服务地址,如果同时声明 url 和 name 则以 url 为准,此时 name 仅作为该 FeignClient 的名称而已;
  • name:指定当前 FeignClient 的名称,如果项目使用了 Ribbon,那么 name 属性会作为微服务的名称,用于服务的发现
  • value:实际上和 name 是用一个属性,因为这两个属性互相使用了别名,使用的时候两者选其一即可;
  • path:定义当前 FeignClient 的统一前缀,即表示所有调用的远程服务都会走这个 path 声明的 http 前缀;
  • configuration:Feign 的配置类,可以自定义 Feign 的 Encoder、Decoder、LogLevel、Contract 等;
  • fallback:定义容错的处理类,当调用远程接口失败或超时,会调用对应接口的容错逻辑,fallback 指定的类必须实现 @FeignClient 标记的接口。

简单示例如下:

@FeignClient(url = "https://xxx.abcdef.com", name = "SubmitTaskClient",
configuration = OpenFeignFormConfig.class, fallback = HystrixFallbackConfig.class)
public interface SubmitTaskClient { /**
* 调用远程接口实现,入参为 json 字符串
* @param paramJsonStr
* @param header
* @return
*/
@PostMapping
String submitNormalTask(@RequestBody String paramJsonStr, @RequestHeader Map<String, String> header); /**
* 调用远程接口实现,入参为 map 的表单形式
* @param map
* @return
*/
@PostMapping(value = "/task/create", headers = {"content-type=application/x-www-form-urlencoded"})
String submitTransTask(Map<String, ?> map); }

1.2注意事项

在远程服务调用一般存在两种情况:

  1. 远程服务在注册中心

    如果远程服务的提供方已经注册到注册中心,那么 name 或者 value 的值为:注册中心的服务名称,且必须为所有客户端指定一个 name 或者 value。

    @FeignClient(name = "SubmitTaskService", configuration = OpenFeignFormConfig.class, fallback = HystrixFallbackConfig.class)
  2. 单独的远程 http 接口

    此处 name 的值为当前 feignClient 客户端的名称,指定的 url 则为远程服务的地址。

    @FeignClient(url = "https://xxx.abcdef.com", name = "SubmitTaskClient", configuration = OpenFeignFormConfig.class)

    以上两种方式都能正常进行远程服务调用。name 可以为注册中心的服务名称,同时有 url 属性时,name 就与注册中心服务名称无关。


二、常见的Http API

OpenFeign 本质上还是使用 http 请求完成服务的调用,其实使用以下的这些 Http API 经过适当的改造后,也可以达到效果。

2.1Apache

在后端领域,出现比较早而且使用仍然很广泛的 HTTP 客户端框架非 Apache HttpClien 莫属了,目前大量项目和公司仍在采用该框架。

Apache HttpClient 有着不错的性能、丰富的功能以及强大的自定义实现等特色。但是随着技术的发展和设计理念的改变,Apache HttpClient 显的有些落伍了。

个人认为其最不受欢迎的点主要在于 API 的设计过于臃肿,大量的配置需要手动声明,当见过了更多好的的 Http API 后你可能就会不太想继续用了。当然公司框架正在使用 Apache HttpClient 的情况下也无可厚非,虽然复杂点,但用还是可以用的。

引入 pom 依赖:

        <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>

POST 请求示例如下:

    public String apacheHttpClientPost(String url, String params) throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Content-Type", "application/json");
String charSet = "UTF-8";
StringEntity entity = new StringEntity(params, charSet);
httpPost.setEntity(entity);
CloseableHttpResponse response = null;
try {
response = httpclient.execute(httpPost);
StatusLine status = response.getStatusLine();
int state = status.getStatusCode();
if (state == 200) {
HttpEntity responseEntity = response.getEntity();
return EntityUtils.toString(responseEntity);
}
} finally {
if (response != null) {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}

2.2Okhttp

OkHttp3 是 OkHttp 发展到版本3.0之后的名字。在 maven 中央仓库搜索 okhttp,可以看到 3.0 之后的版本统一称为 OkHttp3。

OKHttp3 是一个当前主流的网络请求的开源框架,由 Square 公司开发,目标是用于替代 HttpUrlConnection 和 Apache HttpClient。

        <!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.1</version>
</dependency>

POST 请求示例如下:

	public String okHttpPostMethod(String url,String body,  OkHttpClient okHttpClient) throws IOException {
MediaType JSON_TYPE = MediaType.parse("application/json");
Request request = new Request.Builder()
.url(url).post(RequestBody.create(JSON_TYPE, body)).addHeader("Content-Type", "application/json")
.build();
Response response = null;
try {
response = okHttpClient.newCall(request).execute();
} catch (Exception e) {
e.printStackTrace();
}
assert response != null;
if (response.isSuccessful()) {
return response.body() == null ? "" : response.body().string();
}
return null;
}

对于需要单独处理 POST、GET 等请求的情况来说,OkHttp3 是很适合的。

但是对于一些通用请求,比如在一个通用方法的参数里只需要传入 Method 枚举(POST、GET 等)就可以实现对应类型的请求,Hutool 和 RestTemplate 可能更为合适。

2.3Hutool

Hutool 中的工具方法来自每个用户的精雕细琢,它涵盖了 Java 开发底层代码中的方方面面,是国内 Java 开发工具类库的集大成者,很多公司的很多项目都在用。

其中 Hutool 的 http 部分是基于 HttpUrlConnection 的 Http 客户端封装,大致发起调用的步骤:首先构建一个http请求,包括请求的地址、请求方式、请求头、请求参数等信息,然后执行请求返回一个 http 响应类,最后通过这个相应类可以获取响应的主体、是否请求成功等信息。

但遗憾的是,团队里也有比较明确的安全规定:不允许在项目中引入 Hutool 依赖包。

引入 pom 依赖:

        <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.8</version>
</dependency>

创建通用请求的示例如下:

    public String huToolMethod(String url, HttpMethod httpMethod, RequestBody body) {
Map<String, String> headers = new HashMap<>();
headers.put(HttpHeaders.CONTENT_TYPE, "application/json;charset=utf-8");
// 创建通用请求, 可以涵盖所有常见的 HTTP 方法, 同时放入 url
HttpRequest request = HttpUtil.createRequest(Method.valueOf(httpMethod.name()), url);
// 放入请求的 header 和 body
HttpResponse response = request.addHeaders(headers).body(JSON.toJSONString(body)).execute();
return response.body();
}

三、RestTemplate

RestTemplate 是 Spring 框架用来访问 RESTFUL 服务的客户端模板类,主要功能有:

1、发起 HTTP 请求,包括 RESTful 风格的 GET,POST,PUT,DELETE 等常见方法;

2、自动将响应结果映射为 Java 对象,不用手动解析 JSON 或 XML。

3、自定义设置请求头、消息转码、Cookie 等功能。

4、对不同的输入/输出类型提供对应的方法,如字符串、对象、多部分等。

5、同时还支持远程调用,不受同源策略限制。

引入 pom 依赖:

        <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

配置类:

@Configuration
public class RestTemplateConfig { @Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory){
return new RestTemplate(factory);
} @Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(10000);
factory.setReadTimeout(10000);
return factory;
}
}

3.1详解.execute()

.execute() 是 RestTemplate 中最常见的关于执行 HTTP 请求的方法,它允许开发人员高度定制 HTTP 请求。

先来给一段示例:

    public String restTemplateExecuteMethod(String url, String token, Object body, HttpMethodName method){
HttpHeaders httpHeaders = new HttpHeaders();
// headers:HttpHeaders 类型,包括所有头信息
httpHeaders.add("Authorization", token);
httpHeaders.add("Content-Type", "application/json;charset=utf-8");
// body:请求体,可以是任何对象,也可以是 null
HttpEntity<Object> httpEntity = new HttpEntity<>(JSON.toJSONString(body), httpHeaders);
RequestCallback requestCallback = restTemplate.httpEntityCallback(httpEntity, Object.class);
ResponseExtractor<ResponseEntity<Object>> responseExtractor = restTemplate.responseEntityExtractor(Object.class);
// 发送请求,method.name() 表示传入的方法,包括 GET、POST、DELETE 等
ResponseEntity<Object> entity = restTemplate.execute(url, HttpMethod.valueOf(method.name()), requestCallback, responseExtractor);
// 直接返回 body
Assert.notNull(entity, "返回体为空!");
log.info("---返回的内容:{}---", JSON.toJSONString(entity.getBody()));
return JSON.toJSONString(entity.getBody());
}

下面是一些对象的介绍:

  • HttpEntity 对象

    它主要有两个作用:

    1、表示 HTTP 请求:当表示 HTTP 请求时,HttpEntity 有两个主要组成部分:请求头和请求体。

    2、表示 HTTP 响应:当表示 HTTP 响应时,有三个部分:状态码、响应头和响应体。

    其中的参数:

    • headers:HttpHeaders 类型,包括所有头信息;
    • body:请求或响应体,可以是任何对象,也可以是null;
    • statusCode:HttpStatus 类型,只有在表示响应时才有效。
  • RequestCallback 对象

    RequestCallback 是 RestTemplate中用来定制HTTP请求的一个接口,可以设置请求头、请求体、查询字符串参数。

    Callback接口只有一个方法:

    void doWithRequest(ClientHttpRequest request) throws IOException

四、文章小结

文章的最后,我选择了 okhttp3 和 RestTemplate 来进行 OpenFeign 的替换工作:okhttp3 处理单个 POST/GET 等请求,使用.execute() 处理通用 HTTP 请求。

那么如何使用 Http API 代替 OpenFeign 进行远程服务调用的分享到这里就结束了,如有不足和错误,还请大家指正。或者你有其它想说的,也欢迎大家在评论区交流!

【解决方案】如何使用 Http API 代替 OpenFeign 进行远程服务调用的更多相关文章

  1. 全国天气预报信息数据 API 功能简介与代码调用实战视频

    此文章对开放数据接口 API 之「全国天气预报信息数据 API」进行了功能介绍.使用场景介绍以及调用方法的说明,供用户在使用数据接口时参考之用,并对实战开发进行了视频演示. 1. 产品功能 接口开放了 ...

  2. Javacard 解释器怎样在API类库中找到源文件调用的类、方法或者静态域?

    申明:本篇非本人原创,是在阅读各种论文文献之后,对论文文献的一种梳理. 主要参考文献为: ------------------------------------------------------- ...

  3. 微信的API都是通过https调用实现的,分为post方法调用和get方法调用。不需要上传数据的采用get方法(使用IntraWeb开发)

    首先需要明确的是,微信的API都是通过https调用实现的,分为post方法调用和get方法调用.不需要上传数据的采用get方法(例如获取AccessToken),而需要向微信服务器提交数据的采用po ...

  4. 依图语音API的C#封装以及调用进行语音转写的处理

    对于语音识别,一般有实时语音识别和语音文件的识别处理等方式,如在会议.培训等场景中,可以对录制的文件进行文字的转录,对于转录文字的成功率来说,如果能够转换90%以上的正确语音内容,肯定能减轻很多相关语 ...

  5. .NET混合开发解决方案11 WebView2加载的网页中JS调用C#方法

    系列目录     [已更新最新开发文章,点击查看详细] WebView2控件应用详解系列博客 .NET桌面程序集成Web网页开发的十种解决方案 .NET混合开发解决方案1 WebView2简介 .NE ...

  6. ASP.NET Web API与Owin OAuth:调用与用户相关的Web API

    在前一篇博文中,我们通过以 OAuth 的 Client Credential Grant 授权方式(只验证调用客户端,不验证登录用户)拿到的 Access Token ,成功调用了与用户无关的 We ...

  7. web api 初体验 解决js调用跨域问题

    跨域界定 常见跨域: 同IP不同端口: http:IP:8001/api/user     http:IP:8002/api/user 不同IP不同端口: http://172.28.20.100:8 ...

  8. Chromuim proxy Api 提取代里proxy调用Chrome隐身多窗口 多COOKIE 工具

    Chromuim proxy Api提取proxy调用Chrome隐身 多COOKIES 多窗口工具每一个代理拥有一个独立的窗口和USERDATA 独立COOKIES 伪装UA UA:<scri ...

  9. mvc Web api 如何在控制器中调用

    关于如何调用 mvc Web api 的方法,网上一搜就是一大把,基本都是在前台jq中调用的,但是如何在后台调用呢? 本楼主做了一下测试,仅供参考. 先写一个简单的api,如下:[域1] namesp ...

  10. 在公网上布署Web Api的时候,不能调用,返回404

    在internet上布署web API做的站点时,发现不能调用web api的任何action, 返回404. 经过很多的努力,也找不到原因,环境是win server 2008, IIS 75. n ...

随机推荐

  1. 【UniApp】-uni-app-数据缓存

    前言 好,经过上个章节的介绍完毕之后,给大家补充了一下 uni-app-数据传递的内容 那么补充了 uni-app-数据传递的内容之后,这篇文章来给大家介绍一下 uni-app-数据缓存 搭建项目 首 ...

  2. java通过url得到文件对象(支持http和https)

    文字标题:java通过url得到文件对象(支持http和https) 作者:锅巴 1.场景:通过一个url地址来得到一个文件,此方式就是通过一个url将文件下载到本地的临时文件,直接上代码 /** * ...

  3. 使用gradle的方式进行Springboot3的web开发(微服务版)

    简要: 最近看了很多的Springboot3的项目,但是发现很多都是用maven来进行版本管理的,很少有用gradle来管理的,通过网上查找资料,看视频,终于自己写一个gradle管理的Springb ...

  4. WinRM服务应用及配置说明

    一.什么是winRM服务 1.1.winRM服务介绍 Windows远程管理(WinRM)服务是Windows Server 2003 R2以上版本中一种新式的方便远程管理的服务.通过WinRM服务, ...

  5. Pikachu漏洞靶场 敏感信息泄露

    敏感信息泄露 概述 由于后台人员的疏忽或者不当的设计,导致不应该被前端用户看到的数据被轻易的访问到. 比如: 通过访问url下的目录,可以直接列出目录下的文件列表; 输入错误的url参数后报错信息里面 ...

  6. Git 的底层原理

    前言 ​ 基于 Git 的使用,已经在前文有过相关的介绍,使用 Git 用作日常的开发基本上是足够的.现在,本文将详细介绍一些有关 Git 的实现原理. 底层命令与上层命令 ​ 一般情况下,正常使用的 ...

  7. 使用推测解码 (Speculative Decoding) 使 Whisper 实现 2 倍的推理加速

    Open AI 推出的 Whisper 是一个通用语音转录模型,在各种基准和音频条件下都取得了非常棒的结果.最新的 large-v3 模型登顶了 OpenASR 排行榜,被评为最佳的开源英语语音转录模 ...

  8. 14、Flutter Card卡片组件

    Card是卡片组件块,内容可以由大多数类型的Widget构成,Card具有圆角和阴影,这让它看起来有立 体感. Card实现一个通讯录的卡片 class MyApp2 extends Stateles ...

  9. Spark SQL快速入门

    Spark SQL快速入门 1.概述 spark SQL是Apache用于处理结构化数据的模块.其中包含SQL.DataFrame API.DataSet API,意味着开发人员可以在不同的API之间 ...

  10. 【菜鸟必看】stm32定时器的妙用

    摘要:本文为你带来关于stm32定时器的使用的便利和优势之处. 使用定时器去计算获取一条的时间 一.初步了解定时器 stm32定时器时钟图如下: 定时器2-7:普通定时器定时器1.8:高级定时器 二. ...