转载

Feign简介

在本教程中,我们将介绍Feign。我们还将谈谈 Spring Boot 和Feign。

在本教程中,我们将了解如何使用Feign编写声明性REST客户端。它通过抽象出我们通常编写的样板代码,使编写Web客户端变得容易。我们需要做的就是声明一个接口并注释它及其方法。实际的实现将由框架在运行时完成,以及调用之间的各种消息转换。我们首先需要设置一个使用已知端点运行的示例 REST API ,以便我们可以使用feign客户端调用它们。

1.服务设置

我们的示例服务是一个简单的spring-boot应用程序,包含一个具有两个端点的REST控制器:

@RestController
<b>public</b> <b>class</b> ProductController {

 <b>private</b> <b>static</b> List < Product > productList = <b>new</b> ArrayList < > ();
 <b>static</b> {
  productList.add(<b>new</b> Product(1, <font>"product-1"</font><font>, 12.0));
  productList.add(<b>new</b> Product(2, </font><font>"product-2"</font><font>, 34.0));
  productList.add(<b>new</b> Product(3, </font><font>"product-3"</font><font>, 9.0));
 }

 @GetMapping(</font><font>"/products"</font><font>)
 <b>public</b> ResponseEntity << ? > getProsucts() {

  <b>return</b> ResponseEntity.ok(productList);

 }

 @GetMapping(</font><font>"/product/{id}"</font><font>)
 <b>public</b> ResponseEntity << ? > getProsucts(@PathVariable <b>int</b> id) {

  Product product = findProduct(id);
  <b>if</b> (product == <b>null</b>) {
   <b>return</b> ResponseEntity.badRequest()
    .body(</font><font>"Invalid product Id"</font><font>);
  }

  <b>return</b> ResponseEntity.ok(product);

 }

 <b>private</b> Product findProduct(<b>int</b> id) {
  <b>return</b> productList.stream()
   .filter(user -> user.getId()
    .equals(id))
   .findFirst()
   .orElse(<b>null</b>);
 }

}
</font>

两个端点是' /products'和' product/{id}',它们返回一个产品列表和一个基于分别传递的id的产品。如果未找到产品,则返回HTTP.BAD_REQUEST响应。以下是application.properties:

server.port=8081
spring.application.name=product-service

Product服务将在端口8081上运行

2.客户端安装

创建Spring启动应用程序的最佳方法是Spring Initializr。选择Spring Boot版本,并添加“Web”,“Feign”依赖项。将它生成为Maven项目,你就完成了。请注意pom.xml中的以下依赖项:

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

Feign最初由Netflix赞助,但后来开源。在spring-boot 1.x版本中,feign依赖项来自Netflix但是从spring-boot 2.x开始使用openfeign。通过在主类中添加' @EnableFeignClients' 使项目能够使用Feign客户端:

@SpringBootApplication
@EnableFeignClients
<b>public</b> <b>class</b> FeignClientExampleApplication {

 <b>public</b> <b>static</b> <b>void</b> main(String[] args) {
  SpringApplication.run(FeignClientExampleApplication.<b>class</b>, args);
 }
}

让我们来定义我们的产品服务Feign客户端。首先,我们必须创建一个接口ProductServiceClient,并通过@FeignClient指定名称和产品服务的URL运行来对其进行注释。至于方法,我们只需要声明它并注释就像Spring MVC样式一样。我们还需要告诉它可能需要哪些输入以及转换所需的响应类型。

@FeignClient(name = <font>"product-service"</font><font>, url = </font><font>"http://localhost:8081"</font><font>)
<b>public</b> <b>interface</b> ProductServiceClient {

 @RequestMapping(value = </font><font>"/products"</font><font>, method = RequestMethod.GET)
 <b>public</b> List < Product > getAllProducts();

 @RequestMapping(value = </font><font>"/product/{id}"</font><font>, method = RequestMethod.GET)
 <b>public</b> Product getProduct(@PathVariable(</font><font>"id"</font><font>) <b>int</b> productId);

}
</font>

Spring将使用openfeign在运行时实现此接口的实现。我们必须在项目中定义Product类,因为我们正在将响应转换为它:

<b>public</b> <b>class</b> Product {

 <b>private</b> Integer id;

 <b>private</b> String name;

 <b>private</b> <b>double</b> price;

 <b>public</b> Product(Integer id, String name, <b>double</b> price) {
  <b>this</b>.id = id;
  <b>this</b>.name = name;
  <b>this</b>.price = price;
 }

 <b>public</b> Integer getId() {
  <b>return</b> id;
 }

 <b>public</b> String getName() {
  <b>return</b> name;
 }

 <b>public</b> <b>double</b> getPrice() {
  <b>return</b> price;
 }
}

现在让我们在AppController中使用这个ProductServiceClient。为此,我们需要@Autowired将ProductServiceClient放入我们的控制器中。

