Feign

Feign是Netflix开源的声明式HTTP客户端 


2 hello world

2.1 加依赖

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

2.2 加注解

在SpringBoot的启动类上加@EnableFeignClients注解,如果SpringBoot无法扫描到(当前包及其子包)Feign的接口需要指定basePackages

@SpringBootApplication
//Feign定义的接口没有在当前项目;是通过依赖引入的接口;需要指定扫描路径
@EnableFeignClients(basePackages = {"com.nobug.user.client"})
public class ClientApplication {
    public static void main(String[] args) {
        new SpringApplicationBuilder(ClientApplication.class)
                .web(WebApplicationType.SERVLET)
                .run(args);
    }
}

2.3 写配置

无需配置文件

2.4 写接口

@FeignClient(name = "user-center")
public interface UserCenterFeignClient {
    /**
     * 添加用户
     */
    @PostMapping("/user/")
    UserDTO addUser(@SpringQueryMap UserDTO user);
    /**
     * 查找用户
     * http://user-center/user/{userName}
     */
    @GetMapping("/user/{userName}")
    UserDTO getUser(@PathVariable("userName") String userName);
    /**
     * 查找所有
     */
    @GetMapping("/user/")
    UserDTO allUser();
}

3 Feign的组成

//feign.Client.Default#convertAndSend
HttpURLConnection convertAndSend(Request request, Options options) throws IOException {
	URL url = new URL(request.url());
	//HttpURLConnection去发送请求 无连接池 无资源管理的概念
	//性能不是很好
	HttpURLConnection connection = this.getConnection(url);
	if (connection instanceof HttpsURLConnection) {
		HttpsURLConnection sslCon = (HttpsURLConnection)connection;
		if (this.sslContextFactory != null) {
			sslCon.setSSLSocketFactory(this.sslContextFactory);
		}
		if (this.hostnameVerifier != null) {
			sslCon.setHostnameVerifier(this.hostnameVerifier);
		}
	}
//org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient
public class LoadBalancerFeignClient implements Client {
    static final Options DEFAULT_OPTIONS = new Options();
    //用到了代理模式
    private final Client delegate; //默认:带有负载均衡的 Client.Default(null, null) 也会用URLConnection发送请求【后面的优化手段就是引入连接池这些】
	
//org.springframework.cloud.openfeign.ribbon.DefaultFeignLoadBalancedConfiguration
@Configuration(proxyBeanMethods = false)
class DefaultFeignLoadBalancedConfiguration {
	@Bean
	@ConditionalOnMissingBean
	public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) {
		return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory, clientFactory);
	}
}

4 指定日志级别

