目录

OpenFeign 浅述

引言

现在我们在微服务开发中,由于服务拆分,我们不可避免的会涉及一个服务需要调用另一个服务的 RPC 场景,对于这一场景的实现有很多方案:我们可以自己封装httpClient,也可以使用RestTemplate或者Dubbo,以及我本文要讲的OpenFeign,这些都可以方便我们来完成远程调用。


示例

假设我们有一个场景:

我们有两个服务:认证权限服务auth-service,还有一个应用服务app-service,应用服务登入的时候需要调用认证权限服务来进行账号认证和权限校验。

auth-service中有一个认证接口:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@RestController
@RequestMapping("/api)
public class AuthController {

    @GetMapping("/auth")
    public Boolean auth(@RequestBody AuthRequest authRequest){
    // 认证逻辑 ...
        return true;
    }
}

而由于我们在app-service中需要去调用auth-service的auth接口,所以我们可以使用OpenFeign来帮我们完成RPC的过程,实现步骤如下:

  1. 首先需要引入maven依赖
1
2
3
4
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

  1. 使用@EnableFeignClients注解来启用OpenFeign的能力,比如可以在app-service启动项AppServiceStater上标注这个注解:
1
2
3
4
5
6
7
@EnableFeignClients
@SpringBootApplication
public class AppServiceStater {
    public static void main(String[] args) {
        SpringApplication.run(ServiceProviderAppStater.class, args);
    }
}

  1. 以接口的方式构建我们需要RPC操作的远程接口配置

比如在目前场景中,我们需要操作认证服务auth-service/api/auth接口,那么我们可以如下操作:

1
2
3
4
5
6
@FeignClient(name = "auth-service",url = "http://127.0.0.1:8080")
public interface AuthServiceRemoteClient {

@GetMapping(value = "/api/auth")
Boolean auth(@RequestBody AuthRequest);
}

  1. 注入3中写的接口并调用方法
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@RestController
public class NacosController {

    @Autowired
    AuthServiceRemoteClient authServiceRemoteClient;

    @GetMapping("/app/login")
    public Boolean login(@RequestBody LoginRequest loginRequest){
        // 转换为AuthRequest auth
        AuthRequest authRequest = Convert(loginRequest);
        return authServiceRemoteClient.auth(authRequest);
    }
}

完成以上几个步骤后,当我们请求app-servie的login接口的时候,内部就会调用auth-service的auth接口进行认证,整个过程还是很丝滑的。


思想内核

上面的案例,相信你很想知道整个OpenFeign的实现是怎么样的,再了解细节之前,我们先站在上帝视角来看下这个思想内核:

构建过程

当项目启动的时候,OpenFeign会扫描指定的标注了@FeignClient的注解,根据上面案例我们可以知道@FeignClient是标注在接口之上的,扫描到这个接口后,OpenFeign会通过JDK动态代理的方式为这个接口生成代理对象;而接口中的每一个方法都是对应了一个远程的API接口,如何在调用指定的方法就可以调到远程的指定接口呢?

这是OpenFeign在解析接口时,接口中的每个方法会被解析成MethodMeradata信息,然后再转换成MethodHandler,最终解析完所有的方法会构成一个Map<Method,MethodHandler>对象,而这个对象会作为InvocationHandler的一个属性而存在,我们都知道InvocationHandler时JDK动态代理的一个核心组件,所有被代理的对象方法调用都会走到InvocationHandler的invoke方法逻辑。

调用执行过程

当已经构建好所有的@FeignClient标注接口的代理对象时,我们调用指定的方法时,会从Map<Method,MethodHandler>对象中根据Method来获取指定的MethodHandler对象,然后执行其invoke方法进行真正的RPC逻辑。

https://cdn.jsdelivr.net/gh/Turbo-King/images/a.jpg
OpenFeign构建调用过程


总结

本篇文章不仅仅描述了OpenFeign的构建执行示例,以及整个JDK动态代理的实现,是在一个很高的层面来看整个OpenFeign的实现原理,而这一切重中之重正是在MethodHandler对象的invoke方法之中。期待未来我们一起探索。