【微服务34】分布式事务Seata源码解析二:Seata Server启动时都做了什么【云原生】_seata preferrednetworks(1)

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

// server端在容器中启动,则从容器环境中读取环境、host、port、server节点以及StoreMode存储模式
private void getEnvParameters() {
// 设置seata的环境
if (StringUtils.isBlank(seataEnv)) {
seataEnv = ContainerHelper.getEnv();
}
// 设置Host
if (StringUtils.isBlank(host)) {
host = ContainerHelper.getHost();
}
// 设置端口号
if (port == 0) {
port = ContainerHelper.getPort();
}
if (serverNode == null) {
serverNode = ContainerHelper.getServerNode();
}
if (StringUtils.isBlank(storeMode)) {
storeMode = ContainerHelper.getStoreMode();
}
if (StringUtils.isBlank(sessionStoreMode)) {
sessionStoreMode = ContainerHelper.getSessionStoreMode();
}
if (StringUtils.isBlank(lockStoreMode)) {
lockStoreMode = ContainerHelper.getLockStoreMode();
}
}


#### 2)初始化监控


![在这里插入图片描述](https://img-blog.csdnimg.cn/66d6c571c83946f49871b241b3679d8f.png)


默认不开启,此处不做过多介绍


#### 3)创建TC与RM/TM通信的RPC服务器


![在这里插入图片描述](https://img-blog.csdnimg.cn/9be3bff833ef4b44a210ba0f8e48d4df.png)


单纯的new一个`NettyRemotingServer`,也没啥可说的;


#### 4)初始化UUID生成器


UUID底层采用雪花算法,其用于生成全局事务id和分支事务id;


**代码执行流程如下:**


![在这里插入图片描述](https://img-blog.csdnimg.cn/cdd1e99462eb4ae5a0d7f14b78e485e8.png)


`UUIDGenerator`会委托IdWorker来生成雪花id,生成的雪花Id由0、10位的workerId、41位的时间戳、12位的sequence序列号组成。


##### IdWorker


IdWorker中有8个重要的成员变量/常量:



/**
* Start time cut (2020-05-03)
*/
private final long twepoch = 1588435200000L;

/**
* The number of bits occupied by workerId
*/
private final int workerIdBits = 10;

/**
* The number of bits occupied by timestamp
*/
private final int timestampBits = 41;

/**
* The number of bits occupied by sequence
*/
private final int sequenceBits = 12;

/**
* Maximum supported machine id, the result is 1023
*/
private final int maxWorkerId = ~(-1 << workerIdBits);

/**
* business meaning: machine ID (0 ~ 1023)
* actual layout in memory:
* highest 1 bit: 0
* middle 10 bit: workerId
* lowest 53 bit: all 0
*/
private long workerId;

/**
* 又是一个雪花算法(64位,8字节)
* timestamp and sequence mix in one Long
* highest 11 bit: not used
* middle 41 bit: timestamp
* lowest 12 bit: sequence
*/
private AtomicLong timestampAndSequence;

/**
* 从一个long数组类型中抽取出一个时间戳伴随序列号,偏向一个辅助性质
* mask that help to extract timestamp and sequence from a long
*/
private final long timestampAndSequenceMask = ~(-1L << (timestampBits + sequenceBits));


**变量/常量解释:**



> 
> 1. 常量`twepoch`表示我们的时间戳时间从`2020-05-03`开始计算,即当前时间的时间戳需要减去`twepoch`的值`1588435200000L`;
> 2. 常量`workerIdBits`表示机器号workerId占10位;
> 3. 常量`timestampBits`表示时间戳timestamp占41位;
> 4. 常量`sequenceBits`表示序列化占12位;
> 5. 常量`maxWorkerId`表示机器号的最大值为1023;
> 6. long类型的变量`workerId`本身也是一个雪花算法,只是从开头往后数,第2位开始,一共10位用来表示workerId,其余位全是0;
> 7. AtomicLong类型的变量`timestampAndSequence`,其本身也是一个雪花算法,头11位不使用,中间41位表示timestamp,最后12位表示sequence;
> 8. long类型的常量`timestampAndSequenceMask`,用于从一个完整的雪花ID(long类型)中摘出`timestamp 和 sequence`
> 
> 
> 


IdWorker构造器中会分别初始化TimestampAndSequence、WorkerId。


##### 1> initTimestampAndSequence()


initTimestampAndSequence()方法负责初始化`timestamp`和`sequence`;



private void initTimestampAndSequence() {
// 拿到当前时间戳 - (2020-05-03 时间戳)的数值,即当前时间相对2020-05-03的时间戳
long timestamp = getNewestTimestamp();
// 把时间戳左移12位,后12位流程sequence使用
long timestampWithSequence = timestamp << sequenceBits;
// 把混合sequence(默认为0)的时间戳赋值给timestampAndSequence
this.timestampAndSequence = new AtomicLong(timestampWithSequence);
}

// 获取当前时间戳
private long getNewestTimestamp() {
//当前时间的时间戳减去2020-05-03的时间戳
return System.currentTimeMillis() - twepoch;
}


##### 2> initWorkerId(Long)


initWorkerId(Long workerId)方法负责初始化workId,默认不会传过来workerId,如果传过来则使用传过来的workerId,并校验其不能大于1023,然后将其左移53位;



private void initWorkerId(Long workerId) {
if (workerId == null) {
// workid为null时,自动生成一个workerId
workerId = generateWorkerId();
}
// workerId最大只能是1023,因为其只占10bit
if (workerId > maxWorkerId || workerId < 0) {
String message = String.format(“worker Id can’t be greater than %d or less than 0”, maxWorkerId);
throw new IllegalArgumentException(message);
}
this.workerId = workerId << (timestampBits + sequenceBits);
}


如果没传则基于MAC地址生成;  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/d66a1c77f7fe4bd3951b499da6d7ca72.png)


如果基于MAC地址生成workerId出现异常,则以1023为基数生成一个随机的workerId;  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/78cd047de2414266b1688a4e21c0adbd.png)


最后同样,校验workerId不能大于1023,然后将其左移53位;


#### 5)设置事务会话(`SessionHolder`)、全局锁(`LockManager`)的持久化方式并初始化


![在这里插入图片描述](https://img-blog.csdnimg.cn/ab09e94c4b984bccbce698c78d7ea7c0.png)


##### 1> SessionHolder


SessionHolder负责事务会话Session的持久化,一个session对应一个事务,事务又分为全局事务和分支事务;


SessionHolder支持db,file和redis的持久化方式,其中redis和db支持集群模式,项目上推荐使用redis或db模式;


**SessionHolder有五个重要的属性,如下:**



// 用于管理所有的Setssion,以及Session的创建、更新、删除等
private static SessionManager ROOT_SESSION_MANAGER;
// 用于管理所有的异步commit的Session,包括创建、更新以及删除
private static SessionManager ASYNC_COMMITTING_SESSION_MANAGER;
// 用于管理所有的重试commit的Session,包括创建、更新以及删除
private static SessionManager RETRY_COMMITTING_SESSION_MANAGER;
// 用于管理所有的重试rollback的Session,包括创建、更新以及删除
private static SessionManager RETRY_ROLLBACKING_SESSION_MANAGER;
// 用于管理分布式锁
private static DistributedLocker DISTRIBUTED_LOCKER;


**这五个属性在`SessionHolder#init()`方法中初始化,init()方法源码如下:**



public static void init(String mode) {
if (StringUtils.isBlank(mode)) {
mode = CONFIG.getConfig(ConfigurationKeys.STORE_SESSION_MODE,
CONFIG.getConfig(ConfigurationKeys.STORE_MODE, SERVER_DEFAULT_STORE_MODE));
}
StoreMode storeMode = StoreMode.get(mode);
// 根据storeMode采用SPI机制初始化SessionManager
// db模式
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});

    DISTRIBUTED\_LOCKER = DistributedLockerFactory.getDistributedLocker(StoreMode.DB.getName());
} else if (StoreMode.FILE.equals(storeMode)) {
    // 文件模式
    String sessionStorePath = CONFIG.getConfig(ConfigurationKeys.STORE\_FILE\_DIR,
            DEFAULT\_SESSION\_STORE\_FILE\_DIR);
    if (StringUtils.isBlank(sessionStorePath)) {
        throw new StoreException("the {store.file.dir} is empty.");
    }
    ROOT\_SESSION\_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.getName(),
        new Object[]{ROOT\_SESSION\_MANAGER\_NAME, sessionStorePath});
    ASYNC\_COMMITTING\_SESSION\_MANAGER = ROOT\_SESSION\_MANAGER;
    RETRY\_COMMITTING\_SESSION\_MANAGER = ROOT\_SESSION\_MANAGER;
    RETRY\_ROLLBACKING\_SESSION\_MANAGER = ROOT\_SESSION\_MANAGER;

    DISTRIBUTED\_LOCKER = DistributedLockerFactory.getDistributedLocker(StoreMode.FILE.getName());
} else if (StoreMode.REDIS.equals(storeMode)) {
    // redis模式
    ROOT\_SESSION\_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.REDIS.getName());
    ASYNC\_COMMITTING\_SESSION\_MANAGER = EnhancedServiceLoader.load(SessionManager.class,
        StoreMode.REDIS.getName(), new Object[]{ASYNC\_COMMITTING\_SESSION\_MANAGER\_NAME});
    RETRY\_COMMITTING\_SESSION\_MANAGER = EnhancedServiceLoader.load(SessionManager.class,
        StoreMode.REDIS.getName(), new Object[]{RETRY\_COMMITTING\_SESSION\_MANAGER\_NAME});
    RETRY\_ROLLBACKING\_SESSION\_MANAGER = EnhancedServiceLoader.load(SessionManager.class,
        StoreMode.REDIS.getName(), new Object[]{RETRY\_ROLLBACKING\_SESSION\_MANAGER\_NAME});

    DISTRIBUTED\_LOCKER = DistributedLockerFactory.getDistributedLocker(StoreMode.REDIS.getName());
} else {
    // unknown store
    throw new IllegalArgumentException("unknown store mode:" + mode);
}
// 根据storeMode重新加载
reload(storeMode);

}


