核心概念

Dubbo 通过动态代理技术,为 Consumer 端的服务接口生成代理对象,将远程调用伪装成本地方法调用。这种透明化设计让开发者无需关心网络通信、序列化等底层细节,像调用本地方法一样调用远程服务。

核心技术

  • 动态代理:Javassist(默认)或 JDK Proxy
  • Invoker 抽象:统一的调用接口
  • 透明化封装:屏蔽网络通信细节

实现原理

1. 动态代理生成

用户代码

// Consumer 端引用远程服务
@Reference
private UserService userService;

// 像调用本地方法一样使用
public void test() {
    User user = userService.getUser(123L);  // 实际是远程调用
    System.out.println(user.getName());
}

底层实现

// Spring 启动时,为 @Reference 注解的字段注入代理对象
@Reference
private UserService userService;  
// ↓ 实际注入的是
// userService = ProxyFactory.getProxy(UserService.class, invoker);

// 调用 userService.getUser(123L) 时:
// 1. 拦截方法调用
// 2. 转换为 RPC 请求
// 3. 发送到远程服务器
// 4. 接收响应并返回结果

2. 代理对象创建流程

// Dubbo 代理工厂
public class ProxyFactory {
    
    /**
     * 为服务接口创建代理对象
     * @param interfaceClass 服务接口
     * @param invoker 调用器(封装了远程调用逻辑)
     */
    public static <T> T getProxy(Class<T> interfaceClass, Invoker<T> invoker) {
        // 1. 使用 Javassist 生成代理类(默认方式)
        return JavassistProxyFactory.getProxy(interfaceClass, invoker);
        
        // 或者使用 JDK 动态代理
        // return JdkProxyFactory.getProxy(interfaceClass, invoker);
    }
}

// Javassist 代理工厂(性能更好)
public class JavassistProxyFactory {
    
    public <T> T getProxy(Class<T> interfaceClass, Invoker<T> invoker) {
        // 动态生成代理类字节码
        String proxyClassName = interfaceClass.getName() + "$Proxy";
        
        ClassPool pool = ClassPool.getDefault();
        CtClass proxyClass = pool.makeClass(proxyClassName);
        
        // 实现接口
        proxyClass.addInterface(pool.get(interfaceClass.getName()));
        
        // 添加 invoker 字段
        proxyClass.addField(CtField.make("private Invoker invoker;", proxyClass));
        
        // 为接口的每个方法生成实现
        for (Method method : interfaceClass.getMethods()) {
            String methodCode = generateMethodCode(method);
            proxyClass.addMethod(CtMethod.make(methodCode, proxyClass));
        }
        
        // 加载类并创建实例
        Class<?> clazz = proxyClass.toClass();
        T proxy = (T) clazz.newInstance();
        setInvoker(proxy, invoker);  // 注入 invoker
        
        return proxy;
    }
    
    // 生成方法实现代码
    private String generateMethodCode(Method method) {
        return String.format(
            "public %s %s(%s) {" +
            "    RpcInvocation invocation = new RpcInvocation();" +
            "    invocation.setMethodName(\"%s\");" +
            "    invocation.setParameterTypes($sig);" +
            "    invocation.setArguments($args);" +
            "    Result result = invoker.invoke(invocation);" +
            "    return (%s) result.getValue();" +
            "}",
            method.getReturnType().getName(),
            method.getName(),
            getParameterString(method),
            method.getName(),
            method.getReturnType().getName()
        );
    }
}

// 生成的代理类示例(伪代码)
public class UserService$Proxy implements UserService {
    
    private Invoker invoker;
    
    @Override
    public User getUser(Long userId) {
        // 构造 RPC 调用信息
        RpcInvocation invocation = new RpcInvocation();
        invocation.setMethodName("getUser");
        invocation.setParameterTypes(new Class[]{Long.class});
        invocation.setArguments(new Object[]{userId});
        
        // 通过 Invoker 发起远程调用
        Result result = invoker.invoke(invocation);
        
        // 返回结果
        return (User) result.getValue();
    }
    
    @Override
    public void updateUser(User user) {
        RpcInvocation invocation = new RpcInvocation();
        invocation.setMethodName("updateUser");
        invocation.setParameterTypes(new Class[]{User.class});
        invocation.setArguments(new Object[]{user});
        
        invoker.invoke(invocation);
    }
}

