المتطلبات الوظيفية هي أن الشركة تحتاج إلى بناء منصة تشغيل كبيرة:
1. منصة التشغيل لديها قاعدة بيانات خاصة بها ، والحفاظ على الوظائف الأساسية مثل المستخدمين والأدوار والقوائم والأجزاء والأذونات.
2. يحتاج منصة التشغيل أيضًا إلى توفير عمليات خلفية للخدمات المختلفة الأخرى (الخدمة A ، الخدمة B) ، وقواعد بيانات الخدمة A والخدمة B مستقلة.
لذلك ، يجب على منصة التشغيل توصيل ما لا يقل عن ثلاث مكتبات: مكتبة التشغيل ، والمكتبة ، ومكتبة B ، وتأمل في التبديل تلقائيًا إلى مصدر البيانات المقابل لكل طلب الوظيفة (تتمثل التنفيذ النهائي في التبديل إلى مستوى الطريقة للخدمة ، والتبديل إلى طريقة كل طبقة DAO. وظائف نظامنا مستقلة نسبيًا نسبيًا).
الخطوة 1: تكوين مصادر بيانات متعددة
1. حدد مصدر البيانات:
مصدر البيانات الذي أستخدمه هو DruidDataSource من Alibaba (لا بأس مع DBCP ، وهذا هو أي شيء). التكوين كما يلي:
<!-OP DataSource-> <bean id = "opdatasource" init-method = "init" DRIDED-METHOD = "CLOSE"> <property name = "url" value = "$ {db.master.url}" /> <property name = "username" value = "$ {db.user}" <property name = "driverClassName" value = "$ {db.master.driver}" /> <property name = "initialsize" value = "5" /> <property name = "maxactive" value = "100" /> <property name = "minidle" name = "testOnBorrow" value = "false" /> <property name = "testOnreturn" value = "false" /> <property name = "testhileIdle" value = "true" /> <property name = "timeBetweenevictionRunsmillis" value = "600000" /> <property name = "minevictiDEmillis" /> <property name = "removeAbandOnedTimeOut" value = "1800" /> <property name = "logAbandoned" value = "true" /> <!-تكوين مرشحات لمراقبة إحصائيات اعتراض-> <property name = "config" value = "config ، mergestat ، wall ، log4j2" /> <property name = DataSource-> <bean id = "serveradaDAtasource" init-method = "init" DRIDER-METHOD = "CLOSE"> <property name = "url" value = "$ {db.servera.master.url}" /> <property name = "username" value = "$ {db.servera.master.user} value = "$ {db.servera.master.password}" /> <property name = "driverClassName" value = "$ {db.servera.master.driver}" /> <property name = "initialsize" value = "5" /> <property name = "maxactive" value = "100" /> value = "60000" /> <property name = "valideedQuery" value = "select 'x'" /> <property name = "testOnBorrow" value = "false" /> <property name = "testOnreturn" name = "timeBetweenevictionRunsMillis" value = "600000" /> <property name = "minevictableDletImemillis" value = "300000" /> <property name = "removaBandOned" value = "true" /> <property name = "removaBandRimeOut" اعتراضات الإحصائيات-> <property name = "filters" value = "config ، mergestat ، wall ، log4j2" /> <property name = "connectionProperties" value = "config.decrypt = true" /> </bean> <! value = "$ {db.serverb.master.url}" /> <property name = "username" value = "$ {db.serverb.master.user}" /> <property name = "password" value = "$ {dB.ServerB.Password}" /> <property name = /> <property name = "initialsize" value = "5" /> <property name = "maxactive" value = "100" /> <property name = "minidle" value = "10" /> <property name = "maxwait value = "false" /> <property name = "testwhileIdle" value = "true" /> <property name = "timeTweenevictionRunsMillis" value = "600000" /> <property name = "minevictableDletImIllis <property name = "logabandoned" value = "true" /> <!-تكوين مرشحات لاستئصال إحصائيات المراقبة-> <property name = "filters" value = "config ، mergestat ، wall ، log4j2 /> <property name =" connectionProperties "value =" config.decrypt = true " />قمت بتكوين ثلاثة مصادر بيانات: OpDataSource (مصدر بيانات النظام الأساسي نفسه) ، ServerAdataSource ، و ServerBdataSource.
2. تكوين multipledataSource
MultipledataSource يعادل وكيل واحد لمصادر البيانات الثلاثة أعلاه. عندما يتم دمجها حقًا مع Spring/MyBatis و MultipleDataSource واستخدام مصدر البيانات الذي تم تكوينه بشكل منفصل لا يختلف:
<!-تكامل الربيع myBatis: تكوين multipledataSource-> <bean id = "sqlsessionfactory"> <property name = "dataSource" ref = "multipledataSource"/> <!-ملف mapping.xml تلقائيًا- <Value> classpath*:/sqlmapperxml/*/*. ref = "globalConfig"/> <property name = "plugins"> <array> <!-تكوين المكون الإضافي للتراجع-> <bean id = "paginationInterceptor"> <property name = "dialeecttype" value = "mysql"/> <property name = "exptimizetype" value = "alidruid"/>/ التنفيذ-> <bean id = "mapperscannerconfigurer"> <!-للتنفيذ الديناميكي للواجهة DAO ، تحتاج إلى معرفة مكان وجود الواجهة-> <property name = "basepackage" value = "com.xxx.platform.mapper التكوين-> <bean id = "globalConfig"> <property name = "idtype" value = "0"/> <property name = "dbColumnArndRine" value = "true"/> </bean> <!
بعد فهم موقع multipledataSource ، دعونا نركز على كيفية تنفيذ multipledataSource. ملف التكوين كما يلي:
<bean id = "multipledataSource"> <property name = "defaultTargetDatasource" ref = "opDatasource" /> <property name = "targetdatasources"> <map> <intring key = "opDataSource" value-ref = "opdatasource key = "serverbdataSource" value-ref = "serverbdatasource"/> </kap> </sprement> </boy>
رمز Java المنفذ هو كما يلي ، وليس هناك حاجة إلى الكثير من التفسير ، وهو واضح للغاية في لمحة:
استيراد org.springframework.jdbc.datasource.lookup.abstractroutingDataSource ؛/** * * * classname: multipledatasource * @description: تكوين مصادر بيانات متعددة <br> * @author: yuzhu.peng * @date: 12 يناير في 4:37 AbstractRoutingDataSource {private static final threadlocal <string> dataSourceKey = new erranitablethreadlocal <string> () ؛ public static void setDataSourceKey (String dataSource) {datasourceKey.set (dataSource) ؛ } Override محمي الكائن DETERNECURRENTOKTOPKEUPKEY () {return datasourceKey.get () ؛ } public static void rediveTasourceKey () {datasourceKey.Remove () ؛ }}ورثت من Spring من AbstractRoutingDataSource ، تنفذ الطريقة التجريدية تحديد ursurrentlookupkey. ستحدد هذه الطريقة مصدر بيانات مصدر البيانات لهذا الاتصال في كل مرة يتم فيها الحصول على اتصال قاعدة البيانات. يمكنك رؤية رمز الربيع ليكون واضحًا:
/*الحصول على اتصال*/ اتصال عام getConnection () يلقي sqlexception {return denterInetArgetDataSource (). getConnection () ؛ } DataSource المحمي DETRIMINETARGETDATASOURCE () {Assert.notnull (this.resolvedDataSources ، "DataSource Router غير تهيئة") ؛ /. DataSource DataSource = (DataSource) this.resolvedDataSources.get (lookupkey) ؛ if ((datasource == null) && ((this.lenientfallback) || (lookupkey == null)))) {datasource = this.resolvedDefaultDataSource ؛ } if (dataSource == null) {رمي جديد alficalstateException ("لا يمكن تحديد مصدر البيانات المستهدف لمفتاح البحث [" + lookupkey + "]") ؛ } إرجاع مصدر البيانات ؛ } /*الواجهة التجريدية: أي الواجهة التي تنفذها multipledataSource* / كائن مجردة محمي DETERNECURRENTOKKEUPKEY () ؛الخطوة 2: تبديل مصدر البيانات بشكل ديناميكي كل طلب (مستوى طريقة الخدمة)
تتمثل فكرة التنفيذ في استخدام فكرة AOP الخاصة بـ Spring لاعتراض كل استدعاء طريقة خدمة ، ثم تبديل مفتاح البيانات في MultipledataSource وفقًا لاسم المسار العام للطريقة. مشروعنا ، لعمليات الخدمات المختلفة ، أي قواعد بيانات مختلفة ، مستقل عن بعضها البعض. لا ينصح بالاتصال بمصادر بيانات مختلفة في نفس طريقة الخدمة. وبهذه الطريقة ، نحتاج إلى تحديد ما إذا كان تواتر التبديل ديناميكيًا يجب وضعه على مستوى DAO ، أي مستوى SQL. بالإضافة إلى ذلك ، إدارة المعاملات ليست مريحة.
دعونا نلقي نظرة على تنفيذ AOP لمصادر بيانات التبديل الديناميكية:
استيراد java.lang.reflect.proxy ؛ استيراد org.apache.commons.lang.classutils ؛ استيراد org.aspectj.lang.joinpoint ؛ استيراد org.aspectj.lang.annotation.Aspect AOP * * Author yuzhu.peng * since 2018-01-15 * / @Quality @order (1) الفئة العامة multipledataSourceInterceptor { /** * يولي اعتراضه اهتمامًا خاصًا لتحويل مصادر البيانات قبل طلب جميع فئات تنفيذ الأعمال. نظرًا لاستخدام مصادر بيانات متعددة ، من الأفضل الاتصال بـ Mapper فقط في *serviceImpl. خلاف ذلك ، عند استدعاء جدول ليس مصدر بيانات افتراضيًا ، سيتم الإبلاغ عن استثناء غير موجود في الجدول*** param joinpoint* throws قابلة للتسمية*/ before ("التنفيذ (* com.xxxx.platform.service ..*. JoinPoint.getTarget (). getClass () ؛ سلسلة className = clazz.getName () ؛ if (classUtils.isAssignable (clazz ، proxy.class)) {className = JoinPoint.getSignature (). getDeclaringTyPename () ؛ } // قم بتعيين مصدر بيانات Servera باسم الفئة ، وإلا فإن الافتراضي هو مصدر البيانات في الخلفية إذا (className.contains (". servera.")) } آخر إذا (className.contains (". serverb.")) {multipledatasource.setDatasourceKey (dbconstant.data_source_serverb) ؛ } else {multipledatasource.setDatasourceKey (dbconstant.data_source_op) ؛ }} /*** عند اكتمال العملية ، إذا تم إصدار مصدر البيانات الحالي ، إذا لم يتم إصداره ، فسيحدث تعارض مصدر البيانات عند النقر بشكل متكرر. إنه جدول لمصدر بيانات آخر ، لكنه سيتم تشغيله إلى مصدر بيانات آخر. التقرير غير موجود** param joinpoint* throws قابلة للتخفيف*/ @After ("التنفيذ (* com.xxxx.service ..*.* serviceImpl. }}اعتراض جميع أساليب ServiceImpl ، الحكم على وظيفة مصدر البيانات تنتمي إلى الاسم المؤهل تمامًا للطريقة ، ثم حدد مصدر البيانات المقابل. بعد اكتمال التوزيع ، حرر مصدر البيانات الحالي. لاحظ أنني استخدمت spring's order ، التعليق التوضيحي ، وسأتحدث عن ذلك بعد ذلك ، عند تحديد AOPs المتعددة ، يكون الطلب مفيدًا للغاية.
آخر:
في البداية ، لم يقدم المشروع المعاملات ، لذلك كان كل شيء على ما يرام. يمكنك الوصول إلى مصدر البيانات الصحيح في كل مرة. بعد الانضمام إلى إدارة المعاملات في الربيع ، لا يمكنك تبديل مصدر البيانات ديناميكيًا (يبدو أن المعاملة غير فعالة ، لكن الاثنين غير صالحين في نفس الوقت). في وقت لاحق ، وجدت أن السبب هو ترتيب تنفيذ AOP ، لذلك استخدمت ترتيب الربيع المذكور أعلاه:
كلما كان الأمر أصغر ، يكون التنفيذ أولاً. في هذه المرحلة ، لا يمكنك تبديل مصادر البيانات ديناميكيًا فحسب ، بل يمكنك أيضًا استخدام المعاملات بنجاح (في نفس مصدر البيانات).
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.