Feign远程调用

Feign远程调用

一.feign远程调用

1.导入包

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

2.加上注解@EnableFeignClients

3.编写client

//name 服务名
@FeignClient(name = "spring-cloud-alibaba-nacos")
public interface UserCenterFeignClient {
    @GetMapping("/user/{id}")
     String user(@PathVariable String id) ;
}

二.feign细粒度配置自定义-代码方式

级别 打印内容
NONE(默认值) 不记录任何日志
BASIC(适合生产环境使用) 仅记录请求方法,URL,响应代码以及执行时间
HEADERS 记录BASIC级别的基础上,记录请求和响应的header
FULL 记录请求和响应的header,body元数据

1.编写配置类

/**
 * feign的配置类
 * 这个类别加@configuration,否则必须挪到@ComponentScan扫描不到的包,及application意外险
 * 否则父子上下文重叠
 */
public class UserCenterFeignConfiguration {
    @Bean
    public Logger.Level level(){
        //让feign打印所有请求
       return  Logger.Level.FULL;
    }
}

2.@FeignClient注解增加配置

@FeignClient(name = "spring-cloud-alibaba-nacos",configuration = UserCenterFeignConfiguration.class)
public interface UserCenterFeignClient {
    @GetMapping("/user/{id}")
     String user(@PathVariable String id) ;
}

3.修改日志级别

logging:
  level:
    com.yz.alibaba.feignclient.UserCenterFeignClient:  debug

二.feign细粒度配置自定义-属性配置

修改配置

logging:
  level:
    com.yz.alibaba.feignclient.UserCenterFeignClient:  debug
feign:
  client:
    config:
      spring-cloud-alibaba-nacos:
        loggerLevel: full

三.feign细粒度配置自定义-全局配置

  1. 修改启动类注解@EnableFeignClients(defaultConfiguration = UserCenterFeignConfiguration.class) UserCenterFeignConfiguration为自定义配置类

  2. 修改配置文件

logging:
  level:
    com.yz.alibaba.feignclient.UserCenterFeignClient:  debug
feign:
  client:
    config:
      default:
        loggerLevel: full

四.支持的配置项

private Level loggerLevel;       //日志级别
private Integer connectTimeout;  //链接超时时间
private Integer readTimeout;     //读取超时时间
private Class<Retryer> retryer;  //重试策略
private Class<ErrorDecoder> errorDecoder; //错误解码器
private List<Class<RequestInterceptor>> requestInterceptors; //拦截器
private Boolean decode404; //是否对404错误码解码
private Class<Decoder> decoder; //解码器
private Class<Encoder> encoder; //编码器
private Class<Contract> contract; //契约
private ExceptionPropagationPolicy exceptionPropagationPolicy; //异常传播策略

五.代码配置,属性配置对比

配置方式 优点 缺点
代码方式 基于代码,更加灵活 如果Feign的配置类加了@Configuration注解,需要注意父子上下文,
线上修改得重新打包
属性配置 易上手
配置更加直观
线上修改无需重新打包发布,优先级更高
极端场景下,没有代码配置方式更加灵活

六.@FeignClient注解异常

Description:The bean 'spring-cloud-alibaba-nacos.FeignClientSpecification' could not be registered. A bean with that name has already been defined and overriding is disabled.Action:Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

@FeignClient name重复,在配置文件增加配置spring.main.allow-bean-definition-overriding=true

七.feign继承

spring官方不推荐使用,不符合微服务特性,但是挺多公司使用,一致性,方便

使用方式

公共访问接口-> 服务提供者实现公共访问接口-> feign继承公共访问接口->接口调用feign

1. 公共访问接口-公共服务类

@RequestMapping("/hs2")public interface InheritanceService {    @RequestMapping(value = "/hello1", method = RequestMethod.GET)    String hello(@RequestParam("name") String name);    @RequestMapping(value = "/hello2", method = RequestMethod.GET)    Book hello(@RequestHeader("name") String name, @RequestHeader("author") String author, @RequestHeader("price") Integer price) throws UnsupportedEncodingException;    @RequestMapping(value = "/hello3", method = RequestMethod.POST)    String hello(@RequestBody Book book);}

