1、生产背景

一个成熟的微服务集群,内部调用必然依赖一个服务调用框架,比如:基于http协议的feign,基于私有tcp协议的dubbo。本文内容介绍feign。

1.1、什么是服务调用

顾名思义,就是服务之间的接口互相调用,在微服务架构中很多功能都需要调用多个服务才能完成某一项功能。

1.2、什么是 Feign

Feign 是一个声明式Web Service 客户端。它的出现使开发 Web Service 客户端变得很简单。使用 Feign 只需要创建一个接口加上对应的注解,比如:@FeignClient 注解。 Feign 有可插拔的注解,包括 Feign 注解和 AX-RS 注解。Feign 也支持编码器和解码器,Spring Cloud Open Feign 对 Feign 进行增强支持 Spring Mvc 注解,可以像 Spring Web 一样使用 HttpMessageConverters 等。

Feign 是一种声明式模板化的 HTTP 客户端。在 Spring Cloud 中使用 Feign,可以做到使用 HTTP 请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问 HTTP 请求。接下来介绍一下 Feign 的特性,具体如下:

  • 可插拔的注解支持,包括 Feign 注解和AX-RS注解。

  • 支持可插拔的 HTTP 编码器和解码器。

  • 支持 Hystrix 和它的 Fallback。

  • 支持 Ribbon 的负载均衡。

  • 支持 HTTP 请求和响应的压缩。Feign 是一个声明式的 WebService 客户端,它的目的就是让 Web Service 调用更加简单。它整合了 Ribbon 和 Hystrix,从而不需要开发者针对 Feign 对其进行整合。Feign 还提供了 HTTP 请求的模板,通过编写简单的接口和注解,就可以定义好 HTTP 请求的参数、格式、地址等信息。Feign 会完全代理 HTTP 的请求,在使用过程中我们只需要依赖注入 Bean,然后调用对应的方法传递参数即可。

1.3、Feign 工作原理

  • 在开发微服务应用时,我们会在主程序入口添加 @EnableFeignClients 注解开启对 Feign Client 扫描加载处理。根据 Feign Client 的开发规范,定义接口并加 @FeignClients 注解。

  • 当程序启动时,会进行包扫描,扫描所有 @FeignClients 的注解的类,并将这些信息注入 Spring IOC 容器中。当定义的 Feign 接口中的方法被调用时,通过JDK的代理的方式,来生成具体的 RequestTemplate。当生成代理时,Feign 会为每个接口方法创建一个 RequetTemplate 对象,该对象封装了 HTTP 请求需要的全部信息,如请求参数名、请求方法等信息都是在这个过程中确定的。

  • 然后由 RequestTemplate 生成 Request,然后把 Request 交给 Client 去处理,这里指的 Client 可以是 JDK 原生的 URLConnection、Apache 的 Http Client 也可以是 Okhttp。最后 Client 被封装到 LoadBalanceclient 类,这个类结合 Ribbon 负载均衡发起服务之间的调用。

@FeignClient 注解
  • name | value:指定 Feign Client 的名称,如果项目使用了 Ribbon,name 属性会作为微服务的名称,用于服务发现。

  • url:url 一般用于调试,可以手动指定。全路径地址或hostname,http或https可选

  • @FeignClient 调用的地址。

  • decode404:当发生404错误时,如果该字段为 true,会调用 decoder 进行解码,否则抛出 FeignException。

  • configuration:Feign 配置类,可以自定义 Feign 的 Encoder、Decoder、LogLevel、Contract。

  • fallback:定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback 指定的类必须实现 @FeignClient 标记的接口。

  • fallbackFactory:工厂类,用于生成 fallback 类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码。

  • path:定义当前 FeignClient 的统一前缀。自动给所有方法的requestMapping前加上前缀,类似与controller类上的requestMapping

2、项目集成 Feign

  • 1、添加依赖

    <!-- spring cloud openfeign -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  • 2、新建RemoteUserService.java服务接口

@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class)
public interface RemoteUserService
{
    /**
     * 通过用户名查询用户信息
     *
     * @param username 用户名
     * @return 结果
     */
    @GetMapping(value = "/user/info/{username}")
    public R<LoginUser> getUserInfo(@PathVariable("username") String username);
}
  • 3、新建RemoteUserFallbackFactory.java降级实现

@Component
public class RemoteUserFallbackFactory implements FallbackFactory<RemoteUserService>
{
    private static final Logger log = LoggerFactory.getLogger(RemoteUserFallbackFactory.class);
​
    @Override
    public RemoteUserService create(Throwable throwable)
    {
        log.error("用户服务调用失败:{}", throwable.getMessage());
        return new RemoteUserService()
        {
            @Override
            public R<LoginUser> getUserInfo(String username)
            {
                return R.fail("获取用户失败:" + throwable.getMessage());
            }
        };
    }
}
  • 4、消费者TestUserController.java新增info查询用户方法

@RestController
public class TestUserController
{
    @Autowired
    private RemoteUserService remoteUserService;
​
    /**
     * 获取当前用户信息
     */
    @GetMapping("/user/{username}")
    public Object info(@PathVariable("username") String username)
    {
        return remoteUserService.getUserInfo(username);
    }
}
  • 5、启动类添加@EnableRyFeignClients注解,默认的@EnableRyFeignClients扫描范围全包。

  • 6、启动后访问 http://localhost:8888/user/admin,返回正确数据表示测试通过。