Fegin简介
Fegin是一个声明式的Http客户端,它使得写Http客户端变得更简单,使用Fegin只需要创建一个接口并注解,它具有可插拔的注解特性,Nacos很好的兼容了Fegin,默认实现了负载均衡的效果,底层使用了HttpClient作为服务框架。
Spring Cloud 集群中,各个角色的通信基于 REST 服务,因此在调用服务时,就不可避免地需要使用 REST 服务的请求客户端。之前一直使用的是 Spring 自带的 RestTemplate,而RestTemplate 使用 了HttpClient 发送请求。接下来将介绍另一个 REST 客户端: Feign。Feign 框架己经被集成到 Spring Cloud Netflix 项目中,使用该框架可以在 Spring Cloud 的集群中更加简单地调用REST 服务。
Web Service
使用 CXF 调用 REST 服务
CXF 是目前一个较为流行的 Web Service 框架,是 Apache 个开源项目。使用 CXF可以发布和调用使用各种协议的服务,包括 SOAP 协议、 XML/HTTP 等。当前 CXF 已经对REST 风格的 Web Service 提供支持,可以发布或调用REST 风格的 Web Service 。由于CXF 可以与 Spring 进行整合使用并且配置简单,因此得到许多开发者的青睐,所使用的 CXF 需要在 Maven 中加入以下依赖:
编写代码请求/person/{persionid}
服务:
客户端中使用了WebClient类发送了请求,获取响应后读取数据流,获取服务返回json字符串。
使用Restlet调用REST服务
Restlet是一个轻量级的REST框架,使用它可以发布和调用REST风格的Web Service。
使用Restlet的api比较简单,但是Maven中央仓库没有Restlet包,需要在pom.xml中添加以下配置:
RestTemplate调用REST服务
RestTemplate位于org.springframework.web.client.RestTemplate
包spring mvc框架的类,也具有调用http协议的功能。
Spring Cloud Netflix Feign框架
Feign框架也是一个类似的框架,简化了Web Service客户端的开发,在使用Feign时可以使用注解来修饰接口,被注解修饰的接口具有访问Web Service的能力。
Spring Cloud 将Feign集成到Netflix项目中,与Eureka,Ribbon集成时,Feign就具有了负载均衡的功能。与Spring 的整合极大的提高了简洁性。
Feign组件的使用
对于Feign的调用和之前使用RestTemplate
完全不一致,首先对于微服务的调用一定都是基于服务治理的,Feign也遵守,使用RestTemplate对象时是基于@RestController
控制器进行,而Feign基于自生注解@FeignClient
实现,不再仅仅是调用API接口实现服务调用,而是整个客户端都由Feign提供。
在服务额调用者中实现:
- 导入依赖
<!--feign实现微服务的调用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.1.3</version>
</dependency>
注意是openfeign而不是netflix-feign
- 配置调用接口
/**
* 声明微服务名称
*/
@FeignClient(name = "bill-service")
public interface BillClient {
//配置微服务调用接口
@GetMapping(value = "/bill/{id}")
BillMessage getBuId(@PathVariable("id") Integer id);
}
- 在启动类上注解激活feign
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class TestModuleApplication {
@LoadBalanced
@Bean
public RestTemplate getRestTemplate(){ return new RestTemplate(); }
public static void main(String[] args) {
SpringApplication.run(TestModuleApplication.class, args);
}
}
//核心是@EnableFeignClients
- 通过自动的接口调用远程服务
/**
* 基于feign实现微服务
*/
@RestController
@RequestMapping(value = "/test1")
public class Test2Controller {
@Autowired
private BillClient billClient;
@GetMapping(value = "/feign/{id}")
public BillMessage method1(@PathVariable("id") Integer id){
BillMessage buId = billClient.getBuId(id);
return buId;
}
}
服务任然可以调用:
还遇到了一个错误就是Could not find class [org.springframework.cloud.client.loadbalancer.LoadBalancerProperties]
出现该错误的原因是spring-cloud-starter-openfeign
版本过低,升级版本即可。
工作原理解析
对比前几个调用方式:
@RestController
@RequestMapping(value = "/test")
public class TestController {
//获取服务中心实例
@Autowired
private DiscoveryClient discoveryClient;
//java代码访问api接口的实例对象
@Autowired
private RestTemplate restTemplate;
//bill_service的调用
@GetMapping(value = "/bill/{id}")
public BillMessage testMethod1(@PathVariable Integer id){
List<ServiceInstance> bill_service = discoveryClient.getInstances("bill_service");
//该服务只有一个
ServiceInstance instance = bill_service.get(0);
BillMessage billMessage = restTemplate.getForObject(instance.getUri() + "/bill/" + id, BillMessage.class);
System.out.println(instance.getUri());
return billMessage;
}
//使用Ribbon获取服务
@GetMapping(value = "/bill1/{id}")
public BillMessage testMethod2(@PathVariable Integer id){
BillMessage billMessage = restTemplate.getForObject("http://bill-service/bill/"+id,BillMessage.class);
return billMessage;
}
}
通过服务实例ServiceInstance
获取服务信息,在通过RestTemplate
访问接口,步骤比较繁琐;第二种通过Ribbon
获取,封装了服务实例的获取,直接通过服务名调比较方便。对于feigin呢?
/**
* 基于feign实现微服务
*/
@RestController
@RequestMapping(value = "/test1")
public class Test2Controller {
@Autowired
private BillClient billClient;
@GetMapping(value = "/feign/{id}")
public BillMessage method1(@PathVariable("id") Integer id){
BillMessage buId = billClient.getBuId(id);
return buId;
}
}
/**
* 声明微服务名称
*/
@FeignClient(name = "bill-service")
public interface BillClient {
//配置微服务调用接口
@GetMapping(value = "/bill/{id}")
BillMessage getBuId(@PathVariable("id") Integer id);
}
Feign设置了代理服务,调用先通过Proxy也兼顾负载均衡功能,还可以用服务名调用,在Rest接口调用时直接通过代理服务获取
甚至省去了url的拼接,极其方便。Spring Cloud对Feign进行了增强,使Feign支持了Spring MVC注解,并整合了Ribbon和Eureka,从而让Feign的使用更加方便。
feign的设计原理
通过Feign的代理可以帮助我们解决很多问题。由于我们的服务调用都是基于Http协议进行的,所以代码中不得不使用Http相应的客户端来进行服务间沟通。RestTemplate是Spring Web提供的Http客户端,但是使用 RestTemplate 时代码可读性差,并且参数复杂 url 难维护。Feign是一个声明式的http客户端,其作用就是帮助我们优雅地实现http请求的发送,解决上面提到的问题。
总结
feign框架的使用四个步骤:
- 导入spring-cloud整合feign的依赖;
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 添加feign的配置启动类
@SpringBootApplication
//@EnableDiscoveryClient
@EnableFeignClients
public class TestModuleApplication {
/*
@LoadBalanced
@Bean
public RestTemplate getRestTemplate(){ return new RestTemplate(); }
*/
public static void main(String[] args) {
SpringApplication.run(TestModuleApplication.class, args);
}
}
- 创建feign接口代理类,解析服务实例
@FeignClient(name = "bill-service")
public interface BillClient {
//配置微服务调用接口
@GetMapping(value = "/bill/{id}")
BillMessage getBuId(@PathVariable("id") Integer id);
}
//FeignClinet代理根据配置名称,通过GetMapping接口获取服务实例信息并解析,其方法返回值为接口返回Bean对象,无需解析赋值过程。
- 获取feign代理服务类,获取返回实例
/**
* 基于feign实现微服务
*/
@RestController
@RequestMapping(value = "/test1")
public class Test2Controller {
//获取feign代理实例
@Autowired
private BillClient billClient;
@GetMapping(value = "/feign/{id}")
public BillMessage method1(@PathVariable("id") Integer id){
//feign代理实例获取的服务实例对象的接口返回值信息赋值,在调用接口赋值。
BillMessage buId = billClient.getBuId(id);
return buId;
}
}
feign的配置
可通过一些自定义配置来覆盖 feign 的默认配置,可以修改的配置如下:
配置Feign日志有两种方式:1.配置文件方式、2.java代码方式
- 配置文件
#全局生效
feign:
client:
config:
default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
loggerLevel: FULL # 日志级别
#局部生效
feign:
client:
config:
userservice: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
loggerLevel: FULL # 日志级别
- 通过Java代码自定义配置
public class FeignClientConfiguration {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.BASIC;
}
}
然后如果是全局配置,则把它放到 @EnableFeignClients 这个注解中:
@SpringBootApplication
@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
如果是局部配置,则把它放到 @FeignClient 这个注解中:
@FeignClient(value = "userservice", configuration = FeignClientConfiguration.class)
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
更多配置信息参考feign自定义配置感谢作者@wenxuehai