2.服务提供者实现公共访问接口-服务A

@RestControllerpublic class InheritanceFeignController implements InheritanceService {    @Override    public String hello(@RequestParam("name") String name) {        return "hello " + name + "!";    }    @Override    public Book hello(@RequestHeader("name") String name, @RequestHeader("author") String author, @RequestHeader("price") Integer price) throws UnsupportedEncodingException {      Book book = new Book();        book.setName(URLDecoder.decode(name,"UTF-8"));        book.setAuthor(URLDecoder.decode(author,"UTF-8"));        book.setPrice(price);        System.out.println(book);        return book;    }    @Override    public String hello(@RequestBody Book book) {        return "书名为:" + book.getName() + ";作者为:" + book.getAuthor();    }}

3.feign继承公共访问接口-服务B

@FeignClient("spring-cloud-alibaba-nacos")public interface InheritanceFeignClient extends InheritanceService {}

4.接口调用feign-服务B

@RestController@RequiredArgsConstructor(onConstructor = @_(@Autowired))public class InheritanceFeignController {    private final InheritanceService inheritanceService;    @RequestMapping("/hello1")    public String hello1() {        return inheritanceService.hello("张三");    }    @RequestMapping(value = "/hello2")    public Book hello2() throws UnsupportedEncodingException {        Book book = inheritanceService.hello(URLEncoder.encode("三国演义","UTF-8"), URLEncoder.encode("罗贯中","UTF-8"), 33);        System.out.println(book);        return book;    }    @RequestMapping("/hello3")    public String hello3() {        Book book = new Book();        book.setName("红楼梦");        book.setPrice(44);        book.setAuthor("曹雪芹");        return inheritanceService.hello(book);    }}

八.如何使用Feign构造多参数的请求

GET请求多参数的URL

假设需请求的URL包含多个参数,例如http://microservice-provider-user/get?id=1&username=张三 ,该如何使用Feign构造呢?

我们知道,Spring Cloud为Feign添加了Spring MVC的注解支持,那么我们不妨按照Spring MVC的写法尝试一下:

@FeignClient("microservice-provider-user")public interface UserFeignClient {  @RequestMapping(value = "/get", method = RequestMethod.GET)  public User get0(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("microservice-provider-user")public interface UserFeignClient {  @GetMapping("/get")  public User get0(@SpringQueryMap User user);}

方法二[推荐]

@FeignClient(name = "microservice-provider-user")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 = "microservice-provider-user")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); 会导致microservice-provider-user 服务接收到的username是"" ,而不是null。

POST请求包含多个参数

下面来讨论如何使用Feign构造包含多个参数的POST请求。假设服务提供者的Controller是这样编写的:

@RestControllerpublic class UserController {  @PostMapping("/post")  public User post(@RequestBody User user) {    ...  }}

我们要如何使用Feign去请求呢?答案非常简单,示例:

@FeignClient(name = "microservice-provider-user")public interface UserFeignClient {  @RequestMapping(value = "/post", method = RequestMethod.POST)  public User post(@RequestBody User user);}

九.RestTemplate VS Feign

角度 RestTemplate Feign
可读性,可维护性 一般 极佳
开发体验 欠佳 极佳
性能 很好 中等
灵活性 极佳 中等

十.feign线程池

导入包

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

还可以使用

<dependency>    <groupId>io.github.openfeign</groupId>    <artifactId>feign-okhttp</artifactId></dependency>
feign:  #okhttp   httpclient:    #是否开启    enabled: true    #最大连接数    max-connections: 200    #最大路由    max-connections-per-route: 50    #存活时间    time-to-live: 900    #存活时间单位    time-to-live-unit: minutes    #跟随重定向    follow-redirects: true    #连接超时时间    connection-timeout: 2000    #超时重试时间    connection-timer-repeat: 3000