Dubbo

Dubbo

#resource / java #type / concept #status / growing

Dubbo

基础概念

什么是 Dubbo

Apache Dubbo 是一款高性能、轻量级的开源 Java RPC 框架,提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。

核心架构

┌─────────────┐      ┌─────────────┐
│   Consumer  │─────▶│  Registry   │
│  (服务消费者)  │      │ (注册中心)    │
└─────────────┘      └─────────────┘
       │                     │
       │ invoke              │ register
       ▼                     ▼
┌─────────────┐      ┌─────────────┐
│   Provider  │◀─────│   Monitor   │
│  (服务提供者)  │      │ (监控中心)    │
└─────────────┘      └─────────────┘

核心组件

  • Provider:服务提供者,暴露服务的服务方
  • Consumer:服务消费者,调用远程服务的服务方
  • Registry:注册中心,用于服务发现和配置管理
  • Monitor:监控中心,统计服务的调用次数和调用时间
  • Container:服务运行容器

核心特性

  • 透明化的远程方法调用:像调用本地方法一样调用远程方法
  • 软负载均衡:支持多种负载均衡策略
  • 容错机制:提供多种容错策略
  • 自动服务注册与发现:支持多种注册中心
  • 高度可扩展:微内核设计,所有功能都是插件
  • 运行时流量调度:内置多种路由规则

使用指南

环境准备

Maven 依赖:

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
    <version>3.2.5</version>
</dependency>

<!-- 注册中心依赖 -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-registry-nacos</artifactId>
    <version>3.2.5</version>
</dependency>

<!-- 序列化依赖 -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-serialization-hessian2</artifactId>
    <version>3.2.5</version>
</dependency>

Spring Boot Starter:

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>3.2.5</version>
</dependency>

定义服务接口

package com.example.service;

/**
 * 用户服务接口
 */
public interface UserService {
    
    /**
     * 根据ID获取用户信息
     */
    User getUserById(Long id);
    
    /**
     * 创建用户
     */
    User createUser(User user);
    
    /**
     * 更新用户信息
     */
    boolean updateUser(User user);
    
    /**
     * 删除用户
     */
    boolean deleteUser(Long id);
    
    /**
     * 分页查询用户
     */
    PageResult<User> getUsers(PageRequest pageRequest);
}

用户实体类:

package com.example.model;

import java.io.Serializable;
import java.time.LocalDateTime;

public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private Long id;
    private String username;
    private String email;
    private String phone;
    private Integer status;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    
    // 构造函数、getter、setter 省略
    
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", email='" + email + '\'' +
                ", phone='" + phone + '\'' +
                ", status=" + status +
                ", createTime=" + createTime +
                ", updateTime=" + updateTime +
                '}';
    }
}

服务提供者实现

服务实现类:

package com.example.service.impl;

import org.apache.dubbo.config.annotation.DubboService;
import com.example.service.UserService;
import com.example.model.User;
import org.springframework.stereotype.Service;

@DubboService(version = "1.0.0", timeout = 5000)
@Service
public class UserServiceImpl implements UserService {
    
    @Override
    public User getUserById(Long id) {
        // 模拟数据库查询
        if (id == null || id <= 0) {
            throw new IllegalArgumentException("User ID must be positive");
        }
        
        User user = new User();
        user.setId(id);
        user.setUsername("user_" + id);
        user.setEmail("user" + id + "@example.com");
        user.setPhone("1388888" + String.format("%04d", id));
        user.setStatus(1);
        user.setCreateTime(LocalDateTime.now().minusDays(id));
        user.setUpdateTime(LocalDateTime.now());
        
        return user;
    }
    
    @Override
    public User createUser(User user) {
        if (user == null) {
            throw new IllegalArgumentException("User cannot be null");
        }
        
        // 模拟数据库插入
        user.setId(System.currentTimeMillis() % 10000);
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        user.setStatus(1);
        
        System.out.println("Created user: " + user);
        return user;
    }
    
    @Override
    public boolean updateUser(User user) {
        if (user == null || user.getId() == null) {
            return false;
        }
        
        // 模拟数据库更新
        user.setUpdateTime(LocalDateTime.now());
        System.out.println("Updated user: " + user);
        return true;
    }
    