3. Invoker 设计

Invoker 是 Dubbo 核心的统一调用接口

/**
 * Invoker 是 Dubbo 的核心抽象
 * 封装了服务的调用逻辑(本地调用或远程调用)
 */
public interface Invoker<T> {
    
    /**
     * 获取服务接口
     */
    Class<T> getInterface();
    
    /**
     * 执行调用
     */
    Result invoke(Invocation invocation) throws RpcException;
}

// Consumer 端的 DubboInvoker(远程调用)
public class DubboInvoker<T> implements Invoker<T> {
    
    private final Class<T> type;
    private final ExchangeClient[] clients;  // Netty 客户端
    
    @Override
    public Result invoke(Invocation invocation) throws RpcException {
        // 1. 选择一个可用的 Client
        ExchangeClient client = clients[index++ % clients.length];
        
        // 2. 发送 RPC 请求
        CompletableFuture<Result> future = client.request(invocation, timeout);
        
        // 3. 等待响应(同步)
        try {
            return future.get(timeout, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            throw new RpcException("调用超时");
        }
    }
}

// Provider 端的 DelegateProviderMetaDataInvoker(本地调用)
public class DelegateProviderMetaDataInvoker<T> implements Invoker<T> {
    
    private final T serviceImpl;  // 真实的服务实现对象
    
