Feign 是Spring Cloud Netflix组件中的一个轻量级Restful的 HTTP 服务客户端,完结了负载均衡和 Rest 调用的开源结构,封装了Ribbon和RestTemplate, 完结了WebService的面向接口编程,进一步降低了项目的耦合度。

先来看咱们以前利用RestTemplate发起长途调用的代码:

String url = "http://user-service:8081/user/"+order.getUserId();
User user = restTemplate.getForObject(url, User.class);

以上的代码存在参数杂乱、URL难以维护等问题,如当我有一台服务地址换了,那么这时候就需求云同步修正url,那要是多台要修正的情况下那就得改很多台,当咱们服务多的时候这是个很麻烦的工作。

1.Feign替代RestTemplate

Fegin的运用过程如下:

1)引进依靠

咱们在order-service服务的pom文件中引进feign的依靠:

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

2)增加注解

在order-service的发动类增加注解敞开Feign的功用:

教你使用Feign替换RestTemplate

3)编写Feign的客户端

在order-service中新建一个接口,内容如下:

package cn.itcast.order.service.feign;
import cn.itcast.order.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("user-service")
public interface UserFeignClient {
    @GetMapping("/user/{id}")
    User queryById(@PathVariable("id") Long id);
}

这个客户端首要是根据SpringMVC的注解来声明长途调用的信息,比方:

  • 服务称号:user-service
  • 恳求办法:GET
  • 恳求路径:/user/{id}
  • 恳求参数:Long id
  • 回来值类型:User

这样,Feign就能够协助咱们发送http恳求,无需自己运用RestTemplate来发送了。底层会通过服务称号:user-service去映射到具体的user服务对应的url地址。

4)测验

修正order-service中的OrderService类中的queryOrderById办法,运用Feign客户端替代RestTemplate:

@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    UserFeignClient feignClient;
    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        User user = feignClient.queryById(orderId);
        order.setUser(user);
        // 4.回来
        return order;
    }
}

测验调用:

教你使用Feign替换RestTemplate

2.自界说装备

Feign能够支撑很多的自界说装备,如下表所示:

类型 效果 说明
feign.Logger.Level 修正日志等级 包含四种不同的等级:NONE、BASIC、HEADERS、FULL
feign.codec.Decoder 呼应成果的解析器 http长途调用的成果做解析,例如解析json字符串为java目标
feign.codec.Encoder 恳求参数编码 将恳求参数编码,便于通过http恳求发送
feign. Contract 支撑的注解格式 默许是SpringMVC的注解
feign. Retryer 失利重试机制 恳求失利的重试机制,默许是没有,不过会运用Ribbon的重试

一般情况下,默许值就能满足咱们运用,假如要自界说时,只需求创立自界说的@Bean覆盖默许Bean即可。

下面以日志为例来演示如何自界说装备。

2.1.装备文件办法

根据装备文件修正feign的日志等级能够针对单个服务:(注意:有时yml装备文件中有中文注释会报错)

feign:
  client:
    config: 
      user-service: # 针对某个微服务的装备
        loggerLevel: FULL #  日志等级

也能够针对一切服务:

feign:
  client:
    config: 
      default: # 这儿用default便是大局装备,假如是写服务称号,则是针对某个微服务的装备
        loggerLevel: FULL #  日志等级

而日志的等级分为四种:

  • NONE:不记载任何日志信息,这是默许值。
  • BASIC:仅记载恳求的办法,URL以及呼应状态码和执行时间
  • HEADERS:在BASIC的基础上,额外记载了恳求和呼应的头信息
  • FULL:记载一切恳求和呼应的明细,包含头信息、恳求体、元数据。

2.2.Java代码办法

也能够根据Java代码来修正日志等级,先声明一个类,然后声明一个Logger.Level的目标:

public class DefaultFeignConfiguration  {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.BASIC; // 日志等级为BASIC
    }
}

假如要大局生效,将其放到发动类的@EnableFeignClients这个注解中:

package cn.itcast.order;
import cn.itcast.order.config.DefaultFeignConfiguration;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@MapperScan("cn.itcast.order.mapper")
    @SpringBootApplication
    // 装备类的办法敞开大局日志记载
    //@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class) // 敞开feign客户端的支撑
    @EnableFeignClients // 敞开feign客户端的支撑
    public class OrderApplication {
        public static void main(String[] args) {
            SpringApplication.run(OrderApplication.class, args);
        }
        //......
    }

假如是局部生效,则把它放到对应的@FeignClient这个注解中:

package cn.itcast.order.feign;
import cn.itcast.order.config.DefaultFeignConfiguration;
import cn.itcast.order.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
// 指定服务日志装备
//@FeignClient(value = "userservice",configuration = DefaultFeignConfiguration.class)
@FeignClient(value = "userservice")
    public interface UserFeignClient {
        @GetMapping("/user/{id}")
        User queryById(@PathVariable("id") Long id);
    }

2.3.Feign运用优化

Feign底层发起http恳求,依靠于其它的结构。其底层客户端完结包含:

•URLConnection:默许完结,不支撑衔接池

•Apache HttpClient :支撑衔接池

•OKHttp:支撑衔接池

