tenant

package
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2026 License: MIT Imports: 4 Imported by: 0

README

tenant 包 — 多租户支持

所属层级: Infrastructure Layer
设计理念: 租户隔离,SaaS 架构支持
设计灵感: Spring Multi-Tenant + Hibernate Multi-Tenancy

概述

tenant 包提供多租户架构支持,参考 Spring Boot 多租户设计。支持租户隔离、租户上下文、租户解析器等功能,适用于 SaaS 应用开发。

核心功能
功能 说明
租户解析器 支持请求头、子域名、JWT 等多种租户识别方式
租户上下文 线程安全的租户上下文管理
租户中间件 HTTP 中间件自动解析和设置租户
租户隔离 支持数据层和业务层的租户隔离

核心接口

Tenant 租户对象
type Tenant struct {
    ID       string
    Name     string
    Domain   string
    Database string
    Enabled  bool
    Metadata map[string]string
}
TenantResolver 租户解析器接口
type TenantResolver interface {
    Resolve(req *http.Request) (string, error)
}
HeaderResolver 请求头解析器
resolver := tenant.NewHeaderResolver("X-Tenant-ID")

从请求头 X-Tenant-ID 中提取租户 ID。

SubdomainResolver 子域名解析器
resolver := tenant.NewSubdomainResolver("example.com")

从子域名中提取租户 ID,例如 tenant1.example.com 解析为 tenant1

TenantManager 租户管理器
type TenantManager struct {
    // ...
}
创建
manager := tenant.NewTenantManager(resolver)
租户管理
// 设置当前租户
manager.SetCurrentTenant("tenant-123")

// 获取当前租户
currentTenant := manager.GetCurrentTenant()

// 清除当前租户
manager.ClearCurrentTenant()

快速开始

基本使用
package main

import (
    "fmt"
    "github.com/xudefa/enhance/tenant"
)

func main() {
    // 创建请求头解析器
    resolver := tenant.NewHeaderResolver("X-Tenant-ID")

    // 创建租户管理器
    manager := tenant.NewTenantManager(resolver)

    // 设置当前租户
    manager.SetCurrentTenant("tenant-123")

    // 获取当前租户
    currentTenant := manager.GetCurrentTenant()
    fmt.Println("Current tenant:", currentTenant)
}

API 参考

使用租户中间件
resolver := tenant.NewHeaderResolver("X-Tenant-ID")
manager := tenant.NewTenantManager(resolver)

// 创建租户中间件
middleware := tenant.NewTenantMiddleware(manager)

// 包装处理器
handler := middleware.Handle(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    tenant := manager.GetCurrentTenant()
    fmt.Fprintf(w, "Hello from tenant: %s", tenant.ID)
}))

http.Handle("/api", handler)
http.ListenAndServe(":8080", nil)
使用子域名解析器
resolver := tenant.NewSubdomainResolver("example.com")
manager := tenant.NewTenantManager(resolver)

middleware := tenant.NewTenantMiddleware(manager)

handler := middleware.Handle(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    tenant := manager.GetCurrentTenant()
    fmt.Fprintf(w, "Tenant: %s", tenant.ID)
}))

http.Handle("/", handler)
http.ListenAndServe(":8080", nil)

使用示例

场景 1: SaaS 应用多租户隔离

SaaS 应用中不同租户数据完全隔离:

func (s *UserService) GetUsers(ctx context.Context) ([]User, error) {
    tenant := tenant.GetCurrentTenant(ctx)
    if tenant == nil {
        return nil, fmt.Errorf("no tenant context")
    }

    // 使用租户 ID 查询数据
    return s.repo.FindByTenant(tenant.ID)
}

最佳实践:

  • 所有数据查询必须包含租户 ID
  • 使用中间件自动设置租户上下文
  • 记录租户操作日志
场景 2: 多租户数据库隔离

不同租户使用不同的数据库或 schema:

func GetDBForTenant(tenantID string) (*sql.DB, error) {
    // 根据租户 ID 获取对应的数据库连接
    config := getTenantConfig(tenantID)
    return sql.Open(config.Driver, config.DSN)
}

func (s *OrderService) CreateOrder(order *Order) error {
    tenant := tenant.GetCurrentTenant()
    
    db, err := GetDBForTenant(tenant.ID)
    if err != nil {
        return err
    }

    return db.Exec("INSERT INTO orders ...", order)
}

最佳实践:

  • 使用连接池管理租户数据库连接
  • 缓存租户配置避免重复查询
  • 定期清理空闲连接
场景 3: 租户配额管理

限制每个租户的资源使用量:

func (s *QuotaService) CheckQuota(tenantID string, resource string) error {
    quota := s.getTenantQuota(tenantID, resource)
    usage := s.getTenantUsage(tenantID, resource)

    if usage >= quota {
        return fmt.Errorf("tenant %s exceeded %s quota", tenantID, resource)
    }

    return nil
}