init()方法中根据storeMode采用SPI机制初始化SessionManager,`SessionManager`有三个实现类:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/a3df578f8a164a03b3d7967081e0623f.png)


##### 2> LockerManager


![在这里插入图片描述](https://img-blog.csdnimg.cn/ce8c3b2611164b67b54887f7753afc0e.png)


和`SessionHolder`一样,`LockManagerFactory#init()`方法同样根据storeMode采用SPI机制初始化LockManager,`LockManager`有三个实现类:


![在这里插入图片描述](https://img-blog.csdnimg.cn/db7cee0a37ed42b685820044889a72fb.png)


#### 6)创建并初始化事务协调器(`DefaultCoordinator`)


`DefaultCoordinator`是事务协调的核心,比如:开启、提交、回滚全局事务,注册、提交、回滚分支事务都是通过DefaultCoordinator进行协调处理的。


![在这里插入图片描述](https://img-blog.csdnimg.cn/238dabcdd008434180d84736300285bb.png)  
 **(1)先来看DefaultCoordinator的创建;**  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/4b377f2faacc40df99f8c2083fe440e6.png)


使用Double Check Lock(DCL-双重检查锁)机制获取到单例的`DefaultCoordinator`;如果`DefaultCoordinator`为实例化过,则new一个:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2731e02558f84eb79d3224d5a95177ca.png)


在`DefaultCoordinator`的类构造器中,首先绑定远程通信的Server的具体实现到内部成员中,然后实例化一个`DefaultCore`,DefaultCore是AT、TCC、XA、Saga四种分布式事务模式的具体实现类;  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/7a416a124f244cbd8430c9720bf827d2.png)


