Spring Cloud-Feign设计原理

article/2025/9/2 10:00:53

什么是Feign?

Feign 的英文表意为“假装,伪装,变形”, 是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求,而不用像Java中通过封装HTTP请求报文的方式直接调用。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,这种请求相对而言比较直观。
Feign被广泛应用在Spring Cloud 的解决方案中,是学习基于Spring Cloud 微服务架构不可或缺的重要组件。
开源项目地址:
https://github.com/OpenFeign/feign

Feign解决了什么问题?

封装了Http调用流程,更适合面向接口化的变成习惯
在服务调用的场景中,我们经常调用基于Http协议的服务,而我们经常使用到的框架可能有HttpURLConnection、Apache HttpComponnets、OkHttp3 、Netty等等,这些框架在基于自身的专注点提供了自身特性。而从角色划分上来看,他们的职能是一致的提供Http调用服务。具体流程如下:

调用方 Client框架 服务方 构造Http请求URL 填写Http请求头信息 填写消息报文信息 发送Http请求 处理请求,返回结 返回报文 提取报文信息,转换成对应的Java bean 根据Bean中 的定义,业务处理 调用方 Client框架 服务方

Feign是如何设计的?

在这里插入图片描述

PHASE 1. 基于面向接口的动态代理方式生成实现类

在使用feign 时,会定义对应的接口类,在接口类上使用Http相关的注解,标识HTTP请求参数信息,如下所示:

interface GitHub {@RequestLine("GET /repos/{owner}/{repo}/contributors")List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
}public static 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");// Fetch and print a list of the contributors to this library.List<Contributor> contributors = github.contributors("OpenFeign", "feign");for (Contributor contributor : contributors) {System.out.println(contributor.login + " (" + contributor.contributions + ")");}}
}