@RestController
<b>public</b> <b>class</b> AppController {

 @Autowired
 ProductServiceClient productServiceClient;

 @GetMapping(<font>"/fetchProducts"</font><font>)
 <b>public</b> ResponseEntity << ? > fetchProducts() {

  <b>return</b> ResponseEntity.ok(productServiceClient.getAllProducts());
 }

 @GetMapping(</font><font>"/fetchProduct/{id}"</font><font>)
 <b>public</b> ResponseEntity << ? > fetchProduct(@PathVariable <b>int</b> id) {

  <b>return</b> ResponseEntity.ok(productServiceClient.getProduct(id));
 }

}
</font>

这就是我们所要做的。让我们使用Postman进行测试:

http://localhost:8080/fetchProduct/1

在简单代码的背后,所有样板代码都由spring和openfeign库处理。这样可以减少代码,减少出错的机会。

3.处理错误的错误

默认情况下,Feign仅针对任何错误情况(其中响应不是2XX或者存在转换错误等)抛出FeignException。

但是,如果找不到产品ID,您可能希望捕获这些错误并在最终处理响应,就像产品服务引发的BAD_REQUEST错误一样。我们首先定义我们的自定义ProductNotFound异常:

<b>public</b> <b>class</b> ProductNotFoundException <b>extends</b> RuntimeException {

 <b>private</b> <b>static</b> <b>final</b> <b>long</b> serialVersionUID = 1 L;

 <b>public</b> ProductNotFoundException(String msg) {
  <b>super</b>(msg);
 }
}

现在让我们为这个应用定义我们的异常处理程序:

@RestControllerAdvice
<b>public</b> <b>class</b> AppExceptionHandler {

 @ResponseBody
 @ExceptionHandler(value = ProductNotFoundException.<b>class</b>)
 <b>public</b> ResponseEntity << ? > handleException(ProductNotFoundException exception) {
  <b>return</b> ResponseEntity.status(HttpStatus.NOT_FOUND)
   .body(exception.getMessage());
 }

}

现在要捕获FeignException并提供您自己的实现,您需要实现feign.codec.ErrorDecoder并将其在Spring应用程序上下文中注册为bean 。

@Component
<b>public</b> <b>class</b> AppFeignErrorDecoder implements ErrorDecoder {

 <b>private</b> <b>final</b> ErrorDecoder defaultErrorDecoder = <b>new</b> Default();

 @Override
 <b>public</b> Exception decode(String methodKey, Response response) {
  <b>if</b> (response.status() >= 400 && response.status() <= 499) {
   <b>throw</b> <b>new</b> ProductNotFoundException(<font>"Product Not Found"</font><font>);
  }

  <b>return</b> defaultErrorDecoder.decode(methodKey, response);
 }

}
</font>

如您所见,我们捕获了所有4xx错误并抛出了我们自己的自定义异常。

4. .使用Feign和Eureka和Ribbon

通常在微服务架构中,所有服务都注册到像Eureka这样的注册服务,并且可能存在运行相同服务的多个实例。因此,您可能不希望在Feign客户端中对URL进行硬编码,也希望连接到响应更快的服务实例。

我们来设置一个Eureka服务器。同样,我们将使用Spring Initializr来创建它。

然后您需要做的就是添加@EnableEurekaServer到主类:

@SpringBootApplication
@EnableEurekaServer
<b>public</b> <b>class</b> EurekaServerApplication {

 <b>public</b> <b>static</b> <b>void</b> main(String[] args) {
  SpringApplication.run(EurekaServerApplication.<b>class</b>, args);
 }
}

以下是application.properties:server.port=8761。因此,我们的Eureka服务器将在8761上运行,这是Spring的推荐端口。

现在注册我们的2项服务。您只需在我们项目中添加以下依赖项pom.xml:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

添加@EnableDiscoveryClient到应用程序的主类,如:

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
<b>public</b> <b>class</b> FeignClientExampleApplication {

 <b>public</b> <b>static</b> <b>void</b> main(String[] args) {
  SpringApplication.run(FeignClientExampleApplication.<b>class</b>, args);
 }
}

(banq注:Spring2 已经不需要@EnableDiscoveryClient)

启动这两个应用程序,您将看到他们在Eureka服务器中注册了。

现在让我们从ProductServiceClient中删除硬编码的URL:

@FeignClient(name = <font>"product-service"</font><font> </font><font><i>/*, url = "http://localhost:8081"*/</i></font><font> )
<b>public</b> <b>interface</b> ProductServiceClient {
 </font><font><i>// same as previous</i></font><font>
}
</font>

现在,幕后,ProductServiceClient将使用name属性从Eureka服务器获取URL (“product-service”)。Ribbon是一个客户端负载均衡器,它附带了我们所包含的依赖项。这将自动获取最佳服务器以供我们进行其余调用。

原文  https://www.jdon.com/51616
正文到此结束
Loading...