– Feign是一个声明性web服务客户机。它使编写web服务客户机变得更容易。
– 它的使用方法是定义一个服务接口并在上面添加注解。
– Feign支持可插拔编码器和解码器。
– Spring Cloud对Feign进行了封装,使其支持SpringMVC标准注解和HttpttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
Feign旨在使编写Java Http客户端变得更容易。
前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口可能被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由它来定义和实现依赖服务接口的定义。在Feign的实现下,只需要创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上标注一个Feign注解即可),即可完成对服务提供方的的接口绑定,简化了使用Spring Cloud Ribbon时,自动封装服务调用客户端的开发量。
利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务的调用。
Feign是SpringCloud组件中一个轻量级RESTful的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务。
org.springframework.cloud
spring-cloud-starter-feign
OpenFeign是SpringCloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等。OpenFeign 的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
在启动类上使用@EnableFeignClients注解开启Feign客户端,并注入RestTemplate
package com.fcant.userservice;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
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;
/**
* UserServiceApplication
*
* encoding:UTF-8
* @author Fcant 9:54 2019/12/9
*/
@EnableFeignClients
@SpringCloudApplication
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
package com.fcant.userservice.feign;
import com.fcant.userservice.bean.Country;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
ICountryService
通过@FeignClient注解指定Feign客户端,名称为要访问的服务名
encoding:UTF-8 *
@author Fcant 10:24 2019/12/9 */
@FeignClient(name = “area-service”) public interface ICountryService {
/**
queryCountry
Feign中没有指定@PathVariable(name = “countryId”)变量映射关系,必须通过name属性指定 *
@param countryId 城市ID
@return com.fcant.userservice.bean.Country
@author Fcant 10:53 2019/12/9 */ @GetMapping(“/country/{countryId}”) Country queryCountry(@PathVariable(name = “countryId”) long countryId);
}
接口层的调用和使用和平常的Controller没什么区别,把Service层注入即可
java
package com.fcant.userservice.controller;
import com.fcant.userservice.bean.Country;
import com.fcant.userservice.bean.User;
import com.fcant.userservice.feign.ICountryService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* FeignController
* <p>
* encoding:UTF-8
*
* @author Fcant 10:06 2019/12/9
*/
@RestController
@RequestMapping("/user")
public class FeignController {
private static final List<User> USER_LIST;
static {
List<User> userList = new ArrayList<>();
userList.add(new User(3L, "User 03", "男", "[email protected]", 1L, ""));
userList.add(new User(4L, "User 04", "女", "[email protected]", 2L, ""));
USER_LIST = userList;
}
private final ICountryService countryService;
public FeignController(ICountryService countryService) {
this.countryService = countryService;
}
@GetMapping("/feign")
public List<User> queryUser() {
List<User> userList = new ArrayList<>(USER_LIST);
userList.forEach(user -> {
Country country = countryService.queryCountry(user.getCountryId());
user.setCountryName(country.getCountryName());
});
return userList;
}
}
A.在接口的注解上添加fallback属性指定异常回调的类
package com.fcant.userservice.feign;
import com.fcant.userservice.bean.Country;
import com.fcant.userservice.feign.impl.CountryServiceImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* ICountryService
* 通过@FeignClient注解指定Feign客户端,名称为要访问的服务名
* <p>
* encoding:UTF-8
*
* @author Fcant 10:24 2019/12/9
*/
@FeignClient(name = "area-service", fallback = CountryServiceImpl.class)
public interface ICountryService {
/**
* queryCountry
* Feign中没有指定@PathVariable(name = "countryId")变量映射关系,必须通过name属性指定
*
* @param countryId 城市ID
* @return com.fcant.userservice.bean.Country
* @author Fcant 10:53 2019/12/9
*/
@GetMapping("/country/{countryId}")
Country queryCountry(@PathVariable(name = "countryId") long countryId);
}
B.在指定的处理异常的类中实现接口的方法进行处理
package com.fcant.userservice.feign.impl;
import com.fcant.userservice.bean.Country; import com.fcant.userservice.feign.ICountryService; import org.springframework.stereotype.Component;
/**
CountryServiceImpl
encoding:UTF-8 *
@author Fcant 14:24 2019/12/9 */ @Component public class CountryServiceImpl implements ICountryService {
private static final Country ERROR = new Country(1L, “ERROR”); /**
queryCountry
Feign中没有指定@PathVariable(name = “countryId”)变量映射关系,必须通过name属性指定 *
@param countryId 城市ID
@return com.fcant.userservice.bean.Country
@author Fcant 10:53 2019/12/9 */ @Override public Country queryCountry(long countryId) { return ERROR; }
}
Fegin的异常处理属于熔断的范围,要在配置文件配置开启熔断-application.yml
feign:
hystrix:
enabled: true
如果不配置访问超时则会报如下异常
feign.RetryableException: Read timed out executing POST http://******
因为Feign接口调用分两层,Ribbon的调用和Hystrix调用,理论上设置Ribbon的时间即可,但是Ribbon的超时时间和Hystrix的超时时间需要结合起来,按照木桶原则最低的就是Feign的超时时间,建议最好配置超时时间一致。经过配置一下application设置后,完美解决了问题。因为第三方接口中需要3~20秒不等的时间,所以这个数值也是根据自己的业务系统情况设置的。
# hystrix的超时时间
hystrix:
command:
default:
execution:
timeout:
enabled: true
isolation:
thread:
timeoutInMilliseconds: 30000
# ribbon的超时时间-设置feign客户端超时时间(openfeign默认支持ribbon)
ribbon:
# 指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ReadTimeout: 30000
# 指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 30000
Feign提供了日志打印功能,可以通过配置来调整日志级别,从而了解Feign中Http请求的细节。对Feign接口的调用情况进行检控和输出。
(1)添加配置类指明日志打印级别
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
(2)application.yml/properties配置文件开启日志的Feign客户端
logging:
level:
#feign日志以什么级别监控哪个接口
com.atguigu.springcloud.service.PaymentFeignService: debug