在Feign 底层,通过基于面向接口的动态代理方式生成实现类,将请求调用委托到动态代理实现类,基本原理如下所示:
在这里插入图片描述

 public class ReflectiveFeign extends Feign{///省略部分代码@Overridepublic <T> T newInstance(Target<T> target) {//根据接口类和Contract协议解析方式,解析接口类上的方法和注解,转换成内部的MethodHandler处理方式Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();for (Method method : target.type().getMethods()) {if (method.getDeclaringClass() == Object.class) {continue;} else if(Util.isDefault(method)) {DefaultMethodHandler handler = new DefaultMethodHandler(method);defaultMethodHandlers.add(handler);methodToHandler.put(method, handler);} else {methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));}}InvocationHandler handler = factory.create(target, methodToHandler);// 基于Proxy.newProxyInstance 为接口类创建动态实现,将所有的请求转换给InvocationHandler 处理。T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {defaultMethodHandler.bindTo(proxy);}return proxy;}//省略部分代码

PHASE 2. 根据Contract协议规则,解析接口类的注解信息,解析成内部表现:

在这里插入图片描述

Feign 定义了转换协议,定义如下:

/*** Defines what annotations and values are valid on interfaces.*/
public interface Contract {/*** Called to parse the methods in the class that are linked to HTTP requests.* 传入接口定义,解析成相应的方法内部元数据表示* @param targetType {@link feign.Target#type() type} of the Feign interface.*/// TODO: break this and correct spelling at some pointList<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType);
}
默认Contract 实现

Feign 默认有一套自己的协议规范,规定了一些注解,可以映射成对应的Http请求,如官方的一个例子:

public interface GitHub {@RequestLine("GET /repos/{owner}/{repo}/contributors")List<Contributor> getContributors(@Param("owner") String owner, @Param("repo") String repository);class Contributor {String login;int contributions;}
}

上述的例子中,尝试调用GitHub.getContributors(“foo”,“myrepo”)的的时候,会转换成如下的HTTP请求:

GET /repos/foo/myrepo/contributors
HOST XXXX.XXX.XXX

Feign 默认的协议规范

注解接口Target使用说明
@RequestLine方法上定义HttpMethod 和 UriTemplate. UriTemplate 中使用{} 包裹的表达式,可以通过在方法参数上使用@Param 自动注入
@Param方法参数定义模板变量,模板变量的值可以使用名称的方式使用模板注入解析
@Headers类上或者方法上定义头部模板变量,使用@Param 注解提供参数值的注入。如果该注解添加在接口类上,则所有的请求都会携带对应的Header信息;如果在方法上,则只会添加到对应的方法请求上
@QueryMap方法上定义一个键值对或者 pojo,参数值将会被转换成URL上的 query 字符串上
@HeaderMap方法上定义一个HeaderMap, 与 UrlTemplate 和HeaderTemplate 类型,可以使用@Param 注解提供参数值

具体FeignContract 是如何解析的,不在本文的介绍范围内,详情请参考代码:
https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/Contract.java

基于Spring MVC的协议规范SpringMvcContract:

当前Spring Cloud 微服务解决方案中,为了降低学习成本,采用了Spring MVC的部分注解来完成 请求协议解析,也就是说 ,写客户端请求接口和像写服务端代码一样:客户端和服务端可以通过SDK的方式进行约定,客户端只需要引入服务端发布的SDK API,就可以使用面向接口的编码方式对接服务:
在这里插入图片描述

我们团队内部就是按照这种思路,结合Spring Boot Starter 的特性,定义了服务端starter,
服务消费者在使用的时候,只需要引入Starter,就可以调用服务。这个比较适合平台无关性,接口抽象出来的好处就是可以根据服务调用实现方式自有切换:

  1. 可以基于简单的Http服务调用;
  2. 可以基于Spring Cloud 微服务架构调用;
  3. 可以基于Dubbo SOA服务治理

这种模式比较适合在SaSS混合软件服务的模式下自有切换,根据客户的硬件能力选择合适的方式部署,也可以基于自身的服务集群部署微服务

至于Spring Cloud 是如何实现 协议解析的,可参考代码:
https://github.com/spring-cloud/spring-cloud-openfeign/blob/master/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java

当然,目前的Spring MVC的注解并不是可以完全使用的,有一些注解并不支持,如@GetMapping,@PutMapping 等,仅支持使用@RequestMapping 等,另外注解继承性方面也有些问题;具体限制细节,每个版本能会有些出入,可以参考上述的代码实现,比较简单。

Spring Cloud 没有基于Spring MVC 全部注解来做Feign 客户端注解协议解析,个人认为这个是一个不小的坑。在刚入手Spring Cloud 的时候,就碰到这个问题。后来是深入代码才解决的… 这个应该有人写了增强类来处理,暂且不表,先MARK一下,是一个开源代码练手的好机会。

PHASE 3. 基于 RequestBean,动态生成Request

根据传入的Bean对象和注解信息,从中提取出相应的值,来构造Http Request 对象:
在这里插入图片描述

PHASE 4. 使用Encoder 将Bean转换成 Http报文正文(消息解析和转码逻辑)

Feign 最终会将请求转换成Http 消息发送出去,传入的请求对象最终会解析成消息体,如下所示:
在这里插入图片描述

在接口定义上Feign做的比较简单,抽象出了Encoder 和decoder 接口:

public interface Encoder {/** Type literal for {@code Map<String, ?>}, indicating the object to encode is a form. */Type MAP_STRING_WILDCARD = Util.MAP_STRING_WILDCARD;/*** Converts objects to an appropriate representation in the template.*  将实体对象转换成Http请求的消息正文中* @param object   what to encode as the request body.* @param bodyType the type the object should be encoded as. {@link #MAP_STRING_WILDCARD}*                 indicates form encoding.* @param template the request template to populate.* @throws EncodeException when encoding failed due to a checked exception.*/void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;/*** Default implementation of {@code Encoder}.*/class Default implements Encoder {@Overridepublic void encode(Object object, Type bodyType, RequestTemplate template) {if (bodyType == String.class) {template.body(object.toString());} else if (bodyType == byte[].class) {template.body((byte[]) object, null);} else if (object != null) {throw new EncodeException(format("%s is not a type supported by this encoder.", object.getClass()));}}}
}
public interface Decoder {/*** Decodes an http response into an object corresponding to its {@link* java.lang.reflect.Method#getGenericReturnType() generic return type}. If you need to wrap* exceptions, please do so via {@link DecodeException}.*  从Response 中提取Http消息正文,通过接口类声明的返回类型,消息自动装配* @param response the response to decode * @param type     {@link java.lang.reflect.Method#getGenericReturnType() generic return type} of*                 the method corresponding to this {@code response}.* @return instance of {@code type}* @throws IOException     will be propagated safely to the caller.* @throws DecodeException when decoding failed due to a checked exception besides IOException.* @throws FeignException  when decoding succeeds, but conveys the operation failed.*/Object decode(Response response, Type type) throws IOException, DecodeException, FeignException;/** Default implementation of {@code Decoder}. */public class Default extends StringDecoder {@Overridepublic Object decode(Response response, Type type) throws IOException {if (response.status() == 404) return Util.emptyValueOf(type);if (response.body() == null) return null;if (byte[].class.equals(type)) {return Util.toByteArray(response.body().asInputStream());}return super.decode(response, type);}}
}

目前Feign 有以下实现:

Encoder/ Decoder 实现说明
JacksonEncoder,JacksonDecoder基于 Jackson 格式的持久化转换协议
GsonEncoder,GsonDecoder基于Google GSON 格式的持久化转换协议
SaxEncoder,SaxDecoder基于XML 格式的Sax 库持久化转换协议
JAXBEncoder,JAXBDecoder基于XML 格式的JAXB 库持久化转换协议
ResponseEntityEncoder,ResponseEntityDecoderSpring MVC 基于 ResponseEntity< T > 返回格式的转换协议
SpringEncoder,SpringDecoder基于Spring MVC HttpMessageConverters 一套机制实现的转换协议 ,应用于Spring Cloud 体系中
PHASE 5. 拦截器负责对请求和返回进行装饰处理

在请求转换的过程中,Feign 抽象出来了拦截器接口,用于用户自定义对请求的操作:

public interface RequestInterceptor {/*** 可以在构造RequestTemplate 请求时,增加或者修改Header, Method, Body 等信息* Called for every request. Add data using methods on the supplied {@link RequestTemplate}.*/void apply(RequestTemplate template);
}

比如,如果希望Http消息传递过程中被压缩,可以定义一个请求拦截器:

public class FeignAcceptGzipEncodingInterceptor extends BaseRequestInterceptor {/*** Creates new instance of {@link FeignAcceptGzipEncodingInterceptor}.** @param properties the encoding properties*/protected FeignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) {super(properties);}/*** {@inheritDoc}*/@Overridepublic void apply(RequestTemplate template) {//  在Header 头部添加相应的数据信息addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER, HttpEncoding.GZIP_ENCODING,HttpEncoding.DEFLATE_ENCODING);}
}
PHASE 6. 日志记录

在发送和接收请求的时候,Feign定义了统一的日志门面来输出日志信息 , 并且将日志的输出定义了四个等级:

级别说明
NONE不做任何记录
BASIC只记录输出Http 方法名称、请求URL、返回状态码和执行时间
HEADERS记录输出Http 方法名称、请求URL、返回状态码和执行时间 和 Header 信息
FULL记录Request 和Response的Header,Body和一些请求元数据
public abstract class Logger {protected static String methodTag(String configKey) {return new StringBuilder().append('[').append(configKey.substring(0, configKey.indexOf('('))).append("] ").toString();}/*** Override to log requests and responses using your own implementation. Messages will be http* request and response text.** @param configKey value of {@link Feign#configKey(Class, java.lang.reflect.Method)}* @param format    {@link java.util.Formatter format string}* @param args      arguments applied to {@code format}*/protected abstract void log(String configKey, String format, Object... args);protected void logRequest(String configKey, Level logLevel, Request request) {log(configKey, "---> %s %s HTTP/1.1", request.method(), request.url());if (logLevel.ordinal() >= Level.HEADERS.ordinal()) {for (String field : request.headers().keySet()) {for (String value : valuesOrEmpty(request.headers(), field)) {log(configKey, "%s: %s", field, value);}}int bodyLength = 0;if (request.body() != null) {bodyLength = request.body().length;if (logLevel.ordinal() >= Level.FULL.ordinal()) {StringbodyText =request.charset() != null ? new String(request.body(), request.charset()) : null;log(configKey, ""); // CRLFlog(configKey, "%s", bodyText != null ? bodyText : "Binary data");}}log(configKey, "---> END HTTP (%s-byte body)", bodyLength);}}protected void logRetry(String configKey, Level logLevel) {log(configKey, "---> RETRYING");}protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response,long elapsedTime) throws IOException {String reason = response.reason() != null && logLevel.compareTo(Level.NONE) > 0 ?" " + response.reason() : "";int status = response.status();log(configKey, "<--- HTTP/1.1 %s%s (%sms)", status, reason, elapsedTime);if (logLevel.ordinal() >= Level.HEADERS.ordinal()) {for (String field : response.headers().keySet()) {for (String value : valuesOrEmpty(response.headers(), field)) {log(configKey, "%s: %s", field, value);}}int bodyLength = 0;if (response.body() != null && !(status == 204 || status == 205)) {// HTTP 204 No Content "...response MUST NOT include a message-body"// HTTP 205 Reset Content "...response MUST NOT include an entity"if (logLevel.ordinal() >= Level.FULL.ordinal()) {log(configKey, ""); // CRLF}byte[] bodyData = Util.toByteArray(response.body().asInputStream());bodyLength = bodyData.length;if (logLevel.ordinal() >= Level.FULL.ordinal() && bodyLength > 0) {log(configKey, "%s", decodeOrDefault(bodyData, UTF_8, "Binary data"));}log(configKey, "<--- END HTTP (%s-byte body)", bodyLength);return response.toBuilder().body(bodyData).build();} else {log(configKey, "<--- END HTTP (%s-byte body)", bodyLength);}}return response;}protected IOException logIOException(String configKey, Level logLevel, IOException ioe, long elapsedTime) {log(configKey, "<--- ERROR %s: %s (%sms)", ioe.getClass().getSimpleName(), ioe.getMessage(),elapsedTime);if (logLevel.ordinal() >= Level.FULL.ordinal()) {StringWriter sw = new StringWriter();ioe.printStackTrace(new PrintWriter(sw));log(configKey, sw.toString());log(configKey, "<--- END ERROR");}return ioe;}
PHASE 7 . 基于重试器发送HTTP请求

Feign 内置了一个重试器,当HTTP请求出现IO异常时,Feign会有一个最大尝试次数发送请求,以下是Feign核心
代码逻辑:

final class SynchronousMethodHandler implements MethodHandler {// 省略部分代码@Overridepublic Object invoke(Object[] argv) throws Throwable {//根据输入参数,构造Http 请求。RequestTemplate template = buildTemplateFromArgs.create(argv);// 克隆出一份重试器Retryer retryer = this.retryer.clone();// 尝试最大次数,如果中间有结果,直接返回while (true) {try {return executeAndDecode(template);} catch (RetryableException e) {retryer.continueOrPropagate(e);if (logLevel != Logger.Level.NONE) {logger.logRetry(metadata.configKey(), logLevel);}continue;}}}

重试器有如下几个控制参数:

重试参数说明默认值
period初始重试时间间隔,当请求失败后,重试器将会暂停 初始时间间隔(线程 sleep 的方式)后再开始,避免强刷请求,浪费性能100ms
maxPeriod当请求连续失败时,重试的时间间隔将按照:long interval = (long) (period * Math.pow(1.5, attempt - 1)); 计算,按照等比例方式延长,但是最大间隔时间为 maxPeriod, 设置此值能够避免 重试次数过多的情况下执行周期太长1000ms
maxAttempts最大重试次数5

具体的代码实现可参考:
https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/Retryer.java

PHASE 8. 发送Http请求

Feign 真正发送HTTP请求是委托给 feign.Client 来做的:

public interface Client {/*** Executes a request against its {@link Request#url() url} and returns a response.*  执行Http请求,并返回Response* @param request safe to replay.* @param options options to apply to this request.* @return connected response, {@link Response.Body} is absent or unread.* @throws IOException on a network error connecting to {@link Request#url()}.*/Response execute(Request request, Options options) throws IOException;}

Feign 默认底层通过JDK 的 java.net.HttpURLConnection 实现了feign.Client接口类,在每次发送请求的时候,都会创建新的HttpURLConnection 链接,这也就是为什么默认情况下Feign的性能很差的原因。可以通过拓展该接口,使用Apache HttpClient 或者OkHttp3等基于连接池的高性能Http客户端,我们项目内部使用的就是OkHttp3作为Http 客户端。

如下是Feign 的默认实现,供参考:

public static class Default implements Client {private final SSLSocketFactory sslContextFactory;private final HostnameVerifier hostnameVerifier;/*** Null parameters imply platform defaults.*/public Default(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier) {this.sslContextFactory = sslContextFactory;this.hostnameVerifier = hostnameVerifier;}@Overridepublic Response execute(Request request, Options options) throws IOException {HttpURLConnection connection = convertAndSend(request, options);return convertResponse(connection).toBuilder().request(request).build();}HttpURLConnection convertAndSend(Request request, Options options) throws IOException {final HttpURLConnectionconnection =(HttpURLConnection) new URL(request.url()).openConnection();if (connection instanceof HttpsURLConnection) {HttpsURLConnection sslCon = (HttpsURLConnection) connection;if (sslContextFactory != null) {sslCon.setSSLSocketFactory(sslContextFactory);}if (hostnameVerifier != null) {sslCon.setHostnameVerifier(hostnameVerifier);}}connection.setConnectTimeout(options.connectTimeoutMillis());connection.setReadTimeout(options.readTimeoutMillis());connection.setAllowUserInteraction(false);connection.setInstanceFollowRedirects(true);connection.setRequestMethod(request.method());Collection<String> contentEncodingValues = request.headers().get(CONTENT_ENCODING);booleangzipEncodedRequest =contentEncodingValues != null && contentEncodingValues.contains(ENCODING_GZIP);booleandeflateEncodedRequest =contentEncodingValues != null && contentEncodingValues.contains(ENCODING_DEFLATE);boolean hasAcceptHeader = false;Integer contentLength = null;for (String field : request.headers().keySet()) {if (field.equalsIgnoreCase("Accept")) {hasAcceptHeader = true;}for (String value : request.headers().get(field)) {if (field.equals(CONTENT_LENGTH)) {if (!gzipEncodedRequest && !deflateEncodedRequest) {contentLength = Integer.valueOf(value);connection.addRequestProperty(field, value);}} else {connection.addRequestProperty(field, value);}}}// Some servers choke on the default accept string.if (!hasAcceptHeader) {connection.addRequestProperty("Accept", "*/*");}if (request.body() != null) {if (contentLength != null) {connection.setFixedLengthStreamingMode(contentLength);} else {connection.setChunkedStreamingMode(8196);}connection.setDoOutput(true);OutputStream out = connection.getOutputStream();if (gzipEncodedRequest) {out = new GZIPOutputStream(out);} else if (deflateEncodedRequest) {out = new DeflaterOutputStream(out);}try {out.write(request.body());} finally {try {out.close();} catch (IOException suppressed) { // NOPMD}}}return connection;}Response convertResponse(HttpURLConnection connection) throws IOException {int status = connection.getResponseCode();String reason = connection.getResponseMessage();if (status < 0) {throw new IOException(format("Invalid status(%s) executing %s %s", status,connection.getRequestMethod(), connection.getURL()));}Map<String, Collection<String>> headers = new LinkedHashMap<String, Collection<String>>();for (Map.Entry<String, List<String>> field : connection.getHeaderFields().entrySet()) {// response messageif (field.getKey() != null) {headers.put(field.getKey(), field.getValue());}}Integer length = connection.getContentLength();if (length == -1) {length = null;}InputStream stream;if (status >= 400) {stream = connection.getErrorStream();} else {stream = connection.getInputStream();}return Response.builder().status(status).reason(reason).headers(headers).body(stream, length).build();}}

Feign 的性能怎么样?

Feign 整体框架非常小巧,在处理请求转换和消息解析的过程中,基本上没什么时间消耗。真正影响性能的,是处理Http请求的环节。
如上所述,由于默认情况下,Feign采用的是JDK的HttpURLConnection,所以整体性能并不高,刚开始接触Spring Cloud 的同学,如果没注意这些细节,可能会对Spring Cloud 有很大的偏见。
我们项目内部使用的是OkHttp3 作为连接客户端。
系统的压测方案后续在贴出来,有兴趣的同学可以持续关注~

另外作者已开通微信订阅号,精品文章同步更新,欢迎关注~
亦山札记公众号


http://chatgpt.dhexx.cn/article/Mvgmhqur.shtml

相关文章

再学一下Feign的原理

简介 Feign是Spring Cloud Netflix组件中的一个轻量级Restful的HTTP服务客户端&#xff0c;它简化了服务间调用的方式。 Feign是一个声明式的web service客户端.它的出现使开发web service客户端变得更简单.使用Feign只需要创建一个接口加上对应的注解, 比如FeignClient注解。…

Feign的底层原理

Feign的底层原理 1 EnableFeignClients2 根据接口上的注解创建RequestTemplate3 发送请求 1 EnableFeignClients 这个注解标注在springboot的启动类上,作用是开启feign接口扫描 FeignClientsRegistrar.registerFeignClients()扫描被FeignClient标识的接口生成代理类 public vo…

深入理解Feign之源码解析

转载请标明出处&#xff1a; https://blog.csdn.net/forezp/article/details/73480304 本文出自方志朋的博客 出自方志朋的博客 个人博客纯净版&#xff1a;https://www.fangzhipeng.com/springcloud/2017/08/11/sc-feign-raw.html 什么是Feign Feign是受到Retrofit&#xff…

SpringCloud 中Feign原理(图解)

1 SpringCloud 中Feign原理 1.1 Feign简介 Feign是Netflix公司开源的轻量级rest客户端&#xff0c;使用Feign可以非常方便的实现Http 客户端。Spring Cloud引入Feign并且集成了Ribbon实现客户端负载均衡调用。 1.2 Feign远程调用的基本流程 Feign远程调用&#xff0c;核心就是…

Feign(简介和使用)

1. Feign介绍 通过RestTemplate调用其它服务的API时&#xff0c;所需要的参数须在请求的URL中进行拼接&#xff0c;如果参数少的话或许我们还可以忍受&#xff0c;一旦有多个参数的话&#xff0c;这时拼接请求字符串就会效率低下 Feign是一个声明式的Web Service客户端&#…

什么是Feign?

服务间调用介绍 现有的服务调用方式 利用拼接的方式。 虽然上面有的用不错就很好了 Feign解决了什么问题 Feign的调用方式 Feign体系架构解析-武装到牙齿 上一节我们了解了feign的主要功能&#xff0c;它就像一个自拍杆一样&#xff0c;方便了Eureka的远程调用。可是怎么看…

Feign 的实现原理

Feign 实现原理 Feign是申明式的 HTTP 客户端。代码中创建一个接口并加上FeingClient 注解即可使用。其底层封装了 HTTP 客户端构建并发送的复杂逻辑。同时也可以整合注册中心及 Ribbon 为其提供负载均衡能力&#xff1b;通过整合 Histrix/sentinal 实现熔断限流功能。本期主要…

OpenFeign的实现原理(附Feign和OpenFeign的区别)

目录 问题现象&#xff1a; 问题分析&#xff1a; 拓展&#xff1a; 1、Feign&#xff1a; 2、OpenFeign&#xff1a; 问题现象&#xff1a; 最近在复习SpringCloud的时候看到一个面试题&#xff1a; OpenFeign的实现原理&#xff1f; 问题分析&#xff1a; OpenFeign是Sp…

Feign原理

是一个HTTP请求调用轻量级框架&#xff0c;可以以Java接口注解的方式调用HTTP请求&#xff0c;而不用像Java中通过封装HTTP请求报文的方式直接调用。 Feign解决了什么问题 在服务调用的场景中&#xff0c;我们经常调用基于HTTP协议的服务&#xff0c;而我们经常使用到的框架可…

Feign的使用及原理剖析

feign使用及原理剖析 一、简介 Feign是一个http请求调用的轻量级框架&#xff0c;可以以Java接口注解的方式调用Http请求。Feign通过处理注解&#xff0c;将请求模板化&#xff0c;当实际调用的时候&#xff0c;传入参数&#xff0c;根据参数再应用到请求上&#xff0c;进而转…

Feign基本使用(超详细)

目录 一、Feign概述 二、Feign入门 1.创建服务提供者(provider) 2.创建feign接口 3、创建服务消费者(consumer) 三、Feign 原理 四、Feign优化 1、开启feign日志 2、feign超时问题 3、http连接池 4、gzip压缩 前言 当我们通过RestTemplate调用其它服务的API时&#xff0c;所…

简单理解Feign的原理与使用

文章目录 SpringCloud 总架构图一、简介1.1、负载均衡的概念2.2、Feign概念 二、入门案例2.1、导入依赖2.2、Feign的客户端2.3、调用Feign2.4、开启Feign功能2.5、启动测试2.6、Feign实现原理简单分析 三、负载均衡(Ribbon)四、熔断器支持五、请求压缩和响应压缩六、配置日志级…

【第四章】详解Feign的实现原理

1.1 Feign概述 这篇文章主要讲述如何通过Feign去消费服务&#xff0c;以及Feign的实现原理的解析。 Feign是Netflix开发的声明式、模板化的HTTP客户端&#xff0c;Feign可以帮助我们更快捷、优雅地调用HTTP API。 Feign是⼀个HTTP请求的轻量级客户端框架。通过 接口 注解的…

Feign底层原理分析-自动装载动态代理

本篇文章仅介绍Feign的核心机制&#xff0c;包括如何交由Spring容器托管、动态代理机制等内容&#xff0c;不会过分深入细节。 1、什么是Feign&#xff1f; 这里套用Feign官方Github上的介绍&#xff1a;“Feign是一个灵感来自于Retrofit、JAXRS-2.0、WebSocket的Java Http客户…

Feign原理 (图解)

1.1 简介&#xff1a;Feign远程调用的 Feign远程调用&#xff0c;核心就是通过一系列的封装和处理&#xff0c;将以JAVA注解的方式定义的远程调用API接口&#xff0c;最终转换成HTTP的请求形式&#xff0c;然后将HTTP的请求的响应结果&#xff0c;解码成JAVA Bean&#xff0c;放…

Feign的工作原理

Feign的工作原理 Feign是一个伪Java Http 客户端&#xff0c;Feign 不做任何的请求处理。Feign 通过处理注解生成Request模板&#xff0c;从而简化了Http API 的开发。开发人员可以使用注解的方式定制Request API模板。 在发送Http Request请求之前&#xff0c;Feign通过处理…

Linux命令——tar与gzip详解:文件的打包压缩与解压缩解打包

Linux系统中&#xff0c;最常用的打包命令就是tar了&#xff0c;不仅如此&#xff0c;tar命令还可以解打包解压缩&#xff0c;十分方便。如果单纯想压缩文件&#xff0c;就需要我们的gzip命令了。 使用tar打包归档的包叫做tar包&#xff0c;以.tar结尾 使用gzip压缩的文件&…

Linux 下使用 tar 命令打包指定目录下的所有文件,不包括目录

一&#xff0c;问题描述 无论是 Linux 系统&#xff0c;还是 macOS 系统&#xff0c;我们都可以使用 tar 命令进行文件的压缩打包。命令格式如下&#xff1a; # tar cvf xxxx.tar 要压缩的文件或目录名称但如果要压缩的目录层级比较多时&#xff0c;比如&#xff1a; # tar cvf…

linux tar (打包、压缩、解压)命令

打包程序&#xff1a;tar c: 创建文档t&#xff1a; 列出存档内容x&#xff1a;提取存档f&#xff1a; filename 要操作的文档名v&#xff1a;详细信息 一&#xff1a;打包 打包&#xff1a;是指把文件整合在一起&#xff0c;不压缩 1.将文件打包&#xff1a;tar cf a.ta…

(21)tar打包命令详解

Linux 系统中,最常用的归档(打包)命令就是 tar,该命令可以将许多文件一起保存到一个单独的磁盘中进行归档。不仅如此,该命令还可以从归档文件中还原所需文件,也就是打包的反过程,称为解打包。1.tar命令做打包操作  当 tar 命令用于打包操作时,该命令的基本格式为: …