Shardingsphere整合Narayana对XA分布式事务的支持(4)

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Apache ShardingSphere 是一套开源的分布式数据库中间件解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar(规划中)这 3 款相互独立,却又能够混合部署配合使用的产品组成。它们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ShardingSphere 已于2020年4月16日成为 Apache 软件基金会的顶级项目。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Narayana简单介绍"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Narayana(https:\/\/narayana.io\/),是由Jboss团队提供的XA分布式事务的解决方案。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它具有以下特点:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"标准的基于JTA实现。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TransactionManager(TM) 完全去中心化设计,与业务耦合,无需单独部署。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事务日志支持数据库存储,支持集群模式下的事务恢复。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"ShardingTransactionManager初始化XATransactionDataSource流程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ShardingSphere对XA的支持提供一整套的SPI接口,在初始化话的时候,根据事务类型,先进行TransactionManager的初始化。我们先进入"},{"type":"codeinline","content":[{"type":"text","text":"org.apache.shardingsphere.transaction.xa.XAShardingTransactionManager"}]},{"type":"text","text":"。代码如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" private final Map cachedDataSources = new HashMap<>();\n\n private final XATransactionManager xaTransactionManager = XATransactionManagerLoader.getInstance().getTransactionManager();\n\n @Override\n public void init(final DatabaseType databaseType, final Collection resourceDataSources) {\n for (ResourceDataSource each : resourceDataSources) {\n cachedDataSources.put(each.getOriginalName(), new XATransactionDataSource(databaseType, each.getUniqueResourceName(), each.getDataSource(), xaTransactionManager));\n }\n \/\/ Narayana的初始化\n xaTransactionManager.init();\n }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先会根据配置的datasource将其转换成XATransactionDataSource,具体代码在"},{"type":"codeinline","content":[{"type":"text","text":"new XATransactionDataSource(databaseType, each.getUniqueResourceName(), each.getDataSource(), xaTransactionManager))"}]},{"type":"text","text":"。我们跟进去,代码如下:"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"public XATransactionDataSource(final DatabaseType databaseType, final String resourceName, final DataSource dataSource, final XATransactionManager xaTransactionManager) {\n this.databaseType = databaseType;\n this.resourceName = resourceName;\n this.dataSource = dataSource;\n if (!CONTAINER_DATASOURCE_NAMES.contains(dataSource.getClass().getSimpleName())) {\n \/\/ 重点关注 1 ,返回了xaDatasource\n xaDataSource = XADataSourceFactory.build(databaseType, dataSource);\n this.xaTransactionManager = xaTransactionManager;\n \/\/ 重点关注2 注册资源\n xaTransactionManager.registerRecoveryResource(resourceName, xaDataSource);\n }\n }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们重点来关注 "},{"type":"codeinline","content":[{"type":"text","text":"XADataSourceFactory.build(databaseType, dataSource)"}]},{"type":"text","text":",从名字我们就可以看出,这应该是返回"},{"type":"codeinline","content":[{"type":"text","text":"JTA规范里面的XADataSource"}]},{"type":"text","text":",在ShardingSphere里面很多的功能,可以从代码风格的命名上就能猜出来,这就是优雅代码(吹一波)。不多逼逼,我们进入该方法。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"public final class XADataSourceFactory {\n\n public static XADataSource build(final DatabaseType databaseType, final DataSource dataSource) {\n return new DataSourceSwapper(XADataSourceDefinitionFactory.getXADataSourceDefinition(databaseType)).swap(dataSource);\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先又是一个SPI定义的 "},{"type":"codeinline","content":[{"type":"text","text":"XADataSourceDefinitionFactory"}]},{"type":"text","text":",它根据不同的数据库类型,来加载不同的方言。然后我们进入 "},{"type":"codeinline","content":[{"type":"text","text":"swap"}]},{"type":"text","text":"方法。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" public XADataSource swap(final DataSource dataSource) {\n XADataSource result = createXADataSource();\n setProperties(result, getDatabaseAccessConfiguration(dataSource));\n return result;\n }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很简明,第一步创建,"},{"type":"codeinline","content":[{"type":"text","text":"XADataSource"}]},{"type":"text","text":",第二步给它设置属性(包含数据的连接,用户名密码等),然后返回。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Narayana 初始化过程详解"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/45\/0c\/459f4db7d8bdc59813e8ef21f5fc9e0c.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们首先进入"},{"type":"codeinline","content":[{"type":"text","text":"org.apache.shardingsphere.transaction.xa.narayana.manager.NarayanaXATransactionManager"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"public final class NarayanaXATransactionManager implements XATransactionManager {\n \/\/加载transactionManger\n private final TransactionManager transactionManager = jtaPropertyManager.getJTAEnvironmentBean().getTransactionManager();\n\n\/\/获取事务恢复模块\n private final XARecoveryModule xaRecoveryModule = XARecoveryModule.getRegisteredXARecoveryModule();\n\n private final RecoveryManagerService recoveryManagerService = new RecoveryManagerService();\n\n @Override\n public void init() {\n RecoveryManager.delayRecoveryManagerThread();\n recoveryManagerService.create();\n\/\/开启事务恢复\n recoveryManagerService.start();\n }\n\n @Override\n public void registerRecoveryResource(final String dataSourceName, final XADataSource xaDataSource) {\n xaRecoveryModule.addXAResourceRecoveryHelper(new DataSourceXAResourceRecoveryHelper(xaDataSource));\n }\n\n @Override\n public void removeRecoveryResource(final String dataSourceName, final XADataSource xaDataSource) {\n xaRecoveryModule.removeXAResourceRecoveryHelper(new DataSourceXAResourceRecoveryHelper(xaDataSource));\n }\n\n @SneakyThrows({SystemException.class, RollbackException.class})\n @Override\n public void enlistResource(final SingleXAResource singleXAResource) {\n transactionManager.getTransaction().enlistResource(singleXAResource.getDelegate());\n }\n\n @Override\n public TransactionManager getTransactionManager() {\n return transactionManager;\n }\n\n @Override\n public void close() throws Exception {\n recoveryManagerService.stop();\n recoveryManagerService.destroy();\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先我们关注"},{"type":"codeinline","content":[{"type":"text","text":"jtaPropertyManager.getJTAEnvironmentBean().getTransactionManager()"}]},{"type":"text","text":"获取TransactionManager,这是整个 Narayana初始化的核心。进入代码 "},{"type":"codeinline","content":[{"type":"text","text":"com.arjuna.common.internal.util.propertyservice.BeanPopulator.getNamedInstance()"}]},{"type":"text","text":"。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"private static T getNamedInstance(Class beanClass, String name, Properties properties) throws RuntimeException {\n StringBuilder sb = new StringBuilder().append(beanClass.getName());\n if (name != null)\n sb.append(\":\").append(name);\n String key = sb.toString();\n \/\/ we don't mind sometimes instantiating the bean multiple times,\n \/\/ as long as the duplicates never escape into the outside world.\n if(!beanInstances.containsKey(key)) {\n T bean = null;\n try {\n \/\/ 初始化 JTAEnvironmentBean 这个类\n bean = beanClass.newInstance();\n if (properties != null) {\n configureFromProperties(bean, name, properties);\n } else {\n \/\/初始化属性配置\n Properties defaultProperties = PropertiesFactory.getDefaultProperties();\n configureFromProperties(bean, name, defaultProperties);\n }\n } catch (Throwable e) {\n throw new RuntimeException(e);\n }\n beanInstances.putIfAbsent(key, bean);\n }\n return (T) beanInstances.get(key);\n }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们重点关注 "},{"type":"codeinline","content":[{"type":"text","text":"Properties defaultProperties = PropertiesFactory.getDefaultProperties();"}]},{"type":"text","text":" 。最后会进入"},{"type":"codeinline","content":[{"type":"text","text":"com.arjuna.common.util.propertyservice.AbstractPropertiesFactory.getPropertiesFromFile()"}]},{"type":"text","text":"。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" public Properties getPropertiesFromFile(String propertyFileName, ClassLoader classLoader) {\n String propertiesSourceUri = null;\n try\n {\n \/\/ 文件名称为:jbossts-properties.xml 加载顺序为:This is the point where the search path is applied - user.dir (pwd), user.home, java.home, classpath\n propertiesSourceUri = com.arjuna.common.util.propertyservice.FileLocator.locateFile(propertyFileName, classLoader);\n }\n catch(FileNotFoundException fileNotFoundException)\n {\n \/\/ try falling back to a default file built into the .jar\n \/\/ Note the default- prefix on the name, to avoid finding it from the .jar at the previous stage\n \/\/ in cases where the .jar comes before the etc dir on the classpath.\n URL url = AbstractPropertiesFactory.class.getResource(\"\/default-\"+propertyFileName);\n if(url == null) { commonLogger.i18NLogger.warn_could_not_find_config_file(url);\n } else {\n propertiesSourceUri = url.toString();\n }\n }\n catch (IOException e)\n {\n throw new RuntimeException(\"invalid property file \"+propertiesSourceUri, e);\n }\n Properties properties = null;\n try {\n if (propertiesSourceUri != null) {\n \/\/加载配置文件\n properties = loadFromFile(propertiesSourceUri);\n }\n \/\/ 叠加系统配置属性\n properties = applySystemProperties(properties);\n } catch(Exception e) {\n throw new RuntimeException(\"unable to load properties from \"+propertiesSourceUri, e);\n }\n return properties;\n }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"加载文件名称为 "},{"type":"codeinline","content":[{"type":"text","text":"jbossts-properties.xml"}]},{"type":"text","text":", 加载路径优先级别为 :user.dir > user.home >java.home >classpath。最后再叠加上系统属性,然后返回。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们再来看一下 jbossts-properties.xml的参考格式如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"\n YES\n com.arjuna.ats.internal.arjuna.objectstore.jdbc.JDBCStore\n com.arjuna.ats.internal.arjuna.objectstore.jdbc.accessors.DynamicDataSourceJDBCAccess;ClassName=com.mysql.jdbc.jdbc2.optional.MysqlDataSource;DatabaseName=jbossts;ServerName=172.25.4.62;PortNumber=3306;User=j_jbossts;Password=9MfNHoRncCi8\n Action\n true\n com.arjuna.ats.internal.arjuna.objectstore.jdbc.JDBCStore\n com.arjuna.ats.internal.arjuna.objectstore.jdbc.accessors.DynamicDataSourceJDBCAccess;ClassName=com.mysql.jdbc.jdbc2.optional.MysqlDataSource;DatabaseName=jbossts;ServerName=172.25.4.62;PortNumber=3306;User=j_jbossts;Password=9MfNHoRncCi8\n stateStore\n true\n com.arjuna.ats.internal.arjuna.objectstore.jdbc.JDBCStore\n com.arjuna.ats.internal.arjuna.objectstore.jdbc.accessors.DynamicDataSourceJDBCAccess;ClassName=com.mysql.jdbc.jdbc2.optional.MysqlDataSource;DatabaseName=jbossts;ServerName=172.25.4.62;PortNumber=3306;User=j_jbossts;Password=9MfNHoRncCi8\n Communication\n true\n ON\n 1\n 1\n \n com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter\n com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter\n com.arjuna.ats.internal.jta.recovery.arjunacore.JTAActionStatusServiceXAResourceOrphanFilter\n \n 0\n \n com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule\n com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule\n \n \n com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner\n \n 4712\n \n 0\n \n NO\n 1\n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它被视为标准java.util.Properties文件的XML格式并按需加载。entry名称的形式为:"},{"type":"codeinline","content":[{"type":"text","text":"类名.属性名"}]},{"type":"text","text":"。提供的配置类都在"},{"type":"codeinline","content":[{"type":"text","text":"com.arjuna.ats.arjuna.common"}]},{"type":"text","text":"包下,以bean结尾的实体类。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件加载后,它会被缓存,直到JVM重新启动才重新读取。对属性文件的更改需要重新启动才能生效"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在属性加载之后,将检查EnvironmentBean,对于每个字段,如果属性在搜索顺序中包含如下匹配的键,则使用属性的值调用该字段的setter方法,或者使用不同的系统属性调用该字段的setter方法。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然后将bean返回给调用者,调用者可以通过调用setter方法进一步覆盖值。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章