Dubbo(86)如何设计一个多租户的Dubbo服务?

设计一个多租户的Dubbo服务需要考虑多个方面,包括租户隔离、数据隔离、配置管理、认证与授权等。以下是一个详细的设计步骤和代码示例,展示如何构建一个多租户的Dubbo服务。

1. 多租户Dubbo服务设计原则

  1. 租户隔离:确保不同租户的数据和服务隔离,防止数据泄露和混淆。
  2. 数据隔离:使用租户ID作为数据隔离的关键,确保每个租户只能访问自己的数据。
  3. 配置管理:支持多租户的动态配置管理,确保每个租户的配置独立。
  4. 认证与授权:使用租户ID进行认证与授权,确保只有合法的租户才能访问服务。
  5. 日志审计:记录多租户的关键操作日志,便于审计和排查问题。

2. 项目结构

我们将创建一个简单的项目结构,包含一个服务提供者和一个服务消费者:

css 复制代码
dubbo-demo
├── dubbo-api
│   └── src/main/java/com/example/dubbo/api
│       └── UserService.java
├── dubbo-provider
│   └── src/main/java/com/example/dubbo/provider
│       ├── service
│       │   └── UserServiceImpl.java
│       ├── tenant
│       │   ├── TenantContext.java
│       │   ├── TenantInterceptor.java
│       │   └── TenantConfig.java
│       ├── config
│       │   └── DubboProviderConfig.java
│       ├── DubboProviderApplication.java
│       └── logback-spring.xml
├── dubbo-consumer
│   └── src/main/java/com/example/dubbo/consumer
│       ├── controller
│       │   └── UserController.java
│       ├── tenant
│       │   ├── TenantContext.java
│       │   ├── TenantInterceptor.java
│       │   └── TenantConfig.java
│       ├── config
│       │   └── DubboConsumerConfig.java
│       ├── DubboConsumerApplication.java
│       └── logback-spring.xml
└── pom.xml

3. 创建服务接口模块(dubbo-api)

服务接口模块 dubbo-api 定义了服务接口。

3.1 创建 pom.xml

dubbo-api 模块中创建 pom.xml 文件:

xml 复制代码
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://www.w3.org/2001/04/xmldsig-more#">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>dubbo-api</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.7.8</version>
        </dependency>
    </dependencies>
</project>

3.2 创建服务接口

dubbo-api/src/main/java/com/example/dubbo/api 目录下创建 UserService 接口:

java 复制代码
package com.example.dubbo.api;

public interface UserService {
    String getUser(String userId);
}

4. 创建服务提供者模块(dubbo-provider)

服务提供者模块 dubbo-provider 实现了服务接口并提供服务。

4.1 创建 pom.xml

dubbo-provider 模块中创建 pom.xml 文件:

xml 复制代码
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.w3.org/2001/04/xmldsig-more#">

    <parent>
        <groupId>com.example</groupId>
        <artifactId>dubbo-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <artifactId>dubbo-provider</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>dubbo-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>
</project>

4.2 创建服务实现

dubbo-provider/src/main/java/com/example/dubbo/provider/service 目录下创建 UserServiceImpl 类:

java 复制代码
package com.example.dubbo.provider.service;

import com.example.dubbo.api.UserService;
import com.example.dubbo.provider.tenant.TenantContext;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@DubboService
public class UserServiceImpl implements UserService {
    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Override
    public String getUser(String userId) {
        String tenantId = TenantContext.getTenantId();
        logger.info("Fetching user with ID: {} for tenant: {}", userId, tenantId);
        return "User: " + userId + " for tenant: " + tenantId;
    }
}

4.3 创建租户上下文和拦截器

dubbo-provider/src/main/java/com/example/dubbo/provider/tenant 目录下创建 TenantContext 类、TenantInterceptor 类和 TenantConfig 类。

TenantContext 类:

java 复制代码
package com.example.dubbo.provider.tenant;

public class TenantContext {
    private static final ThreadLocal<String> TENANT_ID = new ThreadLocal<>();

    public static void setTenantId(String tenantId) {
        TENANT_ID.set(tenantId);
    }

    public static String getTenantId() {
        return TENANT_ID.get();
    }

    public static void clear() {
        TENANT_ID.remove();
    }
}

TenantInterceptor 类:

java 复制代码
package com.example.dubbo.provider.tenant;

import org.apache.dubbo.rpc.*;

