服务调用:openfeign

         openfeign,通过定义接口和使用注解的方式,实现服务调用,相比直接通过http调用,它更符合面向对象编程,并且具有更好的复用和维护效果。要想使用openfeign,需要引入依赖:

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

并在主启动类,添加@EnableFeignClients注解,开启服务调用的功能:

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class FeignConsumer {
    public static void main(String[] args) {
        SpringApplication.run(FeignConsumer.class,args);
    }
}

然后,就可以编写接口进行服务调用了:

@FeignClient(name = "feign-provider")
public interface TestFeignService {

    @GetMapping("/provider/{msg}")
    String feignProvider(@PathVariable("msg") String msg);

    @GetMapping("/provider/timeout")
    String testTimeout();
}

feign-provider是服务提供者的服务名,两个接口地址,对应于服务提供者的接口地址:

@RestController
public class ProviderController {

    @GetMapping("/provider/{msg}")
    public String testFeign(@PathVariable("msg") String msg){
        return "feignProvider "+msg;
    }

    // 测试调用超时
    @GetMapping("/provider/timeout")
    public String timeout(){
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "test feign timeout";
    }
}

这里,主要是使用了@FeignClient注解,该注解的定义如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
    @AliasFor("name")
    String value() default "";

    /** @deprecated */
    @Deprecated
    String serviceId() default "";

    String contextId() default "";

    @AliasFor("value")
    String name() default "";

    String qualifier() default "";

    String url() default "";

    boolean decode404() default false;

    Class<?>[] configuration() default {};

    Class<?> fallback() default void.class;

    Class<?> fallbackFactory() default void.class;

    String path() default "";

    boolean primary() default true;
}

关注以下几个属性:

  1. name:如果没有配置url属性,在name属性为服务提供方的微服务名;如果配置了url属性,则该值我了解到的是没有任何意义,可随便定义
  2. url:一般是配置服务提供方的ip:port的形式
  3. fallback:配置项需要实现接口,服务调用失败后(比如超时、服务不可用等),提供默认的处理
  4. fallbackFactory:配置项需要实现FallbackFactory<T>接口,和fallback效果类似,
    配置一个就可以了,目前没有测试哪个的优先级更高

       这几个属性,大家起了项目,动手测试一下,就能理解了,下面主要聊聊使用过程中遇到的一些问题:

(1)fallback不起作用

一开始配置了fallback属性,以为就会起作用:

@FeignClient(name = "feign-provider",fallback = TestFeignServiceFallback.class)

 TestFeignServiceFallback 实现:

public class TestFeignServiceFallback implements TestFeignService{
    @Override
    public String feignProvider(String msg) {
        return "feignProvider 默认返回";
    }

    @Override
    public String testTimeout() {
        return "testTimeout 默认返回";
    }
}

 但是,只是这样配置是不够的,还需要将TestFeignServiceFallback类交给Spring管理,并且打开一个开关:

feign:
  hystrix:
    enabled: true

打开这个开关,才会有熔断的效果,才会调用默认的处理

(2)请求消息的丢失

但是,熔断开关打开后,却会导致feign调用过程的消息丢失,比如,我在网关处的请求头添加了一个认证信息:

@Component
public class MyGlobalFilter implements GlobalFilter,Ordered{
    /**
     * 网关在请求头添加的信息,如果 请求头 有这个值,表示已经经过网关认证,可以访问微服务
     */
    @Value("${auth.gateway.key}")
    private String gatewayHeader;

    @Value("${auth.gateway.value}")
    private String gatewayValue;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        // TODO 获取请求的url,如果是白名单则放行
        // TODO 获取token,如果token正确则放行
        // 认证通过之后,在请求头添加认证标志
        request = exchange.getRequest().mutate().header(gatewayHeader,gatewayValue).build();
        //把新的 exchange放回到过滤链
        return chain.filter(exchange.mutate().request(request).build());
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

feign调用之间传递请求头信息:

@Configuration
public class FeignConfig {

    @Bean
    public RequestInterceptor headerInterceptor() {
        return template -> {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (null != attributes) {
                HttpServletRequest request = attributes.getRequest();
                Enumeration<String> headerNames = request.getHeaderNames();
                if (headerNames != null) {
                    while (headerNames.hasMoreElements()) {
                        String name = headerNames.nextElement();
                        String values = request.getHeader(name);
                        template.header(name, values);
                    }
                }
            }
        };
    }

}

       在服务调用方可用取到该请求头的认证信息,但是到了服务提供方,却拿到了该配置信息,如果没有打开熔断开关,都可以拿到该请求头的认证信息。

(3)超时控制

由于openfeign集成了ribbon:

因此可以通过配置ribbon,实现超时控制: 

ribbon:
  ConnectionTimeout: 10000  #连接超时 10 秒
  ReadTimeout: 6000        #读取超时 6 秒

示例代码地址:GitHub - qiuxinfa/cloud-alibaba

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值