远程调用Feign源码解析

708 阅读2分钟

技术背景

  在开发过程中 ,常常会遇到RPC服务/远程服务调用,如服务A(feign-producer)  暴露出一个接口 DemoService ,
那么对于服务B(feign-consumer)来说 ,如何能远程调用服务A 方法?

解决方案

1 手写HttpUtil *** , B调用A (很原始/拉胯)
2 Feign工具(本文推荐)

前期技术准备(Jdk动态代理)(良辰建议先温习一遍)如下

问题: 假设我有一个学生操作类,如何动态增加审查日志

public interface StudentService {
    public void add();

}
//TODO 如何增加审查日志  遵循开闭原则 对扩展开放 修改关闭
public class StudentServiceImpl implements StudentService {
    @Override
    public void add() {
        System.out.println("正在添加学生...................");
    }
}

解决: 1.1 创建动态代理类,实现核心接口 InvocationHandler

public class DynamicStudentService implements InvocationHandler {
    /**
     * 要代理的真实对象
     */
    private Object student;
    
    public DynamicStudentService(Object student) {
        this.student = student;
    }
    /**
     * 动态代理上的所有方法应用
     *
     * @param proxy  代理类实例
     * @param method 被调用的方法对象
     * @param args   参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("开始操作");
        Object returnValue = method.invoke(student, args);
        System.out.println("结束操作");
        return returnValue;
    }
}

1.2 创建代理对象/验证

public class TestStudent {

    public static void main(String[] args) {
        //配置系统属性为true,代理类生成时将自动写入磁盘
 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        //代理的真实对象
        StudentService studentService = new StudentServiceImpl();
        InvocationHandler invocationHandler = new DynamicStudentService(studentService);
        ClassLoader classLoader = studentService.getClass().getClassLoader();
        Class[] interfaces = studentService.getClass().getInterfaces();

        /**
         * 生成代理对象
         * 核心参数
         * 1 定义代理类的类加载器
         * 2 代理类要实现的接口列表
         * 3 将方法调用分派到具有指定调用处理程序的代理实例的调用处理程序
         *               由指定的类加载器定义的代理类
         *                并实现指定的接口
         *
         */
        StudentService service = (StudentService) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        service.add();
    }
}

Feign 源码解析

1.1 feign-consumer 程序启动时
Spring容器创建动态代理对象new ReflectiveFeign.FeignInvocationHandler
这一步相当于 Spring和Feign 相关远程调用服务的Service Bean 并将这些Bean进行了动态代理 / 并且初始化目标调用地址/见图

11.jpg

12.jpg

13.jpg

1.2 当消费者服务访问远程DemoService时,Spring判断此Service Bean 被ReflectiveFeign.FeignInvocationHandler动态代理,并进入代理方法

14.jpg

1.3 追踪代理类方法Invoke内部 取出远程服务启动时加载保存的相关信息

15.jpg

1.4 继续追踪方法 看到feign拼装目标服务器请求地址/参数 并进行接口请求 见图

16.jpg

17.jpg

到这步就请求被执行完毕了 后续操作****

更多访问杨少的gitHUb

源码地址下载