在我们平时的工作中,我们经常会遇到要调用内部API或者其他第三方服务的API,在遇到Fegin之前我们基本会使用以下几种方式。
1.HttpURLConnection
简介:
URLConnection是个抽象类,它有两个直接子类分别是HttpURLConnection和JarURLConnection。
另外一个重要的类是URL,通常URL可以通过传给构造器一个String类型的参数来生成一个指向特定地址的URL实例。 每个 HttpURLConnection 实例都可用于生成单个请求,但是其他实例可以透明地共享连接到 HTTP 服务器的基础网络。
请求后在 HttpURLConnection 的 InputStream 或 OutputStream 上调用 close() 方法可以释放与此实例关联的网络资源,但对共享的持久连接没有任何影响。
如果在调用 disconnect() 时持久连接空闲,则可能关闭基础套接字。
任何网络连接都需要经过socket才能连接,HttpURLConnection不需要设置socket,所以,HttpURLConnection并不是底层的连接,而是在底层连接上的一个请求。
这就是为什么HttpURLConneciton只是一个抽象类,自身不能被实例化的原因。HttpURLConnection只能通过URL.openConnection()方法创建具体的实例。
虽然底层的网络连接可以被多个HttpURLConnection实例共享,但每一个HttpURLConnection实例只能发送一个请求。
请求结束之后,应该调用HttpURLConnection实例的InputStream或OutputStream的close()方法以释放请求的网络资源,不过这种方式对于持久化连接没用。对于持久化连接,得用disconnect()方法关闭底层连接的socket。
HttpURLConnection请求响应流程:
2.HttpClient
简介:
HttpClient是Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
它是一个HTTP通信库、一个工具包,因此它只提供一个通用浏览器应用程序所期望的功能子集。
HttpClient与浏览器最根本的区别是:HttpClient中没有用户界面,浏览器需要一个渲染引擎来显示页面,并解释用户输入(例如鼠标点击显示页面上的某处之后如何响应、计算如何显示HTML页面、级联样式表和图像、javascript解释器运行嵌入HTML页面或从HTML页面引用的javascript代码、来自用户界面的事件被传递到javascript解释器进行处理等等等等)。
HttpClient只能以编程的方式通过其API用于传输和接受HTTP消息,它对内容也是完全不可知的。
HttpClient还是有很多好的特点(摘自Apache HttpClient官网): 1.基于标准、纯净的java语言。实现了HTTP1.0和HTTP1.1; 2.以可扩展的面向对象的结构实现了HTTP全部的方法(GET, POST等7种方法); 3.支持HTTPS协议; 4.通过HTTP代理建立透明的连接; 5.利用CONNECT方法通过HTTP代理建立隧道的HTTPS连接; 6.Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO/Kerberos认证方案; 7.插件式的自定义认证方案; 8.便携可靠的套接字工厂使它更容易的使用第三方解决方案; 9.连接管理器支持多线程应用;支持设置最大连接数,同时支持设置每个主机的最大连接数,发现并关闭过期的连接; 10.自动处理Set-Cookie中的Cookie; 11.插件式的自定义Cookie策略; 12.Request的输出流可以避免流中内容直接缓冲到socket服务器; 13.Response的输入流可以有效的从socket服务器直接读取相应内容; 14.在HTTP1.0和HTTP1.1中利用KeepAlive保持持久连接; 15.直接获取服务器发送的response code和 headers; 16.设置连接超时的能力; 17.实验性的支持HTTP1.1 response caching; 18.源代码基于Apache License 可免费获取。
Get Demo
public static void main(String[] args) throws IOException { //1.打开浏览器 CloseableHttpClient httpClient = HttpClients.createDefault(); //2.声明get请求 HttpGet httpGet = new HttpGet("http://www.baidu.com/s?wd=java"); //3.发送请求 CloseableHttpResponse response = httpClient.execute(httpGet); //4.判断状态码 if(response.getStatusLine().getStatusCode()==200){ HttpEntity entity = response.getEntity(); //使用工具类EntityUtils,从响应中取出实体表示的内容并转换成字符串 String string = EntityUtils.toString(entity, "utf-8"); System.out.println(string); } //5.关闭资源 response.close(); httpClient.close(); }
Post Demo
public static void main(String[] args) throws IOException { //1.打开浏览器 CloseableHttpClient httpClient = HttpClients.createDefault(); //2.声明get请求 HttpPost httpPost = new HttpPost("https://www.oschina.net/"); //3.开源中国为了安全,防止恶意攻击,在post请求中都限制了浏览器才能访问 httpPost.addHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"); //4.判断状态码 List<NameValuePair> parameters = new ArrayList<NameValuePair>(0); parameters.add(new BasicNameValuePair("scope", "project")); parameters.add(new BasicNameValuePair("q", "java"));
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters,"UTF-8");
httpPost.setEntity(formEntity);
//5.发送请求 CloseableHttpResponse response = httpClient.execute(httpPost);
if(response.getStatusLine().getStatusCode()==200){ HttpEntity entity = response.getEntity(); String string = EntityUtils.toString(entity, "utf-8"); System.out.println(string); } //6.关闭资源 response.close(); httpClient.close(); }
3.OKHttp
给大家推荐一篇文章,个人觉得写得很好。
https://blog.csdn.net/iispring/article/details/51661195?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param
4.RestTemplate
简介:
RestTemplate 是从 Spring3.0 开始支持的一个 HTTP 请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。
RestTemplate 继承自 InterceptingHttpAccessor 并且实现了 RestOperations 接口,其中 RestOperations 接口定义了基本的 RESTful 操作,这些操作在 RestTemplate 中都得到了实现。接下来我们就来看看这些操作方法的使用。
Get Demo
public void doHttpGetTest() throws UnsupportedEncodingException { // -------------------------------> 获取Rest客户端实例 RestTemplate restTemplate = new RestTemplate(); // -------------------------------> 解决(响应数据可能)中文乱码 的问题 List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters(); converterList.remove(1); // 移除原来的转换器 // 设置字符编码为utf-8 HttpMessageConverter<?> converter = new StringHttpMessageConverter(StandardCharsets.UTF_8); converterList.add(1, converter); // 添加新的转换器(注:convert顺序错误会导致失败) restTemplate.setMessageConverters(converterList); // -------------------------------> (选择性设置)请求头信息 // HttpHeaders实现了MultiValueMap接口 HttpHeaders httpHeaders = new HttpHeaders(); // 给请求header中添加一些数据 httpHeaders.add("Java 学习部落", "这是一个学习技术的公众号!"); // -------------------------------> 注:GET请求 创建HttpEntity时,请求体传入null即可 // 请求体的类型任选即可;只要保证 请求体 的类型与HttpEntity类的泛型保持一致即可 String httpBody = null; HttpEntity<String> httpEntity = new HttpEntity<String>(httpBody, httpHeaders); // -------------------------------> URI StringBuffer paramsURL = new StringBuffer("http://127.0.0.1:8080/restTemplate/doHttpGet"); // 字符数据最好encoding一下;这样一来,某些特殊字符才能传过去(如:flag的参数值就是“&”,不encoding的话,传不过去) paramsURL.append("?flag=" + URLEncoder.encode("&", "utf-8")); URI uri = URI.create(paramsURL.toString()); // -------------------------------> 执行请求并返回结果 // 此处的泛型 对应 响应体数据 类型; // 即:这里指定响应体的数据装配为String ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, httpEntity, String.class); // -------------------------------> 响应信息 //响应码,如:401、302、404、500、200等 System.err.println(response.getStatusCodeValue()); Gson gson = new Gson(); // 响应头 System.err.println(gson.toJson(response.getHeaders())); // 响应体 if(response.hasBody()) { System.err.println(response.getBody()); } }
Post Demo
public void doHttpPostTest() throws UnsupportedEncodingException { // 获取Rest客户端实例 RestTemplate restTemplate = new RestTemplate(); // 解决(响应数据可能)中文乱码 的问题 List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters(); converterList.remove(1); // 移除原来的转换器 // 设置字符编码为utf-8 HttpMessageConverter<?> converter = new StringHttpMessageConverter(StandardCharsets.UTF_8); // 添加新的转换器(注:convert顺序错误会导致失败) converterList.add(1, converter); restTemplate.setMessageConverters(converterList); // (选择性设置)请求头信息 // HttpHeaders实现了MultiValueMap接口 HttpHeaders httpHeaders = new HttpHeaders(); // 设置contentType httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8); // 给请求header中添加一些数据 httpHeaders.add("Java 学习部落", "这是一个学习java的公众号 !"); // 将请求头、请求体数据,放入HttpEntity中 // 请求体的类型任选即可;只要保证 请求体 的类型与HttpEntity类的泛型保持一致即可 // 这里手写了一个json串作为请求体 数据 (实际开发时,可使用fastjson、gson等工具将数据转化为json串) String httpBody = "{\"motto\":\"java真强大啊 !\"}"; HttpEntity<String> httpEntity = new HttpEntity<String>(httpBody, httpHeaders); // URI StringBuffer paramsURL = new StringBuffer("http://127.0.0.1:8080/restTemplate/doHttpPost"); // 字符数据最好encoding一下;这样一来,某些特殊字符才能传过去(如:flag的参数值就是“&”,不encoding的话,传不过去) paramsURL.append("?flag=" + URLEncoder.encode("&", "utf-8")); URI uri = URI.create(paramsURL.toString()); // 执行请求并返回结果 // 此处的泛型 对应 响应体数据 类型;即:这里指定响应体的数据装配为String ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.POST, httpEntity, String.class); // 响应信息 //响应码,如:401、302、404、500、200等 System.err.println(response.getStatusCodeValue()); Gson gson = new Gson(); // 响应头 System.err.println(gson.toJson(response.getHeaders())); // 响应体 if(response.hasBody()) { System.err.println(response.getBody()); }}
此处只是简单介绍下RestTemplate的Get请求和Post请求,并未深入探讨RestTemplate,以及实现RestTemplate的其它形式的请求。
因为我已经迫不及待的想和Fegin来一个美丽的邂逅了。
什么是Fegin?
Feign是Netflix开发的声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API。
在Spring Cloud中,使用Feign非常简单:创建一个接口,并在接口上添加一些注解,代码就完成了。Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等。
Spring Cloud对Feign进行了增强,使Feign支持了Spring MVC注解,并整合了Ribbon和Eureka,从而让Feign的使用更加方便。
Spring Cloud Feign是基于Netflix feign实现,整合了Spring Cloud Ribbon和Spring Cloud Hystrix,除了提供这两者的强大功能外,还提供了一种声明式的Web服务客户端定义的方式。
Spring Cloud Feign帮助我们定义和实现依赖服务接口的定义。在Spring Cloud feign的实现下,只需要创建一个接口并用注解方式配置它,即可完成服务提供方的接口绑定,简化了在使用Spring Cloud Ribbon时自行封装服务调用客户端的开发量。
Spring Cloud Feign具备可插拔的注解支持,支持Feign注解、JAX-RS注解和Spring MVC的注解。
Feign实际上是对普通HTTP客户端的一层封装,其目的是降低集成成本、提升可靠性。Feign支持三种HTTP客户端,包括JDK自带的HttpURLConnection、Apache HttpClient和Square OkHttp,默认使用Apache HttpClient。
Fegin有什么作用?
Feign可以把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样。
你不用再自己拼接url,拼接参数等等操作,一切都交给Feign去做。
在这个时间节点上,很多人对这“两种”Feign傻傻分不清楚,不知有何区别和联系,本文将给与告知。首先需要明确:他俩是属于同一个东西,Feign属于Netflix
开源的组件。针对于于差异性,下面分这几个方面展开做出对比
1、GAV坐标差异:
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-core</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
</dependency>
2、官网地址差异:https://github.com/Netflix/feign
和https://github.com/OpenFeign/feign
。不过现在访问https://github.com/Netflix/feign
已经被重定向到了后者上。
3、发版历史:
1.0.0
发布于2013.6,于2016.7月发布其最后一个版本8.18.0
9.0.0
版,于2016.7月发布,然后一直持续发布到现在(未停止)
从以上3个特点其实你可以很清楚的看到两者的区别和联系,简单的理解:Netflix Feign
仅仅只是改名成为了Open Feign
而已,然后Open Feign项目在其基础上继续发展至今。
9.0版本之前它叫Netflix Feign,自9.0版本起它改名叫Open Feign了。
但是请注意,虽然GAV完全变了,但是源码的包名和核心API是没有任何变化的,所以扔具有很好的向下兼容性(并不是100%向下兼容)。
他俩的差异类似于上述描述的差异,也从几个方面说明:
1、GAV坐标差异:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
发版历史:
1.4.7.RELEASE
1.4.0.RELEASE
开始,它的1.4.x的发版轨迹完全同下的1.4.x的发版轨迹
1.4.0.RELEASE
。现在仍持续更新中,当下最新版为2.2.1.RELEASE
说明:1.4.7.RELEASE是整个Spring Cloud1.x关于Feign方面的最终版本,2.x版本还在持续维护更新中
注意:老的spring-cloud-starter-feign 从1.2.0.RELEASE
开始 已放弃Netflix feign而全面使用更新的Open Feign版本,而spring-cloud-starter-openfeign更是和Netflix Feign已经没有关系了。
对于版本,可粗略的理解为:spring-cloud-starter-openfeign
是为Spring Cloud2.x准备的,只不过维持了一段时间的对1.x的兼容。而spring-cloud-starter-feign
是专为Spring Cloud1.x服务。
核心API包名:Spring Cloud的大版本升级具有向下不兼容性,这也体现在了Feign上:
@FeignClient注解:
说明:这里的1.x不仅仅指的feign,还包括openfeign的1.4.x版本哦
1.x版本包名是org.springframework.cloud.netflix.feign.FeignClient
,所在Jar是spring-cloud-netflix-core
2.x版本包名是org.springframework.cloud.openfeign.FeignClient
,所在Jar是spring-cloud-openfeign-core
@EnableFeignClients注解:差异同上
----引自:https://cloud.tencent.com/developer/article/1588509
Fegin 的九大组件
1.注解翻译器 Contract
我们都知道,在 Feign 中可以通过定义 API 接口的方式来调用远程的 Http API,在定义调用 Client 的时候需要增加一些注解来描述这个调用 API 的基本信息,比如请求类型是 GET 还是 POST,请求的 URI 是什么。
Contract 允许用户自定义注解翻译器去解析注解信息。
Contract 决定了哪些注解可以标注在接口/接口方法上是有效的,并且提取出有效的信息,组装成为MethodMetadata元信息。
最典型的应用场景就是在 Spring Cloud 中使用 Feign,我们可以使用 Spring MVC 的注解来定义 Feign 的客户端,就是因为 Spring Cloud OpenFeign 中实现了自己的 SpringMvcContract。
2.Encoder 编码器
将我们的请求信息通过指定的编码方式进行编码到Http请求体中进行传输。
3.QueryMapEncoder 参数查询编码器
QueryMapEncoder 是针对实体类参数查询的编码器,可以基于 QueryMapEncoder 将实体类生成对应的查询参数。
4.Decoder 解码器
Decoder 解码器作用于Response,用于解析Http请求的响应,提取有用信息数据。
Decoder 解码器将HTTP响应feign.Response
解码为指定类型的单一对象。
5.ErrorDecoder 错误解码器
ErrorDecoder 错误解码器是在发生错误、异常情况时使用的解码器,允许你对异常进行特殊处理。
6.Logger 日志记录
Feign自己抽象出来的一个日志记录器,负责 Feign 中记录日志的,可以指定 Logger 的级别以及自定义日志的输出。
7.Client 请求执行组件
Client 是负责 HTTP 请求执行的组件,Feign 将请求信息封装好后会交由 Client 来执行。
默认情况下,服务之间调用使用的HttpURLConnection,没有使用连接池,每次使用都会创建HttpURLConnection连接,效率非常低。
为了通过连接池提高效率,我们可以使用appache httpclient做为连接池。当然了配置OkHttpClient连接池,也是类似的方法。
8.Retryer 重试组件
重试并不是报错以后的重试,而是负载均衡客户端发现远程请求实例不可到达后,去重试请求其他实例。
Feign本身也具备重试能力,在早期的Spring Cloud中,Feign使用的是 feign.Retryer.Default#Default() ,重试5次。 但Feign整合了Ribbon,Ribbon也有重试的能力,此时,就可能会导致行为的混乱。 Spring Cloud意识到了此问题,因此做了改进,将Feign的重试改为 feign.Retryer#NEVER_RETRY ,如需使用Feign的重试,只需使用Ribbon的重试配置即可。 9.RequestInterceptor 请求拦截器 我们可以通过实现RequestInterceptor接口的apply方法,在请求执行前设置一些扩展的参数信息或者是修改请求头信息,因为feign在发送请求之前都会调用该接口的apply方法,所以我们也可以通过实现该接口来记录请求发出去的时间点。
Fegin 的执行过程
Fegin的原生API的使用
public interface GitHub {
@RequestLine("GET /repos/{owner}/{repo}/contributors") List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repository);
class Contributor { String login; int contributions; }}
public class MyApp { public static void main(String[] args) { GitHub github = Feign.builder() .decoder(new GsonDecoder()) .target(GitHub.class, "https://api.github.com");
/* The owner and repository parameters will be used to expand the owner and repo expressions * defined in the RequestLine. * * the resulting uri will be https://api.github.com/repos/OpenFeign/feign/contributors */ github.contributors("OpenFeign", "feign"); }}
这段代码是我从 OpenFeign 的 GitHub 主页上示例代码。
这是一个 GET 请求的示列,定义了一个 GitHub 的接口,接口中定义了一个查询的方法和参数。在方法上有 @RequestLine 注解,定义了请求类型和请求的 URI,URI 中有对应的参数占位符,返回值是集合,集合中是对应的返回结构对象。
我们通过 Feign 的 builder 模式构建了 GitHub 接口对象后,就可以直接通过 GiuHub 接口对象调用里面的 contributors 方法,然后可以得到返回结果。Feign 的这种方式就跟 Dubbo 中的调用方式是一样的,就像调用本地方法一样。
使用原生的 Feign 来调用 API,只需要通过特定的注解来描述调用的 API 信息,这些信息的请求方式可以是 GET 或者 POST 等,请求参数是什么?请求的地址是什么? 把这些信息定义好后就可以直接使用这个定好了的接口来调用对应的远程 API。
Fegin结合SpringCloud使用
1.首先创建一个OpenFeginClient模块,pom.xml代码如下:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
2.在启动类上添加 @EnableFeignClients 启用 Feign
package com.javaer.study.openfeginclient;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;import org.springframework.cloud.openfeign.EnableFeignClients;
/** * @Author 公众号【Java学习部落】 * * 关注公众号获取4000G精品视频资源 * * 更有更多学习Java后端、中间件,大数据、微服务、分布式、大数据等学习干货 **/@SpringBootApplication@EnableFeignClients@EnableEurekaClientpublic class OpenfeginclientApplication {
public static void main(String[] args) { SpringApplication.run(OpenfeginclientApplication.class, args); }
}
server: port: 8888spring: application: name: spring-cloud-feign-openClienteureka: client: service-url: defaultZone: http://localhost:8761/eureka/
package com.javaer.study.openfeginclient;
import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestParam;
/** * 一个可以调用的客户端 * * @author topJavaer * 公众号:【Java学习部落】 * @create 2020 11 10 * @Version 1.0.0 *///@FeignClient 即是指定客户端信息注解,务必声明在接口上面,url手动指定了客户端的接口地址@FeignClient(name = "github-client", url = "https://api.github.com")public interface GitHubApiClient { @RequestMapping(value = "/search/repositories", method = RequestMethod.GET) String searchRepositories(@RequestParam("q") String queryStr);}
5.添加测试代码
package com.javaer.study.openfeginclient;
import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/** * 测试类 * * @author topJavaer * 公众号:【Java学习部落】 * @create 2020 11 10 * @Version 1.0.0 */
@RestControllerpublic class GitHubApiTest { @Resource private GitHubApiClient gitHubApiClient;
@RequestMapping("/search/github/repository") public String searchGithubRepositoryByName(@RequestParam("name") String repositoryName) { return gitHubApiClient.searchRepositories(repositoryName); }}
6.最后我们启动之前再讲述Eureka时编写的Eureka Server,然后启动该模块。
访问:http://localhost:8888/search/github/repository?name=spring-cloud-dubbo
得到如下结果:
上述就是Spring Cloud 使用OpenFegin的基本流程,是不是很简单,看一眼,操作一遍,立马就会了,当然了,如果说想要深入了解,仅仅这样是远远不够的,还是需要大家自己继续去专研,去深入探究。
Fegin的核心注解@FeignClient详解
package org.springframework.cloud.netflix.feign; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor;
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FeignClient { @AliasFor("name") String value() default ""; @Deprecated String serviceId() default ""; @AliasFor("value") String name() default ""; String qualifier() default ""; String url() default ""; boolean decode404() default false; Class<?>[] configuration() default {}; Class<?> fallback() default void.class; Class<?> fallbackFactory() default void.class; String path() default ""; boolean primary() default true; }
name: 指定Feign Client的名称,如果项目使用了 Ribbon,name属性会作为微服务的名称,用于服务发现。
serviceId: 用serviceId做服务发现已经被废弃,所以不推荐使用该配置。
value: 指定Feign Client的serviceId,如果项目使用了 Ribbon,将使用serviceId用于服务发现,但上面可以看到serviceId做服务发现已经被废弃,所以也不推荐使用该配置。
qualifier: 为Feign Client 新增注解@Qualifier
url: 请求地址的绝对URL,或者解析的主机名
decode404: 调用该feign client发生了常见的404错误时,是否调用decoder进行解码异常信息返回,否则抛出FeignException。
fallback: 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback 指定的类必须实现@FeignClient标记的接口。实现的法方法即对应接口的容错处理逻辑。
fallbackFactory: 工厂类,用于生成fallback 类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码。
path: 定义当前FeignClient的所有方法映射加统一前缀。
primary: 是否将此Feign代理标记为一个Primary Bean,默认为ture
Fegin的核心配置
Fegin的配置方式有两种。
一种是Java 代码配置,需要在主程序启动入口用@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.java)来引用配置。
第二种是直接配置文件配置,在application.yml或者application.properties中配置。
我们应该知道的是,如果通过Java代码的方式配置 Feign,然后有通过属性文件的方式配置Feign,属性文件中的Frign的配置会覆盖Java代码的配置。
但是可以配置 feign.client.default-to-properties=fasle来改变Feign配置生效的优先级。
本文主要讲解一些日常常用的配置,通过配置文件。
Spring Cloud Feign 支持请求和响应进行GZIP压缩来提高通信效率。
# 开启GZIP压缩配置 #请求GZIP压缩 feign.compression.request.enable=true #响应GIZP压缩 feign.compression.response.enable=true #压缩支持的mime type feign.compression.request.mime-types=text/xml,application/xml.application/json feign.compression.request.min-request-size=1024 #压缩数据大小的最小值
Feign 为每一个FeignClient都提供了feign.logger实例,可在配置中或者java代码中开启日志。
NONE 表示不输出日志。
BASIC 表示只输出请求方法的 URL 和响应的状态码以及执行的时间。
HEADERS 将 BASIC 信息和请求头信息输出。
FULL 会输出全部完整的请求信息。
logging: level: com.javaer.study: debug
feign: client: config: # 要调用服务的名称 java-master: loggerLevel: full
Fegin的超时配置
Ribbon 超时配置
当系统出现Read time out,说明是 Ribbon 超时了,需要在配置文件中进行控制处理
### Ribbon 配置ribbon: # 连接超时 ConnectTimeout: 2000 # 响应超时 ReadTimeout: 5000
Hystrix 超时配置
Fegin 高版本的 Hystrix 默认是关闭的。需要以下配置开启:
### Feign 配置feign: # 开启断路器(熔断器) hystrix: enabled: true
为了避免超时,我们可以根据业务情况来配置自己的超时时间,此处配置熔断时间为:5000/毫秒。
注意:建议 Ribbon 的超时时间不要大于 Hystrix 的超时时间
### Hystrix 配置hystrix: # 这样将会自动配置一个 Hystrix 并发策略插件的 hook,这个 hook 会将 SecurityContext 从主线程传输到 Hystrix 的命令。 # 因为 Hystrix 不允许注册多个 Hystrix 策略,所以可以声明 HystrixConcurrencyStrategy # 为一个 Spring bean 来实现扩展。Spring Cloud 会在 Spring 的上下文中查找你的实现,并将其包装在自己的插件中。 shareSecurityContext: true command: default: circuitBreaker: # 当在配置时间窗口内达到此数量的失败后,进行短路。默认20个 requestVolumeThreshold: 1 # 触发短路的时间值,当该值设为5000时,则当触发 circuit break 后的5000毫秒内都会拒绝request # 也就是5000毫秒后才会关闭circuit。默认5000 sleepWindowInMilliseconds: 15000 # 强制打开熔断器,如果打开这个开关,那么拒绝所有request,默认false forceOpen: false # 强制关闭熔断器 如果这个开关打开,circuit将一直关闭且忽略,默认false forceClosed: false execution: isolation: thread: # 熔断器超时时间,默认:1000/毫秒 timeoutInMilliseconds: 5000
Fegin的继承特性
Spring Cloud Feign提供了继承特性,所谓的继承特性就是将一些公共操作提取到一个父接口中,从而继承父接口中的操作,减少代码的重复开发,节约开发成本。
1、优点
可以将接口的定义从 Controller 中剥离,同时配合 Maven 私有仓库就可以轻易地实现接口定义的共享,不用再复制粘贴接口进行绑定,而是实现在构建期的接口绑定,从而有效减少服务客户端的绑定配置。
2、缺点
由于接口在构建期间就建立起了依赖,那么接口变动就会对项目构建造成影响,可能服务提供方修改了一个接口定义,那么会直接导致客户端工程的构建失败。
所以,如果开发团队通过此方法来实现接口共享的话,建议在开发评审期间严格遵守面向对象的开闭原则,尽可能地做好前后版本的兼容,防止牵一发而动全身的后果,增加团队不必要的维护工作量。
Fegin重试机制
在 Spring Cloud Feign 中默认实现了请求的重试机制,下面配置作用是:当访问到故障请求的时候,它会再尝试访问一次当前实例(次数由 MaxAutoRetries 配置),如果不行,就换一个实例进行访问,如果还不行,再换一次实例访问(更换次数由 MaxAutoRetriesNextServer 配置),如果依然不行,返回失败信息。
注意:Ribbon 的超时与 Hystrix 的超时是两个概念。为了让上述实现有效,我们需要让 Hystrix 的超时时间大于 Ribbon 的超时时间,否则 Hystrix 命令超时后,该命令直接熔断,重试机制就没有任何意义了。
#断路器的超时时长需要大于Ribbon的超时时间,不然不会触发重试hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
#请求连接超时时间fegin-service.ribbon.ConnectTimeout=500#请求处理的超时时间fegin-service.ribbon.ReadTimeout=1000#对所有请求都进行重试(是否所有操作都重试,若false则仅get请求重试)fegin-service.ribbon.OkToRetryOnAllOperations=true#对当前实例的重试次数fegin-service.ribbon.MaxAutoRetries=1#切换实例的重试次数fegin-service.ribbon.MaxAutoRetriesNextServer=2
本文从面试经常问的几个方面来介绍什么是Fegin。
从Fegin的简介和作用,Fegin九大核心组件,Fegin的执行过程,Fegin的核心注解@FeignClient的详解,Fegin原生Api的使用,Fegin结合Spring Cloud使用,Fegin的常用配置详解,Fegin的超时机制,继承特性以及重试机制都是我们在日常面试中经常遇见的。
也许本文讲的不够深入,但是希望能给大家一个系统学习Fegin的框架,大家根据这个框架在不断的深入直到完全掌握。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。