盘点 Seata : Server 端事务的 Session 初始化

本文解析了Seata Server中Session处理的核心环节,包括global_table和branch_table的数据结构、操作流程,以及Session的初始化步骤。重点介绍了global_table用于全局事务管理和branch_table标识分支事务的功能及处理逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜

文章合集 : 🎁 https://juejin.cn/post/6941642435189538824
Github : 👉 https://github.com/black-ant
CASE 备份 : 👉 https://gitee.com/antblack/case

一 .前言

前面说了 Seata Client 的请求流程 , 这一篇从 Session 的处理来看一下 Seata Server 端的处理 .

每一次 Seata 的全局操作都会创建一个 Session , 并且往表中插入事务数据.

二 . global_table 表

先来看一下 global_table 的表结构

CREATE TABLE `global_table` (
  `xid` varchar(128) NOT NULL,
  `transaction_id` bigint(20) DEFAULT NULL,
  `status` tinyint(4) NOT NULL,
  `application_id` varchar(32) DEFAULT NULL,
  `transaction_service_group` varchar(32) DEFAULT NULL,
  `transaction_name` varchar(128) DEFAULT NULL,
  `timeout` int(11) DEFAULT NULL,
  `begin_time` bigint(20) DEFAULT NULL,
  `application_data` varchar(2000) DEFAULT NULL,
  `gmt_create` datetime DEFAULT NULL,
  `gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`xid`),
  KEY `idx_gmt_modified_status` (`gmt_modified`,`status`),
  KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE `branch_table` (
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(128) NOT NULL,
  `transaction_id` bigint(20) DEFAULT NULL,
  `resource_group_id` varchar(32) DEFAULT NULL,
  `resource_id` varchar(256) DEFAULT NULL,
  `branch_type` varchar(8) DEFAULT NULL,
  `status` tinyint(4) DEFAULT NULL,
  `client_id` varchar(64) DEFAULT NULL,
  `application_data` varchar(2000) DEFAULT NULL,
  `gmt_create` datetime(6) DEFAULT NULL,
  `gmt_modified` datetime(6) DEFAULT NULL,
  PRIMARY KEY (`branch_id`),
  KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

三 . Server Session 处理一览

我们通过启动参数 -m 对请求 STORE_MODE 进行配置 : seata-server.bat -m db

整个 Session 的处理会分别对2个操作进行处理 , 一个为 global_table , 一个为 branch_table , 依次来说 :

Pro 1 : global_table 的作用

global_table 用于持久化全局事务 , 可以通过 store.db.global.table 进行配置

Pro 2 : branch_table 的作用

branch_table 用于标识分支事务 , 可以通过 store.db.branch.table 进行配置

数据结构

# C- LogStoreDataBaseDAO # insertGlobalTransactionDO : 插入 global_table   
INSERT INTO `seata`.`global_table` 
    ( `xid`, 
    `transaction_id`, 
    `status`, 
    `application_id`, 
    `transaction_service_group`, 
    `transaction_name`, 
    `timeout`, 
    `begin_time`, 
    `application_data`, 
    `gmt_create`, 
    `gmt_modified` )
VALUES
    ( '192.168.181.2:8091:8466916507467911205',
    8466916507467911205,
    1, 
    'business-seata-example', 
    'business-service-seata-service-group', 
    'dubbo-gts-seata-example', 
    300000, 
    1624863673423, 
    NULL, 
    '2021-06-28 15:01:28', 
    '2021-06-28 15:01:28' );



# C- LogStoreDataBaseDAO # insertBranchTransactionDO  
INSERT INTO `seata`.`branch_table`
    (`branch_id`, 
    `xid`, 
    `transaction_id`, 
    `resource_group_id`,
    `resource_id`, 
    `branch_type`, 
    `status`, 
    `client_id`, 
    `application_data`,
    `gmt_create`, 
    `gmt_modified`) 
VALUES
    (8466916507467911829, 
    '192.168.181.2:8091:8466916507467911205', 
    8466916507467911205, 
    NULL, 
    'jdbc:mysql://127.0.0.1:3306/seata', 
    'AT', 
    0, 
    'storage-seata-example:192.168.181.2:51964', 
    NULL, 
    '2021-06-28 15:35:18.534107', 
    '2021-06-28 15:35:18.534107');

3.1 global_table 的处理流程

配置 STORE_MODE 为 db 后 , 会使用 DataBaseSessionManager 和 DataBaseTransactionStoreManager 进行业务的处理

// 创建的调用入口 (此处忽略前置逻辑 , 但从 Session 的创建开始)
C- AbstractSessionManager # onBegin
C- DataBaseSessionManager # addGlobalSession
C- DataBaseTransactionStoreManager # writeSession (此处类型为 GLOBAL_ADD((byte)1))

从 Step 1 中可以看到 , 添加时会调用 writeSession , 这是个很重要的方法 , 基本上所有的编辑session 操作都会经历该类 , 可以通过 Debug 该部分

/**
* DataBaseTransactionStoreManager 该方法中进行了全局事务和分支事务的管理
**/
public boolean writeSession(LogOperation logOperation, SessionStorable session) {
    if (LogOperation.GLOBAL_ADD.equals(logOperation)) {
        // 插入全局事务
        return logStore.insertGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session));
    } else if (LogOperation.GLOBAL_UPDATE.equals(logOperation)) {
        // 更新全局事务
        return logStore.updateGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session));
    } else if (LogOperation.GLOBAL_REMOVE.equals(logOperation)) {
        // 删除全局事务
        return logStore.deleteGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session));
    } else if (LogOperation.BRANCH_ADD.equals(logOperation)) {
        // 插入分支事务
        return logStore.insertBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session));
    } else if (LogOperation.BRANCH_UPDATE.equals(logOperation)) {
        // 更新分支事务
        return logStore.updateBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session));
    } else if (LogOperation.BRANCH_REMOVE.equals(logOperation)) {
        // 删除分支事务
        return logStore.deleteBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session));
    } else {
            throw new StoreException("Unknown LogOperation:" + logOperation.name());
    }
}

[Pro31001] : logOperation 的作用和来源

LogOperation 作用 :

LogOperation 是一个枚举类 , 用于表示操作的类型

enum LogOperation {
    GLOBAL_ADD((byte)1),
    GLOBAL_UPDATE((byte)2),
    GLOBAL_REMOVE((byte)3),
    
    BRANCH_ADD((byte)4),
    BRANCH_UPDATE((byte)5),
    BRANCH_REMOVE((byte)6);

    private byte code;
}

LogOperation 的来源:

在调用该流程的时候 , 会传入对应的 LogOperation.code . 例如 DataBaseSessionManager 操作中


C- DataBaseSessionManager
    M- addGlobalSession
        - transactionStoreManager.writeSession(LogOperation.GLOBAL_UPDATE, session);
    M- updateGlobalSessionStatus
        - transactionStoreManager.writeSession(LogOperation.GLOBAL_UPDATE, session);
    M- removeGlobalSession
        - transactionStoreManager.writeSession(LogOperation.GLOBAL_REMOVE, session)

3.2 branch_table 的处理逻辑

//======== 以下是 Beanch 逻辑    
C- DataBaseTransactionStoreManager # writeSession (此处类型为 BRANCH_ADD((byte)4))

// 最终调用有以下方法
C- LogStoreDataBaseDAO 
    M- insertBranchTransactionDO  
    M- updateBranchTransactionDO
    M- deleteBranchTransactionDO

四 . Session 的初始化流程

4.1 Session 的初始化

Step 1 : 启动入口 Server # main , 其中会开启 Session

// 在 server # main 启动方法中 , 会调用以下语句
SessionHolder.init(parameterParser.getStoreMode());

Step 2 : SessionHolder init 流程

C- SessionHolder # init 中进行了如下操作 :

  • 获得配置的 store.mode , 从下面得代码可以看到支持 DB , FILE , REDIS
  • 通过 EnhancedServiceLoader#load 加载 SessionManager
public static void init(String mode) {
    
    // 获取配置文件中的 store.mode 属性
	if (StringUtils.isBlank(mode)) {
		mode = CONFIG.getConfig(ConfigurationKeys.STORE_MODE);
	}
    
    // 构建 StoreMode
	StoreMode storeMode = StoreMode.get(mode);
	if (StoreMode.DB.equals(storeMode)) {
        	// 基础会话管理器
            ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName());
        	// 异步会话管理器
            ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName(),
                new Object[] {ASYNC_COMMITTING_SESSION_MANAGER_NAME});
    		// 重试提交会话管理器
            RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName(),
                new Object[] {RETRY_COMMITTING_SESSION_MANAGER_NAME});
		   // 重试回退会话管理器	
            RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName(),
                new Object[] {RETRY_ROLLBACKING_SESSION_MANAGER_NAME});
	} else if (StoreMode.FILE.equals(storeMode)) {
          //..... 省略
	} else if (StoreMode.REDIS.equals(storeMode)) {
          //..... 省略
	} else {
		// unknown store
		throw new IllegalArgumentException("unknown store mode:" + mode);
	}
    // 详见最后 , 刷新操作
	reload(storeMode);
}

Step 3 : InnerEnhancedServiceLoader 加载的方式

public static <S> S load(Class<S> service, String activateName) throws EnhancedServiceNotFoundException {
    // SPI 核心 : 这里就是一个简单的下层调用 , 这里简单说一下 InnerEnhancedServiceLoader
	return InnerEnhancedServiceLoader.getServiceLoader(service).load(activateName, findClassLoader());
}


// getServiceLoader 获取 ServiceLoader
private static <S> InnerEnhancedServiceLoader<S> getServiceLoader(Class<S> type) {
// 主要就是通过 SERVICE_LOADERS 获取整个集合 , 另外可以看到 , 这里是每次调用时为空就会先创建 , 再缓存一遍
return (InnerEnhancedServiceLoader<S>)CollectionUtils.computeIfAbsent(SERVICE_LOADERS, type,
	key -> new InnerEnhancedServiceLoader<>(type));
}


[PRO:] InnerEnhancedServiceLoader 的作用 ?

InnerEnhancedServiceLoaderEnhancedServiceLoader 的内部类 :

// Pro : EnhancedServiceLoader 作用 
EnhancedServiceLoaderSeata SPI 实现核心类 , Seata 通过 SPI 机制来实现 seata 的扩展 , 使其可以兼容多种注册中心 :

EnhancedServiceLoader 中 load 有如下的方式加载一个服务 : 
	M- load(Class<S> service, ClassLoader loader) : 通过服务类型和加载器加载
	M- load(Class<S> service) : 通过服务类型加载
	M- load(Class<S> service, String activateName) : 通过服务类型和激活名 (加载 ExtensionDefinition 会使用该名称)
	M- load(Class<S> service, String activateName, ClassLoader loader)
	M- load(Class<S> service, String activateName, Object[] args) : 带属性参数 (Instance 创建实例时会使用该参数)
    // load 同时提供载入一组服务
	M- loadAll(Class<S> service)
	M- loadAll(Class<S> service, Class[] argsType, Object[] args)
        
// Pro :  SPI Server 存放的位置
SeataService 类和 Spring factories 基本上一直 , 也是放在 META-INF.service 中 , 其中提供了如下配置  -> PIC30001 
        
        
// Pro :  EnhancedServiceLoader 的 子类
EnhancedServiceLoader 中有一个内部类 : C- InnerEnhancedServiceLoader  , 主要作用为避免多次载入时出现不必要的载入
   
InnerEnhancedServiceLoader 中提供了如下的参数 : 
// class 对应的 InnerEnhancedServiceLoader 集合
ConcurrentMap<Class<?>, InnerEnhancedServiceLoader<?>> SERVICE_LOADERS = new ConcurrentHashMap<>();
// Holder 内部有一个 volatile 参数用于保存对象, 保证多线程可见
Holder<List<ExtensionDefinition>> definitionsHolder = new Holder<>();
// ExtensionDefinition 集合
ConcurrentMap<ExtensionDefinition, Holder<Object>> definitionToInstanceMap = new ConcurrentHashMap<>();
// name 对应的 ExtensionDefinition 集合
ConcurrentMap<String, List<ExtensionDefinition>> nameToDefinitionsMap = new ConcurrentHashMap<>();
// ExtensionDefinition class类型 对应的 ExtensionDefinition
ConcurrentMap<Class<?>, ExtensionDefinition> classToDefinitionMap = new ConcurrentHashMap<>();

        

PIC30001 : META-INF.service 数据

image.png

Step 4 : 指定classLoader来加载 server provider

该单元是主要的处理流程 , 用于

C- EnhancedServiceLoader
private S loadExtension(String activateName, ClassLoader loader, Class[] argTypes,
                                Object[] args) {
	
    // activateName 判空操作 , 为空抛出异常 IllegalArgumentException        
    try {
        // 1 . 从配置文件 (META-INF 中加载所有的 Extension 对象)
        loadAllExtensionClass(loader);
        // 2 . 通过激活名获得 ExtensionDefinition 类数据
        ExtensionDefinition cachedExtensionDefinition = getCachedExtensionDefinition(activateName);
        // 3 . 获得实例
        return getExtensionInstance(cachedExtensionDefinition, loader, argTypes, args);
    } catch (Throwable e) {
        // .... 异常处理省略
    }
}



Step 5 : loadAllExtensionClass 从配置文件中获取所有的 Extension

C- EnhancedServiceLoader
// 1. 判断和发起加载
private List<Class> loadAllExtensionClass(ClassLoader loader) {
	List<ExtensionDefinition> definitions = definitionsHolder.get();
	if (definitions == null) {
		synchronized (definitionsHolder) {
			definitions = definitionsHolder.get();
			if (definitions == null) {
                  // 加锁后查询所有的 ExtensionDefinition , 避免线程冲突
				definitions = findAllExtensionDefinition(loader);
				definitionsHolder.set(definitions);
			}
		}
	}
	return definitions.stream().map(def -> def.getServiceClass()).collect(Collectors.toList());
}

// 2. 加载流程
private List<ExtensionDefinition> findAllExtensionDefinition(ClassLoader loader) {
    
    // 从 META-INF.service 和  META-INF.seata 中获取配置
	List<ExtensionDefinition> extensionDefinitions = new ArrayList<>();
	try {
		loadFile(SERVICES_DIRECTORY, loader, extensionDefinitions);
		loadFile(SEATA_DIRECTORY, loader, extensionDefinitions);
	} catch (IOException e) {
		throw new EnhancedServiceNotFoundException(e);
	}

	// 加载所有扩展后,按顺序对缓存进行排序 -> nameToDefinitionsMap
	if (!nameToDefinitionsMap.isEmpty()) {
		for (List<ExtensionDefinition> definitions : nameToDefinitionsMap.values()) {
			Collections.sort(definitions, (def1, def2) -> {
				int o1 = def1.getOrder();
				int o2 = def2.getOrder();
				return Integer.compare(o1, o2);
			});
		}
	}

    // 对加载的 extensionDefinitions 进行排序
	if (!extensionDefinitions.isEmpty()) {
		Collections.sort(extensionDefinitions, (definition1, definition2) -> {
			int o1 = definition1.getOrder();
			int o2 = definition2.getOrder();
			return Integer.compare(o1, o2);
		});
	}

	return extensionDefinitions;
}


Step 6 : getCachedExtensionDefinition 获取 BeanDefinition 基础信息

// 比较简单 , 就是获取 ConcurrentMap<String, List<ExtensionDefinition>> nameToDefinitionsMap 
private ExtensionDefinition getCachedExtensionDefinition(String activateName) {
	List<ExtensionDefinition> definitions = nameToDefinitionsMap.get(activateName);
	return CollectionUtils.getLast(definitions);
}

Step 7 : 反射进行初始化

// 发起流程 :
loadExtension -> getExtensionInstance -> createNewExtension

// getExtensionInstance 逻辑比较简单 , 就是判断是否为单例从而进行了一个单例模式的创建

// createNewExtension 创建实例
private S createNewExtension(ExtensionDefinition definition, ClassLoader loader, Class[] argTypes, Object[] args) {
    Class<?> clazz = definition.getServiceClass();
    try {
        S newInstance = initInstance(clazz, argTypes, args);
        return newInstance;
    } catch (Throwable t) {
        throw new IllegalStateException(....);
    }
}

// initInstance 初始化实例
private S initInstance(Class implClazz, Class[] argTypes, Object[] args)
        throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    S s = null;
    if (argTypes != null && args != null) {
        // 获取 构造函数的参数
        Constructor<S> constructor = implClazz.getDeclaredConstructor(argTypes);
        // 如果有参数 ,通过参数创建实例
        s = type.cast(constructor.newInstance(args));
    } else {
        // 使用默认构造器创建 (无参数的情况)
        s = type.cast(implClazz.newInstance());
    }
    if (s instanceof Initialize) {
        // 核心 7-1 实例init初始化
        ((Initialize)s).init();
    }
    return s;
}

Step 7-1 : DataBaseSessionManager 中 init 操作

其中关于 DataBase 会使用 DataBaseSessionManager 操作 , 这一块看一下整体的体系 :

seata-Initialize-system.png

public void init() {
    // 初始化 DataBaseTransactionStoreManager
    transactionStoreManager = DataBaseTransactionStoreManager.getInstance();
}

// PS : Initialize 的实现类都需要实现 init 方法
public interface Initialize {
    void init();
}


Step 7-2 : 构造器操作



C- DataBaseTransactionStoreManager
    P- int DEFAULT_LOG_QUERY_LIMIT = 100;
    
private DataBaseTransactionStoreManager() {
        logQueryLimit = CONFIG.getInt(ConfigurationKeys.STORE_DB_LOG_QUERY_LIMIT, DEFAULT_LOG_QUERY_LIMIT);
        // 获取 Datasource 类型
        String datasourceType = CONFIG.getConfig(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE);
        // 初始化 dataSource
        DataSource logStoreDataSource = EnhancedServiceLoader.load(DataSourceProvider.class, datasourceType).provide();
        // 构建 LogStoreDataBaseDAO
        logStore = new LogStoreDataBaseDAO(logStoreDataSource);
}


// [Pro] : ConfigurationKeys 的参数
String STORE_DB_LOG_QUERY_LIMIT = STORE_DB_PREFIX + "queryLimit";

// [Pro] : DataSourceProvider 的实现类
io.seata.server.store.DbcpDataSourceProvider
io.seata.server.store.DruidDataSourceProvider
io.seata.server.store.HikariDataSourceProvider


// 此处构建了一个 DataSource
@LoadLevel(name = "hikari")
public class HikariDataSourceProvider extends AbstractDataSourceProvider {

    @Override
    public DataSource generate() {
        Properties properties = new Properties();
        properties.setProperty("dataSource.cachePrepStmts", "true");
        properties.setProperty("dataSource.prepStmtCacheSize", "250");
        properties.setProperty("dataSource.prepStmtCacheSqlLimit", "2048");
        properties.setProperty("dataSource.useServerPrepStmts", "true");
        properties.setProperty("dataSource.useLocalSessionState", "true");
        properties.setProperty("dataSource.rewriteBatchedStatements", "true");
        properties.setProperty("dataSource.cacheResultSetMetadata", "true");
        properties.setProperty("dataSource.cacheServerConfiguration", "true");
        properties.setProperty("dataSource.elideSetAutoCommits", "true");
        properties.setProperty("dataSource.maintainTimeStats", "false");

        HikariConfig config = new HikariConfig(properties);
        config.setDriverClassName(getDriverClassName());
        config.setJdbcUrl(getUrl());
        config.setUsername(getUser());
        config.setPassword(getPassword());
        config.setMaximumPoolSize(getMaxConn());
        config.setMinimumIdle(getMinConn());
        config.setAutoCommit(true);
        config.setConnectionTimeout(getMaxWait());
        config.setInitializationFailTimeout(-1);
        return new HikariDataSource(config);
    }
}


Step 8 : reload 刷新 SessionManager

在执行完上述逻辑后还没完全 , 注意 Step 2 中最后还有个 Reload 操作 ,该操作会继续处理 DataBaseSessionManager

c- SessionHolder 

// 这里会议一下之前的属性
private static SessionManager ROOT_SESSION_MANAGER;
private static SessionManager ASYNC_COMMITTING_SESSION_MANAGER;
private static SessionManager RETRY_COMMITTING_SESSION_MANAGER;
private static SessionManager RETRY_ROLLBACKING_SESSION_MANAGER;

protected static void reload(StoreMode storeMode) {
        if (ROOT_SESSION_MANAGER instanceof Reloadable) {
            ((Reloadable) ROOT_SESSION_MANAGER).reload();
        }
        
        // 
        Collection<GlobalSession> allSessions = ROOT_SESSION_MANAGER.allSessions();
        if (CollectionUtils.isNotEmpty(allSessions)) {
            List<GlobalSession> removeGlobalSessions = new ArrayList<>();
            Iterator<GlobalSession> iterator = allSessions.iterator();
            while (iterator.hasNext()) {
                GlobalSession globalSession = iterator.next();
                GlobalStatus globalStatus = globalSession.getStatus();
                // 通过属性来判断处理的方式
                switch (globalStatus) {
                    case UnKnown:
                    case Committed:
                    case CommitFailed:
                    case Rollbacked:
                    case RollbackFailed:
                    case TimeoutRollbacked:
                    case TimeoutRollbackFailed:
                    case Finished:
                        removeGlobalSessions.add(globalSession);
                        break;
                    case AsyncCommitting:
                        if (storeMode == StoreMode.FILE) {
                            queueToAsyncCommitting(globalSession);
                        }
                        break;
                    default: {
                        // TODO : 此处的原理在后面说 Lock 逻辑的时候统一说
                        if (storeMode == StoreMode.FILE) {
                            lockBranchSessions(globalSession.getSortedBranches());
                            // 如果上述都没有 , 需要先处理分支事务
                            switch (globalStatus) {
                                case Committing:
                                case CommitRetrying:
                                    queueToRetryCommit(globalSession);
                                    break;
                                case Rollbacking:
                                case RollbackRetrying:
                                case TimeoutRollbacking:
                                case TimeoutRollbackRetrying:
                                    queueToRetryRollback(globalSession);
                                    break;
                                case Begin:
                                    globalSession.setActive(true);
                                    break;
                                default:
                                    throw new ShouldNeverHappenException("NOT properly handled " + globalStatus);
                            }
                        }
                        break;
                    }
                }
            }
            for (GlobalSession globalSession : removeGlobalSessions) {
                removeInErrorState(globalSession);
            }
        }
}

C-Database 为例 ,allSessions 又如下操作
String ROOT_SESSION_MANAGER_NAME = "root.data";
String ASYNC_COMMITTING_SESSION_MANAGER_NAME = "async.commit.data";
String RETRY_COMMITTING_SESSION_MANAGER_NAME = "retry.commit.data";
String RETRY_ROLLBACKING_SESSION_MANAGER_NAME = "retry.rollback.data";

public Collection<GlobalSession> allSessions() {
        // get by taskName
    if (SessionHolder.ASYNC_COMMITTING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) {
        return findGlobalSessions(new SessionCondition(GlobalStatus.AsyncCommitting));
    } else if (SessionHolder.RETRY_COMMITTING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) {
        return findGlobalSessions(new SessionCondition(new GlobalStatus[] {GlobalStatus.CommitRetrying, GlobalStatus.Committing}));
    } else if (SessionHolder.RETRY_ROLLBACKING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) {
        return findGlobalSessions(new SessionCondition(new GlobalStatus[] {GlobalStatus.RollbackRetrying,
                GlobalStatus.Rollbacking, GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying}));
    } else {
        // all data
        return findGlobalSessions(new SessionCondition(new GlobalStatus[] {
                GlobalStatus.UnKnown, GlobalStatus.Begin,
                GlobalStatus.Committing, GlobalStatus.CommitRetrying, GlobalStatus.Rollbacking,
                GlobalStatus.RollbackRetrying,
                GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting}));
    }
}
    

[Pro] : Reloadable 对象体系

public interface Reloadable {
    void reload();
}

// 这里的实现类主要为 FileSessionManager

五 . 扩展知识点

5.1 LoadLevel 的作用

@LoadLevel(name = "file", scope = Scope.PROTOTYPE)


LoadLevel 注解提供了三个参数 :

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface LoadLevel {

    String name();
    
    // 在类似链式调用的过程中 , 可以对 Provider 进行排序
    int order() default 0;

    Scope scope() default Scope.SINGLETON;
}

// 作用域范围
public enum Scope {

    SINGLETON,
    PROTOTYPE

}


总结

LoadLevel 和 MATA-INF 真正的作用是用于扩展不同的数据库 , 后续等 seata 梳理完成后 , 再来看一下如何进行定制.

自此 Session 的处理类初始化完成 , 后面来看一下 Session 在调用过程中的处理和数据库处理

<think>好的,我现在需要帮助用户解决关于Dify循环实现的问题。首先,我需要明确用户的具体需求。他们提到“dify 循环实现”或“dify 如何处理循环”,但Dify本身可能没有直接称为“循环”的功能,所以需要结合现有文档中的信息来推断可能的解决方案。 根据用户提供的引用内容,Dify工作流中有迭代节点[^1]。这可能类似于编程中的循环结构,用于重复执行某个任务。例如,在处理列表数据或需要多次执行同一操作时,迭代节点可以派上用场。我需要详细解释迭代节点的配置和使用步骤,确保用户能够理解如何设置循环次数或遍历数据集。 另外,用户可能对如何处理循环中的错误或性能问题感兴趣。引用[2]提到在Dify中处理PEM文件的问题,这可能涉及在循环中正确处理数据格式,避免错误积累。需要提醒用户在循环中注意错误处理和资源管理,确保每次迭代的稳定性。 引用[3]中的例子展示了如何结合FastAPI和Selenium进行实时监控并生成GIF,这可能涉及循环截取浏览器内容。虽然Dify本身可能不直接控制这类循环,但可以通过外部脚本或定时任务触发工作流,模拟循环执行。这部分需要说明如何将外部循环机制与Dify结合使用。 接下来,我需要组织回答结构。首先介绍迭代节点的基本概念和配置步骤,然后讨论循环中的数据处理和错误处理,最后补充外部触发循环的方法。同时,生成相关问题以帮助用户进一步探索,比如优化循环性能或处理复杂数据结构。 需要确保回答符合格式要求,使用正确的中文表达,行内公式用$...$,独立公式用$$...$$,但当前问题不涉及数学公式,所以可能不需要使用。同时,引用标识要自然地添加到相关段落末尾,如、[^2]、[^3]。 最后,检查是否有遗漏点,比如Dify的其他循环相关功能或用户可能遇到的常见问题,确保回答全面且易于理解。</think>在Dify中处理循环逻辑主要通过**迭代节点**实现,以下是具体实现方式和应用场景的解析: ### 一、Dify循环实现机制 Dify通过**工作流设计器**中的迭代节点处理循环需求,其核心原理类似编程中的`for循环`。迭代节点可遍历以下数据类型: - 数组列表:`["A","B","C"]` - 字典集合:`{"key1":"value1", "key2":"value2"}` - 数值范围:通过`range()`函数生成序列 配置示例: ```python # 模拟迭代节点的数据输入 input_data = { "dataset": [1,2,3,4,5], "process_logic": "item * 2" # 对每个元素执行乘以2的操作 } ``` ### 二、迭代节点的关键配置步骤 1. **数据源绑定**:将数组/字典类型变量连接到迭代节点的输入端口 2. **循环变量命名**:设定当前元素的变量名(默认为`item`) 3. **子流程设计**:在迭代节点内部构建需要重复执行的逻辑模块 4. **结果聚合**:通过`outputs`收集所有迭代结果,支持数组或对象格式 $$ \text{总耗时} = \sum_{i=1}^{n}(单次迭代时间_i) + 系统开销 $$ ### 三、循环中的特殊处理 1. **错误中断控制**: - 启用`continueOnError`参数可跳过失败迭代 - 通过`try-catch`模块包裹敏感操作 2. **并行优化**: ```python # 伪代码示例 Parallel.forEach(dataset, lambda item: process(item)) ``` 3. **结果过滤**: ```python filtered = filter(lambda x: x%2==0, processed_results) ``` ### 四、应用场景案例 1. **批量文件处理**:遍历存储桶中的文件列表进行格式转换 2. **数据清洗**:对数据库查询结果集进行逐条校验 3. **API轮询**:定时循环调用第三方接口直到满足特定条件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值