    @Override
    public boolean deleteUser(Long id) {
        if (id == null || id <= 0) {
            return false;
        }
        
        // 模拟数据库删除
        System.out.println("Deleted user with id: " + id);
        return true;
    }
    
    @Override
    public PageResult<User> getUsers(PageRequest pageRequest) {
        List<User> users = new ArrayList<>();
        int pageSize = pageRequest.getPageSize();
        int pageNum = pageRequest.getPageNum();
        
        // 模拟分页数据
        for (int i = 1; i <= pageSize; i++) {
            User user = getUserById((long) ((pageNum - 1) * pageSize + i));
            users.add(user);
        }
        
        return new PageResult<>(users, pageNum, pageSize, 1000L);
    }
}

Provider 配置(application.yml):

# Dubbo 配置
dubbo:
  application:
    name: user-service-provider
    version: 1.0.0
  registry:
    address: nacos://127.0.0.1:8848
    parameters:
      namespace: dubbo
  protocol:
    name: dubbo
    port: 20880
  provider:
    timeout: 5000
    retries: 2
    loadbalance: roundrobin
  metadata-report:
    address: nacos://127.0.0.1:8848

# Spring 配置
spring:
  application:
    name: user-service-provider
server:
  port: 8081

# 日志配置
logging:
  level:
    com.example: DEBUG
    org.apache.dubbo: INFO

Provider 启动类:

package com.example.provider;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubbo(scanBasePackages = "com.example.service.impl")
public class ProviderApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
        System.out.println("User service provider started successfully!");
    }
}

服务消费者实现

Consumer 配置(application.yml):

# Dubbo 配置
dubbo:
  application:
    name: user-service-consumer
    version: 1.0.0
  registry:
    address: nacos://127.0.0.1:8848
    parameters:
      namespace: dubbo
  consumer:
    timeout: 5000
    retries: 2
    check: false

# Spring 配置
spring:
  application:
    name: user-service-consumer
server:
  port: 8080

logging:
  level:
    com.example: DEBUG
    org.apache.dubbo: INFO

Consumer 服务调用:

package com.example.consumer.controller;

import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.*;
import com.example.service.UserService;
import com.example.model.User;

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @DubboReference(version = "1.0.0", timeout = 5000)
    private UserService userService;
    
    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.getUserById(id);
    }
    
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.createUser(user);
    }
    
    @PutMapping("/{id}")
    public boolean updateUser(@PathVariable Long id, @RequestBody User user) {
        user.setId(id);
        return userService.updateUser(user);
    }
    
    @DeleteMapping("/{id}")
    public boolean deleteUser(@PathVariable Long id) {
        return userService.deleteUser(id);
    }
    
    @GetMapping
    public PageResult<User> getUsers(
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "10") Integer pageSize) {
        PageRequest pageRequest = new PageRequest(pageNum, pageSize);
        return userService.getUsers(pageRequest);
    }
}

Consumer 启动类:

package com.example.consumer;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubbo
public class ConsumerApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
        System.out.println("User service consumer started successfully!");
    }
}

实战经验

高级配置

多注册中心配置:

dubbo:
  registries:
    nacos1:
      address: nacos://192.168.1.100:8848
      parameters:
        namespace: production
    nacos2:
      address: nacos://192.168.1.101:8848
      parameters:
        namespace: production
    zookeeper:
      address: zookeeper://192.168.1.102:2181

多协议配置:

dubbo:
  protocols:
    dubbo:
      name: dubbo
      port: 20880
    rest:
      name: rest
      port: 8080
    http:
      name: http
      port: 8081

负载均衡策略

// 在服务消费者端配置负载均衡
@DubboReference(
    version = "1.0.0",
    loadbalance = "roundrobin"  // 轮询
    // loadbalance = "random"      // 随机
    // loadbalance = "leastactive" // 最少活跃调用数
    // loadbalance = "consistenthash" // 一致性哈希
)
private UserService userService;

容错策略

// 配置容错策略
@DubboReference(
    version = "1.0.0",
    cluster = "failover",  // 失败自动切换(默认)
    retries = 2           // 重试次数
    // cluster = "failfast"   // 快速失败
    // cluster = "failsafe"   // 失败安全
    // cluster = "failback"   // 失败自动恢复
    // cluster = "forking"    // 并行调用
)
private UserService userService;

