[TOC]
*****
# 1. 使用Feign实现远程HTTP调用
```
fegin的github地址: https://github.com/openfeign/feign
Feign是一个声明式WebService客户端.使用Feign能让编写WebService客户端更加简单,它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解.
Feign也支持可拔插式的编码器和解码器.Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters.
Feign可以与nacos/Eureka和Ribbon组合使用以支持负载均衡.
解决的问题:
1. 代码不可读
2. 复杂的url难以维护
3. 难以相应需求的变化
4. 编程体验不统一
```
# 2. fegin的使用
```
1. 加依赖
compile("org.springframework.cloud:spring-cloud-starter-openfeign")
2. 在启动类或者配置类上加注解 @EnableFeignClients
3. 写配置 使用默认配置
```
```
/**
* name: 请求服务的名称
*/
@FeignClient(name = "ali-pay-service")
public interface UserCenterFeginClient {
/**
* http://ali-pay-service/users/{id}
* @param id
* @return
*/
@GetMapping("/users/{id}")
String findById(@PathVariable Integer id);
}
```
# 3. Feign的组成
![](https://img.kancloud.cn/57/d0/57d087ca9af2f5b9d5755b833ddc286c_824x395.png)
# 4. 细粒度配置自定义
```
4-1. 细粒度配置自定义-Java代码方式-指定日志级别
场景: 通过feign请求服务时,打印请求信息的日志
```
**4-1. Feign的日志级别:**
![](https://img.kancloud.cn/dd/84/dd84b0d36cc545958496e2c4f9c17b2f_768x269.png)
**4-2. 代码方式配置日志级别:**
```
/**
* name: 请求服务的名称
*/
@FeignClient(name = "ali-pay-service", configuration = UserCenterFeignConfiguration.class)
public interface UserCenterFeginClient {
/**
* http://ali-pay-service/users/{id}
* @param id
* @return
*/
@GetMapping("/users/{id}")
String findById(@PathVariable Integer id);
}
```
```
/**
* 自定义日志级别
* 若改类上面有@Configuration注解,就要把它放在启动类之外的包内
* 原因:父子上下文的关系,会被@ComponentScan扫描到用在所有的feignClient上
* 出现一些奇怪的问题,所以建议不要加@Configuration注解
*/
public class UserCenterFeignConfiguration {
@Bean
public Logger.Level level() {
//让feign打印所有请求的细节
return Logger.Level.FULL;
}
}
```
```
logging:
level:
# 自定义feign的日志级别是建立在debug基础上的,若为其他如info,则不会打印任何日志
com.example.feginclient.UserCenterFeginClient: debug
```
**4-3. 配置属性方式-指定日志级别:**
```
feign:
client:
config:
#想要调用的微服务名称
ali-pay-service:
loggerLevel: full
```
**4-4. 全局配置-01-Java代码方式-指定日志级别**
```
@EnableFeignClients(defaultConfiguration = UserCenterFeignConfiguration.class)
```
**4-5. 全局配置-02-配置属性方式-指定日志级别**
```
feign:
client:
config:
#全局配置
default:
loggerLevel: full
```
**4-6. 支持的配置项**
```
1. 代码方式支持的配置项
```
![](https://img.kancloud.cn/9d/e0/9de0d516e93b651a1d91e5ab4a216603_713x372.png)
```
2. 属性方式支持的配置项
```
![](https://img.kancloud.cn/df/6c/df6c83d5afa843d19c4ba7d5c3d57b33_746x434.png)
**4-7. 日志级别配置最佳实践总结**
![](https://img.kancloud.cn/42/ea/42ea9daf80bffd228ac06cab60073472_830x368.png)![](https://img.kancloud.cn/0c/d3/0cd3e51f331c660900284b1ddbdafba1_817x413.png)![](https://img.kancloud.cn/e1/8d/e18d06aa7cedbb51316f9464ff7f3c51_787x333.png)
# 5. 多参数请求构造
```
1. GET方法
/**
* name: 请求服务的名称
*/
@FeignClient(name = "ali-pay-service", configuration = UserCenterFeignConfiguration.class)
public interface UserCenterFeginClient {
@GetMapping("/query")
public User get0(@SpringQueryMap User user);
}
此时会报405的错误,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("ali-pay-service")
public interface UserFeignClient {
@GetMapping("/get")
public User get0(@SpringQueryMap User user);
}
方法二[推荐]
@FeignClient(name = "ali-pay-service")
public interface UserFeignClient {
@RequestMapping(value = "/get", method = RequestMethod.GET)
public User get1(@RequestParam("id") Long id, @RequestParam("username") String username);
}
这是最为直观的方式,URL有几个参数,Feign接口中的方法就有几个参数。使用@RequestParam注解指定请求的参数是什么.
方法三[不推荐]
多参数的URL也可使用Map来构建。当目标URL参数非常多的时候,可使用这种方式简化Feign接口的编写。
@FeignClient(name = "ali-pay-service")
public interface UserFeignClient {
@RequestMapping(value = "/get", method = RequestMethod.GET)
public User get2(@RequestParam Map<String, Object> map);
}
在调用时,可使用类似以下的代码。
public User get(String username, String password) {
HashMap<String, Object> map = Maps.newHashMap();
map.put("id", "1");
map.put("username", "张三");
return this.userFeignClient.get2(map);
}
注意:这种方式不建议使用。主要是因为可读性不好,而且如果参数为空的时候会有一些问题,例如map.put("username", null);
会导致"ali-pay-service 服务接收到的username是 "" ,而不是null.
```
```
2. post方法
@FeignClient(name = "ali-pay-service")
public interface UserFeignClient {
@RequestMapping(value = "/post", method = RequestMethod.POST)
public User post(@RequestBody User user);
}
```
# 6. Feign脱离Ribbon使用
```
场景: 用feign调用的服务没有注册到nacos / eureka上,怎么实现?
```
```
/**
* name: 可以随便写,但必须有
*/
@FeignClient(name = "baidu", url = "http://www.baidu.com")
public interface TestBaiduFeignClient {
@GetMapping("")
String index();
}
```
# 7. RestTemplate 对比 Feign
![](https://img.kancloud.cn/a1/b1/a1b1066a5d807754ddc2f45ee344b9b2_796x258.png)
# 8. Feign性能优化
```
1. 为feign配置连接池[提升15%左右] 以apache的httpClient为例(okhttp同理,需要自己指定版本)
第一步: 加依赖: compile ("io.github.openfeign:feign-httpclient")
第二步: 写配置
feign:
httpclient:
#让feign使用apache httpclient做请求,而不是默认的urlConnection
enabled: true
#feign的最大连接数
max-connections: 200
#feign单个路径的最大连接数
max-connections-per-route: 50
```
```
2. 日志级别(默认不打印的, 建议生产环境设置成BASIC)
```
# 9. 常见问题总结
```
参考: https://www.imooc.com/article/289005
```