文章目录
Feign 声明式远程调用
1.简介
Feign 是一个声明式的HTTP 客户端,它的目的就是让远程调用更加简单。Feign 提供了 HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好 HTTP 请求的参数、格式、地址等信息。
Feign 整合了 Ribbon(负载均衡)和 Hystrix(服务熔断),可以让我们不再需要显式地使用这两个组件。
SpringCloudFeign 在 NetflixFeign 的基础上扩展了对 SpringMVC 注解的支持,在其实现下,我们只需创建一个接口并用注解的方式来配置它,即可完成对服务提供方的接口绑定。简化了SpringCloudRibbon 自行封装服务调用客户端的开发量
2.如何使用呢?
步骤:
- 导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
-
创建接口并在接口上加@FeignClient(name=“要调用的服务名”) 可以使用占位符
-
启动类上加上EnableClients(basePackages = “feign接口所在包名”) 可以使用占位符
小结:
建议使用模块化方式,在编写一个服务时将该服务拆分为三个模块:- clien模块用来专门放feign接口的,以便其它服务直接依赖使用
- common模块用来存放各个微服务公用的一些实体类或工具等
- server模块服务核心模块
例如:
3.组成
以上所列出的属性均可参考Feign官网配置进行配置
原理
4.Feign日志
Feign默认是不输出日志的,那么如何为feign配置日志输出呢?
参考官网:官网配置feign日志
Feign的日志级别:
级别 | 打印内容 |
---|---|
NONE(默认值) | 不记录任何日志 |
BASIC | 仅记录请求方法,URL,响应状态代码以及执行时间(最常用) |
HEADERS | 记录BASIC级别的基础上,记录请求和响应的header |
FULL | 记录请求和响应的header,body和元数据 |
问题1: 细粒度配置
用Java代码实现让特定的Feign接口为Full日志级别
解决1:
// 自定义一个接口:
@FeignClient(name = "user-center",configuration = UserCenterFeignConfiguration.class)
public interface UserCenterFeignClient {
/**
* http://user-center/users/{id}
* @param id
* @return
*/
@GetMapping("/users/{id}")
UserDTO findById(@PathVariable Integer id);
}
/**
再写一个配置类:
该配置类并没加@Configuration注解,否则就必须挪到@Component扫描的包以外,如果加了并且没将其挪到上述扫描的包以外的话,那么我们所定义的所有Feign的日志级别都将为FULL 。这个问题是典型的父子上下文问题
*/
public class UserCenterFeignConfiguration {
@Bean
public Logger.Level level() {
return Logger.Level.FULL;
}
}
// 最后我们还要在配置文件中加入配置才可以实现:(吐槽太麻烦了)
logging:
level:
/**这里加feign接口的全路径的目的在于,想要打印feign日志必须建立在feign接口的日志级别为debug之上*/
com:
ray:
content_center:
feignclient:
UserCenterFeignClient: debug
解决2: 属性配置方式
@FeignClient(name = "user-center")
public interface UserCenterFeignClient {
/**
* http://user-center/users/{id}
* @param id
* @return
*/
@GetMapping("/users/{id}")
UserDTO findById(@PathVariable Integer id);
}
feign:
client:
config:
#想要调用的微服务的名称
user-center:
loggerLevel: full
问题2:全局配置
用Java代码方式实现所有Feign接口都为指定日志级别
- 方式一:出现我们在上面的细粒度配置的父子上下文问题(不推荐)
- 方式二:在@EnableFeignClients(defaultConfiguration=xxx.class)
配置属性方式public class xxx { @Bean public Logger.Level level() { return Logger.Level.FULL; } }
feign: client: config: #全局配置 default: loggerLevel: full
5.配置最佳实践总结
6.多参数请求构造
Tips: 版本:Spring Cloud Greenwich SR1,理论上支持更高版本
GET请求多参数的URL
假设需请求的URL包含多个参数,例如http://localhost:8080/get?id=1&userName=“ray”,该如何使用Feign构造呢?
通过官网我们知道:Feign添加了Spring MVC的注解支持:那就尝试一波
public class User{
String userName;
Long id;
}
@FeignClient("user-service")
public interface UserFeignClient {
@RequestMapping(value = "/get", method = RequestMethod.GET)
public User get(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方法发送请求
正确写法如下:
- 方式一:官网推荐食用
@FeignClient("user-service") public interface UserFeignClient { @GetMapping("/get") public User get(@SpringQueryMap User user); }
- 方式二:
@FeignClient(name = "user-service") public interface UserFeignClient { @RequestMapping(value = "/get", method = RequestMethod.GET) public User get(@RequestParam("id") Long id, @RequestParam("username") String userName); }
POST请求包含多个参数
假设服务提供者的Controller是这样的:
@RestController
public class UserController {
@PostMapping("/post")
public User post(@RequestBody User user) {}
}
咱们如何使用Feign去请求呢?如下:
@FeignClient(name = "user-service")
public interface UserFeignClient {
@PostMapping("/post")
public User post(@RequestBody User user);
}
7.Feign脱离Ribbon使用
Feign如何调用没有注册到注册中心的服务,如何做呢?
比如我想调用baidu:www.baidu.com
//这里的name一定要写,不然就会报错
@FeignClient(name = "baidu", url = "http://www.baidu.com")
public interface TestBaiduFeignClient {
@GetMapping("")
String index();
}
其它场景:
比如你想调用一个外部系统的HTTP接口,就可以这么玩啦。
举2个例子:
- 你想调用支付宝的HTTP API;
- 你的Spring Cloud微服务,想要调用遗留项目的接口(遗留项目还没有来得及重构成Spring Cloud)
总而言之, Feign本身就是一个Http客户端,你可以用Feign发送你想要发送的http请求。想请求哪儿调哪儿哈
8.RestTemplate VS Feign
角度 | RestTemplate | Feign |
---|---|---|
可读性,可维护性 | 一般 | 极佳 |
开发体验 | 欠佳 | 极佳 |
性能 | 很好 | 中等(RestTemplate的50%左右) |
灵活性 | 极佳 | 中等(内置功能可满足绝大多数需求) |
如何选择呢?
原则: 尽量用Feign,杜绝使用RestTemplate,合理选择
9. Feign的性能优化
- 连接池(提升15%左右)
1.使用apache httpclient优化的 先引入依赖: <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency> feign: httpclient: # 让feign使用apache httpclient做请求;而不是默认的urlconnection enabled: true # feign的最大连接数 max-connections: 200 # feign单个路径的最大连接数 max-connections-per-route: 50 2.使用okhttpclient优化 同样引入包,写配置
- 日志级别(设置为BASIC)