`DefaultCore`的类构造器中首先通过SPI机制加载出所有的AbstractCore的子类,一共有四个:ATCore、TccCore、SagaCore、XACore;然后将`AbstractCore`子类可以处理的事务模式作为Key、`AbstractCore`子类作为Value存储到一个缓存Map(`Map<BranchType, AbstractCore> coreMap`)中;



private static Map<BranchType, AbstractCore> coreMap = new ConcurrentHashMap<>();


后续通过BranchType(分支类型)就可以从coreMap中获取到相应事务模式的具体AbstractCore实现类。


**(2)初始化DefaultCoordinator;**


![在这里插入图片描述](https://img-blog.csdnimg.cn/61df6ceee2254538b5c1c1756f18016f.png)


所谓的初始化,其实就是后台启动一堆线程做定时任务;去定时处理重试回滚、重试提交、异步提交、超时的检测,以及定时清理undo\_log。


除定时清理undo\_log外,其余定时任务的处理逻辑基本都是:



> 
> 1. 首先获取所有可回滚的全局事务会话Session,如果可回滚的分支事务为空,则直接返回;
> 2. 否者,遍历所有的可回滚Session;为了防止重复回滚,如果session的状态是正在回滚中并且session不是死亡的,则直接返回;
> 3. 如果Session重试回滚超时,从缓存中删除已经超时的回滚Session;
> 4. 发布session回滚完成事件给到Metric,对回滚中的Session添加Session生命周期的监听;
> 5. 使用DefaultCoordinator组合的DefaultCore执行全局回滚。
> 
> 
> 


**以处理重试回滚的方法`handleRetryRollbacking()`为例:**



protected void handleRetryRollbacking() {
SessionCondition sessionCondition = new SessionCondition(rollbackingStatuses);
sessionCondition.setLazyLoadBranch(true);
// 获取所有的可回滚的全局事务session
Collection rollbackingSessions =
SessionHolder.getRetryRollbackingSessionManager().findGlobalSessions(sessionCondition);
// 如果可回滚的分支事务为空,则直接返回
if (CollectionUtils.isEmpty(rollbackingSessions)) {
return;
}
long now = System.currentTimeMillis();
// 遍历所有的可回滚Session,
SessionHelper.forEach(rollbackingSessions, rollbackingSession -> {
try {
// prevent repeated rollback
// 防止重复回滚:如果session的状态是正在回滚中并且session不是死亡的,则直接返回。
if (rollbackingSession.getStatus().equals(GlobalStatus.Rollbacking)
&& !rollbackingSession.isDeadSession()) {
// The function of this ‘return’ is ‘continue’.
return;
}
// 判断回滚是否重试超时
if (isRetryTimeout(now, MAX_ROLLBACK_RETRY_TIMEOUT.toMillis(), rollbackingSession.getBeginTime())) {
if (ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE) {
rollbackingSession.clean();
}
// Prevent thread safety issues
// 删除已经超时的回滚Session
SessionHolder.getRetryRollbackingSessionManager().removeGlobalSession(rollbackingSession);
LOGGER.error(“Global transaction rollback retry timeout and has removed [{}]”, rollbackingSession.getXid());

            SessionHelper.endRollbackFailed(rollbackingSession, true);

            // rollback retry timeout event
            // 发布session回滚完成事件给到Metric
            MetricsPublisher.postSessionDoneEvent(rollbackingSession, GlobalStatus.RollbackRetryTimeout, true, false);

            //The function of this 'return' is 'continue'.
            return;
        }
        // 对回滚中的Session添加Session生命周期的监听
        rollbackingSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
        // 使用DefaultCoordinator组合的DefaultCore执行全局回滚
        core.doGlobalRollback(rollbackingSession, true);
    } catch (TransactionException ex) {
        LOGGER.info("Failed to retry rollbacking [{}] {} {}", rollbackingSession.getXid(), ex.getCode(), ex.getMessage());
    }
});

}


#### 7)注册ServerRunner销毁(Spring容器销毁)的回调钩子函数DefaultCoordinator


#### 8)启动NettyServer(NettyRemotingServer)


启动NettyRemotingServer时会做两件事:注册消息处理器、初始化并启动`NettyServerBootstrap`;  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/02deccd686944a86b9113b0e95e71a50.png)


##### 1> 首先注册消息处理器


消息处理器是用来处理消息的,其根据消息的不同类型选择不同的消息处理器来处理消息(属于典型的策略模式);


![在这里插入图片描述](https://img-blog.csdnimg.cn/ee7c90a3e24c4be5aaca21a174aa5900.png)


**每个消息类型和对应的处理器关系如下:**


所谓的注册消息处理器本质上就是将处理器`RemotingProcessor`和处理消息的线程池`ExecutorService`包装成一个`Pair`,然后将Pair作为Value,messageType作为key放入一个Map(`processorTable`)中;  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/6007efe4edb54d61a9fe2b5d3d422188.png)



/**
* This container holds all processors.
* processor type {@link MessageType}
*/
protected final HashMap<Integer/*MessageType*/, Pair<RemotingProcessor, ExecutorService>> processorTable = new HashMap<>(32);


##### 2> 初始化`NettyRemotingServer`


在初始化`NettyRemotingServer`之前会通过`AtomicBoolean`类型的原子变量`initialized` + CAS操作确保仅会有一个线程进行`NettyRemotingServer`的初始化;


![在这里插入图片描述](https://img-blog.csdnimg.cn/b844071d3a8d42089f03e3e9e91992a7.png)


再看`NettyRemotingServer`的类继承图:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/3f2eecb2221a40f59155906380bdc86f.png)