服务分组和版本

// Provider 端
@DubboService(
    group = "default",
    version = "1.0.0"
)
public class UserServiceImpl implements UserService {
    // 实现
}

// Consumer 端
@DubboReference(
    group = "default",
    version = "1.0.0"
)
private UserService userService;

异步调用

// Provider 端
@DubboService
public class AsyncUserServiceImpl implements UserService {
    
    public CompletableFuture<User> getUserByIdAsync(Long id) {
        return CompletableFuture.supplyAsync(() -> {
            // 异步处理逻辑
            return getUserById(id);
        });
    }
}

// Consumer 端
@DubboReference
private UserService userService;

public void asyncCall() {
    // 异步调用
    CompletableFuture<User> future = RpcContext.getContext()
        .asyncCall(() -> userService.getUserById(1L));
    
    future.whenComplete((user, throwable) -> {
        if (throwable == null) {
            System.out.println("Async result: " + user);
        } else {
            System.err.println("Async error: " + throwable.getMessage());
        }
    });
}

过滤器(Filter)

// 自定义过滤器
@Activate(group = {PROVIDER, CONSUMER})
public class LoggingFilter implements Filter {
    
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) 
            throws RpcException {
        long startTime = System.currentTimeMillis();
        
        try {
            Result result = invoker.invoke(invocation);
            long duration = System.currentTimeMillis() - startTime;
            
            System.out.printf("Method [%s] executed in %d ms%n", 
                invocation.getMethodName(), duration);
            
            return result;
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            System.err.printf("Method [%s] failed after %d ms: %s%n", 
                invocation.getMethodName(), duration, e.getMessage());
            throw e;
        }
    }
}

参数验证

// 使用 JSR-303 验证
@DubboService(validation = "true")
public class UserServiceImpl implements UserService {
    
    @Override
    public User createUser(@Valid User user) {
        // 实现逻辑
        return user;
    }
}

// 实体类验证注解
public class User implements Serializable {
    @NotNull(message = "用户名不能为空")
    @Size(min = 2, max = 50, message = "用户名长度必须在2-50之间")
    private String username;
    
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
    private String phone;
}

泛化调用

// 不依赖接口的服务调用
@DubboReference(
    interfaceName = "com.example.service.UserService",
    version = "1.0.0",
    generic = true
)
private GenericService genericService;

public void genericCall() {
    // 泛化调用
    Object result = genericService.$invoke(
        "getUserById", 
        new String[]{"java.lang.Long"}, 
        new Object[]{1L}
    );
    
    System.out.println("Generic call result: " + result);
}

经验总结

优势

  • 高性能:基于 NIO 的高性能网络框架
  • 透明化调用:像调用本地方法一样调用远程服务
  • 负载均衡:内置多种负载均衡算法
  • 容错机制:提供多种容错策略
  • 服务治理:完善的服务注册发现机制
  • 可扩展性:微内核设计,易于扩展

适用场景

适合使用 Dubbo:

  • 大型分布式系统
  • 微服务架构
  • 高并发场景
  • 需要服务治理的系统
  • Java 技术栈项目

不适合使用 Dubbo:

  • 小型项目
  • 跨语言通信(建议使用 gRPC)
  • 简单的 HTTP API
  • 对性能要求不高的场景

最佳实践

  1. 接口设计

    • 保持接口简洁,避免过多参数
    • 使用包装对象传递复杂参数
    • 合理设计返回值类型
  2. 服务分组和版本管理

    • 使用语义化版本号
    • 合理使用服务分组
    • 做好版本兼容性
  3. 性能优化

    • 合理设置超时时间
    • 选择合适的序列化协议
    • 合理配置连接池参数
  4. 监控和运维

    • 配置监控中心
    • 设置合理的日志级别
    • 使用服务治理平台

常见问题

  1. 服务无法发现

    • 检查注册中心配置
    • 确认网络连通性
    • 验证服务注册状态
  2. 调用超时

    • 调整超时配置
    • 检查网络延迟
    • 优化服务实现
  3. 序列化异常

    • 确保实体类实现 Serializable
    • 检查版本兼容性
    • 统一序列化协议

信息参考

创建于 2025/1/1 更新于 2026/5/27