学习 spring-cloud-aibaba第四篇,声明式HTTP通讯客户端 Feign


前情提要:nacos上注册了content-centeruser-center两个服务,content-center使用Feign调用user-center服务,使用Ribbon做负载均衡

1.RestTemplate VS Feign

在这里插入图片描述

  • Feign让我们的代码可读性、可维护性极佳,这是决胜点
  • Feign 的唯一短板性能只有RestTemplate的一半,但是也有优化提升的空间
  • 所以尽量使用 Feign
  • 然而事无绝对,合理选择

2.Feign的组成

在这里插入图片描述
我们需要关注的几个部分

  • Client 可以自定义请求Client,提高性能
  • Logger 默认是不打印日志,所以这里需要配置
  • RequestInterceptor 拦截器,很有用,例如给每个请求加Header

3.项目添加Feign

content-center 项目添加Feign组件,依然三板斧

3.1 加依赖

pom.xml

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

3.2 加注解

启动类ContentApplication添加注解@EnableFeignClients

@SpringBootApplication
@MapperScan("com.zengchen.content.mapper")
@EnableFeignClients
public class ContentApplication {

    public static void main(String[] args) {
        SpringApplication.run(ContentApplication.class, args);
    }

}

3.3 写配置

没有必须写的配置

4.项目使用Feign

4.1 声明 FeignClient(UserCenterFeignClient)

新建个包feignclient下面新建接口UserCenterFeignClient

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(name = "user-center")
public interface UserCenterFeignClient{

    /**
     * FeignClient的name + GetMapping的value
     * 相当于 http://user-center/reciteHis/testAno
     * 和RestTemplate里写的url一模一样
     * @return Page
     */
    @GetMapping(value = "/reciteHis/testAno")
    Page memberRctHisAno();
}

4.2 使用UserCenterFeignClient

重构Controller里的方法,代码很简洁,并且从client接口里知晓每个方法是做什么的,可读性变强了

    @Autowired
    private UserCenterFeignClient userCenterFeignClient;

    @GetMapping(value = "testRibbon")
    public Object testRibbon() {
          //1. 未使用Ribbon时
//        List<ServiceInstance> instances = discoveryClient.getInstances("user-center");
//        String targetUrl = instances.stream()
//                .map(instance -> instance.getUri().toString() + "/reciteHis/testAno")
//                .findFirst()
//                .orElseThrow(() -> new IllegalArgumentException("当前没有实例!"));

          // 2.Ribbon + restTemplate
//        String targetUrl= "http://user-center/reciteHis/testAno";
//        Page page = restTemplate.getForObject(targetUrl, Page.class);

        // 3.Feign
        Page page = userCenterFeignClient.memberRctHisAno();
        return ResponseVO.success(page);
    }

4.3 测试

请求content-center的http://localhost:8082/reciteHis/testAno。使用Feign成功
在这里插入图片描述

5.Feign的日志级别

开发环境推荐使用FULL,生产环境推荐BASIC
在这里插入图片描述

5.1 java代码方式自定义日志级别

  • 设置UserCenterFeignClient的项目(不是Feign的日志,注意这两个日志的概念区别)日志级别为debug
    只有这个文件的日志级别设置为debug,Feign的日志才能打印出来,这是先决条件
logging:
  file: D://springlog/content/log.log
  level:
    com.zengchen.content.feignclient.UserCenterFeignClient: debug
  • 新建UserCenterFeignClientConfiguration
           和Ribbonjava代码配置一样,写个配置类,返回日志级别。注意这里的Logger,得是feign包下面的Logger,别引用错了
           如果你加了@Configuration,也会产生父子上下文问题,变成了Feign的全局配置
           所以,要么别加@Configuration注解,要么把这个配置类放到一个启动类扫描不到的包里,like ribbon的配置类
           这里推荐不加@Configuration注解的方式,因为简单啊。
           那么ribbon的配置类问什么不采用这种不加注解的方式呢?因为ribbon的配置类必须加@Configuration,不加不行啊,所以ribbon没办法像feign一样
