前面的代理都是爲了寫源碼做鋪墊的,因爲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方法我們下篇文章去寫