public class TenantInterceptor implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        String tenantId = invocation.getAttachment("tenantId");
        if (tenantId != null) {
            TenantContext.setTenantId(tenantId);
        }
        try {
            return invoker.invoke(invocation);
        } finally {
            TenantContext.clear();
        }
    }
}

TenantConfig 类:

java 复制代码
package com.example.dubbo.provider.tenant;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.apache.dubbo.rpc.Filter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableDubbo(scanBasePackages = "com.example.dubbo.provider.service")
public class TenantConfig {
    @Bean
    public Filter tenantInterceptor() {
        return new TenantInterceptor();
    }
}

4.4 创建配置类

dubbo-provider/src/main/java/com/example/dubbo/provider/config 目录下创建 DubboProviderConfig 类:

java 复制代码
package com.example.dubbo.provider.config;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableDubbo(scanBasePackages = "com.example.dubbo.provider.service")
public class DubboProviderConfig {
}

4.5 创建启动类

dubbo-provider/src/main/java/com/example/dubbo/provider 目录下创建 DubboProviderApplication 类:

java 复制代码
package com.example.dubbo.provider;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DubboProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(DubboProviderApplication.class, args);
    }
}

4.6 配置文件

dubbo-provider/src/main/resources 目录下创建 application.yml 配置文件:

yaml 复制代码
spring:
  application:
    name: dubbo-provider
  main:
    web-application-type: none

dubbo:
  application:
    name: dubbo-provider
  registry:
    address: zookeeper://localhost:2181
  protocol:
    name: dubbo
    port: 20880
  scan:
    base-packages: com.example.dubbo.provider.service

logging:
  level:
    com.example.dubbo: INFO
  file:
    name: logs/dubbo-provider.log

5. 创建服务消费者模块(dubbo-consumer)

服务消费者模块 dubbo-consumer 调用服务提供者提供的服务。

5.1 创建 pom.xml

dubbo-consumer 模块中创建 pom.xml 文件:

xml 复制代码
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://www.w3.org/2001/04/xmldsig-more#">

    <parent>
        <groupId>com.example</groupId>
        <artifactId>dubbo-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <artifactId>dubbo-consumer</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>dubbo-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>
</project>

5.2 创建控制器

dubbo-consumer/src/main/java/com/example/dubbo/consumer/controller 目录下创建 UserController 类:

java 复制代码
package com.example.dubbo.consumer.controller;

import com.example.dubbo.api.UserService;
import com.example.dubbo.consumer.tenant.TenantContext;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    @DubboReference
    private UserService userService;

    @GetMapping("/getUser")
    public String getUser(@RequestParam String userId, @RequestParam String tenantId) {
        TenantContext.setTenantId(tenantId);
        logger.info("Fetching user with ID: {} for tenant: {}", userId, tenantId);
        return userService.getUser(userId);
    }
}

5.3 创建租户上下文和拦截器

dubbo-consumer/src/main/java/com/example/dubbo/consumer/tenant 目录下创建 TenantContext 类、TenantInterceptor 类和 TenantConfig 类。

TenantContext 类:

java 复制代码
package com.example.dubbo.consumer.tenant;

public class TenantContext {
    private static final ThreadLocal<String> TENANT_ID = new ThreadLocal<>();

    public static void setTenantId(String tenantId) {
        TENANT_ID.set(tenantId);
    }

   
相关推荐
爱勇宝1 小时前
第 1 章:别把“需求文档”当成真正的需求
前端·后端·程序员
IT_陈寒6 小时前
闭包陷阱让我加了两天班,JavaScript你真行
前端·人工智能·后端
易协同低代码7 小时前
通达OA核心类库TD类深度解析
后端
Gopher_HBo7 小时前
Go语言学习笔记(十八)Gin处理Session
后端
谭光志8 小时前
工具塞满上下文窗口怎么办?深度拆解 AI Agent Tool Search 按需加载实现原理
前端·后端·ai编程
她说..8 小时前
Java 默认值设置方式
java·开发语言·后端·springboot
foggyprojects8 小时前
从0开始,一句话启动AI DataAgent
后端·数据分析·ai编程
郡杰8 小时前
一些基础和问题解决
后端
陈随易8 小时前
前端项目部署只要30秒
前端·后端·程序员
YIAN9 小时前
从零手写文件读取 MCP 服务:一文吃透 Model Context Protocol 全链路通信原理
前端·后端·mcp