2020-04-07 09:40:03.482 DEBUG 4060 --- [nio-9761-exec-1] c.n.u.client.UserCenterInfoFeignClient   : [UserCenterInfoFeignClient#info] <--- HTTP/1.1 200 (1788ms)
2020-04-07 09:40:03.482 DEBUG 4060 --- [nio-9761-exec-1] c.n.u.client.UserCenterInfoFeignClient   : [UserCenterInfoFeignClient#info] content-length: 17
2020-04-07 09:40:03.482 DEBUG 4060 --- [nio-9761-exec-1] c.n.u.client.UserCenterInfoFeignClient   : [UserCenterInfoFeignClient#info] content-type: text/plain;charset=UTF-8
2020-04-07 09:40:03.482 DEBUG 4060 --- [nio-9761-exec-1] c.n.u.client.UserCenterInfoFeignClient   : [UserCenterInfoFeignClient#info] date: Tue, 07 Apr 2020 01:40:03 GMT
2020-04-07 09:40:03.482 DEBUG 4060 --- [nio-9761-exec-1] c.n.u.client.UserCenterInfoFeignClient   : [UserCenterInfoFeignClient#info] 
2020-04-07 09:40:03.482 DEBUG 4060 --- [nio-9761-exec-1] c.n.u.client.UserCenterInfoFeignClient   : [UserCenterInfoFeignClient#info] 192.168.56.1:8764
2020-04-07 09:40:03.483 DEBUG 4060 --- [nio-9761-exec-1] c.n.u.client.UserCenterInfoFeignClient   : [UserCenterInfoFeignClient#info] <--- END HTTP (17-byte body)

4.1 细粒度配置 

4.1.1 Java代码配置 

@FeignClient(name="user-center", configuration = UserCenterFeignConfiguration.class)
public interface UserCenterInfoFeignClient {
    /**
     * 返回服务器的ip和端口信息
     */
    @GetMapping("/user/info")
    String info();
}
/**
 * 自定义日志级别
 * 这个类别加@Configuration注解了,否则必须挪到@ComponentScan能扫描到的包以外,不然就成为全局配置了【父子上下文重复扫描】
 */
public class UserCenterFeignConfiguration {
    @Bean
    public Logger.Level level(){
        //打印所有请求的细节
        return Logger.Level.FULL;
    }
}
logging:
  level:
    com.nobug.user.client.UserCenterInfoFeignClient: debug

4.1.2 配置文件

feign.client.config.<feignName>.loggerLevel: full
feign:
  client:
    config:
      user-center:
        loggerLevel: full

4.2 全局配置

4.2.1 Java代码配置

方式一:让父子上下文ComponentScan重叠(强烈不建议使用

方式二【唯一正确的途径】: @EnableFeignClients(defaultConfiguration=xxx.class)

@EnableFeignClients(basePackages = {"com.nobug.user.client"}, defaultConfiguration = GlobalFeignConfiguration.class) //全局配置
@SpringBootApplication
public class ClientApplication {
    public static void main(String[] args) {
        new SpringApplicationBuilder(ClientApplication.class)
                .web(WebApplicationType.SERVLET)
                .run(args);
    }
}
public class GlobalFeignConfiguration {
    @Bean
    public Logger.Level level(){
        //打印所有请求的细节
        return Logger.Level.FULL;
    }
}

4.2.2 配置文件

将想要调用的微服务名称换成default就可以实现属性方式的全局配置

feign.client.config.<feignName>.loggerLevel: full
feign:
  client:
    config:
      default:
        loggerLevel: full

5 支持的配置项

5.1 代码方式

配置项作用
Feign.BuilderFeign的入口
ClientFeign底层用什么去请求
Contract契约,注解支持
Encoder编码器,用于将对象转换成HTTP请求消息体
Decoder解码器,将响应消息体转换成对象
Logger日志管理器
Logger.Level指定日志级别
Retryer指定重试策略
ErrorDecoder指定错误解码器
Request.Options超时时间
Collection<RequestInterceptor>拦截器
SetterFactory用于设置Hystrix的配置属性,Feign整合Hystrix才会用

5.2 属性方式


6 配置最佳实践总结

6.1 Ribbon配置 vs Feign配置


6.2 Feign代码方式 vs 属性方式

配置方式优点缺点
代码配置基于代码,更加灵活如果Feign的配置类加了Configuration注解,需注意父子上下文;线上修改得重新打包、发布
属性配置

易上手;配置更加直观;线上修改无需重新打包、发布;优先级更高

【全局代码 < 全局属性 < 细粒度代码 < 细粒度属性】

极端场景下没有代码配置方式灵活

6.3 最佳实践

  • 尽量使用属性配置,属性方式实现不了的情况下再考虑用代码配置
  • 在同一个微服务内尽量保持单一性,比如统一使用属性配置,不要两种方式混用,增加定位代码的复杂性

7 Feign的继承

  • 官方观点:不建议使用
  • 业界观点:很多公司使用

https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#spring-cloud-feign-inheritance

It is generally not advisable to share an interface between a server and a client. It introduces tight coupling, and also actually doesn’t work with Spring MVC in its current form (method parameter mapping is not inherited).


8 多参数请求构造

http://www.imooc.com/article/289000

@FeignClient(name = "user-center")
public interface UserCenterFeignClient {
    /**
     * ~/user/q?username=1&password=12346
     */
    @GetMapping("/user/q")
    UserDTO query(@SpringQueryMap UserDTO user);
}

如果有两个以上的FeignClient接口name相同会报错

spring:
  main:
    allow-bean-definition-overriding: true

9 Feign脱离Ribbon使用

/**
 * 脱离ribbon的使用
 * name必须有,随便取
 */
@FeignClient(name = "baidu", url = "www.baidu.com")
public interface BaiduFeignClient {
    @GetMapping("")
    String getIndexHtml();
}

10 RestTemplate vs Feign

角度RestTemplateFeign
可读性、可维护性一般极佳
开发体验欠佳极佳
性能很好中等  (RestTemplate的50%左右)
灵活性极佳中等 (内置功能可满足绝大多数需求)

如何选择?

原则:尽量用Feign,杜绝使用RestTemplate


11 Feign性能优化

11.1 连接池【提升15%左右】

加依赖

<!--httpclient-->
<!--feign优化-->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

写配置

feign:
  httpclient:
    max-connections: 200           # feign的最大连接数
    max-connections-per-route: 50  # feign单个路径最大的连接数
    enabled: true                  # 让feign使用apache httpclient发送请求,而不是默认的urlConnection

 okhttp

<!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-okhttp -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
    <version>10.9</version>
</dependency>
feign:
  httpclient:
    max-connections: 200
    max-connections-per-route: 50
  okhttp:
    enabled: true

11.2 日志级别


12 常见问题

http://www.imooc.com/article/289005

https://keepgoon.blog.csdn.net/article/details/101297264 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值