电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

MyBatis使用Zookeeper保存數(shù)據(jù)庫(kù)的配置可動(dòng)態(tài)刷新的實(shí)現(xiàn)代碼

瀏覽:3日期:2023-10-18 11:05:44

核心關(guān)鍵點(diǎn): 封裝一個(gè)DataSource, 重寫 getConnection 就可以實(shí)現(xiàn)

我們一步一步來看.

環(huán)境: Spring Cloud + MyBatis

MyBatis常規(guī)方式下配置數(shù)據(jù)源: 使用Spring的Configuration

package com.cnscud.cavedemo.fundmain.config;import com.cnscud.xpower.dbn.SimpleDBNDataSourceFactory;import org.apache.ibatis.session.SqlSessionFactory;import org.mybatis.spring.SqlSessionFactoryBean;import org.mybatis.spring.SqlSessionTemplate;import org.mybatis.spring.annotation.MapperScan;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.boot.jdbc.DataSourceBuilder;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import org.springframework.transaction.PlatformTransactionManager;import javax.sql.DataSource;/** * Database Config 多數(shù)據(jù)源配置: 主數(shù)據(jù)源. * * @author Felix Zhang 2021-08-02 17:30 * @version 1.0.0 */@Configuration@MapperScan(basePackages = {'com.cnscud.cavedemo.fundmain.dao'},sqlSessionFactoryRef = 'sqlSessionFactoryMainDataSource')public class MainDataSourceConfig { //常規(guī)配置: 使用application.yml里面的配置. @Primary @Bean(name = 'mainDataSource') @ConfigurationProperties('spring.datasource.main') public DataSource mainDataSource() throws Exception {return DataSourceBuilder.create().build(); } @Primary @Bean(name = 'sqlSessionFactoryMainDataSource') public SqlSessionFactory sqlSessionFactoryMainDataSource(@Qualifier('mainDataSource') DataSource mainDataSource) throws Exception {SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();//org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();//configuration.setMapUnderscoreToCamelCase(true);//factoryBean.setConfiguration(configuration);factoryBean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource('classpath:mybatis-config.xml'));// 使用mainDataSource數(shù)據(jù)源, 連接mainDataSource庫(kù)factoryBean.setDataSource(mainDataSource);//下邊兩句僅僅用于*.xml文件,如果整個(gè)持久層操作不需要使用到xml文件的話(只用注解就可以搞定),則不加//指定entity和mapper xml的路徑//factoryBean.setTypeAliasesPackage('com.cnscud.cavedemo.fundmain.model');factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources('classpath:com/cnscud/cavedemo/fundmain/mapper/*.xml'));return factoryBean.getObject(); } @Primary @Bean public SqlSessionTemplate sqlSessionTemplateMainDataSource(@Qualifier('sqlSessionFactoryMainDataSource') SqlSessionFactory sqlSessionTemplateMainDataSource) throws Exception {//使用注解中配置的Factoryreturn new SqlSessionTemplate(sqlSessionTemplateMainDataSource); } @Primary @Bean public PlatformTransactionManager mainTransactionManager(@Qualifier('mainDataSource') DataSource prodDataSource) {return new DataSourceTransactionManager(prodDataSource); }}

這里面獲取數(shù)據(jù)源的關(guān)鍵函數(shù)是 mainDataSource, 我們自己來實(shí)現(xiàn)就好了:

因?yàn)檫@個(gè)是一次性的工作, 所以我們無法修改DataSource的指向, 只能在DataSource內(nèi)部做文章, 所以我們需要自己實(shí)現(xiàn)一個(gè)DataSource.

其中的步驟比較多, 我們來看看最終結(jié)果:

最終的DataSourceWrapper

它完全封裝了一個(gè)DataSource, 自己并沒有任何DataSource的功能:

package com.cnscud.xpower.dbn;import javax.sql.DataSource;import java.io.PrintWriter;import java.sql.Connection;import java.sql.SQLException;import java.sql.SQLFeatureNotSupportedException;import java.util.logging.Logger;/** * Datasource wrapper, 為了方便動(dòng)態(tài)創(chuàng)建DataSource. * * @author Felix Zhang 2021-08-05 14:14 * @version 1.0.0 */public class DynamicByZookeeperDataSourceWrapper implements DataSource { protected SimpleDBNConnectionPool simpleDBNConnectionPool; protected String bizName; public DynamicByZookeeperDataSourceWrapper(SimpleDBNConnectionPool simpleDBNConnectionPool, String bizName) {this.simpleDBNConnectionPool = simpleDBNConnectionPool;this.bizName = bizName; } protected DataSource pickDataSource() throws SQLException{return simpleDBNConnectionPool.getDataSource(bizName); } @Override public Connection getConnection() throws SQLException {return pickDataSource().getConnection(); } @Override public Connection getConnection(String username, String password) throws SQLException {return pickDataSource().getConnection(username, password); } @Override public <T> T unwrap(Class<T> iface) throws SQLException {return pickDataSource().unwrap(iface); } @Override public boolean isWrapperFor(Class<?> iface) throws SQLException {return pickDataSource().isWrapperFor(iface); } @Override public PrintWriter getLogWriter() throws SQLException {return pickDataSource().getLogWriter(); } @Override public void setLogWriter(PrintWriter out) throws SQLException {pickDataSource().setLogWriter(out); } @Override public void setLoginTimeout(int seconds) throws SQLException {pickDataSource().setLoginTimeout(seconds); } @Override public int getLoginTimeout() throws SQLException {return pickDataSource().getLoginTimeout(); } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException {throw new SQLFeatureNotSupportedException(); }}

SimpleDBNConnectionPool

支持多個(gè)數(shù)據(jù)源的暫存池, 可以根據(jù)name獲取不同的數(shù)據(jù)庫(kù)DataSource實(shí)例:

這個(gè)類負(fù)責(zé)創(chuàng)建DataSource, 保存在Map里. 并且能監(jiān)聽Zookeeper的變化, 一旦偵聽到變化, 就close現(xiàn)有的DataSource.