因此提高Feign的功能首要手段便是运用衔接池替代默许的URLConnection。

这儿咱们用Apache的HttpClient来演示。

1)引进依靠

在order-service的pom文件中引进Apache的HttpClient依靠:

<!--httpClient的依靠 -->
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-httpclient</artifactId>
</dependency>

2)装备衔接池

在order-service的application.yml中增加装备:

feign:
  client:
    config:
      default: # default大局的装备
        loggerLevel: BASIC # 日志等级,BASIC便是根本的恳求和呼应信息
  httpclient:
    enabled: true # 敞开feign对HttpClient的支撑
    max-connections: 200 # 最大的衔接数
    max-connections-per-route: 50 # 每个路径的最大衔接数

max-connections解释:比方有以下情况,有一个服务A一起有可能会拜访B服务和C服务,这时候装备的最大衔接数指的便是A在拜访B和C时,总的衔接数不超过200。

max-connections-per-route解释:指的是A服务拜访B服务时的路径最大衔接数据为50,也便是200个衔接,A服务到B服务的拜访最多只会有50个衔接,当超出50个衔接时,其他衔接就会路由到B服务之外的服务中。

接下来,在FeignClientFactoryBean中的loadBalance办法中打断点:

Debug办法发动order-service服务,能够看到这儿的client,底层便是Apache HttpClient:

教你使用Feign替换RestTemplate

改成http衔接池后,从演示项目后台的恳求日志中能够发现会从原来的几十ms变成个位数ms,有爱好的小伙伴能够自己测验一下。

2.4.最佳实践-抽取feign-api接口

现在存在一个问题,咱们现在演示的是只有一个order-service调用user-service,那假如当有多个服务要去调userservice的时候呢,那是否需求在每个service里都去写一份长途调用user-service的代码?完结没必要是不是?所以,把这部分代码直接抽成一个module打成jar包,在需求调用的当地引进即可。

教你使用Feign替换RestTemplate

将Feign的Client抽取为独立模块,并且把接口有关的POJO、默许的Feign装备都放到这个模块中,提供给一切顾客运用。

行将UserClient、User、Feign的默许装备都抽取到一个feign-api包中,一切微服务引用该依靠包,即可直接运用。

1)抽取

首先创立一个module,命名为feign-api:

项目结构:

教你使用Feign替换RestTemplate

在feign-api中然后引进feign的starter依靠

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

然后,order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中

2)在order-service中运用feign-api

首先,删除order-service中的UserClient、User、DefaultFeignConfiguration等类或接口。

在order-service的pom文件中中引进feign-api的依靠:

<dependency>
  <groupId>cn.itcast.demo</groupId>
  <artifactId>feign-api</artifactId>
  <version>1.0</version>
</dependency>

修正order-service中的一切与上述三个组件有关的导包部分,改成导入feign-api中的包

扫描包问题

最终,包的扫描要指定一下,否则发动会报错找不到userfeignclient

办法一:

指定Feign应该扫描的包:

@EnableFeignClients(basePackages = “cn.itcast.feign.clients”)

办法二:

指定需求加载的Client接口:

@EnableFeignClients(clients = {UserClient.class})

Feign完结原理

Feign的底层源码完结首要包含以下几个部分:

  1. 接口界说

Feign的接口界说类似于Java的接口界说,可是它运用了注解来描述HTTP恳求的参数和回来值。例如,@RequestMapping注解用于指定HTTP恳求的URL和恳求办法,@RequestParam注解用于指定HTTP恳求的参数,@RequestBody注解用于指定HTTP恳求的恳求体,@PathVariable注解用于指定HTTP恳求的路径参数等。

  1. 动态署理

Feign运用了Java的动态署理技术来生成HTTP恳求的完结类。当应用程序调用Feign接口的办法时,Feign会动态生成一个HTTP恳求的完结类,并将恳求参数传递给该完结类。该完结类会将恳求参数转换为HTTP恳求,并发送给长途服务。当长途服务回来呼应时,该完结类会将呼应转换为Java目标,并回来给应用程序。

  1. 编码器和解码器

Feign运用了编码器和解码器来将Java目标转换为HTTP恳求和呼应。编码器将Java目标转换为HTTP恳求的恳求体,解码器将HTTP呼应的呼应体转换为Java目标。Feign支撑多种编码器和解码器,例如JSON编码器和解码器、XML编码器和解码器等。

  1. 负载均衡

Feign能够与负载均衡器无缝集成,以完结服务的负载均衡。当应用程序调用Feign接口的办法时,Feign会将恳求发送给负载均衡器,负载均衡器会选择一个可用的服务实例,并将恳求转发给该实例。假如该实例不可用,则负载均衡器会选择另一个可用的服务实例,并将恳求转发给该实例。

  1. 断路器

Feign能够与断路器无缝集成,以完结服务的容错。当应用程序调用Feign接口的办法时,Feign会将恳求发送给断路器,断路器会检查服务实例的可用性。假如服务实例不可用,则断路器会回来一个默许的呼应,以避免应用程序出现异常。假如服务实例可用,则断路器会将恳求转发给该实例,并回来实例的呼应。