最佳实践:

  • 在关键操作前检查配额
  • 提供配额使用情况查询接口
  • 支持配额动态调整

最佳实践

1. 使用中间件自动解析租户
// ✅ 推荐:使用中间件自动解析
resolver := tenant.NewHeaderResolver("X-Tenant-ID")
manager := tenant.NewTenantManager(resolver)
middleware := tenant.NewTenantMiddleware(manager)

// ⚠️ 不推荐:手动解析每个请求
func handler(w http.ResponseWriter, r *http.Request) {
    tenantID := r.Header.Get("X-Tenant-ID")
    if tenantID == "" {
        http.Error(w, "Missing tenant", 400)
        return
    }
    manager.SetCurrentTenant(tenantID)
}
2. 使用上下文传递租户信息
// ✅ 推荐:使用 context 传递租户
ctx := context.WithValue(r.Context(), "tenant", tenant)
next.ServeHTTP(w, r.WithContext(ctx))

// 获取租户
func GetCurrentTenant(ctx context.Context) *Tenant {
    if t, ok := ctx.Value("tenant").(*Tenant); ok {
        return t
    }
    return nil
}

// ⚠️ 不推荐:使用全局变量
var currentTenant *Tenant
3. 数据查询包含租户隔离
// ✅ 推荐:所有查询包含租户条件
func (r *UserRepository) FindByTenant(tenantID string) ([]User, error) {
    return r.db.Where("tenant_id = ?", tenantID).Find(&users)
}

// ⚠️ 不推荐:查询不包含租户条件
func (r *UserRepository) FindAll() ([]User, error) {
    return r.db.Find(&users)
}
4. 缓存租户配置
// ✅ 推荐:缓存租户配置
type TenantCache struct {
    cache map[string]*TenantConfig
    mu    sync.RWMutex
}

func (c *TenantCache) Get(tenantID string) (*TenantConfig, error) {
    c.mu.RLock()
    if config, ok := c.cache[tenantID]; ok {
        c.mu.RUnlock()
        return config, nil
    }
    c.mu.RUnlock()
    
    // 从数据库加载
    config, err := loadFromDB(tenantID)
    if err != nil {
        return nil, err
    }
    
    c.mu.Lock()
    c.cache[tenantID] = config
    c.mu.Unlock()
    
    return config, nil
}

// ⚠️ 不推荐:每次查询都从数据库加载
func GetTenantConfig(tenantID string) (*TenantConfig, error) {
    return loadFromDB(tenantID) // 每次都查询数据库
}
5. 与依赖注入集成
// ✅ 推荐:将 TenantManager 注册为 Bean
container.Register(
    reflect.TypeOf(&tenant.TenantManager{}),
    core.Bean(createTenantManager()),
    core.Singleton(),
)

// 注入使用
type UserService struct {
    TenantManager *tenant.TenantManager `inject:"tenantManager"`
}

func (s *UserService) GetUsers(ctx context.Context) ([]User, error) {
    tenant := s.TenantManager.GetCurrentTenant()
    return s.repo.FindByTenant(tenant.ID)
}
6. 设计要点
  • 支持多种租户解析方式
  • 使用上下文传递租户信息
  • 中间件自动解析和设置租户
  • 线程安全的租户上下文管理
  • 零外部依赖,仅使用 Go 标准库

Documentation

Overview

Package tenant 提供多租户支持,用于 enhance 框架。

该模块提供多租户架构支持,包括租户上下文管理、租户隔离、租户数据源切换等功能。 参考 Saas 多租户架构的设计理念。

架构设计

  • Tenant: 租户结构体,包含租户基本信息
  • TenantResolver: 租户解析器接口,从请求中解析租户
  • TenantManager: 租户管理器接口,管理租户生命周期
  • TenantMiddleware: 租户中间件接口,自动设置租户上下文
  • TenantIsolation: 租户隔离器接口,提供数据隔离功能
  • TenantRegistry: 租户注册表接口,管理所有租户
  • TenantProvider: 租户提供者接口,提供便捷访问方法

核心功能

  • 租户上下文: 提供线程安全的租户上下文管理
  • 租户解析: 支持从域名、请求头、URL 参数等解析租户
  • 租户隔离: 支持数据级和逻辑级的租户隔离
  • 数据源切换: 支持按租户动态切换数据源

使用方式

创建租户管理器:

resolver := tenant.NewHeaderResolver("X-Tenant-ID")
manager := tenant.NewTenantManager(resolver)

注册租户:

manager.RegisterTenant(&tenant.Tenant{
    ID:       "tenant-1",
    Name:     "租户 1",
    Database: "tenant_1_db",
    Enabled:  true,
})

使用租户中间件:

middleware := tenant.NewTenantMiddleware(manager)
handler := middleware.Handle(nextHandler)

从 Context 获取租户:

tenant, ok := tenant.TenantFromContext(ctx)

多租户架构模式

  • 数据库级别隔离:每个租户独立数据库
  • Schema 级别隔离:每个租户独立 Schema
  • 数据级别隔离:所有租户共享数据库,通过 tenant_id 字段隔离

Package tenant 提供多租户支持,用于 enhance 框架。

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Tenant

type Tenant struct {
	// ID 租户 ID。
	ID string
	// Name 租户名称。
	Name string
	// Domain 租户域名。
	Domain string
	// Database 租户数据库。
	Database string
	// Enabled 是否启用。
	Enabled bool
	// Metadata 租户元数据。
	Metadata map[string]string
}

Tenant 租户结构体。

包含租户的基本信息和配置。

func TenantFromContext

func TenantFromContext(ctx context.Context) (*Tenant, bool)

TenantFromContext 从 context 获取租户。

type TenantIsolation

type TenantIsolation interface {
	// IsolateDatabase 数据库隔离。
	IsolateDatabase(tenantID string) (string, error)

	// IsolateSchema 模式隔离。
	IsolateSchema(tenantID string) (string, error)

	// IsolateRow 行级隔离。
	IsolateRow(tenantID string) string
}

TenantIsolation 租户隔离器接口。

提供租户数据隔离功能,支持数据库级、Schema 级和行级隔离。

func NewTenantIsolation

func NewTenantIsolation(manager TenantManager) TenantIsolation

NewTenantIsolation 创建租户隔离器。

type TenantManager

type TenantManager interface {
	// RegisterTenant 注册租户。
	RegisterTenant(tenant *Tenant)

	// GetTenant 获取租户。
	GetTenant(tenantID string) (*Tenant, error)

	// SetCurrentTenant 设置当前租户。
	SetCurrentTenant(tenantID string) error

	// GetCurrentTenant 获取当前租户。
	GetCurrentTenant() *Tenant

	// ClearCurrentTenant 清除当前租户。
	ClearCurrentTenant()

	// ResolveFromRequest 从 HTTP 请求解析租户。
	ResolveFromRequest(req *http.Request) (string, error)
}

TenantManager 租户管理器接口。

管理租户的注册、查询和当前租户上下文。

func NewTenantManager

func NewTenantManager(resolver TenantResolver) TenantManager

NewTenantManager 创建租户管理器。

type TenantMiddleware

type TenantMiddleware interface {
	// Handle 处理 HTTP 请求。
	Handle(next http.Handler) http.Handler
}

TenantMiddleware 租户中间件接口。

自动从请求中解析租户并设置上下文。

func NewTenantMiddleware

func NewTenantMiddleware(manager TenantManager) TenantMiddleware

NewTenantMiddleware 创建租户中间件。

type TenantProvider

type TenantProvider interface {
	// GetCurrentTenantID 获取当前租户 ID。
	GetCurrentTenantID() string

	// GetCurrentTenantName 获取当前租户名称。
	GetCurrentTenantName() string

	// GetCurrentTenantDatabase 获取当前租户数据库。
	GetCurrentTenantDatabase() (string, error)

	// IsMultiTenant 检查是否为多租户模式。
	IsMultiTenant() bool
}

TenantProvider 租户提供者接口。

提供获取当前租户的便捷方法。

func NewTenantProvider

func NewTenantProvider(manager TenantManager) TenantProvider

NewTenantProvider 创建租户提供者。

type TenantRegistry

type TenantRegistry interface {
	// Add 添加租户。
	Add(tenant *Tenant)

	// Remove 移除租户。
	Remove(tenantID string)

	// Get 获取租户。
	Get(tenantID string) (*Tenant, error)

	// List 列出所有租户。
	List() []*Tenant

	// Count 获取租户数量。
	Count() int
}

TenantRegistry 租户注册表接口。

管理所有租户的注册和查询。

func NewTenantRegistry

func NewTenantRegistry() TenantRegistry

NewTenantRegistry 创建租户注册表。

type TenantResolver

type TenantResolver interface {
	// Resolve 解析租户 ID。
	Resolve(req *http.Request) (string, error)
}

TenantResolver 租户解析器接口。

从 HTTP 请求中解析租户 ID。 支持多种解析策略:请求头、子域名、JWT、路径等。

func NewHeaderResolver

func NewHeaderResolver(headerName string) TenantResolver

NewHeaderResolver 创建基于请求头的租户解析器。

func NewJWTResolver

func NewJWTResolver(claimName string) TenantResolver

NewJWTResolver 创建基于 JWT 的租户解析器。

func NewPathResolver

func NewPathResolver(segmentIndex int) TenantResolver

NewPathResolver 创建基于路径的租户解析器。

func NewSubdomainResolver

func NewSubdomainResolver(baseDomain string) TenantResolver

NewSubdomainResolver 创建基于子域名的租户解析器。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL