1. TM启动流程
TM是一个springboot项目,在springboot启动时,会通过一个applicationRunner启动TM服务,即启动一个基于netty网络框架的服务端。
NettyRpcServerChannelInitializer类的源码如下:
这里重点关注下RpcAnswerHandler处理器。
RpcAnswer是一个接口,有两个实现:
- ServerRpcAnswer
- ClientRpcAnswer
TM端用的是ServerRpcAnswer实现类。重点需要关注callback方法。在callback方法中,将具体的业务处理交给一个业务线程池处理。源码如下:
public void callback(RpcCmd rpcCmd) {
executorService.submit(() -> {
try {
//将RpcCmd对象转换成TransactionCmd
TransactionCmd transactionCmd = parser(rpcCmd);
String action = transactionCmd.getMsg().getAction();
//根据类型(createGroup、joinGroup、notifyGroup)获取具体的执行类
RpcExecuteService rpcExecuteService = rpcBeanHelper.loadManagerService(transactionCmd.getType());
System.out.println("RpcExecuteService = "+rpcExecuteService);
MessageDto messageDto = null;
try {
Serializable message = rpcExecuteService.execute(transactionCmd);
messageDto = MessageCreator.okResponse(message, action);
} catch (Throwable e) {
log.error("rpc execute service error. action: " + action, e);
messageDto = MessageCreator.failResponse(e, action);
} finally {
// 对需要响应信息的请求做出响应
if (rpcCmd.getKey() != null) {
assert Objects.nonNull(messageDto);
try {
messageDto.setGroupId(rpcCmd.getMsg().getGroupId());
rpcCmd.setMsg(messageDto);
rpcClient.send(rpcCmd);
} catch (RpcException ignored) {
}
}
}
} catch (Throwable e) {
if (rpcCmd.getKey() != null) {
log.info("send response.");
String action = rpcCmd.getMsg().getAction();
// 事务协调器业务未处理的异常响应服务器失败
rpcCmd.setMsg(MessageCreator.serverException(action));
try {
rpcClient.send(rpcCmd);
log.info("send response ok.");
} catch (RpcException ignored) {
log.error("requester:{} dead.", rpcCmd.getRemoteKey());
}
}
}
});
}
2. TM创建事务组
如果是创建事务组,那么ServerRpcAnswer的callback方法中的RpcExecuteService即为CreateGroupExecuteService。
只做了一件事,即将事务组id存入Redis中,并设置了过期时间。在Redis中,被存储成一个hash,key为tm:group:groupID,键为root,值为空。
创建事务组时,客户端向服务端发送的数据如下所示:
{"key":"573f860ad9e536",
"msg":{"action":"createGroup","groupId":"573f860ad9e537","state":100},
"remoteKey":"/127.0.0.1:25633"}
服务端想客户端返回的数据如下所示:
{"key":"573f860ad9e536",
"msg":{"action":"createGroup","groupId":"573f860ad9e537","state":200},
"remoteKey":"/127.0.0.1:25633"}
3. TM加入事务组
如果是加入事务组,那么ServerRpcAnswer的callback方法中的RpcExecuteService即为JoinGroupExecuteService。
只做了一件事,将事务信息保存在Redis中。key为tm:group:groupID,键为事务对应的unitId,值为事务相关信息。
加入事务组时,客户端请求数据为:
{
"key": "573f8609f99537",
"msg": {
"action": "joinGroup",
"data": {
"groupId": "573f860ad9e537",
"transactionState": 1,
"unitId": "fa8dc78c9b7ad90a30bc4066226749b5",
"unitType": "lcn"
},
"groupId": "573f860ad9e537",
"state": 100
},
"remoteKey": "/127.0.0.1:25632"
}
服务端响应数据为:
{"key":"573f8609f99537",
"msg":{"action":"joinGroup","groupId":"573f860ad9e537","state":200},
"remoteKey":"/127.0.0.1:25632"}
4. TM通知事务组
如果是通知事务组,那么ServerRpcAnswer的callback方法中的RpcExecuteService即为NotifyGroupExecuteService。
如果事务状态为1,表示需要commit,如果事务状态为0则需要rollback,并返回状态。但最终都会调用如下方法:
从Redis中获取该事务组对应的子事务,并遍历子事务。获取到客户端的ip地址,向客户端发送消息。如果发送失败,则将信息保存到数据库中。
通知事务组时,客户端发送的数据为:
{"key":"573f860879e537",
"msg":{"action":"notifyGroup","data":{"groupId":"573f860ad9e537","state":0},"groupId":"573f860ad9e537","state":100},
"remoteKey":"/127.0.0.1:25633"}
响应结果为:
{"key":"573f860879e537",
"msg":{"action":"notifyGroup","data":0,"groupId":"573f860ad9e537","state":200},
"remoteKey":"/127.0.0.1:25633"}
5. 总结
TC向TM发送的数据包括创建事务组、加入事务组、通知事务组三类消息。
当TM接收到这三类消息后,在ServerRpcAnswer类中,调用对应类型的处理器进行处理。