关键词:
源代码:https://gitee.com/mr_wenpan/basis-enhance
使用参考:TestMongoMutilSourceController
一、功能介绍
- 支持一个springboot应用程序中配置多个MongoDB数据源
- 使用注解
@EnableMongoMultiSource
开启MongoDB多数据源功能启用与禁用,实现了动态可插拔功能。 - 提供
MongoMultiSourceClient
来获取并操作某个指定的数据源 - 对应每个MongoDB集合都支持自定义分片算法
- 提供默认的一致性hash算法实现,解决使用一致性hash分片算法时可能存在的多数据源数据倾斜问题
- 提供默认的一致性hash分片算法实现,对于MongoDB集合可以采用一致性hash算法来动态选取存放数据的数据源。
- 使用时只用在代码里使用
@Autowired
注入mongoTemplateConsistentHash
即可使用一致性hash算法。
- 使用时只用在代码里使用
二、使用教程
1、基础配置
①、下载项目并打包到自己的maven仓库
下载源码,然后mvn install
到自己的maven仓库。
②、项目中引入插件依赖
<dependency>
<groupId>org.basis.enhance</groupId>
<artifactId>enhance-boot-mongo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
③、application.yml配置
spring:
data:
mongodb:
# 默认数据源
uri: $MONGODB_DEFAULT_URL:mongodb://用户名:密码@ip:端口/库名
# 开启多数据源分片
enable-sharding: true
# 多数据源配置
datasource:
datasource1:
order: 1
uri: $MONGODB_DEFAULT_URL:mongodb://用户名:密码@ip:端口/库名
datasource2:
order: 2
uri: $MONGODB_DEFAULT_URL:mongodb://用户名:密码@ip:端口/库名
④、启动类上开启MongoDB多数据源
// 使用@EnableMongoMultiSource注解开启MongoDB多数据源
@EnableMongoMultiSource
@SpringBootApplication
public class EnhanceMongoDemoApplication
public static void main(String... args)
SpringApplication.run(EnhanceMongoDemoApplication.class, args);
2、使用
// MongoDB多数据源使用示例
public void multiSourceExample()
// 通过一致性hash算法查找对应数据源的MongoTemplate
MongoTemplate templateByHash = mongoMultiSourceClient.getMongoTemplateByHash("wenpan");
// 获取默认的MongoDB数据源
MongoTemplate defaultMongoTemplate = mongoMultiSourceClient.getDefaultMongoTemplate();
// 通过数据源名称获取对应的MongoDB数据源
MongoTemplate datasource1MongoTemplate = mongoMultiSourceClient.getMongoTemplate("datasource1MongoTemplate");
// 通过集合 + 分片key 获取对应的MongoDB数据源
MongoTemplate mongoTemplate = mongoMultiSourceClient.getMongoTemplate("enhance_delivery_confirm", "xxx");
// 使用对应数据源的MongoTemplate去操作MongoDB
// 省略......
三、核心实现流程
- 首先基于
spring-boot-starter-data-mongodb
,先不用思考如何实现多数据源功能,配置好常规的springboot 和MongoDB整合,先把项目启动起来 - 项目能正常启动后,主要关注mongoTemplate在springboot中是如何通过自动配置被自动注入的,开始debug源码
- 主要关注
MongoAutoConfiguration
、MongoDatabaseFactoryDependentConfiguration
、MongoDatabaseFactoryConfiguration
这几个类,在这几个类中可以看到分别有对mongoClient、mongoTemplateFactory、mongoTemplate的注入 - 关注核心流程,对注入mongoClient、mongoTemplateFactory、mongoTemplate的地方打上断点,单步调试这三个类注入的详细流程以及所需的参数
- debug完上述三个类的注入流程后,我们大概的清楚在springboot整合MongoDB并注入方便使用的MongoTemplate,主要是使用Mongo连接工厂去创建对mongo服务器的连接,而mongo连接工厂的创建又需要用到mongoClient去创建连接。
- 所以综上所述,我们要实现mongo多数据源,只需要参考springboot对于MongoDB的自动配置是如何注入MongoTemplate、MongoClientFactory以及MongoFactory就行。然后按照他的注入模式自己创建这些类的对象。
- 为了方便使用,需要为使用方提供一个便于操作的MongoMultiSourceClient,使用方可以使用这个MongoMultiSourceClient去动态的切换MongoDB数据源、也可以对于MongoDB的某个集合,通过【自定义分片算法 + 集合名称 + 分片key】去动态的选择MongoDB数据源。
四、问题难点
1、对于如何获取application.yml配置文件中使用方动态配置的数据源信息(比如使用方可以配置3个数据源,也可以是4个数据源,也可以是五个数据源等等),并为这些数据源创建对应的可用连接,并创建出对应的MongoTemplate然后注入到容器这是一个难题
- 我们可以通过
@ConfigurationProperties注解 + hashMap
将application.yml配置文件中有关MongoDB多数据源的配置映射到map中,map中一个key-value键值对就表示一个数据源的具体配置信息 - 由于数据源的个数是由使用方动态配置的,所以在程序启动前我们并不知道有多少个数据源,所以无法在配置文件中通过
@Bean
的方式硬编码。所以我们利用factoryBean + spring的后置处理器
可以实现在程序启动过程中动态的获取application.yml文件中的数据源配置,然后根据数据源个数动态的为每个MongoDB数据源注入对应的mongoTemplate
五、核心实现
1、多数据源注入
public final class MonogoMultiDataSourceRegistrar implements EnvironmentAware, ImportBeanDefinitionRegistrar
private final Logger logger = LoggerFactory.getLogger(getClass());
private Environment environment;
@Override
public void setEnvironment(@NonNull Environment environment)
this.environment = environment;
/**
* 为每个mongo数据源注入BeanDefinition
*/
@Override
public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata,
@NonNull BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator)
Set<String> names = EnvironmentUtil.loadMongoDataSourceName((AbstractEnvironment) environment);
if (names.size() <= 0)
logger.error("no mongo multi datasource config, inject multi datasource failed. please check config.");
return;
logger.info("register mongo datasource: ", names);
for (String name : names)
registerMongoTemplateBeanDefinition(name, MongoTemplateFactoryBean.class, registry);
/**
* 注册 MongoTemplate BeanDefinition
*/
protected final void registerMongoTemplateBeanDefinition(String alias, Class<?> type, BeanDefinitionRegistry registry)
// BeanDefinition构建器
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(type);
// 设置通过名称注入
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
builder.addConstructorArgValue(null);
builder.addPropertyValue(MongoDataSourceContext.FIELD_DATASOURCE_NAME, alias);
BeanDefinition beanDefinition = builder.getBeanDefinition();
beanDefinition.setPrimary(false);
String beanName = alias + EnhanceMongoConstant.MultiSource.MONGO_TEMPLATE;
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, beanName, new String[]alias + "-template");
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
/**
* 创建 MongoTemplate 的 FactoryBean
* FactoryBean一般用于构建复杂的bean
*/
protected final class MongoTemplateFactoryBean extends MongoDataSourceContext implements FactoryBean<Object>
private final Logger logger = LoggerFactory.getLogger(getClass());
/**
* 返回要创建的bean对象
*/
@Override
public Object getObject() throws Exception
// 为该数据源创建一个Mongo连接工厂,连向指定的数据源
DynamicMongoTemplateFactory dynamicMongoTemplateFactory = getDynamicMongoTemplateFactory();
logger.info("Dynamic create a MongoTemplate named ", getDataSourceName());
MongoTemplate mongoTemplate = dynamicMongoTemplateFactory.createMongoTemplate();
// 由于这里没有注入spring容器,需要手动设置上applicationContext
mongoTemplate.setApplicationContext(applicationContext);
return mongoTemplate;
@Override
public Class<?> getObjectType()
return MongoTemplate.class;
2、mongo连接工厂创建
public DynamicMongoTemplateFactory getDynamicMongoTemplateFactory()
MongoProperties mongoProperties = getMongoProperties(dataSourceName);
MongoClientSettingsBuilderCustomizer builderCustomizers = MongoClientCreator.createMongoPropertiesCustomizer(mongoProperties, environment);
MongoClientSettings mongoClientSettings = MongoClientCreator.createMongoClientSettings();
MongoClient mongoClient = MongoClientCreator.createMongoClient(Collections.singletonList(builderCustomizers), mongoClientSettings);
return new DynamicMongoTemplateFactory(mongoClient, mongoProperties, applicationContext);
3、mongo数据源对应的mongoTemplate创建
public MongoTemplate createMongoTemplate() throws ClassNotFoundException
// 创建mongo客户端工厂
SimpleMongoClientDatabaseFactory factory = new SimpleMongoClientDatabaseFactory(mongoClient, properties.getMongoClientDatabase());
MappingMongoConverter mappingMongoConverter = createMappingMongoConverter(factory);
// 根据工厂创建template
return new MongoTemplate(factory, mappingMongoConverter);
mongodb单事务内使用多线程加速的简单例子(代码片段)
...事务内使用多线程的例子packagecom.mindsuite.saas.ztest;importcom.mongodb.MongoClient;importcom.mongodb.MongoClientURI;importcom.mongodb.client.ClientSession 查看详情
mongodb单事务内使用多线程加速的简单例子(代码片段)
...事务内使用多线程的例子packagecom.mindsuite.saas.ztest;importcom.mongodb.MongoClient;importcom.mongodb.MongoClientURI;importcom.mongodb.client.ClientSession 查看详情
springboot整合mongodb(实现一个简单缓存)(代码片段)
目录前言创建MongoDB数据库和项目创建MongoDB数据库创建项目预备工作缓存查询缓存更新、删除缓存更新缓存删除前言SpringBoot是常用开发框架,而MongoDB也是最近越来越火的非关系型数据库,这里使用SpringBoot+MongoDB实现一... 查看详情
java实现对mongodb的增删改查(代码片段)
一、连接数据库连接数据库,你需要指定数据库名称,如果指定的数据库不存在,mongo会自动创建数据库。连接数据库的Java代码如下(无需密码的连接): publicclassMongTest publicstaticvoidmain(String[]args) &... 查看详情
入门airflow如何实现动态dags?利用多任务提升效率(代码片段)
静态多任务造点假数据fromfakerimportFakerimportpymongofaker=Faker(locale='zh_CN')client=pymongo.MongoClient("mongodb://localhost:27018")coll=client.get_database("test_db") 查看详情
纯mongodb实现中文全文搜索(代码片段)
广州天勤数据有限公司赖勇浩摘要MongoDB在2.4版中引入全文索引后几经迭代更新已经比较完美地支持以空格分隔的西语,但一直不支持中日韩等语言,社区版用户不得不通过挂接ElasticSearch等支持中文全文搜索的数据库来实... 查看详情
mongodb——聚合管道之$lookup操作(代码片段)
...up的语法三、数据准备四、关联查询示例一、$lookup的概述Mongodb3.2版本新增,主要用来实现多表关联查询,相当关系型数据库中多表关联查询。每个输入待处理的文档,经过$lookup阶段的处理,输出的新文档中会包... 查看详情
mongodb单事务内使用多线程加速的简单例子(代码片段)
...事务内使用多线程的例子packagecom.mindsuite.saas.ztest;importcom.mongodb.MongoClient;importcom.mongodb.MongoClientURI;importcom.mongodb.client.ClientSession;importorg.springframework.data.mongodb.core.MongoTemplate;importorg.springframework.web.bind.annotation.GetMapping;importorg.springfr... 查看详情
mongodb单事务内使用多线程加速的简单例子(代码片段)
...事务内使用多线程的例子packagecom.mindsuite.saas.ztest;importcom.mongodb.MongoClient;importcom.mongodb.MongoClientURI;importcom.mongodb.client.ClientSession;importorg.springframework.data.mongodb.core.MongoTemplate;importorg.springframework.web.bind.annotation.GetMapping;importorg.springfr... 查看详情
mongodb在评论中台的实践(代码片段)
本文主要讲述vivo评论中台在数据库设计上的技术探索和实践。一、业务背景随着公司业务发展和用户规模的增多,很多项目都在打造自己的评论功能,而评论的业务形态基本类似。当时各项目都是各自设计实现,存在较多重复... 查看详情
mongodb简介与应用场景docker安装mongo整合springboot实现crud(代码片段)
(目录)1MongoDB相关概念1.1业务应用场景传统的关系型数据库(如MySQL),在数据操作的“三高”需求以及应对Web2.0的网站需求面前,显得力不从心。解释:“三高”需求:而MongoDB可应对“三高”需求。具体的应用场景如:这些应... 查看详情
通过swooletable实现swoole多进程数据共享(代码片段)
...享数据,需要借助第三方存储媒介实现:数据库:MySQL、MongoDB缓存:Redis、Memcached磁盘文件但是这也会引入新的问题,多进程同时操作一条记录或一个文件存在并发访问问题,以数据库操作为例,两个进程可能会同时读取一条数... 查看详情
mongodb——mongodb安装+增删改查操作(代码片段)
MongoDB安装+MongoDB安装+增删改查操作MongoDB相关概念MongoDB简介MongoDB的特点MongoDB安装启动MongoDB启动Mongodb多进程关闭MongoDB基本常用命令选择和创建数据库数据库的删除集合操作集合的显示创建(了解)集合的隐式创建... 查看详情
mongodb——mongodb安装+增删改查操作(代码片段)
MongoDB安装+MongoDB安装+增删改查操作MongoDB相关概念MongoDB简介MongoDB的特点MongoDB安装启动MongoDB启动Mongodb多进程关闭MongoDB基本常用命令选择和创建数据库数据库的删除集合操作集合的显示创建(了解)集合的隐式创建... 查看详情
mongodb2.0认识mogodb及其数据类型(代码片段)
目录MongoDB-认识MongoDB及数据类型启动MogoDB的数据MogoDB的数据类型1.ObjectID:Documents自生成的_id2.string:字符串,必须是utf-83.Boolean:布尔值,true或者false(小写)4.Integer:整数(int32int64)5.Double:浮点数(没有float类型,所有小数都是Double)6.Arrays:数组... 查看详情
mongoshake实现mongodb数据同步(代码片段)
...以Golang语言编写的通用平台型服务工具,它通过读取MongoDB的Oplog操作日志来复制MongoDB的数据以实现特定需求。MongoShake还提供了日志数据的订阅和消费功能,可通过SDK、Kafka、MetaQ等方式的灵活对接,适用于日志订阅、... 查看详情
mongodb——索引类型之多键索引(multikeyindex)(代码片段)
目录一、MongoDB官网地址二、多键索引(MultikeyIndex)2.1、多键索引(MultikeyIndex)的概述2.1、多键索引(MultikeyIndex)的官网图解三、数据准备四、创建多键索引的示例五、创建复合多键索引5.1、创建升序复... 查看详情
入门airflow如何实现动态dags?利用多任务提升效率(代码片段)
...r=Faker(locale='zh_CN')client=pymongo.MongoClient("mongodb://localhost:27018")coll=client.get_database("test_db").get_collection("10w")docs=["username":faker.language_name(),"age":faker.random_int(0,100),"email":faker.email(... 查看详情