    @Override
    public Result invoke(Invocation invocation) throws RpcException {
        // 直接调用本地方法(反射)
        Method method = getMethod(invocation.getMethodName());
        Object result = method.invoke(serviceImpl, invocation.getArguments());
        return new RpcResult(result);
    }
}

4. 完整调用链路

// ==================== 用户代码 ====================
User user = userService.getUser(123L);

// ==================== 代理层 ====================
// userService 实际是 UserService$Proxy 对象
UserService$Proxy.getUser(123L) {
    // 构造调用信息
    RpcInvocation invocation = new RpcInvocation();
    invocation.setMethodName("getUser");
    invocation.setParameterTypes(new Class[]{Long.class});
    invocation.setArguments(new Object[]{123L});
    
    // 委托给 Invoker
    Result result = invoker.invoke(invocation);
    return (User) result.getValue();
}

// ==================== Invoker 层 ====================
DubboInvoker.invoke(invocation) {
    // 1. 获取 Netty 客户端
    ExchangeClient client = getClient();
    
    // 2. 发送请求
    Request request = new Request();
    request.setData(invocation);
    
    // 3. 异步转同步
    DefaultFuture future = new DefaultFuture(channel, request);
    client.send(request);
    
    // 4. 等待响应
    return future.get(timeout);
}

// ==================== 网络传输 ====================
// Netty 发送数据到 Provider

// ==================== Provider 端 ====================
DelegateProviderMetaDataInvoker.invoke(invocation) {
    // 1. 获取真实的服务实现对象
    UserServiceImpl serviceImpl = getServiceImpl();
    
    // 2. 反射调用方法
    Method method = UserServiceImpl.class.getMethod("getUser", Long.class);
    User user = (User) method.invoke(serviceImpl, 123L);
    
    // 3. 返回结果
    return new RpcResult(user);
}

// ==================== 响应返回 ====================
// Provider 将结果序列化并通过 Netty 返回
// Consumer 接收响应,唤醒 Future,返回结果给用户代码

透明化设计细节

1. 异常透明化

// Provider 端抛出的异常,会传递到 Consumer 端
@Service
public class UserServiceImpl implements UserService {
    @Override
    public User getUser(Long userId) {
        if (userId == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
        // ...
    }
}

// Consumer 端捕获异常
try {
    User user = userService.getUser(null);
} catch (IllegalArgumentException e) {
    // 能够捕获到 Provider 端抛出的异常
    System.out.println(e.getMessage());  // "用户ID不能为空"
}

// 底层实现:异常序列化传输
Response response = new Response();
response.setStatus(Response.SERVICE_ERROR);
response.setErrorMessage(exception.getMessage());
response.setException(exception);  // 异常对象序列化

2. 泛型支持

// 泛型方法调用
public interface UserService {
    <T> T query(Query<T> query);
}

// Consumer 端调用
Query<User> query = new Query<>();
User user = userService.query(query);  // 泛型信息保留

// 底层实现:保存泛型信息
RpcInvocation invocation = new RpcInvocation();
invocation.setMethodName("query");
invocation.setParameterTypes(new Class[]{Query.class});
invocation.setArguments(new Object[]{query});
// 附加泛型信息
invocation.setAttachment("generic", "true");
invocation.setAttachment("actualType", User.class.getName());

3. 上下文传递

// Consumer 端设置上下文
RpcContext.getContext().setAttachment("traceId", "123456");
User user = userService.getUser(123L);

// Provider 端获取上下文
String traceId = RpcContext.getContext().getAttachment("traceId");
System.out.println("TraceId: " + traceId);  // "123456"

// 底层实现:通过 RpcInvocation 传递
RpcInvocation invocation = new RpcInvocation();
invocation.setAttachments(RpcContext.getContext().getAttachments());
// 序列化时一起传输

性能优化

1. Javassist vs JDK Proxy

// Javassist(默认,性能更好)
// - 生成真实的字节码类
// - 直接方法调用,无反射开销
// - 性能约为 JDK Proxy 的 10 倍

// JDK Proxy(兼容性好)
// - 使用 Proxy.newProxyInstance()
// - 每次调用都通过 InvocationHandler.invoke()
// - 有反射开销

// 配置代理方式
dubbo:
  provider:
    proxy: javassist  #  jdk

2. 代理缓存

// Dubbo 会缓存生成的代理类,避免重复生成
private static final Map<Class<?>, Object> PROXY_CACHE = new ConcurrentHashMap<>();

public <T> T getProxy(Class<T> interfaceClass, Invoker<T> invoker) {
    Object proxy = PROXY_CACHE.get(interfaceClass);
    if (proxy == null) {
        proxy = createProxy(interfaceClass, invoker);
        PROXY_CACHE.put(interfaceClass, proxy);
    }
    return (T) proxy;
}

3. 异步调用

// 同步调用(阻塞)
User user = userService.getUser(123L);

// 异步调用(非阻塞,性能更好)
CompletableFuture<User> future = RpcContext.getContext()
    .asyncCall(() -> userService.getUser(123L));

future.thenAccept(user -> {
    System.out.println(user.getName());
});

与其他框架对比

Spring Cloud OpenFeign

// OpenFeign 也是通过动态代理实现
@FeignClient(name = "user-service")
public interface UserService {
    @GetMapping("/user/{id}")
    User getUser(@PathVariable Long id);
}

// 底层使用 JDK Proxy
UserService proxy = Proxy.newProxyInstance(
    classLoader,
    new Class[]{UserService.class},
    new FeignInvocationHandler()
);

// 区别:
// - Dubbo:基于 RPC(二进制协议)
// - Feign:基于 HTTP(REST 风格)

答题总结

Dubbo 实现本地调用体验的核心机制

1. 动态代理技术

  • Javassist(默认):生成字节码,性能高
  • JDK Proxy:基于反射,兼容性好
  • 为服务接口生成代理对象,拦截方法调用

2. Invoker 抽象

  • 统一的调用接口,屏蔽本地/远程差异
  • Consumer 端:DubboInvoker → 发起网络调用
  • Provider 端:DelegateProviderMetaDataInvoker → 反射调用本地方法

3. 透明化设计

  • 异常透明:Provider 端异常序列化传递到 Consumer
  • 泛型支持:保留泛型信息
  • 上下文传递:通过 RpcContext 传递附加信息

4. 完整流程

用户调用 → 代理拦截 → 构造 RpcInvocation 
→ Invoker.invoke() → 网络传输 
→ Provider 接收 → 反射调用本地方法 
→ 返回结果 → Consumer 接收 → 返回给用户

性能优化

  • Javassist 比 JDK Proxy 快 10 倍
  • 代理类缓存,避免重复生成
  • 支持异步调用,提升性能

面试技巧:强调动态代理 + Invoker 抽象是核心,可以画出调用链路图,对比 Feign 的实现差异,体现对 RPC 框架设计的深入理解。