微服务架构整理-(十一SpringCloud实战之OpenFeign)
Posted 浦江之猿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微服务架构整理-(十一SpringCloud实战之OpenFeign)相关的知识,希望对你有一定的参考价值。
SpringCloud实战之OpenFeign
OpenFeign介绍
OpenFeign是 Netflix 公司开发的一个声明式的 REST 调用客户端,其前身为Feign,Feign在2019年5月停止了更新,直接转为OpenFeign,OpenFeign继承了Feign的所有特性,并且在OpenFeign中可以使用SpringMvc的注解,使其在调用服务时更方便。
之前的博客中提到过Ribbon和Hystrix[1,2,3], 它们是SpringCloud进行微服务开发过程中经常使用到的基础组件,每次使用时两者基本都会同时出现,并且配置也是非常相似,因此相同代码很多,从而造成代码冗余,在此基础上OpenFeign整合了Ribbon和Hystrix两个组件,让开发变得更加简单,不仅在配置上大大简化了开发工作,同时还提供了一种声明式的 Web 服务客户端定义方式。
本文给出使用OpenFeign定义消费者,并实现负责均衡和熔断的功能。
实现消费者功能
在之前的博文中,给出了服务提供者serive-product, 以及对应的服务消费者service-order, 这里使用OpenFeign定义一个服务消费者service-openfeign来代替service-order 。
创建Spring Boot 工程
springboot版本:2.2.6.RELEASE 。工程名:open_feign。启动类为:OpenFeignApplication
添加依赖
需要添加3个依赖,分别为eureka,hystrix,openfeign.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
添加注解
启动类上添加@EnableFeignClients
注解
@SpringBootApplication
//开启openfeign,此注解与feign注解一样
@EnableFeignClients
public class OpenFeignApplication
public static void main(String[] args)
SpringApplication.run(OpenFeignApplication.class, args);
声明服务
定义一个接口,接口中声明需要调用的服务,接口上一定要加@FeignClient
注解来指定服务名称(忽略大小写),进而绑定服务,然后再通过SpringMVC 中提供的注解来绑定服务提供者提供的接口。这里以绑定SERVICE-PRODUCT
中的/product/id
为例。
@FeignClient(value = "SERVICE-PRODUCT")
public interface OrderService
@GetMapping(value = "/product/id")
public Product findById(@PathVariable("id") Long id);
SERVICE-PRODUCT
中的/product/id
接口源码如下:
/**
* @author :浦江之猿
*/
@RestController
@RequestMapping("/product")
public class ProductController
@Autowired
ProductService productService;
@GetMapping(value = "/id")
public Product findById(@PathVariable Long id)
Product product = productService.findById(id);
product.setName(product.getName() + "1");
return product;
@GetMapping(value = "/id/price")
public Product findByIdAndPrice(@PathVariable Long id, @PathVariable BigDecimal price)
Product product = productService.findProductByIdAndPrice(id, price);
product.setName(product.getName() + "1");
return product;
@PostMapping(value = "/addProduct")
public Product addProduct(@RequestParam String name, @RequestParam BigDecimal price)
Product product = new Product();
product.setName(name);
product.setPrice(price);
return productService.addProduct(product);
@PutMapping(value = "/updateProduct")
public void updateProduct(@RequestParam Long id, @RequestParam BigDecimal price)
Product product = productService.findById(id);
product.setPrice(price);
productService.updateProduct(product);
@DeleteMapping(value = "/deleteProduct/id")
public void deleteProduct(@PathVariable Long id)
productService.deleteById(id);
在Controller 中调用服务
定义一个Controller 来调用上面的service层,可以发现service层只有接口,没有接口的实现。
/**
* @author :浦江之猿
*/
@RestController
public class OrderController
@Resource
OrderService orderService;
@GetMapping(value = "/buy/id")
public Product findById(@PathVariable("id") Long id)
return orderService.findById(id);
添加配置文件
因为OpenFeign需要获取服务提供者且自己本身也是一个服务,因此配置文件中需要添加eureka。
server:
port: 9007 #端口
spring:
favicon:
enabled: false
application:
name: service-openfeign #服务名称
#配置eureka server
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
#注册到两个eureka上
defaultZone: http://localhost:9005/eureka/,http://localhost:9006/eureka/
结果
到这,整个OpenFeign客户端就搭建好了,浏览器中输入http://localhost:9007/buy/1
。其结果如下:
"id": 1,
"name": "apple2",
"price": 8000.00
Ok,整个服务就打通了,接下来看看OpenFeign是如何实现负载均衡功能和熔断功能的。
实现负载均衡功能
在声明服务创建接口时,就已经添加了负载均衡功能(通过@RequestMapping注解,可以理解为OpenFeign内置了Ribbon),只是这里的负载均衡算法用的是默认算法(轮询)。如果需要改变负载算法,则需要定义一个配置类,然后在配置类中注入需要的算法。以改成随机算法为例:
/**
* @author :浦江之猿
* @date :Created in 2022/5/31 22:02
*/
@Configuration
public class OpenFeignConfig
@Bean
public IRule getIRule()
return new RandomRule();
关于负载均衡的其算法可以参考博文SpringCloud实战之Ribbon
实现熔断功能
OpenFeign整合了Hystrix,所有熔断原理一样,当出现异常时直接进行服务降级,回调异常处理函数。但是这里的异常处理函数与接口一一对应,不是太好理解,可以看下面的例子。OpenFeign中实现熔断需要两步:
配置熔断开关
配置文件中将熔断开关打开
#熔断开关打开
feign:
hystrix:
enabled: true
实现回调函数
为了更好的解释异常处理函数与接口一一对应,再声明一个服务,即在OrderService接口中再绑定一个服务提供者提供的接口。
@GetMapping(value = "/product/id/price")
public Product findByIdAndPrice(@PathVariable Long id, @PathVariable BigDecimal price);
接下来实现OrderService接口,实现类中实现的每个方法就是此接口对应的熔断回调函数。注意:@Component
注解一定要解,不然启动失败,报找不到实例异常。
@Component
public class OrderServiceImpl implements OrderService
@Override
public Product findById(Long id)
return handleBreakCircuit(id, "A fake product from findById");
@Override
public Product findByIdAndPrice(Long id, BigDecimal price)
return handleBreakCircuit(id, "A fake product from findByIdAndPrice");
public Product handleBreakCircuit(Long id, String des)
Product product = new Product();
product.setId(0L);
product.setName(des);
product.setPrice(new BigDecimal(0));
return product;
最后需要在声明接口中回调OrderServiceImple中的函数,只需要在@FeignClient
注解添加fallback
属性即可,完整的OrderService如下:
@FeignClient(value = "SERVICE-PRODUCT", fallback = OrderServiceImpl.class )
public interface OrderService
@GetMapping(value = "/product/id")
public Product findById(@PathVariable("id") Long id);
@GetMapping(value = "/product/id/price")
public Product findByIdAndPrice(@PathVariable Long id, @PathVariable BigDecimal price);
相应的,在服务提供者1中针对findById
选择制造一个超时(默认为1000ms),服务提供者2正常。
@GetMapping(value = "/id")
public Product findById(@PathVariable Long id)
Product product = productService.findById(id);
//故意制造一个超时
try
Thread.sleep(4000);
catch (InterruptedException e)
e.printStackTrace();
product.setName(product.getName() + "1");
return product;
最终结果在以下两者之间随机切换:
//服务提供者1返回的结果
"id": 0,
"name": "A fake product from findById",
"price": 0
//服务提供者2返回的结果
"id": 1,
"name": "apple2",
"price": 8000.00
获取异常信息
通过fallback
可以回调服务降级函数 ,但时当服务提供抛出异常后没法获取异常信息。OpenFeign中提供了fallbackFactory
属性,在fallbackFactory中实现实现OrderService接口,就可以获取异常信息。用下面的类代替上面的OrderServiceImpl不仅能够回调降级函数 ,还能获取异常信息。
//注解不能少
@Component
public class OrderFallbackFactory implements FallbackFactory<OrderService>
@Override
public OrderService create(Throwable arg0)
return new OrderService()
@Override
public Product findById(Long id)
return handleBreakCircuit(id, "A fake product from findById");
@Override
public Product findByIdAndPrice(Long id, BigDecimal price)
return handleBreakCircuit(id, arg0.getMessage());
public Product handleBreakCircuit(Long id, String des)
Product product = new Product();
product.setId(0L);
product.setName(des);
product.setPrice(new BigDecimal(0));
return product;
;
相应的,在服务提供者2中针对findByIdAndPrice
选择制造除0异常,服务提供者1正常。
@GetMapping(value = "/id/price")
public Product findByIdAndPrice(@PathVariable Long id, @PathVariable BigDecimal price)
//制造一个除0异常
int i = 1 / 0;
Product product = productService.findProductByIdAndPrice(id, price);
product.setName(product.getName() + "2");
return product;
最终结果在以下两者之间随机切换:
//服务提供者1返回的结果
"id": 1,
"name": "apple1",
"price": 8000.00
//服务提供者2返回的结果
"id": 0,
"name": "[500] during [GET] to [http://SERVICE-PRODUCT/product/1/8000] [OrderService#findByIdAndPrice(Long,BigDecimal)]: [\\"timestamp\\":\\"2022-06-02T12:51:06.846+0000\\",\\"status\\":500,\\"error\\":\\"Internal Server Error\\",\\"message\\":\\"/ by zero\\",\\"path\\":\\"/product/1/8000\\"]",
"price": 0
总结
关于OpenFeign的基本使用就介绍完了,使用其来不是很难,细心就可以了。最后,希望本文能帮助大家,祝大家在IT之路上少走弯路,一路绿灯不堵车,测试一性通过,bug秒解!
源码下载
以上是关于微服务架构整理-(十一SpringCloud实战之OpenFeign)的主要内容,如果未能解决你的问题,请参考以下文章
微服务架构整理-(七SpringCloud实战之RestTemplate)
微服务架构整理-(七SpringCloud实战之RestTemplate)
微服务架构整理-(八SpringCloud实战之Hystrix [1])
微服务架构整理-(八SpringCloud实战之Hystrix [1])