Feign导航

简介

最近都在弄微服务的东西,现在来记录下收获。我从一知半解到现在能从0搭建使用最大的感触有两点

1.微服务各大组件的版本很多,网上很多博客内容不一定适合你的版本,很多时候苦苦琢磨都是无用功

2.网上博客参差不齐,有些甚至错误的。更离谱的是,好的文章阅读量除非高出天际,不然就都很低,比那些复制粘贴,随便应付的都低(这个搜索推荐算法不知道基于什么的)

通过这段时间学习,我觉得最重要是从好的博客入手,先不要着急怎么组件怎么使用,而是先了解组件的作用,大概的原理,然后才是使用,这样搭建和尝试的过程中才能更好的定位问题,最后再次回到原理和一些实际问题的处理(不知道实际问题怎样的,直接搜那个组件的面试题往往效果最好)

接下来的内容,都以导航的形式展现给大家(毕竟优秀的轮子很多,直接看大佬写的不香嘛),再顺带提些自己的理解

传送门

更多微服务的介绍可点击下方链接

微服务介绍Nginx导航Nacos导航Gateway导航Ribbon导航Feign导航Sentinel导航

博主微服务git练手项目:https://github.com/NiceJason/SpringCloudDemo

Feign导航

Feign详细讲解:https://blog.csdn.net/luanlouis/article/details/82821294

文中有几个地方的点提一下

1.将调用抽象为starter

文中提到,作者团队将远程调用抽象成接口,定义了服务端starter,服务消费者在使用的时候,只需要引入Starter,就可以调用服务。这个比较适合平台无关性,接口抽象出来的好处就是可以根据服务调用实现方式自有切换。这种思路跟Spring里面的log框架类似,我们调用log都是调用接口,Spring内部默认有一套日志类实现了此接口,我们经常自己指定log4j2去代替它,只要引用配置和相关starter就行,代码上丝毫不用动。

 

2.高版本Feign

@GetMapping,@PutMapping在高版本的Feign中也是可用的了(目前直接把Controller的方法直接复制到@FeignClient接口中调用是没问题)

 

3.让Feign支持文件传输

我们可以看到好的框架扩展性真的强,如Encode和Decode都是接口暴露出来,我们经常用JacksonEncoder,JacksonDecoder去替代它又或者SpringCloud这种框架有自己的替代方案。我们虽然写业务较多,没什么机会写这种牛逼的组件,但这设计思想还是可以用的。顺便提一下由于Feign默认的读取的配置FeignClientsConfiguration,里面的Encoder默认是springEncoder,该类型不支持文件格式,所以要换成以下

 1 import feign.form.spring.SpringFormEncoder;
 2 import org.springframework.beans.factory.ObjectFactory;
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
 5 import org.springframework.cloud.openfeign.support.SpringEncoder;
 6 import org.springframework.context.annotation.Bean;
 7 import org.springframework.context.annotation.Configuration;
 8 
 9 /**
10  * @Author DiaoJianBin
11  * @Description Feign的通用配置
12  * @Date 2021/3/11 9:56
13  */
14 @Configuration(proxyBeanMethods = false)
15 public class FeignConfig {
16 
17     @Autowired
18     private ObjectFactory<HttpMessageConverters> messageConverters;
19 
20     @Bean
21     public SpringFormEncoder feignFormEncoder(){
22         return new SpringFormEncoder(new SpringEncoder(messageConverters));
23     }
24 }

其中SpringFormEncoder的部分代码如下,可以看到开头判断了是否是MultipartFile类或者数组

 1 @Override
 2   public void encode (Object object, Type bodyType, RequestTemplate template) throws EncodeException {
 3     if (bodyType.equals(MultipartFile[].class)) {
 4       val files = (MultipartFile[]) object;
 5       val data = new HashMap<String, Object>(files.length, 1.F);
 6       for (val file : files) {
 7         data.put(file.getName(), file);
 8       }
 9       super.encode(data, MAP_STRING_WILDCARD, template);
10     } else if (bodyType.equals(MultipartFile.class)) {
11       val file = (MultipartFile) object;
12       val data = singletonMap(file.getName(), object);
13       super.encode(data, MAP_STRING_WILDCARD, template);
14     } else if (isMultipartFileCollection(object)) {
15       val iterable = (Iterable<?>) object;
16       val data = new HashMap<String, Object>();
17       for (val item : iterable) {
18         val file = (MultipartFile) item;
19         data.put(file.getName(), file);
20       }
21       super.encode(data, MAP_STRING_WILDCARD, template);
22     } else {
23       super.encode(object, bodyType, template);
24     }
25   }

 

4.Feign的重试注意点

