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

article/2025/9/2 13:37:36

1.1 Feign概述

这篇文章主要讲述如何通过Feign去消费服务,以及Feign的实现原理的解析。

FeignNetflix开发的声明式、模板化的HTTP客户端,Feign可以帮助我们更快捷、优雅地调用HTTP API

Feign是⼀个HTTP请求的轻量级客户端框架。通过 接口 + 注解的方式发起HTTP请求调用,面向接口编程,而不是像Java中通过封装HTTP请求报文的方式直接调用。服务消费方拿到服务提供方的接⼝,然后像调⽤本地接⼝⽅法⼀样去调⽤,实际发出的是远程的请求。让我们更加便捷和优雅的去调⽤基于 HTTPAPI,被⼴泛应⽤在 Spring Cloud的解决⽅案中。

在前面的文章中可以发现当我们通过RestTemplate调用其它服务的API时,所需要的参数须在请求的URL中进行拼接,如果参数少的话或许我们还可以忍受,一旦有多个参数的话,这时拼接请求字符串就会效率低下,并且显得好傻。

那么有没有更好的解决方案呢?答案是确定的有,Netflix已经为我们提供了一个框架:Feign

Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息。

Feign则会完全代理HTTP请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。Feign整合了RibbonHystrix(关于Hystrix我们后面再讲),可以让我们不再需要显式地使用这两个组件。

总起来说,Feign具有如下特性:

  • 采用的是基于接口可插拔的注解支持,包括Feign注解和JAX-RS注解;
  • 支持可插拔的HTTP编码器和解码器;
  • 支持Hystrix和它的Fallback,具有熔断降级的能力;
  • 支持Ribbon的负载均衡,具有负载均衡的能力;
  • 支持HTTP请求和响应的压缩。

这看起来有点像我们Spring MVC模式的Controller层的RequestMapping映射。这种模式是我们非常喜欢的。Feign是用@FeignClient来映射服务的。

1.2 为什么使用Feign

Feign 的首要目标就是减少HTTP 调用的复杂性。在微服务调用的场景中,我们调用很多时候都是基于HTTP协议的服务,如果服务调用只使用提供 HTTP调用服务的 HTTP Client框架(e.g. Apache HttpComponnets、HttpURLConnection OkHttp 等),我们需要关注哪些问题呢?

在这里插入图片描述
相比这些 HTTP请求框架,Feign封装了HTTP 请求调用的流程,而且会强制使用者去养成面向接口编程的习惯(因为 Feign 本身就是要面向接口)。

1.3 Feign详解

1.3.1 代码示例

首先第一步,在原来的基础上新建一个Feign模块,接着引入相关依赖,引入Feign依赖,会自动引入Hystrix依赖的,如下所示:

    <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-eureka</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-feign</artifactId></dependency>

application.yml配置如下所示:

server:port: 8083
spring:application:name: feign-consumer
eureka:client:service-url:defaultZone: http://localhost:8888/eureka/,http://localhost:8889/eureka/

接着在前面文章中的的的两个provider1provider2两个模块的服务新增几个方法,如下代码所示:

@RestController
public class HelloController {@RequestMapping("/hello")public String hello(){System.out.println("访问来1了......");return "hello1";}@RequestMapping("/hjcs")public List<String> laowangs(String ids){List<String> list = new ArrayList<>();list.add("laowang1");list.add("laowang2");list.add("laowang3");return list;}//新增的方法@RequestMapping(value = "/hellol", method= RequestMethod.GET)public String hello(@RequestParam String name) {return "Hello " + name;}@RequestMapping(value = "/hello2", method= RequestMethod.GET)public User hello(@RequestHeader String name, @RequestHeader Integer age) {return new User(name, age);}@RequestMapping(value = "/hello3", method = RequestMethod.POST)public String hello (@RequestBody User user) {return "Hello "+ user. getName () + ", " + user. getAge ();}}

接着是上面代码所需用到的User类,代码如下所示:

public class User {private String name;private Integer age;//序列化传输的时候必须要有空构造方法,不然会出错public User() {}public User(String name, Integer age) {this.name = name;this.age = age;}//...getter setter省略
}

接下来用Feign@FeignClient(“服务名称”)映射服务调用。代码如下所示:

package hjc;import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.*;//configuration = xxx.class  这个类配置Hystrix的一些精确属性
//value=“你用到的服务名称”@FeignClient(value = "hello-service",fallback = FeignFallBack.class)
public interface FeignService {//服务中方法的映射路径@RequestMapping("/hello")String hello();@RequestMapping(value = "/hellol", method= RequestMethod.GET)String hello(@RequestParam("name") String name) ;@RequestMapping(value = "/hello2", method= RequestMethod.GET)User hello(@RequestHeader("name") String name, @RequestHeader("age") Integer age);@RequestMapping(value = "/hello3", method= RequestMethod.POST)String hello(@RequestBody User user);
}

接着在Controller层注入FeiService这个接口,进行远程服务调用,代码如下:

@RestController
public class ConsumerController {@AutowiredFeignService feignService;@RequestMapping("/consumer")public String helloConsumer(){return feignService.hello();}@RequestMapping("/consumer2")public String helloConsumer2(){String r1 = feignService.hello("hjc");String r2 = feignService.hello("hjc", 23).toString();String r3 = feignService.hello(new User("hjc", 23));return r1 + "-----" + r2 + "----" + r3;}}

接着在Feign模块的启动类哪里打上Eureka客户端的注解@EnableDiscoveryClient Feign客户端的注解@EnableFeignClients,代码如下所示:

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class FeignApplication {public static void main(String[] args) {SpringApplication.run(FeignApplication.class, args);}
}

接着启动启动类,浏览器上输入localhost:8083/consumer 运行结果如下所示:

在这里插入图片描述

在这里插入图片描述

可以看到负载均衡轮询出现hello1,hello2

接着继续在浏览器上输入localhost:8083/consumer2,运行结果如下:

在这里插入图片描述

接下来我们进行Feign声明式调用服务下的,服务降级的使用,那么我们就必须新建一个FeignFallBack类来继承FeiService,代码如下:

package hjc;import org.springframework.stereotype.Component;@Component
public class FeignFallBack implements FeignService{//实现的方法是服务调用的降级方法@Overridepublic String hello() {return "error";}@Overridepublic String hello(String name) {return "error";}@Overridepublic User hello(String name, Integer age) {return new User();}@Overridepublic String hello(User user) {return "error";}
}

接着我们再把那两个服务提供模块provider1,provider2模块进行停止,运行结果如下所示:

在这里插入图片描述

可以看到我们这几个调用,都进行了服务降级了。

那么如果我们想精确的控制一下Hystrix的参数也是可以的,比方说跟Hystrix结合的参数,那么可以在FeignClient注解里面配置一个Configuration=XXX类.class属性,在哪个类里面精确的指定一下属性。

或者在application.yml里面配置,如下:

hystrix:command:default:execution:isolation:thread:timeoutinMilliseconds: 5000ribbon:connectTimeout: 500#如果想对单独的某个服务进行详细配置,如下
hello-service:ribbon:connectTimeout: 500

1.3.2 参数处理

Feign处理远程服务调用时,传递参数是通过HTTP协议传递的,参数存在的位置是请求头或请求体中。请求头传递的参数必须依赖@RequestParam注解来处理请求参数,请求体传递的参数必须依赖@RequestBody注解来处理请求参数。

1.3.2.1 代码环境如下

Contronller层通过feignClient调用微服务 获取所有任务

@Controller
@RequestMapping("tsa/task")
public class TaskController{@AutowiredTaskFeignClient taskFeignClient;@PostMapping("/getAll")@ResponseBodypublic List<TaskVO> getAll() {List<TaskVO> all = taskFeignClient.getAll();return all;}
}

@FeignClient用于通知Feign组件对该接口进行代理(不需要编写接口实现),使用者可直接通过@Autowired注入。

Spring Cloud应用在启动时,Feign会扫描标有@FeignClient注解的接口,生成代理,并注册到Spring容器中。生成代理时Feign会为每个接口方法创建一个RequetTemplate对象,该对象封装了HTTP请求需要的全部信息,请求参数名、请求方法等信息都是在这个过程中确定的,Feign的模板化就体现在这里。

@FeignClient(qualifier = "taskFeignClient", name = "service-tsa",fallback = TaskFeignClientDegraded.class)
public interface TaskFeignClient {@PostMapping(value = "taskApiController/getAll")List<TaskVO> getAll();
}

微服务端

@Slf4j
@RestController
@RequestMapping("taskApiController")
public class TaskApiController{@Autowiredprivate TaskService taskService;@PostMapping("/getAll")public List<TaskVO> getAll() {log.info("--------getAll-----");List<TaskVO>  all = taskService.getAll();return all;}
}

1.3.2.2 几个坑

1、坑一

首先再次强调Feign是通过http协议调用服务的,重点是要理解这句话

如果FeignClient中的方法有@PostMapping注解 则微服务TaskApiController中对应方法的注解也应当保持一致为@PostMapping

如果不一致,则会报404的错误

调用失败后会触发它的熔断机制,如果@FeignClient中不写@FeignClient(fallback = TaskFeignClientDegraded.class),会直接报错:

11:00:35.686 [http-apr-8086-exec-8] DEBUG c.b.p.m.b.c.AbstractBaseController - Got an exception
com.netflix.hystrix.exception.HystrixRuntimeException: TaskFeignClient#getAll() failed and no fallback available.at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:819)at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:804)

2、坑2:这个是最惨的了

自己写好的微服务没有运行起来,然后自己的客户端调用这个服务怎么也调用不成功还不知道问题在哪,当时自己微服务运行后,控制台如下:

Process finished with exit code 0

我以前以为Process finished with exit code 1才是运行失败的意思 ,所以只要出现 Process finished with exit code就说明运行失败

服务成功启动的标志为:

11:29:16.483 [restartedMain] INFO  c.b.p.ms.tsa.TsaServiceApplication - Started TsaServiceApplication in 37.132 seconds (JVM running for 39.983)

3、坑3、RequestParam.value() was empty on parameter 0

如果在FeignClient中的方法有参数传递一般要加@RequestParam(“xxx”)注解

错误写法

@FeignClient(qualifier = "taskFeignClient", name = "service-tsa",fallback = TaskFeignClientDegraded.class)
public interface TaskFeignClient {@PostMapping(value = "taskApiController/getAll")List<TaskVO> getAll(String name);
}

@PostMapping(value = "taskApiController/getAll")
List<TaskVO> getAll(@RequestParam String name);

正确写法

@PostMapping(value = "taskApiController/getAll")
List<TaskVO> getAll(@RequestParam("name") String name);

在微服务那边可以不写这个注解,这个也是自己开发的时候烦的小错误,吸取教训。

疑问
SpringMVCSpringboot 中都可以使用 @RequestParam 注解,不指定 value的用法,为什么到了 Spring cloud中的Feign 这里就不行了呢?

这是因为和 Feign的实现有关。Feign 的底层使用的是httpclient,在低版本中会产生这个问题,听说高版本中已经对这个问题修复了。

4、 坑四 FeignClientpost传递对象和`consumes = "application/json"

按照坑三的意思,应该这样写

@FeignClient(qualifier = "taskFeignClient", name = "service-tsa",fallback = TaskFeignClientDegraded.class)
public interface TaskFeignClient {@PostMapping(value = "taskApiController/getAll")List<TaskVO> getAll(@RequestParam("vo") TaskVO vo);
}

很意外报错

16:00:33.770 [http-apr-8086-exec-1] DEBUG c.b.p.a.s.PrimusCasAuthenticationFilter - proxyReceptorRequest = false
16:00:33.770 [http-apr-8086-exec-1] DEBUG c.b.p.a.s.PrimusCasAuthenticationFilter - proxyTicketRequest = false
16:00:33.770 [http-apr-8086-exec-1] DEBUG c.b.p.a.s.PrimusCasAuthenticationFilter - requiresAuthentication = false
16:00:34.415 [hystrix-service-tsa-2] DEBUG c.b.p.m.b.f.PrimusSoaFeignErrorDecoder - 
error json:{
"timestamp":1543564834395,
"status":500,
"error":"Internal Server Error",
"exception":"org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException",
"message":"Failed to convert value of type 'java.lang.String' to required type 'com.model.tsa.vo.TaskVO'; 
nested exception is java.lang.IllegalStateException: 
Cannot convert value of type 'java.lang.String' to required type 'com.model.tsa.vo.TaskVO':
no matching editors or conversion strategy found","path":"/taskApiController/getAll"  }

看着错误信息想了半天突然想明白了:
Feign本质是通过http 请求的,http怎么能直接传递对象呢,一般都是把对象转换为json通过post请求传递的。

正确写法应当如下

@FeignClient(qualifier = "taskFeignClient", name = "service-tsa",fallback = TaskFeignClientDegraded.class)
public interface TaskFeignClient {@PostMapping(value = "taskApiController/getAll",,consumes = "application/json")List<TaskVO> getAll(TaskVO vo);
}

也可以这样写

  @PostMapping(value = "taskApiController/getAll")List<TaskVO> getAll(@RequestBody TaskVO vo);

此时不用,consumes = "application/json"

但是第一种写法最正确的 因为FeignClient是在我们本地直接调用的,根本不需要这个注解,Controller调用方法的时候就是直接将对象传给FeignClient,而FeignClient通过http调用服务时则需要将对象转换成json传递。

微服务代码如下所示:

@Slf4j
@RestController
@RequestMapping("taskApiController")
public class TaskApiController{@Autowiredprivate TaskService taskService;@PostMapping("/getAll")public List<TaskVO> getAll(@RequestBody TaskVO vo) {log.info("--------getAll-----");List<TaskVO>  all = taskService.getAll();return all;}
}

我第一次写这个的时候方法参数里面什么注解都没加,可以正常跑通,但是传过去的对象却为初始值,实际上那是因为对象根本就没传。

当然还是推荐使用post请求传递对象的:
在使用Feign来调用Get请求接口时,如果方法的参数是一个对象,例如:

@FeignClient ( "microservice-provider-user" )  
public  interface  UserFeignClient {@RequestMapping (value =  "/user" , method = RequestMethod.GET)   public  User get0(User user);       
}

那么在调试的时候你会一脸懵逼,因为报了如下错误:

feign.FeignException: status  405  reading UserFeignClient#get0(User); content:{ "timestamp" : 1482676142940 , "status" : 405 , "error" : "Method Not Allowed" ,  "exception" : "org.springframework.web.HttpRequestMethodNotSupportedException" , "message" : "Request method 'POST' not supported" , "path" : "/user" }

明明定义的Get请求,怎么被转换成了Post?

调整不用对象传递,一切OK,没毛病,可仔细想想,你想写一堆长长的参数吗?用一个不知道里边有什么鬼的Map吗?或者转换为post?这似乎与REST风格不太搭,会浪费url资源,我们还需要在url定义上来区分Get或者Post

我很好奇,我定义的Get请求怎么就被转成了Post,于是就开始逐行调试,直到我发现了这个:

private  synchronized  OutputStream getOutputStream0()  throws  IOException {try  {   if (! this .doOutput) {  throw  new  ProtocolException( "cannot write to a URLConnection if doOutput=false - call setDoOutput(true)" );        }  else  {      if ( this .method.equals( "GET" )) {    this .method =  "POST" ;}

这段代码是在 HttpURLConnection 中发现的,jdk原生的http连接请求工具类,这个是Feign默认使用的连接工具实现类,但我记得我们的工程用的是apachhttpclient替换掉了原生的UrlConnection,我们用了如下配置:

feign:    httpclient:enabled:  true

同时在依赖中引入apachehttpclient

<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version> 4.5.3 </version>
</dependency>

发现我们少配置了一个依赖:

<!-- 使用Apache HttpClient替换Feign原生httpclient -->       
<dependency>     <groupId>com.netflix.feign</groupId>     <artifactId>feign-httpclient</artifactId><version>${feign-httpclient}</version>    
</dependency>

那我加上这个依赖后,请求通了,但是接口接收到对象里边属性值是NULL;再看下边的定义是不是少点什么

@RequestMapping (value =  "/user" , method = RequestMethod.GET)public  User get0(User user);

对,少了一个注解:@RequestBody,既然使用对象传递参数,那传入的参数会默认放在RequesBody中,所以在接收的地方需要使用@RequestBody来解析,最终就是如下定义:

@RequestMapping (value =  "/user" , method = RequestMethod.GET,consumer="application/json")   public  User get0( @RequestBody  User user);

1.3.2.3 传递对象的另一种方法和多参传递

1、GET请求多参数的URL
假设我们请求的URL包含多个参数,例如http://microservice-provider-user/get?id=1&username=张三 ,要怎么办呢?

我们知道Spring CloudFeign添加了Spring MVC的注解支持,那么我们不妨按照Spring MVC的写法尝试一下:

@FeignClient("microservice-provider-user")
public interface UserFeignClient {@RequestMapping(value = "/get", method = RequestMethod.GET)public User get0(User user);
}

然而我们测试时会发现该写法不正确,我们将会收到类似以下的异常:

feign.FeignException: status 405 reading UserFeignClient#get0(User); content:
{"timestamp":1482676142940"status":405"error":"Method Not Allowed""exception":"org.springframework.web.HttpRequestMethodNotSupportedException""message":"Request method 'POST' not supported""path":"/get"}

由异常可知,尽管指定了GET方法,Feign依然会发送POST请求。

正确写法如下:

(1) 方法一

@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {@RequestMapping(value = "/get", method = RequestMethod.GET)public User get1(@RequestParam("id") Long id, @RequestParam("username") String username);
}

这是最为直观的方式,URL有几个参数,Feign接口中的方法就有几个参数。使用@RequestParam注解指定请求的参数是什么。

(2) 方法二

@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {@RequestMapping(value = "/get", method = RequestMethod.GET)public User get2(@RequestParam Map<StringObject> map);
}

多参数的URL也可以使用Map去构建

当目标URL参数非常多的时候,可使用这种方式简化Feign接口的编写。

POST请求包含多个参数
下面我们来讨论如何使用Feign构造包含多个参数的POST请求。

实际就是坑四,把参数封装成对象传递过去就可以了

1.3.2.4 最后总结一下

Feign的Encoder、Decoder和ErrorDecoder

Feign将方法签名中方法参数对象序列化为请求参数放到HTTP请求中的过程,是由编码器(Encoder)完成的。同理,将HTTP响应数据反序列化为java对象是由解码器(Decoder)完成的。

默认情况下,Feign会将标有@RequestParam注解的参数转换成字符串添加到URL中,将没有注解的参数通过Jackson转换成json放到请求体中。

注意,如果在@RequetMapping中的method将请求方式指定为get,那么所有未标注解的参数将会被忽略,例如:

@RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET)
void update(@PathVariable("groupId") Integer groupId, @RequestParam("groupName") String groupName, DataObject obj);

此时因为声明的是GET请求没有请求体,所以obj参数就会被忽略。

Spring Cloud环境下,FeignEncoder只会用来编码没有添加注解的参数。如果你自定义了Encoder, 那么只有在编码obj参数时才会调用你的Encoder。对于Decoder, 默认会委托给SpringMVC中的MappingJackson2HttpMessageConverter类进行解码。只有当状态码不在200 ~ 300之间时ErrorDecoder才会被调用。ErrorDecoder的作用是可以根据HTTP响应信息返回一个异常,该异常可以在调用Feign接口的地方被捕获到。我们目前就通过ErrorDecoder来使Feign接口抛出业务异常以供调用者处理。

FeignHTTP Client

Feign在默认情况下使用的是JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTPpersistence connection。我们可以用ApacheHTTP Client替换Feign原始的http client, 从而获取连接池、超时时间等与性能息息相关的控制能力。Spring CloudBrixtion.SR5版本开始支持这种替换,首先在项目中声明Apache HTTP Clientfeign-httpclient依赖:

<!-- 使用Apache HttpClient替换Feign原生httpclient --><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId></dependency><dependency><groupId>com.netflix.feign</groupId><artifactId>feign-httpclient</artifactId><version>${feign-httpclient}</version></dependency>

然后在application.properties中添加:

feign.httpclient.enabled=true

通过Feign, 我们能把HTTP远程调用对开发者完全透明,得到与调用本地方法一致的编码体验。这一点与阿里Dubbo中暴露远程服务的方式类似,区别在于Dubbo是基于私有二进制协议,而Feign本质上还是个HTTP客户端。如果是在用Spring Cloud Netflix搭建微服务,那么Feign无疑是最佳选择。

1.4 调用原理解析

Feign远程调用,核心就是通过一系列的封装和处理,将以JAVA注解的方式定义的远程调用API接口,最终转换成HTTP的请求形式,然后将HTTP的请求的响应结果,解码成JAVA Bean,放回给调用者。Feign远程调用的基本流程,大致如下图所示。

在这里插入图片描述
从上图可以看到,Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的 Request请求。通过Feign以及JAVA的动态代理机制,使得Java开发人员,可以不用通过HTTP框架去封装HTTP请求报文的方式,完成远程服务的HTTP调用。

Feign优化
(1)GZIP压缩
gzip是一种数据格式,采用deflate算法压缩数据。当Gzip压缩到一个纯文本数据时,可以减少70%以上的数据大小。

gzip作用:网络数据经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页加载的速度。

只配置Feign请求-应答的GZIP压缩

# feign gzip
# 局部配置。只配置feign技术相关的http请求-应答中的gzip压缩。
# 配置的是application client和application service之间通讯是否使用gzip做数据压缩。
# 和浏览器到application client之间的通讯无关。
# 开启feign请求时的压缩, application client -> application service
feign.compression.request.enabled=true
# 开启feign技术响应时的压缩,  application service -> application client
feign.compression.response.enabled=true
# 设置可以压缩的请求/响应的类型。
feign.compression.request.mime-types=text/xml,application/xml,application/json
# 当请求的数据容量达到多少的时候,使用压缩。默认是2048字节。
feign.compression.request.min-request-size=512
配置全局的GZIP压缩# spring boot gzip
# 开启spring boot中的gzip压缩。就是针对和当前应用所有相关的http请求-应答的gzip压缩。
server.compression.enabled=true
# 哪些客户端发出的请求不压缩,默认是不限制
server.compression.excluded-user-agents=gozilla,traviata
# 配置想压缩的请求/应答数据类型,默认是 text/html,text/xml,text/plain
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain
# 执行压缩的阈值,默认为2048
server.compression.min-response-size=512

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

相关文章

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 命令用于打包操作时,该命令的基本格式为: …

vector中删除某个指定元素

class Solution { public:int removeElement(vector<int>& v, int val) {for (auto it v.begin(); it ! v.end(); it) {if (*it val) { // 条件语句v.erase(it); // 移除他it--; // 让该迭代器指向前一个}}return v.size();} };

vector删除指定元素

C vector中实际删除元素使用的是容器vecrot中std::vector::erase()方法。 C 中std::remove()并不删除元素&#xff0c;因为容器的size()没有变化&#xff0c;只是元素的替换。 1.std::vector::erase() 函数原型&#xff1a;iterator erase (iterator position);  //删除指定…

C++中vector中删除/添加指定位置处的元素

1、函数介绍 C中vector容器可以删除/添加制定位置处的元素&#xff0c;分别使用erase()与insert()函数。其中函数内需要两个参数&#xff0c;第一个为指定删除/添加的位置&#xff0c;第二个元素为删除/添加的元素值。 iterator insert(const_iterator _Where, _Ty&&…

C/C++ vector 删除指定元素

C vector 删除符合条件的元素C vector中实际删除元素使用的是容器vecrot中std::vector::erase()方法。C 中std::remove()并不删除元素&#xff0c;因为容器的size()没有变化&#xff0c;只是元素的替换。1.std::vector::erase()  函数原型&#xff1a;iterator erase (iterat…

【c++】vector中删除元素

目录 1.删除指定范围的元素2.删除指定大小的元素3.C20 std::erase, std::erase_if (std::vector)注意点 1.删除指定范围的元素 vector删除元素之pop_back(),erase(),remove() 向量容器vector的成员函数pop_back()可以删除最后一个元素. 而函数erase()可以删除由一个iterator指…

光纤光学原理相关基础知识点

记&#xff1a;研究生导师的方向是光纤光学和机器学习交叉的&#xff0c;导师给推荐了本书&#xff0c;书里面的理论和推导公式作为小白的我真的是不太行&#xff0c;后来在中国大学mooc上找了视频课跟着学习&#xff0c;顺便做下笔记&#xff0c;为日后使用&#xff0c;因此会…

机器人学重点知识点总结

机器人学重点知识点总结 坐标转换与机械臂运动学雅克比矩阵机械臂逆向动力学&#xff08;牛顿欧拉递推&#xff09;机械臂正向动力学运动轨迹生成动力学轨迹跟踪控制 这篇博客主要用来记录一下现代机器人学里面比较基础也比较重要的一些知识点&#xff0c;所有内容均仅仅记录是…

大学物理(下)知识点总结

大学物理&#xff08;下&#xff09;知识点总结&#xff08;持续更新&#xff09; 文章目录 大学物理&#xff08;下&#xff09;知识点总结&#xff08;持续更新&#xff09;静电场知识点总结习题精选 磁场知识点总结习题精选 期中复习总结电磁场知识点总结习题精选 狭义相对论…

Lumerical官方案例、FDTD时域有限差分法仿真学习(二)——宽带光栅耦合器(Broadband grating coupler (2D))

这个 2D FDTD 示例展示了如何获得光栅耦合器的宽带特性&#xff0c;并将结果与实验数据进行比较。 此外&#xff0c;该应用示例演示了多频波束计算的正确使用和优势&#xff0c;并将结果与标准单频波束计算进行了比较。 要更好地了解单频和多频波束计算之间的差异&#xff0c;请…

三维计算机视觉(四)--关键点

关键点又称为感兴趣的点&#xff0c;是低层次视觉通往高层次视觉的捷径&#xff0c;抑或是高层次感知对低层次处理手段的妥协。 RangeImage 1.关键点&#xff0c;线&#xff0c;面 关键点特征点&#xff1b; 关键线边缘&#xff1b; 关键面foreground&#xff1b; 上述三个概…

信号完整性基础04:串扰(1)

说在开头&#xff1a;关于x射线&#xff08;2&#xff09; 1895年11月8日&#xff0c;德国维尔茨堡大学的一间实验室里&#xff0c;校长伦琴正在做克鲁克斯管实验&#xff0c;当时房间没开灯&#xff0c;屋子里一片漆黑&#xff0c;放电管用黑纸包得很严实&#xff0c;他突然发…

基于地平面的单目视觉里程计绝对尺度估计

点击上方“3D视觉工坊”&#xff0c;选择“星标” 干货第一时间送达 ●论文摘要 对于基于单目相机的系统来说&#xff0c;从单目相机中恢复绝对度量尺度是一个具有挑战性的问题。利用有限的已知条件&#xff0c;提出了多种尺度估计方法&#xff0c;如根据相机的高度、物体大小等…

comsol 学习笔记【基础知识,磁场与结构场耦合为主】

感悟 随时补充学习中的感悟&#xff0c;放在开头是为了后来者在学习过程中有所参考。 掌握基本操作&#xff0c;学会寻找资料先看基础视频&#xff0c;然后找专题视频&#xff0c;再去学习案例&#xff08;以官方视频优先、案例配有相关的文件&#xff09;学习案例会涉及到其…