import feign.Logger;
import org.springframework.context.annotation.Bean;
//@Configuration
public class UserCenterFeignClientConfiguration {

    @Bean
    public Logger.Level loggerLevel(){
        //全日志
        return Logger.Level.FULL;
    }
}
  • UserCenterFeignClient使用UserCenterFeignClientConfiguration
    configuration = UserCenterFeignClientConfiguration.class
@FeignClient(name = "user-center",configuration = UserCenterFeignClientConfiguration.class)
public interface UserCenterFeignClient {

    /**
     * FeignClient的name + GetMapping的value
     * 相当于 http://user-center/reciteHis/testAno
     * 和RestTemplate里写的url一模一样
     * @return Page
     */
    @GetMapping(value = "/reciteHis/testAno")
    Page memberRctHisAno();
}

5.2 属性配置方式自定义日志级别

  • 先把UserCenterFeignClient的注解注释掉
//@FeignClient(name = "user-center",configuration = UserCenterFeignClientConfiguration.class)
@FeignClient(name = "user-center")
public interface UserCenterFeignClient {

    /**
     * FeignClient的name + GetMapping的value
     * 相当于 http://user-center/reciteHis/testAno
     * 和RestTemplate里写的url一模一样
     * @return Page
     */
    @GetMapping(value = "/reciteHis/testAno")
    Page memberRctHisAno();
}
  • 写配置
logging:
  file: D://springlog/content/log.log
  level:
    com.zengchen.content.feignclient.UserCenterFeignClient: debug
feign:
  client:
    config:
      # 想要调用的微服务的名称
      user-center:
        loggerLevel: basic

6.Feign的全局配置

上小节的日志配置,都是只针对于user-center服务的起作用,如果content-center还要调用比如,短信服务,广告服务等等,就看不到feign的日志了,因为feign默认不打印日志,全局配置就是content-center调用其它所有的服务都起作用的配置

6.1 java代码方式

  • 利用父子上下文ComponentScan的bug
    这虽然是一种方式,但是这是一种病态的方式,强烈不建议使用
  • 启动类@EnableFeignClientsdefaultConfiguration属性
    注释application.yml里的配置
#feign:
#  client:
#    config:
#      # 想要调用的微服务的名称
#      user-center:
#        loggerLevel: basic

给@EnableFeignClients添加
defaultConfiguration = UserCenterFeignClientConfiguration.class属性,这样UserCenterFeignClientConfiguration就变成了全局的配置,不再仅限于调用user-center的时候

@SpringBootApplication
@MapperScan("com.zengchen.content.mapper")
@EnableFeignClients(defaultConfiguration = UserCenterFeignClientConfiguration.class)
//@EnableFeignClients(basePackages = "com.zengchen.user.client")
public class ContentApplication {

    public static void main(String[] args) {
        SpringApplication.run(ContentApplication.class, args);
    }

}

6.2 属性配置方式

  • 注释java代码全局配置
@SpringBootApplication
@MapperScan("com.zengchen.content.mapper")
@EnableFeignClients//(defaultConfiguration = UserCenterFeignClientConfiguration.class)
//@EnableFeignClients(basePackages = "com.zengchen.user.client")
public class ContentApplication {

    public static void main(String[] args) {
        SpringApplication.run(ContentApplication.class, args);
    }

}
  • 修改属性配置
    把原来的user-center改成default就行
feign:
  client:
    config:
      # 全局配置
      default:
        loggerLevel: basic

6.3 java代码方式 vs 属性配置方式

这两种方式所支持的配置项是不同的 !!

  • java代码方式支持的配置项
    在这里插入图片描述
  • 属性配置方式支持的配置项
    在这里插入图片描述

7.配置实践总结

