前面的代理都是为了写源码做铺垫的,因为LCN模式就是用的静态代理,采用对数据库连接的代理来完成分布式事务的。
首先看我们的服务端,前面在例子中已经把服务端和客户端的代码都贴出来了,服务端很简单,只需要一个注解就能开启分布式事务服务端功能。
注解@EnableTransactionManagerServer
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(value = {TMAutoConfiguration.class})
public @interface EnableTransactionManagerServer {
}
引入了TMAutoConfiguration类,所有的功能都在TMAutoConfiguration类中,从名称上看是自动配置类
@Configuration
@ComponentScan
@Import({TxLoggerConfiguration.class, MessageConfiguration.class})
@EnableJpaRepositories("com.codingapi.txlcn.tm.support.db.jpa")
@EntityScan("com.codingapi.txlcn.tm.support.db.domain")
public class TMAutoConfiguration {
@Bean(destroyMethod = "shutdown")
public ExecutorService executorService() {
int coreSize = Runtime.getRuntime().availableProcessors() * 2;
return new ThreadPoolExecutor(coreSize, coreSize, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>()) {
@Override
public void shutdown() {
super.shutdown();
try {
this.awaitTermination(10, TimeUnit.MINUTES);
} catch (InterruptedException ignored) {
}
}
};
}
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
}
@Bean
@ConditionalOnMissingBean
public FastStorageProvider fastStorageProvider(RedisTemplate<String, Object> redisTemplate,
StringRedisTemplate stringRedisTemplate, TxManagerConfig managerConfig) {
return () -> new RedisStorage(redisTemplate, stringRedisTemplate, managerConfig);
}
@Bean
public FastStorage fastStorage(FastStorageProvider fastStorageProvider) {
return fastStorageProvider.provide();
}
@Bean
public TxLcnApplicationRunner txLcnApplicationRunner(ApplicationContext applicationContext) {
return new TxLcnApplicationRunner(applicationContext);
}
@Bean
@ConditionalOnMissingBean
public ModIdProvider modIdProvider(ConfigurableEnvironment environment, ServerProperties serverProperties) {
return () -> ApplicationInformation.modId(environment, serverProperties);
}
}
这里算是所有的配置都在这了,
1、引入日志配置(日志就不细说了)
2、引入消息配置MessageConfiguration类,配置了一些客户端与服务端通信的类
3、开启了jpa支持把一些事物信息写入数据库(不细说)
4、自定义线程池
5、构建了restTemplate Bean
6、构建FastStorageProvider Bean 此类用来提供快速存储类,需要三个参数,这里默认构建RedisStorage
7、根据FastStorageProvider 构建RedisStorage
8、构建TxLcnApplicationRunner Bean 用于spring boot启动后做些事情
9、构建ModIdProvider Bean 返回模块标识。
MessageConfiguration
public class MessageConfiguration {
@Bean
@ConditionalOnMissingBean
@ConfigurationProperties("tx-lcn.message.netty")
//rpc的配置如心跳,重试,缓存锁数量
public RpcConfig rpcConfig() {
return new RpcConfig();
}
@Bean
@ConditionalOnMissingBean
//rpc应答类,只是打印了消息
public RpcAnswer rpcClientAnswer() {
return rpcCmd -> log.info("cmd->{}", rpcCmd);
}
@Bean
@ConditionalOnMissingBean
//rpc负载均衡策略,采用RandomLoadBalance类
public RpcLoadBalance rpcLoadBalance() {
return new RandomLoadBalance();
}
@Bean
@ConditionalOnMissingBean
//客户端初始化回调类,只打印日志
public ClientInitCallBack clientInitCallBack() {
return new DefaultClientInitCallback();
}
@Bean
@ConditionalOnMissingBean
//rpc连接监听器,默认实现
public RpcConnectionListener rpcConnectionListener(){
return new DefaultRpcConnectionListener();
}
@Bean
@ConditionalOnMissingBean
//心跳监听器,默认空实现
public HeartbeatListener heartbeatListener(){
return new DefaultHeartbeatListener();
}
}
FastStorageProvider 快速存储生成接口
FastStorageProvider 这实际是个接口,只有一个方法provide,用来生成快速存储类
public interface FastStorageProvider {
/**
* TM FastStorage's implementation.
*
* @return fast storage's implementation
*/
FastStorage provide();
}
RedisStorage 快速存储类
FastStorage的唯一实现,用于操作redis完成事物的操作,内部封装了RedisTemplate与StringRedisTemplate。
其功能比如:事物组相关操作;事物状态相关操作;分布式锁的操作;TM的机器列表操作;token操作等。
TxLcnApplicationRunner
TxLcnApplicationRunner类实现了ApplicationRunner接口,并实现了run方法,springboot在启动后会自动调用实现了ApplicationRunner接口类的run方法,代码如下
public class TxLcnApplicationRunner implements ApplicationRunner, DisposableBean {
private final ApplicationContext applicationContext;
private List<TxLcnInitializer> initializers;
@Autowired
public TxLcnApplicationRunner(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public void run(ApplicationArguments args) throws Exception {
//获取容器中实现了TxLcnInitializer接口的类,并排序
Map<String, TxLcnInitializer> runnerMap = applicationContext.getBeansOfType(TxLcnInitializer.class);
initializers = runnerMap.values().stream().sorted(Comparator.comparing(TxLcnInitializer::order))
.collect(Collectors.toList());
//循环调用txLcnInitializer的init()方法
for (TxLcnInitializer txLcnInitializer : initializers) {
txLcnInitializer.init();
}
}
@Override
public void destroy() throws Exception {
for (TxLcnInitializer txLcnInitializer : initializers) {
txLcnInitializer.destroy();
}
}
}
重点是run方法,找到容器中的所有的TxLcnInitializer并调用其init方法
ModIdProvider 模块标识
这个类的作用是生成当前模块的标识,标识生成策略在ApplicationInformation类的modId方法中
public static String modId(ConfigurableEnvironment environment, ServerProperties serverProperties) {
String applicationName = environment.getProperty("spring.application.name");
applicationName = StringUtils.hasText(applicationName) ? applicationName : "application";
return applicationName + ":" + serverPort(serverProperties);
}
public static int serverPort(ServerProperties serverProperties) {
return Objects.isNull(serverProperties) ? 0 : (Objects.isNull(serverProperties.getPort()) ? 8080 :
serverProperties.getPort());
}
标识生成规则是
1、如果配置了spring.application.name 与server.port 则,则模块标识为spring.application.name :server.port
2、如果没有则是application:8080
大概的代码流程就是这些,重要的工作在于执行所有TxLcnInitializer的init方法我们下篇文章去写