في الآونة الأخيرة ، أقوم بتحسين استفسار توازن الهامش. عندما يبدأ المشروع ، يجب تحميل التوازن بالكامل في ذاكرة التخزين المؤقت المحلية ، لأن توازن الهامش لجميع الدراجين يحتاج إلى الاستعلام بالكامل. من أجل عدم التأثير على أداء قاعدة البيانات الرئيسية ، فكر في ترك الاستعلام. لذلك يتضمن الحاجة إلى تكوين مصادر بيانات متعددة في المشروع وتكون قادرة على التبديل ديناميكيًا. بعد بعض الاستكشاف ، يتم تحقيق التبديل الديناميكي تمامًا ، ويتم تسجيل طريقة التكوين للرجوع إليها.
أفكار التصميم الشاملة
تدرك طريقة SPRING-BOOT+AOP تبديل المصادر المتعددة data ، ويرث ABSTRACTRACTROUTINGDATASOURCE لتحقيق اكتساب مصدر البيانات الديناميكي ، ويحدد مصادر البيانات باستخدام التعليقات التوضيحية في طبقة الخدمة.
خطوة
1. تكوين مصدر البيانات المتعددة
في application.properties ، تكويننا مثل هذا
#مصدر البيانات druid.master.url = jdbc: mysql: // url/masterdb؟ useUnicode = true & directionoding = utf8 & zerodatetimebehavior = converttonulldruid.master.username = xxxdruid.master.password = 123dru id.master.driver-class-name = com.mysql.jdbc.driverddruid.master.max-wait = 5000druid.master.max-Active = 100druid.master.test-on Barrow = truedruid.master.vilidation-query = select 1#من مصدر البيانات druid.slave.url = jdbc: mysql: // url/slavedb؟ useUnicode = true & nickleRencoding = utf8 & zerodatetimebehavior = converttonulldruid.slave.username = xxxdruid.slave.password = 123dru id.slave.driver-class-name = com.mysql.jdbc.driverddruid.slave.max-wait = 5000druid.slave.max-Active = 100druid.slave.test-on Barrow = truedruid.slave.validation-query = select 1
قراءة التكوين
<!-Master Data Source-> <bean primary = "true" id = "masterdb" init-method = "init" DRIDED-METHOD = "CLOSE"> <!-url url url ، المستخدم ، كلمة المرور-> <property name = "driverClassName name = "username" value = "$ {druid.master.username}"/> <property name = "password" value = "$ {druid.master.password}"/> <!-تكوين الحد الأقصى للتهيئة-> <property name = "maxactive" value = "$ {druid.master.max-} name = "maxwait" value = "$ {druid.master.max-wait}"/> <property name = "ValidationQuery" value = "$ {druid.master.validation-query}"/> <property name = "stestonborrow" value = "$ {druid.master.test-on-borrard} id = "slavedb" init-method = "init" destroy-method = "close"> <!-url الخصائص الأساسية ، المستخدم ، كلمة المرور-> <property name = "driverClassName" value = "com.mysql.jdbc.driver"/> <property name = "url" value = $ {druid.slave.url} value = "$ {druid.slave.username}"/> <property name = "password" value = "$ {druid.slave.password}"/> <!-تكوين حجم التهيئة ، الحد الأدنى ، والحد الأقصى-> name = "maxwait" value = "$ {druid.slave.max-wait}"/> <property name = "validationQuery" value = "$ {druid.slave.validation-query}"/> <property name = "testOnBorrow احصل على استنادًا إلى التعليقات التوضيحية على واجهة الخدمة-> <bean id = "datasource"> <property name = "targetDatasources"> <map key-type = "java.lang.string"> <intern key = "slave" value-ref = "slavedb"/> name = "defaultTargetDataSource" ref = "masterdb"/> </bean> <!-Spring jdbCtEmplate-> <bean id = "jdbctemplate"> <property name = "dataSource" ref = "dataSource"/> </bean> <! /> </bean> <bean id = "transactiontemplate"> <property name = "TransactionManager" ref = "TransactionManager"/> </bean> <tx: strend-revation-divenager-manager = "TransactionManager" proxy-target = "true" order = "2"/> <property name = "dataSource" ref = "datasource"/> <property name = "mapperlocations" value = "classpath*: mapper-xxdb/*mapper*.xml"/> </bean> <bean> <property name = "basepackage" value = "xxdb.mapper"/> value = "sqlsessionfactory"/> </ban>2. مصدر البيانات الديناميكي
يوفر لنا Spring مع AbstractRoutingDataSource ، مصدر بيانات مع التوجيه. بعد الوراثة ، نحتاج إلى تنفيذ DENTERENCURRENTOLKUPKEY () ، والذي يتم استخدامه لتخصيص طريقة التوجيه لاسم مصدر البيانات الفعلي. نظرًا لأننا نقوم بحفظ المعلومات إلى ThreadLocal ، نحتاج فقط إلى إخراجها.
يمتد DynamicDataSource من الطبقة العامة abstractroutingDataSource {private logger logger = loggerfactory.getLogger (this.getClass ()) ؛ Override محمي الكائن DETERNECURRENTOKTOFKUPKEY () {String dataSource = JDBCCONTEXTHOLDER.GETDATASOURCE () ؛ logger.info ("مصدر البيانات هو {}" ، dataSource) ؛ إرجاع مصدر البيانات ؛ }}3. فئة التبديل الديناميكي لمصدر البيانات
يعتمد تبديل مصدر البيانات الديناميكي على AOP ، لذلك نحتاج إلى الإعلان عن قسم AOP وإجراء تبديل مصدر البيانات أمام القسم. بعد اكتمال القسم ، تتم إزالة اسم مصدر البيانات.
@Side @order (1) // قم بتعيين ترتيب تنفيذ AOP (يجب أن يكون قبل المعاملة ، وإلا فإن المعاملة ستحدث فقط في المكتبة الافتراضية) componentpublic class dataSourCeaspect {private logger logger = loggerFactory.getLogger (this.getClass ()) ؛ // cut point @pointcut ("التنفيذ (*com.xxx.service.*.*. طريقة السلسلة = point.getSignature (). getName () ؛ class <؟> classz = target.getClass () ؛ // الحصول على فئة الفئة المستهدفة <؟> [] parametertypes = ((MaysIgnature) point.getSignature ()) .getMethod (). getParameterTypes () ؛ حاول {method m = classz.getMethod (method ، parametertypes) ؛ if (m! = null && m.isannotationpresent (myDatasource.Class)) {myDatasource data = m.getAnnotation (myDatasource.Class) ؛ logger.info ("الطريقة: {} ، dataSource: {}" ، m.getName () ، data.value (). getName ()) ؛ jdbccontextholder.putdatasource (data.value (). getName ()) ؛ // ضع مصدر البيانات في مؤشر الترابط الحالي}} catch (استثناء e) {logger.error ("Get DataSource Error" ، e) ؛ // master jdbccontextholder.putdatasource (datasourceType.master.getName ()) ؛ // ضع مصدر البيانات في مؤشر الترابط الحالي}} efterReturning ("Side ()") public void بعد (نقطة انضمام) {jdbcontextholder.cleardataSource () ؛ }}4. فئة إدارة مصدر البيانات
الفئة العامة jdbccontextholder {private final static threadlocal <string> local = new threadlocal <> () ؛ putDataSource static putDataSource (اسم السلسلة) {local.set (name) ؛ } سلسلة ثابتة عامة getDataSource () {return local.get () ؛ } public static void clearDataSource () {local.remove () ؛ }}5. التعليقات التوضيحية لمصدر البيانات وتعدادها
عندما نقوم بتبديل مصادر البيانات ، فإننا ننفذها عمومًا قبل استدعاء طريقة الواجهة المحددة ، لذلك نحدد توضيح الطريقة. عندما يكتشف AOP أن التعليق التوضيحي موجود على الطريقة ، فإنه يتحول وفقًا للاسم المقابل للقيمة في التعليقات التوضيحية.
repention (attreentionpolicy.runtime) target (elementType.method) public interface myDataSource {dataSourCetype value () ؛} التعداد العام dataSourCeType {// master ("Master") ، // slave ("slave") ؛ اسم السلسلة الخاصة ؛ dataSourCeType (اسم السلسلة) {this.name = name ؛ } السلسلة العامة getName () {return name ؛ } public void setName (اسم السلسلة) {this.name = name ؛ }}6.
نظرًا لأن مصدر البيانات الديناميكي لدينا يحتوي على مكتبة افتراضية ، إذا كانت الطريقة هي تشغيل المكتبة الافتراضية ، فليست هناك حاجة إلى التعليقات التوضيحية. إذا كنت ترغب في تشغيل مصدر بيانات غير متفرغ ، فنحن بحاجة إلى إضافة التعليق التوضيحي لـ myDataSource ("DataSource Name") إلى الطريقة ، بحيث يمكنك استخدام AOP لتحقيق التبديل الديناميكي.
componentpublic class xxxSserviceImpl {resource private xxxmapperext xxxmapperext ؛ myDataSource (value = dataSourCeType.Slave) قائمة عامة <Object> getAll () {return xxxmapperext.getall () ؛ }}ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.