7.1 Ribbon配置 vs Feign配置

在这里插入图片描述

7.2 Feign代码方式 vs 属性方式

在这里插入图片描述
我测试了一下,Ribbon代码方式比属性方式优先级高,Feign代码方式比属性方式优先级低

7.3 最佳配置搭配推荐

  • 尽量使用属性配置,属性配置实现不了再考虑用代码配置
  • 同一个微服务尽量保持配置方式单一性,不要两种配置方式混用,会增加定位代码问题的复杂性

8.多参数请求构造

参考大目老师手记:https://www.imooc.com/article/289000

9.Feign请求非注册服务的接口

这种方式不会使用到Ribbon,所以也叫Feign脱离Ribbon的使用方式

  • 重新定义一个 TestImoocFeignClient
    @FeignClient(name = “getImooc”,url = “www.baidu.com”),name随便自己定义,url就是你想请求的链接。关键就是这个url参数
//@FeignClient(name = "getImooc",url = "localhost:8082/reciteHis/1")
//@FeignClient(name = "getImooc",url = "www.sogou.com")
//@FeignClient(name = "getImooc",url = "www.imooc.com")
@FeignClient(name = "getImooc",url = "www.baidu.com")
public interface TestImoocFeignClient {

    @GetMapping(value = "")
    String index();
}
  • 新建 TestImoocController 使用TestImoocFeignClient里的index方法
@RestController
public class TestImoocController {

    @Autowired
    TestImoocFeignClient testImoocFeignClient;

    @GetMapping(value = "getImooc")
    public String imoocIndex(){
        return this.testImoocFeignClient.index();
    }
}
@FeignClient(name = "getImooc",url = "localhost:8082/reciteHis/1")

10 Feign性能优化

Feign的性能只有RestTemplate50%,但是不用为Feign担心,一般项目的瓶颈不会发生在Feign上,优化仅是为了更好!

10.1 优化原理

第二节 Feign的组成里有Client组件,就是优化这个Client

  • 不和Ribbon配合使用的时候,Client 是默认的Feign.client.default ,这个default里使用的是HttpURLConnection,这个HttpURLConnection,不支持连接池,所以性能不高
  • 和Ribbon配合使用的时候,Client 使用的是LoadBalancerFeignClient,支持代理模式,默认的也是Feign.client.default
    在这里插入图片描述
    从上图可以看出,还有两个构造client的类,一个是HttpClient,一个是OKHttp。优化就是用它们替换default

10.2 使用HttpClient优化

  • 引入依赖
		<dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
        </dependency>
  • 写配置
feign:
  client:
    config:
      # 全局配置
      default:
        loggerLevel: basic
  httpclient:
    # 让feign使用apache httpclient做请求,而不是默认的httpurlconnection
    enabled: true
    # feign 最大连接数
    max-connections: 200
    # feign 单个路径的最大连接数
    max-connections-per-route: 50

10.3 使用OKHttp优化

  • 引入依赖
		<dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
            <!--<version>10.3.0</version>-->
        </dependency>
  • 写配置
feign:
  client:
    config:
      # 全局配置
      default:
        loggerLevel: basic
  okhttp:
    enabled: true
  httpclient:
    # 让feign使用apache httpclient做请求,而不是默认的httpurlconnection
#    enabled: true
    # feign 最大连接数
    max-connections: 200
    # feign 单个路径的最大连接数
    max-connections-per-route: 50

max-connectionsmax-connections-per-route使用的是httpclient里面的

11 Feign常见问题总结

参考大幕老师手记:https://www.imooc.com/article/289005

12 Feign的继承特性

https://cloud.spring.io/spring-cloud-openfeign/reference/html/#spring-cloud-feign-inheritance
在这里插入图片描述

12.1 user-center项目拆分成三个模块

这个可以参照我的另一篇博文 :
https://blog.csdn.net/hantangduhey/article/details/99306490

12.2 未完。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章