Spring Cloud Netflix 声明性REST客户端:Feign

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Wyx_wx/article/details/89367797

部分内容摘自 Spring Cloud 官方文档中文版

本文源码地址:https://github.com/Wyxwx/SpringCloudDemo2

目录

名词解释:Feign

Feign 的基本使用

覆盖 Feign 默认值

Feign Hystrix 支持

Feign 请求/响应压缩

Feign 日志记录


名词解释:Feign

Feign 是一个声明式的 Web 服务客户端,这使得 Web 服务客户端的写入更加方便。Feign 具有包括Feign注释和JAX-RS注释的可插拔式的注释支持。Feign还支持可插拔编码器和解码器。Spring Cloud集成Ribbon和Eureka以在使用Feign时提供负载均衡的http客户端。

Feign 整合了ribbon,具有负载均衡的能力

Feign 的基本使用

根据 服务发现:Eureka (一) 注册和运行 创建一个服务注册中心(eureka_server)和两个功能相同的 Eureka 客户端(eureka_client_1、eureka_client_2)

两个客户端的配置文件分别改为:

server.port=8762
 
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
 
spring.application.name=ribbonClient
 
server.port=8763
 
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
 
spring.application.name=ribbonClient

两个客户端的 spring.application.name 相同,代表提供了同一种服务,分配不同的端口模拟在不同服务器的场景

在两个客户端模块中分别创建一个相同的 HelloController

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @Value("${server.port}")
    private String port;

    @RequestMapping(value = "/hello")
    public String hello(){
        return "my port is " + port;
    }

    @RequestMapping(value = "/hi/{name}", method = RequestMethod.GET)
    public String hi(@PathVariable("name") String name){
        return "Hi, " + name + ", my port is " + port;
    }

}

接下来,按照创建 Eureka 客户端的步骤创建一个新的 Module: feign

在创建之后的 pom.xml 里加入以下依赖


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>

编写配置文件 application.properties

eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

server.port=8765

spring.application.name=feignClient

修改启动类,为其加入 @EnableEurekaClient、 @EnableFeignClients(开启 Feign 功能)

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
public class FeignApplication {

    public static void main(String[] args) {
        SpringApplication.run(FeignApplication.class, args);
    }

}

创建一个 Java 类 HelloWorld

import com.example.demo.config.FeignConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;


@FeignClient(name = "ribbonClient")
public interface HelloWorld {

    @RequestMapping(method = RequestMethod.GET, value = "/hello")
    String hello();

    @RequestMapping(method = RequestMethod.GET, value = "/hi/{name}")
    String hi(@PathVariable("name") String name);

}

创建一个 HelloWorldController

import com.example.demo.interfacePackage.HelloWorld;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;


@RestController
public class HelloWorldController {

    @Resource
    private HelloWorld helloWorld;

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello(){
        return helloWorld.hello();
    }

    @RequestMapping(value = "/hi/{name}", method = RequestMethod.GET)
    public String hi(@PathVariable("name") String name){
        return helloWorld.hi(name);
    }

}

依次启动 eureka_server、eureka_client_1、eureka_client_2、feign

多次访问 http://localhost:8765/hi/wyx 可以看到 port 8762 和 port 8763 交替出现

覆盖 Feign 默认值

Spring Cloud可以通过使用@FeignClient声明额外的配置(FeignClientsConfiguration)来完全控制客户端

修改 HelloWorld,在 @FeignClient 中添加 configuration 属性

import com.example.demo.config.FeignConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;


@FeignClient(name = "ribbonClient", configuration = FeignConfiguration.class)
public interface HelloWorld {

    @RequestMapping(method = RequestMethod.GET, value = "/hello")
    String hello();

    @RequestMapping(method = RequestMethod.GET, value = "/hi/{name}")
    String hi(@PathVariable("name") String name);

}

表明用 FeignConfiguration中的配置覆盖 FeignClientsConfiguration(默认配置)中的配置,对于在 FeignConfiguration中未出现的配置,仍然按照默认配置来

注意:

FeignConfiguration 类可以不必加入 @Configuration 注解。

加入了该注解后,若是该类在 Application Context 的 @ComponentScan 中,它将成为feign.Decoder, feign.Encoder, feign Contract 等的默认来源。所以在使用时,需要采取措施来避免这一现象。可以通过将其放置在任何 @ComponentScan 或 @SpringBootApplication 的单独的不重叠的包中,或者可以在 @ComponentScan 中明确排除。

