StringCloud 服务调用 OpenFeign

2021-04-11 23:51:11

spring 这个团队之所以能做到今天这样受欢迎,写其说是理念先进不如说是工程上的契合。spring 的产品总能给你惊喜,让你用最少的代价实现你的目标,而且是可继承的,可组合的。在 spring cloud 生态下的服务调用方案更是如此,OpenFeign 可以通过 http 协议实现 rpc 调用,而且可以单独使用,可以结合注册服务器使用,也可以自动负载,更重要的是使用非常简单。


创建项目

使用你熟悉的方式,创建一个 spring boot 项目,使用使用 Idea 可以直接选择依赖项 Spring Cloud Routing -> OpenFeignSpring Cloud Discovery -> Eureka Discovery Client,会自动配置必要的依赖项。如果手动添加也没问题,请按照如下步骤:

依赖

# 假设使用 maven 管理依赖

# 我们的主角
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

# 如果使用注册服务管理服务必须
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

开启配置

在主类上添加一个注解

// 在主类上参加开启 OpenFeign 注解

@SpringBootApplication
@EnableFeignClients

如果使用 Eureka 管理服务还需要做以下配置,如果单独使用则不需要。

# application.properties

#向注册中心注册的名字
spring.application.name=feign-client
server.port=8004

# 服务地址
eureka.instance.hostname=localhost

#注册中心路径,表示我们向这个注册中心注册服务,如果向多个注册中心注册,用“,”进行分隔
eureka.client.serviceUrl.defaultZone=http://${user}:${password}@127.0.0.1:8001/eureka/
# 如果没有认证可以去掉用户名和密码 
#eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8001/eureka/

# 表示是否将自己注册到EurekaServer 默认true,如果只是消费者不要开启
eureka.client.register-with-eureka=true

#访问路径显示ip地址
eureka.instance.prefer-ip-address=true

编写一个实例

// StoreClient.java
// 这是官方文档上的一个例子,简单来说首先需要用 @FeignClient("stores") 来标注一个任意的接口,参数就是 eureka 服务名称,然后在每个接口可以使用 SpringMvc 注解来写格式,注意与服务提供方的保持一致,之后就可以愉快的使用了。 
@FeignClient("stores")
public interface StoreClient {
    @RequestMapping(method = RequestMethod.GET, value = "/stores")
    List<Store> getStores();

    @RequestMapping(method = RequestMethod.GET, value = "/stores")
    Page<Store> getStores(Pageable pageable);

    @RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
    Store update(@PathVariable("storeId") Long storeId, Store store);
}

超时配置

可以编写配置类,不过最方便的还是在配置文件中,截止到现在的 spring cloud 2020.0.3 版本可以直接配置

#application.properties

# 连接超时时间 
feign.client.config.default.connect-timeout=1000

# 数据读取时间
feign.client.config.default.read-timeout=5000

验证

在调用 feign 时主要面向内部接口,所以权限验证与用户访问的认证与授权有所区分,一般会更简单不需要太复杂的粒度控制,所以这里统一配置在 header 中。配置 header 值有多种不同的方式。

自定义 feign 拦截器

//FeignConfig.java

@Configuration
public class FeignConfig {

    @Bean
    public RequestInterceptor requestInterceptor() {

        return new RequestInterceptor() {

            @Override
            public void apply(RequestTemplate template) {

                template.header("Auth-Token", "your_token");
            }
        };
    }

}

@RequestMapping 注解

@RequestMapping 注解是 Spring MVC 系列注解,直接在接口上配置 header 属性即可,不过需要注意语法,需要使用等号连接键与值。

// DateService.java

@FeignClient("provider-service")

public interface DateService {
    @GetMapping(value = "/test",headers = {"token=${app.token}"})
    public String test();
}

这里的 app.token 值可以在配置文件中,当然直接在代码里写魔术字符串也可以。

# application.properties

app.token: appToken

@RequestHeader 注解

@RequestHeader 注解也是 Spring MVC 系列的,不过需要加在方法参数中,适合于需要调用接口是由代码输入的参数。

// DateService.java

@FeignClient("provider-service")

public interface DateService {
    @GetMapping(value = "/test")
    public String test(@RequestHeader("token") String token);
}

这样在调用 test 这个接口时就要动态的传入 token 的值,对于验证来说并不实用。

@Headers 注解

这是 feign 自带注解,默认情况下是不生效的,要使用需要首先打开 feign 自带的注解。

 // 在配置类中直接配置
 
@Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }

之后在接口代码中直接使用

// DateService.java
//configuration 可以更详细的控制来源于哪个配置类
@FeignClient(name="provider-service",configuration = FeignConfig.class)

public interface DateService {
    @RequestLine("GET /test")
    @Headers({"token: {token}"})
    public String test();
}

因为我们主要在 spring 全家桶中使用,所以不推荐这种方式。

@HeaderMap 注解

仍然是 fiegn 自带注解,开启方式与 @Headers 注解相同,使用上需要加到方法参数中与 @RequestHeader 类似。

// DateService.java
//configuration 可以更详细的控制来源于哪个配置类
@FeignClient(name="provider-service",configuration = FeignConfig.class)

public interface DateService {
    @RequestLine("GET /test")
    public String test(@HeaderMap HttpHeaders headers);
}

仍然不推荐这种方式,只是做个备注。

Copyright tg-blog 京ICP备15066502号-2