文中讲了Feign的重试,一般微服务都会整合Ribbon,发送的负载均衡,重试等都是由Ribbon负责的,可以看Feign里的源码

我们想要启用Feign会在SpringbootApplication类上加注解@EnableFeignClients

 

 

 这个注解里有如下说明,告诉我们默认配置看FeignClientsConfiguration

 

 

 然后看FeignClientsConfiguration类可以看到,可以在这里打个断点,然后启动项目就能了解清楚啦

 

 

 

 

 

从这可以看到,Feign默认是关闭重试的

具体想开启重试,即要开启Ribbon的重试,具体内容可以在传送门点击Ribbon导航,里面有介绍

5.Feign开启OkHttpClient

目前效率最高的当属okHttpClient,以下是怎么在Feign中开启和配置相关内容

#使用okHttp作为客户端
feign.httpclient.enabled=false
feign.okhttp.enabled=true

#配置其中的属性,开启okhttp后下面几个无效
#默认最大连接数
#feign.httpclient.max-connections=1000
#单个调用最大连接数
#feign.httpclient.max-connections-per-route=100
#当http返回码为3xxx时,默认会重定向操作
#feign.httpclient.follow-redirect=false

#配置Sentinel
feign.hystrix.enabled=true
feign.sentinel.enabled=true

6.Feign的回退注意点

从上面的配置中,有人可能会问,为啥开启了hystrix又开启sentinel,两者不都是熔断限流的组件嘛,在我项目里用的是sentinel,所以sentinel需要开启

而hystrix开启是因为Feign的回退(fallbackFactory或者fallback)需要开启这个才能正常执行,像我们经常这样写

 1 /**
 2  * @Author DiaoJianBin
 3  * @Description 访问BusinessService服务的接口
 4  *              有2种回退策略
 5  *              1.fallback = BusinessServiceTestClientFallback.class
 6  *              2.fallbackFactory = BusinessServiceTestFallballFactory.class
 7  *              当两者存在时,只能有一种生效,fallback优先级高于fallbackFactory
 8  *              但是fallback无法处理异常,就是你不知道啥原因进入了降级
 9  *              推荐用fallback优先级高于fallbackFactory
10  * @Date 2021/3/11 11:23
11  */
12 @Primary
13 @FeignClient(value = "business-service",path = "/test",decode404 = true,fallbackFactory = BusinessServiceTestFallballFactory.class)
14 public interface BusinessServiceTestClient {
15 
16     /**
17      * 即使目标实际Controller的方法有HttpServletRequst
18      * 但是不需要传此参数,就不用写
19      * @param message
20      * @return
21      */
22     @PostMapping(value = "/getSlowMessage")
23     String getSlowMessage(@RequestBody String message);
24 }

如果用了fallbackFactory或者fallback,这里需要注意@Primary注解要加上,不然@Autowired注入不了,因为FeignClient会根据接口生成一个代理类,而fallbackFactory或者fallback指定的类又得@Component才行,这样注入Spring会混乱,但你没法指定代理类生成的名字

7.@FeignClient中的name和url参数

关于调用目前有两种:
从源码可以得知,name是value的别名,value也是name的别名。两者的作用是一致的,name指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现。
1、接口提供方在注册中心。
如果服务提供方已经注册到注册中心了,那么name或者value的值为:服务提供方的服务名称。必须为所有客户端指定一个name或者value
@FeignClient(value="run-product",fallback = ProductClientServiceFallBack.class)
 
2、单独的一个http接口,接口提供方没有注册到注册中心。
@FeignClient(name="runClient11111",url="localhost:8001")
此处name的值为:调用客户端的名称。
 
以上两种方式都能正常调用。name可以为注册中心的实例名称,加上url属性时,name的值就与注册中心实例名称无关(URL一般用于本地调试)。

8.Feign自定义拦截器

 1 /**
 2  * @Author DiaoJianBin
 3  * @Description  openFeign的拦截器,可以在请求发送前做些处理
 4  * @Date 2021/3/14 16:10
 5  */
 6 public class UserFeignInterceptor implements RequestInterceptor {
 7     @Override
 8     public void apply(RequestTemplate template) {
 9         System.out.println("例如在这里添加token");
10         template.header("token","123");
11     }
12 }

小结

        本篇博客主要讲了Feign一些注意点,正常的Feign的使用大家可以自行搜索,很简单。目前个人使用Feign主要还是系统服务的内部调用,访问第三方服务(如微信支付)往往要SSL验证等,这个时候用RestTemplate比较方便。希望能帮到大家~

posted @ 2021-05-25 21:10  Nice斌  阅读(251)  评论(0编辑  收藏  举报