package com.cnscud.xpower.dbn;import com.github.zkclient.IZkDataListener;import com.zaxxer.hikari.HikariDataSource;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.sql.DataSource;import java.sql.Connection;import java.sql.SQLException;import java.util.HashSet;import java.util.Map;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;import static java.lang.String.format;/** * The simple datasource pool. * * 根據(jù)名字存放多個(gè)數(shù)據(jù)庫(kù)的DataSource, 并且會(huì)監(jiān)聽Zookeeper配置, 動(dòng)態(tài)重建. * * @author adyliu (imxylz@gmail.com) * @since 2011-7-27 */public class SimpleDBNConnectionPool { final Logger logger = LoggerFactory.getLogger(getClass()); private Map<String, DataSource> instances = new ConcurrentHashMap<>(); private final Set<String> watcherSchema = new HashSet<String>(); public DataSource getInstance(String bizName) {try { return findDbInstance(bizName);}catch (SQLException e) { e.printStackTrace();}return null; } public Connection getConnection(String bizName) throws SQLException {DataSource ds = getDataSource(bizName);return ds.getConnection(); } public DataSource getDataSource(String bizName) throws SQLException {return findDbInstance(bizName); } protected void destroyInstance(final String bizName) {synchronized (instances) { DataSource oldInstanceIf = instances.remove(bizName); logger.warn(format('destoryInstance %s and %s', bizName, oldInstanceIf != null ? 'close datasource' : 'do nothing')); if (oldInstanceIf != null) {closeDataSource(oldInstanceIf); }} } protected void closeDataSource(DataSource ds) {if (ds instanceof HikariDataSource) { try {((HikariDataSource) ds).close(); } catch (Exception e) {logger.error('Close datasource failed. ', e); }} } private DataSource createInstance(Map<String, String> dbcfg) {return new SimpleDataSourceBuilder().buildDataSource(dbcfg); } private DataSource findDbInstance(final String bizName) throws SQLException {DataSource ins = instances.get(bizName);if (ins != null) { return ins;}synchronized (instances) {// 同步操作 ins = instances.get(bizName); if (ins != null) {return ins; } boolean success = false; try {Map<String, String> dbcfg = SchemeNodeHelper.getInstance(bizName);if (dbcfg == null) { throw new SQLException('No such datasouce: ' + bizName);}ins = createInstance(dbcfg);//log.warn('ins put '+ins);instances.put(bizName, ins);if (watcherSchema.add(bizName)) { SchemeNodeHelper.watchInstance(bizName, new IZkDataListener() {public void handleDataDeleted(String dataPath) throws Exception { logger.warn(dataPath + ' was deleted, so destroy the bizName ' + bizName); destroyInstance(bizName);}public void handleDataChange(String dataPath, byte[] data) throws Exception { logger.warn(dataPath + ' was changed, so destroy the bizName ' + bizName); destroyInstance(bizName);} });}success = true; } catch (SQLException e) {throw e; } catch (Throwable t) {throw new SQLException('cannot build datasource for bizName: ' + bizName, t); } finally {if (!success) { instances.remove(bizName);} }}return ins; }}

真正創(chuàng)建DataSource的代碼:

package com.cnscud.xpower.dbn;import com.zaxxer.hikari.HikariConfig;import com.zaxxer.hikari.HikariDataSource;import org.apache.commons.lang.StringUtils;import java.util.Map;/** * Hikari DataSource. * * 思考: 可以根據(jù)參數(shù)里面的類型來使用不同的庫(kù)創(chuàng)建DataSource, 例如Druid. (默認(rèn)為HikariDataSource) * * * @author Felix Zhang 2021-08-05 11:14 * @version 1.0.0 */public class SimpleDataSourceBuilder { public HikariDataSource buildDataSource(Map<String, String> args) {HikariConfig config = new HikariConfig();config.setJdbcUrl(getUrl(args));config.setUsername(args.get('username'));config.setPassword(args.get('password'));config.setDriverClassName(getDriverClassName(args));String maximumPoolSizeKey = 'maximum-pool-size';int maximumPoolSize = 30;if(StringUtils.isNotEmpty(args.get(maximumPoolSizeKey))){ maximumPoolSize = Integer.parseInt(args.get(maximumPoolSizeKey));}config.addDataSourceProperty('cachePrepStmts', 'true'); //是否自定義配置,為true時(shí)下面兩個(gè)參數(shù)才生效config.addDataSourceProperty('prepStmtCacheSize', maximumPoolSize); //連接池大小默認(rèn)25,官方推薦250-500config.addDataSourceProperty('prepStmtCacheSqlLimit', '2048'); //單條語(yǔ)句最大長(zhǎng)度默認(rèn)256,官方推薦2048config.addDataSourceProperty('useServerPrepStmts', 'true'); //新版本MySQL支持服務(wù)器端準(zhǔn)備,開啟能夠得到顯著性能提升config.addDataSourceProperty('useLocalSessionState', 'true');config.addDataSourceProperty('useLocalTransactionState', 'true');config.addDataSourceProperty('rewriteBatchedStatements', 'true');config.addDataSourceProperty('cacheResultSetMetadata', 'true');config.addDataSourceProperty('cacheServerConfiguration', 'true');config.addDataSourceProperty('elideSetAutoCommits', 'true');config.addDataSourceProperty('maintainTimeStats', 'false');config.setMaximumPoolSize(maximumPoolSize); //config.setMinimumIdle(10);//最小閑置連接數(shù),默認(rèn)為0config.setMaxLifetime(600000);//最大生存時(shí)間config.setConnectionTimeout(30000);//超時(shí)時(shí)間30秒config.setIdleTimeout(60000);config.setConnectionTestQuery('select 1');return new HikariDataSource(config); } private String getDriverClassName(Map<String, String> args) {return args.get('driver-class-name'); } private String getUrl(Map<String, String> args) {return args.get('jdbc-url') == null ? args.get('url'): args.get('jdbc-url'); }}

為了方便讀取Zookeeper節(jié)點(diǎn), 還有個(gè)SchemeNodeHelper:

支持兩種配置文件的方式 json或者Properties格式:

package com.cnscud.xpower.dbn;import com.cnscud.xpower.configcenter.ConfigCenter;import com.cnscud.xpower.utils.Jsons;import com.github.zkclient.IZkDataListener;import org.apache.commons.lang.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.io.IOException;import java.io.StringReader;import java.util.HashMap;import java.util.Map;import java.util.Properties;/** * 從Zookeeper的 /xpower/dbn節(jié)點(diǎn)下讀取數(shù)據(jù)庫(kù)配置. * 內(nèi)容支持兩種格式: json或者properties格式. * * JSON格式如下: * { * 'jdbc-url': 'jdbc:mysql://127.0.0.1:3306/cavedemo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC', * 'username': 'dbuser', * 'password': 'yourpassword', * 'driver-class-name': 'com.mysql.cj.jdbc.Driver' * } * * Properties格式如下: * jdbc-url: jdbc:mysql://127.0.0.1:3306/cavedemo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC * username: dbuser * password: password * driver-class-name: com.mysql.cj.jdbc.Driver * * @author Felix Zhang * @since 2021-8-5 */public class SchemeNodeHelper { static final Logger logger = LoggerFactory.getLogger(SchemeNodeHelper.class); //支持兩種格式: json, properties public static Map<String, String> getInstance(final String instanceName) throws Exception {String data = ConfigCenter.getInstance().getDataAsString('/xpower/dbn/' + instanceName);if(StringUtils.isEmpty(data)){ return null;}data = data.trim();if (data.startsWith('{')) { //as json Map<String, String> swap = Jsons.fromJson(data, Map.class); Map<String, String> result = new HashMap<>(); if (swap != null) {for (String name : swap.keySet()) { result.put(name.toLowerCase(), swap.get(name));} } return result;}else { //as properties Properties props = new Properties(); try {props.load(new StringReader(data)); } catch (IOException e) {logger.error('loading global config failed', e); } Map<String, String> result = new HashMap<>(); for (String name : props.stringPropertyNames()) {result.put(name.toLowerCase(), props.getProperty(name)); } return result;} } public static void watchInstance(final String bizName, final IZkDataListener listener) {final String path = '/xpower/dbn/' + bizName;ConfigCenter.getInstance().subscribeDataChanges(path, listener); }}

實(shí)際應(yīng)用

最后在MyBatis項(xiàng)目中, 替換原有MainDataSource代碼為:

/** * 添加@Primary注解,設(shè)置默認(rèn)數(shù)據(jù)源,事務(wù)管理器. * 此處使用了一個(gè)可以動(dòng)態(tài)重建的DataSource, 如果Zookeeper配置改變,會(huì)動(dòng)態(tài)重建. */ @Primary @Bean(name = 'mainDataSource') public DataSource mainDataSource() throws Exception {return SimpleDBNDataSourceFactory.getInstance().getDataSource('cavedemo'); }

運(yùn)行項(xiàng)目, 發(fā)現(xiàn)可以連上數(shù)據(jù)庫(kù), 并且不重啟項(xiàng)目的情況下, 動(dòng)態(tài)修改數(shù)據(jù)庫(kù)配置, 能自動(dòng)重連.

項(xiàng)目代碼:

https://github.com/cnscud/xpower/tree/main/xpower-main/src/main/java/com/cnscud/xpower/dbn

其中用到的 ConfigCenter 也在這個(gè)項(xiàng)目里, 也可以自己實(shí)現(xiàn), 就可以脫離本項(xiàng)目了.

本文來自博客園,作者:飛云~風(fēng)之谷,轉(zhuǎn)載請(qǐng)注明原文鏈接:https://www.cnblogs.com/cnscud/p/15103859.html

到此這篇關(guān)于MyBatis使用Zookeeper保存數(shù)據(jù)庫(kù)的配置,可動(dòng)態(tài)刷新的文章就介紹到這了,更多相關(guān)MyBatis使用Zookeeper保存數(shù)據(jù)庫(kù)的配置,可動(dòng)態(tài)刷新內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

相關(guān)文章:
主站蜘蛛池模板: 电动液压篮球架_圆管地埋式篮球架_移动平箱篮球架-强森体育 | 长沙中央空调维修,中央空调清洗维保,空气能热水工程,价格,公司就找维小保-湖南维小保环保科技有限公司 | 【中联邦】增稠剂_增稠粉_水性增稠剂_涂料增稠剂_工业增稠剂生产厂家 | 电销卡 防封电销卡 不封号电销卡 电话销售卡 白名单电销卡 电销系统 外呼系统 | 全国国际学校排名_国际学校招生入学及学费-学校大全网 | 螺杆真空泵_耐腐蚀螺杆真空泵_水环真空泵_真空机组_烟台真空泵-烟台斯凯威真空 | 智能门锁电机_智能门锁离合器_智能门锁电机厂家-温州劲力智能科技有限公司 | 中原网视台| 菏泽知彼网络科技有限公司| 开平机_纵剪机厂家_开平机生产厂家|诚信互赢-泰安瑞烨精工机械制造有限公司 | 福建成考网-福建成人高考网 | 湖南长沙商标注册专利申请,长沙公司注册代理记账首选美创! | 震动筛选机|震动分筛机|筛粉机|振筛机|振荡筛-振动筛分设备专业生产厂家高服机械 | 创绿家招商加盟网-除甲醛加盟-甲醛治理加盟-室内除甲醛加盟-创绿家招商官网 | 常州律师事务所_常州律所_常州律师-江苏乐天律师事务所 | 3d可视化建模_三维展示_产品3d互动数字营销_三维动画制作_3D虚拟商城 【商迪3D】三维展示服务商 广东健伦体育发展有限公司-体育工程配套及销售运动器材的体育用品服务商 | AR开发公司_AR增强现实_AR工业_AR巡检|上海集英科技 | 精密模具制造,注塑加工,吹塑和吹瓶加工,EPS泡沫包装生产 - 济南兴田塑胶有限公司 | 海南在线 海南一家| 北京模型公司-军事模型-工业模型制作-北京百艺模型沙盘公司 | 电子厂招聘_工厂招聘_普工招聘_小时工招聘信息平台-众立方招工网 | 隆众资讯-首页_大宗商品资讯_价格走势_市场行情 | 智慧消防-消防物联网系统云平台 智能化的检漏仪_气密性测试仪_流量测试仪_流阻阻力测试仪_呼吸管快速检漏仪_连接器防水测试仪_车载镜头测试仪_奥图自动化科技 | 幂简集成 - 品种超全的API接口平台, 一站搜索、试用、集成国内外API接口 | SOUNDWELL 编码器|电位器|旋转编码器|可调电位器|编码开关厂家-广东升威电子制品有限公司 | 臭氧发生器_臭氧消毒机 - 【同林品牌 实力厂家】 | 托利多电子平台秤-高精度接线盒-托利多高精度电子秤|百科 | 钢格栅板_钢格板网_格栅板-做专业的热镀锌钢格栅板厂家-安平县迎瑞丝网制造有限公司 | 电地暖-电采暖-发热膜-石墨烯电热膜品牌加盟-暖季地暖厂家 | 楼承板设备-楼承板成型机-免浇筑楼承板机器厂家-捡来 | 闸阀_截止阀_止回阀「生产厂家」-上海卡比阀门有限公司 | 济南铝方通-济南铝方通价格-济南方通厂家-山东鲁方通建材有限公司 | 神超官网_焊接圆锯片_高速钢锯片_硬质合金锯片_浙江神超锯业制造有限公司 | 大型冰雕-景区冰雕展制作公司,3D创意设计源头厂家-[赛北冰雕] | 施工电梯_齿条货梯_烟囱电梯_物料提升机-河南大诚机械制造有限公司 | 罗氏牛血清白蛋白,罗氏己糖激酶-上海嵘崴达实业有限公司 | 电动葫芦-河北悍象起重机械有限公司 | 发电机价格|发电机组价格|柴油发电机价格|柴油发电机组价格网 | 精密模具加工制造 - 富东懿| 沈阳缠绕膜价格_沈阳拉伸膜厂家_沈阳缠绕膜厂家直销 | 广州办公室设计,办公室装修,写字楼设计,办公室装修公司_德科 |