Dify智能体平台源码二次开发笔记(4) - 多租户的SAAS版实现

前言

        Dify 的多租户功能是其商业版的标准功能,我们应当尊重其盈利模式。只有保持良性的商业运作,Dify 才能持续发展,并为用户提供更优质的功能。因此,此功能仅限学习使用。
我们的需求是:实现类似 SaaS 版的账号隔离,包括智能体、知识库、插件和模型配置等模块的完全隔离。为此,需要启用多租户功能。

租户创建

添加controller

首先创建api的调用,这个controller代码,我就创建在controllers/console/auth下面,起名:tenant.py

class TenantCreateApi(Resource):
    """Resource for creating a new tenant."""

    @validate_token
    def post(self):
        """Create a new tenant."""
        parser = reqparse.RequestParser()
        parser.add_argument("name", type=str, required=True, location="json")
        args = parser.parse_args()
        # if not FeatureService.get_system_features().is_allow_create_workspace:
        #     raise NotAllowedCreateWorkspace()

        tenant = TenantService.create_tenant(args["name"])

        return {"result": "success", "data": {"tenant_id": str(tenant.id)}}

这里方法很简单,接收一个租户名称,通过调用

TenantService的租户创建方法,把租户创建成功。

这里@validate_token,是我自己集成我自己云中来平台登录验证的装饰器,因为我是放在我云中来平台调用的,不想用dify的登录验证,如果大家需要用dify的登录验证,然后在代码里需要使用

account = current_user这个登录用户来做业务处理,就可以换成:
@setup_required
@login_required
@account_initialization_required.

这三个装饰器,也是没有登录问题的。

配置路由
api.add_resource(TenantCreateApi, "/tenant/create")
import

我是创建在console下面的,记得在__init__.py里,把tenant import 加上

from .auth import activate, data_source_bearer_auth, data_source_oauth, forgot_password, login, oauth , tenant
集成三方系统调用
def validate_token(view: Optional[Callable] = None, *, fetch_user_arg: Optional[FetchUserArg] = None):
    def decorator(view_func):
        @wraps(view_func)
        def decorated_view(*args, **kwargs):
            auth_header = request.headers.get("Authorization")
            if auth_header is None or " " not in auth_header:
                raise Unauthorized("Authorization不通过!!!")
            auth_scheme, auth_info = auth_header.split(None, 1)
            auth_scheme = auth_scheme.lower()
            if auth_scheme != "bearer":
                raise Unauthorized("Authorization不通过!")
            if not jolywood_redis_client.exists("authorization:token:" + auth_info):
                raise Unauthorized("请登录云中来!!")
            return view_func(*args, **kwargs)

        return decorated_view

    if view is None:
        return decorator
    else:
        return decorator(view)

这个比较简单,验证我自己云中来平台的登录token

添加Service

因为dify本身就有

TenantService这个类了,我们就不要再去创建其它的服务类。直接在类中添加我们需要的方法。
@staticmethod
    def create_tenant(name: str, is_setup: Optional[bool] = False, is_from_dashboard: Optional[bool] = False) -> Tenant:
        """Create tenant"""
        # if (
        #     not FeatureService.get_system_features().is_allow_create_workspace
        #     and not is_setup
        #     and not is_from_dashboard
        # ):
        #     from controllers.console.error import NotAllowedCreateWorkspace
        #
        #     raise NotAllowedCreateWorkspace()
        tenant = Tenant(name=name)

        db.session.add(tenant)
        db.session.commit()

        tenant.encrypt_public_key = generate_key_pair(tenant.id)
        db.session.commit()
        return tenant

几行代码轻松搞定,dify这里已经写好了密钥的生成方法generate_key_pair,拿来用就行。

结果验证

通过postman的测是调用,租户创建成功

同时我minio中的租户密钥也创建成功

租户查询

和创建一样分成controller和service层的实现

class TenantAllListApi(Resource):
    @validate_token
    def get(self):
        parser = reqparse.RequestParser()
        parser.add_argument("page", type=int, required=False, default=1, location="args")
        parser.add_argument("limit", type=int, required=False, default=20, location="args")
        parser.add_argument("search", type=str, required=False, location="args")
        args = parser.parse_args()
        tenants = TenantService.get_all_tenants()
        tenant_list = [{
            "id": str(tenant.id),
            "name": tenant.name,
            "created_at": tenant.created_at.isoformat()
        } for tenant in tenants]
        return {"result": "success", "data": tenant_list}
