springcloud微服务六:声明式服务调用Feign

在有了eureka服务注册中心、ribbon负载均衡及服务消费、hystrix断路器三部分之后,其实在某种程度上就应该能够创建一个基本的spring cloud微服务应用了,只不过这种应用无论是代码层面还是架构层面都还有一定的缺陷。
从代码层面来讲,ribbon和hystrix是一个标准化springcloud应用最基本的模块,通常也都是同时出现的,因此为了简化开发,有一个更高层次的工具来对他们进行了封装整合,这就是Feign。
feign在整合了这两者强大功能的同时,还提供了一种声明式的web服务客户端定义方式及可插拔式的注解,同时支持spring mvc的注解。
在ribbon和hystrix的基础上,feign看起来就比较简单。

一、基本使用

maven依赖

需要在eureka依赖的基础上再引入对feign的依赖:

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

application.properties基本配置

服务名
spring.application.name=feign-client
端口
server.port=9099
允许ip访问
eureka.instance.prefer-ip-address=true
指定注册中心地址
eureka.client.serviceUrl.defaultZone=http://localhost:10010/eureka/

启动类注解,在项目启动类加入如下三个注解,就可以使用feign:

@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication

在加入了上边注解的前提下,就可以编写feign声明式调用服务的接口:

@FeignClient("client")
public interface FeignService {
    @RequestMapping("/hello")
    String sayHello();
}

其中@FeignClient后的client即之前写好的eureka服务的服务名,接口里的方法定义和client里controller类中的定义一摸一样,区别就是没有具体的实现,也不需要具体的实现。
当我们在当前系统中调用FeignService .sayHello方法的时候,就会去服务名为client的服务中调用具体的同名同定义的方法。
如果服务名为client的有多个,那么由于feign内部整合的是ribbon,所以实际上是使用ribbon的默认负载均衡策略进行选择,也就是线性轮询。
如果有用过webservice和hessian这类远程调用服务技术的,应该会发现这里的写法和他们如出一辙。

二、优化

增加fallback

上边的代码就可以基本实现feign的功能,可以正常调用eureka了,但是和单独使用ribbon一样,当服务故障的时候,系统就会出现问题。所以一般@FeignClient这里可能会写成下边这样:

@FeignClient(name = "client", fallback = ClientFallBack.class)

name后边的client和上边的直接一个字符串声明等同,后边的ballback指定当服务出现故障时服务降级之后的处理类。
这个类是一个很简单的java类,需要继承FeignService接口,并实现接口中的方法。这个方法在有的地方被称作本地方法,当服务降级后就会调用这个方法里写的逻辑。

public class ClientFallBack implements FeignService {
    @Override
    public String sayHello() {
        return "say hello";
    }
}

捕获服务异常

上边的实现,可以一定程度上保证服务降级后继续一定的逻辑处理,但是如果需要在降级后的逻辑中捕获降级前的异常信息,就需要把fallback改成fallbackFactory,这个降级处理类就可以写成下边这样:

@Component
public class ClientFallBackFactory implements FallbackFactory {
    @Override
    public FeignService create(Throwable cause) {
        return new FeignService() {
            @Override
            public String sayHello() {
                System.out.println(cause.getMessage());
                return "say hello";
            }
        };
    }
}

推荐文章