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}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章