CAS成功后进入到`NettyRemotingServer`的父类`AbstractNettyRemotingServer#init()`方法;


![在这里插入图片描述](https://img-blog.csdnimg.cn/e1c90dff51f94ce19ddc2ec9d219fd33.png)


**方法中:**


**(1)首先调用父类`AbstractNettyRemoting`的init()方法:**  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/759cdd9fed60466387d2d48678c65036.png)  
 启动一个延时3s,每3s执行一次的定时任务,做请求超时检查;


**(2)紧接着启动`ServerBootstrap`(就正常的nettyServer启动):**


![在这里插入图片描述](https://img-blog.csdnimg.cn/5cdcbbb080a44bd0a2fe5a03c2523cc8.png)  
 NettyRemotingServer在启动的过程中设置了4个ChannelHandler:


1. IdleStateHandler:处理心跳
2. ProtocolV1Decoder:消息解码器
3. ProtocolV1Encoder:消息编码器
4. AbstractNettyRemotingServer.ServerHandler:处理各种消息


###### AbstractNettyRemotingServer.ServerHandler类



![img](https://img-blog.csdnimg.cn/img_convert/4de1417ad16eb40a11534f27b3a27146.png)
![img](https://img-blog.csdnimg.cn/img_convert/52e79a1522cfc9b9e4f9fbe1e862a62f.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**


1. IdleStateHandler:处理心跳
2. ProtocolV1Decoder:消息解码器
3. ProtocolV1Encoder:消息编码器
4. AbstractNettyRemotingServer.ServerHandler:处理各种消息


###### AbstractNettyRemotingServer.ServerHandler类



[外链图片转存中...(img-6fhJk6db-1715003806350)]
[外链图片转存中...(img-cvUxjHaP-1715003806351)]

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 24
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值