servicedId 属性现在已被弃用,有利于 name 属性。

以前,使用 url 属性,不需要 name 属性。现在需要使用 name

name url 属性支持占位符

@FeignClient(name = "${feign.name}", url = "${feign.url}")
public interface StoreClient {
    //..
}

Feign Hystrix 支持

Feign 已经集成了 Hystrix,但是默认是关闭的,若想开启使用,则在配置文件中加入

feign.hystrix.enabled=true

Feign 中,回退操作与纯 hystrix 有些不同

创建类 FeignHystrix 继承 HelloWorld

实现的方法为出错回退时调用的方法

import org.springframework.stereotype.Component;


@Component
public class FeignHystrix implements HelloWorld {
    @Override
    public String hello() {
        return "hello: sorry, there is an error";
    }

    @Override
    public String hi(String name) {
        return "hi: sorry, there is an error";
    }
}

修改 HelloWorld 类,设置回退类为 FeignHystrix

import com.example.demo.config.FeignConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@FeignClient(name = "ribbonClient", fallback = FeignHystrix.class)
public interface HelloWorld {

    @RequestMapping(method = RequestMethod.GET, value = "/hello")
    String hello();

    @RequestMapping(method = RequestMethod.GET, value = "/hi/{name}")
    String hi(@PathVariable("name") String name);

}

断开 eureka_client_1 和 eureka_client_2,重启 feign

访问 http://localhost:8765/hi/wyx,证明断路器成功工作

如果需要访问导致回退触发的原因,可以使用 @FeignClient 内的 fallbackFactory 属性。

创建 Java 类 FeignHystrixFactory

import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

@Component
public class FeignHystrixFactory implements FallbackFactory<HelloWorld> {
    @Override
    public HelloWorld create(Throwable cause) {
        return new HelloWorld() {
            @Override
            public String hello() {
                return "hello: " + cause;
            }

            @Override
            public String hi(String name) {
                return "hi: " + cause;
            }
        };
    }
}

修改 HelloWorld 注解,重启 feign

import com.example.demo.config.FeignConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@FeignClient(name = "ribbonClient", fallbackFactory = FeignHystrixFactory.class)
public interface HelloWorld {

    @RequestMapping(method = RequestMethod.GET, value = "/hello")
    String hello();

    @RequestMapping(method = RequestMethod.GET, value = "/hi/{name}")
    String hi(@PathVariable("name") String name);

}

访问 http://localhost:8765/hi/wyx,证明断路器成功工作

若想在某一客户端上禁用 Hystrix,可以利用 configuration 实现

创建 FeignConfiguration,该方法即禁用 Hystrix

import feign.Feign;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;


public class FeignConfiguration {

    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder(){
        return Feign.builder();
    }

}

修改 HelloWorld

import com.example.demo.config.FeignConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@FeignClient(name = "ribbonClient", configuration = FeignConfiguration.class)
public interface HelloWorld {

    @RequestMapping(method = RequestMethod.GET, value = "/hello")
    String hello();

    @RequestMapping(method = RequestMethod.GET, value = "/hi/{name}")
    String hi(@PathVariable("name") String name);

}

 重启 feign,访问 http://localhost:8765/hi/wyx ,出现报错画面,说明该客户端的断路器已禁用

Feign 请求/响应压缩

在一些场景下,可能需要对请求或响应进行压缩,此时可使用启用Feign的压缩功能。

可以通过启用其中一个属性来启用请求或响应 GZIP 压缩

feign.compression.request.enabled=true
feign.compression.response.enabled=true

设置支持的媒体类型列表,默认是 text/xml,application/xml,application/json

设置最小压缩大小,默认是2048

feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048

Feign 日志记录

每一个被创建的Feign客户端都会有一个logger。该logger默认的名称为Feign客户端对应的接口的全限定名。Feign日志记录只能响应DEBUG日志级别。

在配置文件中开启

logging.level.project.user.UserClient: DEBUG

可以为每个客户端配置的 Logger.Level 对象告诉Feign记录多少

  • NONE,无记录(DEFAULT)。

  • BASIC,只记录请求方法和URL以及响应状态代码和执行时间。

  • HEADERS,记录基本信息以及请求和响应标头。

  • FULL,记录请求和响应的头文件,正文和元数据。

例如

@Configuration
public class FooConfiguration {
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

猜你喜欢

转载自blog.csdn.net/Wyx_wx/article/details/89367797