@staticmethod
    def get_all_tenants() -> list[Tenant]:
        """Get account join tenants"""
        return (
            db.session.query(Tenant).all()
        )

这里写的比较简单,没有分页和查询参数,直接把所有租户显示出来

最后

除了创建、查询,接下来更新和删除我就写了,基本大致相差不大。创建完了租户,接下来就是对用户表和用户租户关联表tenant_account_joins的处理,这个下次再说吧。

### Dify 框架的多租户支持与实现方式 Dify 是一个开源的大语言模型应用开发平台,它融合了后端即服务(Backend as a Service, BaaS)和 LLMOps 的理念,使得即使是非技术人员也能轻松创建生产级别的生成式 AI 应用[^1]。尽管在提供的引用中没有直接提到 Dify多租户支持与实现方式,但可以从其功能架构和设计原则推测其实现机制。 #### 1. 多租户支持的基本概念 多租户(Multi-Tenancy)是一种软件架构设计模式,允许多个用户或组织(租户)共享同一个应用程序实例,同时确保每个租户的数据隔离和独立性。对于像 Dify 这样的平台多租户支持通常体现在以下几个方面: - **数据隔离**:每个租户的数据存储在独立的空间中,避免数据泄露。 - **配置隔离**:每个租户可以拥有独立的配置文件和环境变量。 - **资源分配**:根据租户的需求动态分配计算、存储等资源。 #### 2. Dify多租户实现方式 虽然引用内容未明确提及多租户支持,但基于其功能特性和常见实现方式,可以推测 Dify多租户实现可能包括以下几点: ##### 数据隔离 Dify 支持多种数据源,包括本地文件、网页内容等,并提供了友好的界面用于构建和管理知识库[^1]。为了实现多租户支持,Dify 可能采用了以下策略: - 每个租户的知识库和数据存储在独立的数据库表或分区中。 - 使用租户标识符(Tenant ID)标记每条记录,确保查询时只返回对应租户的数据。 ##### 配置隔离 在安装和配置过程中,Dify 提供了对 `.env` 文件的修改选项,例如调整 `FORCE_VERIFYING_SIGNATURE`、`PLUGIN_MAX_PACKAGE_SIZE` 和 `NGINX_CLIENT_MAX_BODY_SIZE` 等参数[^2]。这些配置可以为每个租户单独设置,从而实现环境的个性化。 ##### 插件管理 Dify 内置了 API 接口,方便开发者集成到现有的工作流中[^1]。通过插件机制,不同租户可以根据需求安装和使用不同的功能模块。例如,通过修改 `.env` 文件中的 `FORCE_VERIFYING_SIGNATURE` 参数为 `false`,可以允许安装未上架的插件[^2]。 ##### 并发处理 在企业微信集成场景中,Dify 提供了异步线程支持,用于启动 `linkai_client` 等功能[^4]。这种并发处理机制可以确保多个租户的操作不会相互干扰。 #### 3. 技术实现细节 从代码结构来看,Dify 的 PDF 文档提取功能展示了其模块化设计[^3]。类似的模块化设计理念也可能应用于多租户支持中: - **租户管理模块**:负责注册、认证和管理租户信息。 - **数据访问层**:通过租户标识符过滤数据,确保数据隔离。 - **API 网关**:为每个租户提供独立的访问入口,支持自定义域名和认证方式。 #### 示例代码 以下是一个简单的多租户数据隔离实现示例,假设 Dify 使用 SQLAlchemy 作为 ORM 工具: ```python from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String # 创建数据库引擎 engine = create_engine('sqlite:///dify.db') # 定义数据表 metadata = MetaData() documents = Table( 'documents', metadata, Column('id', Integer, primary_key=True), Column('tenant_id', String), # 租户标识符 Column('content', String) ) # 插入数据 def insert_document(tenant_id, content): with engine.connect() as conn: stmt = documents.insert().values(tenant_id=tenant_id, content=content) conn.execute(stmt) # 查询数据 def get_documents_by_tenant(tenant_id): with engine.connect() as conn: stmt = documents.select().where(documents.c.tenant_id == tenant_id) result = conn.execute(stmt).fetchall() return result ``` #### 总结 Dify多租户支持可能通过数据隔离、配置隔离、插件管理和并发处理等方式实现。具体实现细节需要参考官方文档或源码分析,但其模块化设计和灵活配置能力为多租户支持奠定了基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天下琴川

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值