MongoDB基础入门到深入(八)MongoDB整合SpringBoot、Chang Streams
文章目录
系列文章索引
MongoDB基础入门到深入(一)安装、文档操作
MongoDB基础入门到深入(二)聚合高级操作
MongoDB基础入门到深入(三)索引高级操作
MongoDB基础入门到深入(四)复制(副本)集
MongoDB基础入门到深入(五)分片集群
MongoDB基础入门到深入(六)多文档事务
MongoDB基础入门到深入(七)建模、调优
MongoDB基础入门到深入(八)MongoDB整合SpringBoot、Chang Streams
十五、MongoDB整合SpringBoot
1、环境准备
1、引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
2、配置yml
spring:data:mongodb:uri: mongodb://root:root@192.168.56.10:27017/test?authSource=admin#uri等同于下面的配置#database: test#host: 192.168.56.10#port: 27017#username: root#password: root#authentication-database: admin#复制集 https://docs.mongodb.com/manual/reference/connection-string/#uri: mongodb://root:root@192.168.56.10:28017,192.168.56.10:28018,192.168.56.10:28019/test?authSource=admin&replicaSet=rs0
连接配置参考文档:https://www.mongodb.com/docs/manual/reference/connection-string/
3、使用时注入mongoTemplate
@Autowired
MongoTemplate mongoTemplate;
4、demo
//连接单点
//MongoClient mongoClient = MongoClients.create("mongodb://192.168.56.10:27017");
//连接副本集
MongoClient mongoClient = MongoClients.create("mongodb://root:root@192.168.56.10:28017,192.168.56.10:28018,192.168.56.10:28019/test?authSource=admin&replicaSet=rs0");
//连接分片集群 节点:mongos
//MongoClient mongoClient = MongoClients.create("mongodb://192.168.56.10:27017,192.168.56.11:27017,192.168.56.12:27017");//获得数据库对象
MongoDatabase database = mongoClient.getDatabase("test");
//获得集合
MongoCollection<Document> collection = database.getCollection("emp");System.out.println("emp文档数:"+collection.countDocuments());
2、集合操作
@Test
public void testCollection() {boolean exists = mongoTemplate.collectionExists("emp");if (exists) {//删除集合mongoTemplate.dropCollection("emp");}//创建集合mongoTemplate.createCollection("emp");
}
3、文档操作
(1)相关注解
@Document
修饰范围: 用在类上
作用: 用来映射这个类的一个对象为mongo中一条文档数据。
属性:( value 、collection )用来指定操作的集合名称
@Id
修饰范围: 用在成员变量、方法上
作用: 用来将成员变量的值映射为文档的_id的值
@Field
修饰范围: 用在成员变量、方法上
作用: 用来将成员变量及其值映射为文档中一个key:value对。
属性:( name , value )用来指定在文档中 key的名称,默认为成员变量名
@Transient
修饰范围:用在成员变量、方法上
作用:用来指定此成员变量不参与文档的序列化
(2)创建实体
import java.util.Date;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;@Document("emp") //对应emp集合中的一个文档
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Employee {@Id //映射文档中的_idprivate Integer id;@Field("username")private String name;@Fieldprivate int age;@Fieldprivate Double salary;@Fieldprivate Date birthday;
}
(3)添加文档
insert方法返回值是新增的Document对象,里面包含了新增后_id的值。如果集合不存在会自动创建集合。通过Spring Data MongoDB还会给集合中多加一个_class的属性,存储新增时Document对应Java中类的全限定路径。这么做为了查询时能把Document转换为Java类型。
@Test
public void testInsert() {Employee employee = new Employee(1, "小明", 30, 10000.00, new Date());//添加文档// sava: _id存在时更新数据//mongoTemplate.save(employee);// insert: _id存在抛出异常 支持批量操作mongoTemplate.insert(employee);List<Employee> list = Arrays.asList(new Employee(2, "张三", 21, 5000.00, new Date()),new Employee(3, "李四", 26, 8000.00, new Date()),new Employee(4, "王五", 22, 8000.00, new Date()),new Employee(5, "张龙", 28, 6000.00, new Date()),new Employee(6, "赵虎", 24, 7000.00, new Date()),new Employee(7, "赵六", 28, 12000.00, new Date()));//插入多条数据mongoTemplate.insert(list, Employee.class);
}
插入重复数据时: insert报 DuplicateKeyException提示主键重复; save对已存在的数据进行更新。
批处理操作时: insert可以一次性插入所有数据,效率较高;save需遍历所有数据,一次插入或更新,效率较低。
(4)查询文档
Criteria是标准查询的接口,可以引用静态的Criteria.where的把多个条件组合在一起,就可以轻松地将多个方法标准和查询连接起来,方便我们操作查询语句。
@Test
public void testFind() {System.out.println("==========查询所有文档===========");//查询所有文档List<Employee> list = mongoTemplate.findAll(Employee.class);list.forEach(System.out::println);System.out.println("==========根据_id查询===========");//根据_id查询Employee e = mongoTemplate.findById(1, Employee.class);System.out.println(e);System.out.println("==========findOne返回第一个文档===========");//如果查询结果是多个,返回其中第一个文档对象Employee one = mongoTemplate.findOne(new Query(), Employee.class);System.out.println(one);System.out.println("==========条件查询===========");//new Query() 表示没有条件//查询薪资大于等于8000的员工//Query query = new Query(Criteria.where("salary").gte(8000));//查询薪资大于4000小于10000的员工//Query query = new Query(Criteria.where("salary").gt(4000).lt(10000));//正则查询(模糊查询) java中正则不需要有////Query query = new Query(Criteria.where("name").regex("张"));//and or 多条件查询Criteria criteria = new Criteria();//and 查询年龄大于25&薪资大于8000的员工//criteria.andOperator(Criteria.where("age").gt(25),Criteria.where("salary").gt(8000));//or 查询姓名是张三或者薪资大于8000的员工criteria.orOperator(Criteria.where("name").is("张三"), Criteria.where("salary").gt(5000));Query query = new Query(criteria);//sort排序//query.with(Sort.by(Sort.Order.desc("salary")));//skip limit 分页 skip用于指定跳过记录数,limit则用于限定返回结果数量。query.with(Sort.by(Sort.Order.desc("salary"))).skip(0) //指定跳过记录数.limit(4); //每页显示记录数//查询结果List<Employee> employees = mongoTemplate.find(query, Employee.class);employees.forEach(System.out::println);
}
@Test
public void testFindByJson() {//使用json字符串方式查询//等值查询//String json = "{name:'张三'}";//多条件查询String json = "{$or:[{age:{$gt:25}},{salary:{$gte:8000}}]}";Query query = new BasicQuery(json);//查询结果List<Employee> employees = mongoTemplate.find(query, Employee.class);employees.forEach(System.out::println);
}
(5)更新文档
在Mongodb中无论是使用客户端API还是使用Spring Data,更新返回结果一定是受行数影响。如果更新后的结果和更新前的结果是相同,返回0。
updateFirst() 只更新满足条件的第一条记录
updateMulti() 更新所有满足条件的记录
upsert() 没有符合条件的记录则插入数据
@Test
public void testUpdate() {//query设置查询条件Query query = new Query(Criteria.where("salary").gte(15000));System.out.println("==========更新前===========");List<Employee> employees = mongoTemplate.find(query, Employee.class);employees.forEach(System.out::println);Update update = new Update();//设置更新属性update.set("salary", 13000);//updateFirst() 只更新满足条件的第一条记录//UpdateResult updateResult = mongoTemplate.updateFirst(query, update, Employee.class);//updateMulti() 更新所有满足条件的记录//UpdateResult updateResult = mongoTemplate.updateMulti(query, update, Employee.class);//upsert() 没有符合条件的记录则插入数据//update.setOnInsert("id",11); //指定_idUpdateResult updateResult = mongoTemplate.upsert(query, update, Employee.class);//返回修改的记录数System.out.println(updateResult.getModifiedCount());//返回匹配的记录数System.out.println(updateResult.getMatchedCount());System.out.println("==========更新后===========");employees = mongoTemplate.find(query, Employee.class);employees.forEach(System.out::println);
}
(6)删除文档
@Test
public void testDelete() {//删除所有文档//mongoTemplate.remove(new Query(),Employee.class);//条件删除Query query = new Query(Criteria.where("salary").gte(10000));mongoTemplate.remove(query, Employee.class);}
(7)聚合操作
MongoTemplate提供了aggregate方法来实现对数据的聚合操作。
基于聚合管道mongodb提供的可操作的内容:
基于聚合操作Aggregation.group,mongodb提供可选的表达式:
// 以聚合管道示例2为例
// 返回人口超过1000万的州
db.zips.aggregate( [{ $group: { _id: "$state", totalPop: { $sum: "$pop" } } },{ $match: { totalPop: { $gt: 10*1000*1000 } } }
] )@Test
public void test(){//$groupGroupOperation groupOperation = Aggregation.group("state").sum("pop").as("totalPop");//$matchMatchOperation matchOperation = Aggregation.match(Criteria.where("totalPop").gte(10*1000*1000));// 按顺序组合每一个聚合步骤TypedAggregation<Zips> typedAggregation = Aggregation.newAggregation(Zips.class, groupOperation, matchOperation);//执行聚合操作,如果不使用 Map,也可以使用自定义的实体类来接收数据AggregationResults<Map> aggregationResults = mongoTemplate.aggregate(typedAggregation, Map.class);// 取出最终结果List<Map> mappedResults = aggregationResults.getMappedResults();for(Map map:mappedResults){System.out.println(map);}
}
// 返回各州平均城市人口
db.zips.aggregate( [{ $group: { _id: { state: "$state", city: "$city" }, cityPop: { $sum: "$pop" } } },{ $group: { _id: "$_id.state", avgCityPop: { $avg: "$cityPop" } } },{ $sort:{avgCityPop:-1}}
] )@Test
public void test2(){//$groupGroupOperation groupOperation = Aggregation.group("state","city").sum("pop").as("cityPop");//$groupGroupOperation groupOperation2 = Aggregation.group("_id.state").avg("cityPop").as("avgCityPop");//$sortSortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC,"avgCityPop");// 按顺序组合每一个聚合步骤TypedAggregation<Zips> typedAggregation = Aggregation.newAggregation(Zips.class, groupOperation, groupOperation2,sortOperation);//执行聚合操作,如果不使用 Map,也可以使用自定义的实体类来接收数据AggregationResults<Map> aggregationResults = mongoTemplate.aggregate(typedAggregation, Map.class);// 取出最终结果List<Map> mappedResults = aggregationResults.getMappedResults();for(Map map:mappedResults){System.out.println(map);}
}
// 按州返回最大和最小的城市
db.zips.aggregate( [{ $group:{_id: { state: "$state", city: "$city" },pop: { $sum: "$pop" }}},{ $sort: { pop: 1 } },{ $group:{_id : "$_id.state",biggestCity: { $last: "$_id.city" },biggestPop: { $last: "$pop" },smallestCity: { $first: "$_id.city" },smallestPop: { $first: "$pop" }}},{ $project:{ _id: 0,state: "$_id",biggestCity: { name: "$biggestCity", pop: "$biggestPop" },smallestCity: { name: "$smallestCity", pop: "$smallestPop" }}},{ $sort: { state: 1 } }
] )@Test
public void test3(){//$groupGroupOperation groupOperation = Aggregation.group("state","city").sum("pop").as("pop");//$sortSortOperation sortOperation = Aggregation.sort(Sort.Direction.ASC,"pop");//$groupGroupOperation groupOperation2 = Aggregation.group("_id.state").last("_id.city").as("biggestCity").last("pop").as("biggestPop").first("_id.city").as("smallestCity").first("pop").as("smallestPop");//$projectProjectionOperation projectionOperation = Aggregation.project("state","biggestCity","smallestCity").and("_id").as("state").andExpression("{ name: \"$biggestCity\", pop: \"$biggestPop\" }").as("biggestCity").andExpression("{ name: \"$smallestCity\", pop: \"$smallestPop\" }").as("smallestCity").andExclude("_id");//$sortSortOperation sortOperation2 = Aggregation.sort(Sort.Direction.ASC,"state");// 按顺序组合每一个聚合步骤TypedAggregation<Zips> typedAggregation = Aggregation.newAggregation(Zips.class, groupOperation, sortOperation, groupOperation2,projectionOperation,sortOperation2);//执行聚合操作,如果不使用 Map,也可以使用自定义的实体类来接收数据AggregationResults<Map> aggregationResults = mongoTemplate.aggregate(typedAggregation, Map.class);// 取出最终结果List<Map> mappedResults = aggregationResults.getMappedResults();for(Map map:mappedResults){System.out.println(map);}}
(8)小技巧:如何去掉_class属性
@Configuration
public class MongoConfig {/*** 定制TypeMapper去掉_class属性* @param mongoDatabaseFactory* @param context* @param conversions* @return*/@BeanMappingMongoConverter mappingMongoConverter(MongoDatabaseFactory mongoDatabaseFactory,MongoMappingContext context, MongoCustomConversions conversions){DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDatabaseFactory);MappingMongoConverter mappingMongoConverter =new MappingMongoConverter(dbRefResolver,context);mappingMongoConverter.setCustomConversions(conversions);//构造DefaultMongoTypeMapper,将typeKey设置为空值mappingMongoConverter.setTypeMapper(new DefaultMongoTypeMapper(null));return mappingMongoConverter;}}
4、事务操作
官方文档:https://www.mongodb.com/docs/upcoming/core/transactions/
(1)编程式事务
/*** 事务操作API* https://docs.mongodb.com/upcoming/core/transactions/*/
@Test
public void updateEmployeeInfo() {//连接复制集MongoClient client = MongoClients.create("mongodb://root:root@192.168.56.10:28017,192.168.56.10:28018,192.168.56.10:28019/test?authSource=admin&replicaSet=rs0");MongoCollection<Document> emp = client.getDatabase("test").getCollection("emp");MongoCollection<Document> events = client.getDatabase("test").getCollection("events");//事务操作配置TransactionOptions txnOptions = TransactionOptions.builder().readPreference(ReadPreference.primary()).readConcern(ReadConcern.MAJORITY).writeConcern(WriteConcern.MAJORITY).build();try (ClientSession clientSession = client.startSession()) {//开启事务clientSession.startTransaction(txnOptions);try {emp.updateOne(clientSession,Filters.eq("username", "张三"),Updates.set("status", "inactive"));int i=1/0;events.insertOne(clientSession,new Document("username", "张三").append("status", new Document("new", "inactive").append("old", "Active")));//提交事务clientSession.commitTransaction();}catch (Exception e){e.printStackTrace();//回滚事务clientSession.abortTransaction();}}
}
(2)声明式事务
1、配置事务管理器
@Bean
MongoTransactionManager transactionManager(MongoDatabaseFactory factory){//事务操作配置
TransactionOptions txnOptions = TransactionOptions.builder().readPreference(ReadPreference.primary()).readConcern(ReadConcern.MAJORITY).writeConcern(WriteConcern.MAJORITY).build();return new MongoTransactionManager(factory);
}
2、编程测试service
@Service
public class EmployeeService {@AutowiredMongoTemplate mongoTemplate;@Transactionalpublic void addEmployee(){Employee employee = new Employee(100,"张三", 21,15000.00, new Date());Employee employee2 = new Employee(101,"赵六", 28,10000.00, new Date());mongoTemplate.save(employee);//int i=1/0;mongoTemplate.save(employee2);}
}
十六、Chang Streams
1、什么是 Chang Streams
Change Stream指数据的变化事件流,MongoDB从3.6版本开始提供订阅数据变更的功能。
Change Stream 是 MongoDB 用于实现变更追踪的解决方案,类似于关系数据库的触发器,但原理不完全相同:
2、Change Stream 的实现原理
Change Stream 是基于 oplog 实现的,提供推送实时增量的推送功能
。它在 oplog 上开启一个 tailable cursor 来追踪所有复制集上的变更操作,最终调用应用中定义的回调函数。
被追踪的变更事件主要包括:
insert/update/delete:插入、更新、删除;
drop:集合被删除;
rename:集合被重命名;
dropDatabase:数据库被删除;
invalidate:drop/rename/dropDatabase 将导致 invalidate 被触发, 并关闭 change stream;
如果只对某些类型的变更事件感兴趣,可以使用使用聚合管道的过滤步骤过滤事件:
var cs = db.user.watch([{$match:{operationType:{$in:["insert","delete"]}}
}])
Change Stream会采用 "readConcern:majority"这样的一致性级别,保证写入的变更不会被回滚
。因此:
未开启 majority readConcern 的集群无法使用 Change Stream;
当集群无法满足 {w: “majority”}
时,不会触发 Change Stream(例如 PSA 架构 中的 S 因故障宕机)。
3、MongoShell测试
# 窗口1
db.user.watch([],{maxAwaitTimeMS:1000000}).pretty()# 窗口2
db.user.insert({name:"xxxx"})
变更事件字段说明
4、Change Stream 故障恢复
假设在一系列写入操作的过程中,订阅 Change Stream 的应用在接收到“写3”之后 于 t0 时刻崩溃,重启后后续的变更怎么办?
想要从上次中断的地方继续获取变更流,只需要保留上次变更通知中的 _id 即可。
Change Stream 回调所返回的的数据带有 _id,这个 _id 可以用于断点恢复。例如:
var cs = db.collection.watch([], {resumeAfter: <_id>})
即可从上一条通知中断处继续获取后续的变更通知。
5、使用场景
跨集群的变更复制——在源集群中订阅 Change Stream,一旦得到任何变更立即写 入目标集群。
微服务联动——当一个微服务变更数据库时,其他微服务得到通知并做出相应的变更。
其他任何需要系统联动的场景。
案例 1.监控
用户需要及时获取变更信息(例如账户相关的表),ChangeStreams 可以提供监控功能,一旦相关的表信息发生变更,就会将变更的消息实时推送出去。
案例 2.分析平台
例如需要基于增量去分析用户的一些行为,可以基于 ChangeStreams 把数据拉出来,推到下游的计算平台, 比如 类似 Flink、Spark 等计算平台等等。
案例 3.数据同步
基于 ChangeStreams,用户可以搭建额外的 MongoDB 集群,这个集群是从原端的 MongoDB 拉取过来的, 那么这个集群可以做一个热备份,假如源端集群发生 网络不通等等之类的变故,备集群就可以接管服务。 还可以做一个冷备份,如用户基于 ChangeStreams 把数据同步到文件,万一源端数据库发生不可服务, 就可以从文件里恢复出完整的 MongoDB 数据库, 继续提供服务。(当然,此处还需要借助定期全量备份来一同完成恢复) 另外数据同步它不仅仅局限于同一地域,可以跨地域,从北京到上海甚至从中国到美国等等。
案例 4.消息推送
假如用户想实时了解公交车的信息,那么公交车的位置每次变动,都实时推送变更的信息给想了解的用 户,用户能够实时收到公交车变更的数据,非常便捷实用。
注意事项
Change Stream 依赖于 oplog,因此中断时间不可超过 oplog 回收的最大时间窗;
在执行 update 操作时,如果只更新了部分数据,那么 Change Stream 通知的也是增量部分;
删除数据时通知的仅是删除数据的 _id。
6、Chang Stream整合Spring Boot
1、引入依赖
<!--spring data mongodb-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
2、配置yml
spring:data:mongodb:uri: mongodb://root:root@192.168.56.10:27017/test?authSource=admin#uri等同于下面的配置#database: test#host: 192.168.56.10#port: 27017#username: root#password: root#authentication-database: admin#复制集 https://docs.mongodb.com/manual/reference/connection-string/#uri: mongodb://root:root@192.168.56.10:28017,192.168.56.10:28018,192.168.56.10:28019/test?authSource=admin&replicaSet=rs0
3、配置 mongo监听器的容器MessageListenerContainer,spring启动时会自动启动监听的任务用于接收changestream
@Configuration
public class MongodbConfig {@BeanMessageListenerContainer messageListenerContainer(MongoTemplate template, DocumentMessageListener documentMessageListener) {Executor executor = Executors.newFixedThreadPool(5);MessageListenerContainer messageListenerContainer = new DefaultMessageListenerContainer(template, executor) {@Overridepublic boolean isAutoStartup() {return true;}};ChangeStreamRequest<Document> request = ChangeStreamRequest.builder(documentMessageListener).collection("user") //需要监听的集合名//过滤需要监听的操作类型,可以根据需求指定过滤条件.filter(Aggregation.newAggregation(Aggregation.match(Criteria.where("operationType").in("insert", "update", "delete"))))//不设置时,文档更新时,只会发送变更字段的信息,设置UPDATE_LOOKUP会返回文档的全部信息.fullDocumentLookup(FullDocument.UPDATE_LOOKUP).build();messageListenerContainer.register(request, Document.class);return messageListenerContainer;}
}
4、配置mongo监听器,用于接收数据库的变更信息
@Component
public class DocumentMessageListener<S, T> implements MessageListener<S, T> {@Overridepublic void onMessage(Message<S, T> message) {System.out.println(String.format("Received Message in collection %s.\n\trawsource: %s\n\tconverted: %s",message.getProperties().getCollectionName(), message.getRaw(), message.getBody()));}}
5、测试
mongo shell插入一条文档
6、控制台输出
相关文章:

MongoDB基础入门到深入(八)MongoDB整合SpringBoot、Chang Streams
文章目录 系列文章索引十五、MongoDB整合SpringBoot1、环境准备2、集合操作3、文档操作(1)相关注解(2)创建实体(3)添加文档(4)查询文档(5)更新文档࿰…...

linux下的nfs概述与实验(openEuler22.03)
目录 什么是NFSNFS工作原理NFS常用文件/etc/exports文件/etc/exports格式 /var/lib/nfs/etab文件 NFS常用选项NFS配置方式1. 安装NFS和RPC2. 启动rpcbind和nfs3. 准备目录4. 配置/etc/exports并重启服务5. 连接测试服务端自我测试客户端测试 补充常用命令永久挂载注意 什么是NF…...

影响Oracle数据库打开速度的因素
当Oracle发起一个事务需要更改数据时,如果所涉及的数据块不在BUFFER CACHE中,那么Oracle服务进程首先会将相关数据块从数据文件中读进BUFFER CACHE进行更改(直接路径读除外),更改后的数据块称之为脏块(DIRT…...

dify:开源 LLMOps平台。
单纯笔记: 一、关于 Dify dify/README_CN.md at main langgenius/dify GitHub Dify 是一款开源的大语言模型(LLM)应用开发平台。它融合了后端即服务(Backend as Service)和 LLMOps 的理念,使开发者可以…...

CCF20220601——归一化处理
CCF20220601——归一化处理 代码如下: #include<bits/stdc.h> using namespace std; int main() {int n,a[1000],sum0;scanf("%d",&n);for(int i1;i<n;i){scanf("%d",&a[i]);suma[i];}double aver1.0,b0.0,d1.0;aversum/(n*1…...

用Ai编写一个电机驱动程序
问:帮我写一个步进电机的控制程序,要有包括加减速,以及电机步中断处理函数。 答:编写一个步进电机的控制程序涉及到硬件层面的操作,通常需要依赖特定的硬件平台和相应的驱动程序。以下是一个简化的示例,它展…...

【C++入门】—— C++入门 (下)_内联函数
前言:在了解完前面的C基础内容后,马上我们就要真正不如C的学习了,但在之前让我们最后了解最后一点点C入门知识!来迟的520特别篇! 本篇主要内容: 内联函数 auto关键字 范围for 指针空值nullptr C入门 1. 内联…...

Java数据结构与算法(最小栈)
前言 设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void pop() 删除堆栈顶部的元素。int top() 获取堆栈顶部的元素。i…...

7 Series FPGAs Integrated Block for PCI Express IP核 Advanced模式配置详解(三)
1 TL Settings Transaction Layer (TL)设置只在Advanced模式下有效。 Endpoint: Unlock and PME_Turn_Off Messages: 与端点的电源管理相关,允许发送解锁和电源管理事件关闭消息。 Root Port: Error Messages: Error Correctable(错误可纠正)…...

k8s 部署mqtt简介
在Kubernetes(K8s)中部署MQTT(Message Queuing Telemetry Transport)服务通常涉及以下几个步骤: 选择MQTT Broker MQTT Broker是MQTT消息传递的中间件。流行的MQTT Broker包括Mosquitto, HiveMQ, EMQ X等。你需要选择一…...

汇凯金业:量化交易中常用的数学模型有哪些
量化交易中运用了多种数学模型来识别市场的潜在机会和建立交易策略。以下是一些在量化交易中常用的数学模型: 1. 时间序列分析模型 时间序列分析是研究和预测数据点随时间顺序变化趋势的方法。在量化交易中,常用的时间序列模型包括: 自回归&a…...

局部直方图均衡化去雾算法
目录 1. 引言 2. 算法流程 3. 代码 4. 去雾效果 1. 引言 局部直方图算法是一种基于块的图像去雾方法,它将图像分割为若干个块,并在每个块内计算块的局部直方图。通过对各个块的直方图进行分析和处理,该算法能够更好地适应图像中不同区域的…...

selenium环境安装和web自动化基础
webUI自动化背景 因为web页面经常会变化,所以UI自动化测试的维护成本很高。不如接口的适用面广,所以大部分公司会做接口自动化测试,但是未必会做UI自动化测试; UI自动化测试要做也是覆盖冒烟测试,不会到很高的覆盖率&a…...

【UE Websocket】“WebSocket Server”插件使用记录
1. 在商城中下载“WebSocket Server”插件 该插件具有如下节点,基本可以满足WebSocket服务端的所有需求 2. 如果想创建一个基本的服务端,我们可以新建一个actor蓝图,添加如下节点 3. UE运行后,我们可以使用在线的websocket测试助手…...

spring中依赖注入(DI)是什么?
好的,让我以尽可能通俗易懂的方式来解释什么是依赖注入(DI,Dependency Injection)。 假设你正在制作一款游戏,游戏中有个角色需要使用武器。在没有依赖注入的情况下,这个角色可能需要自己创建一个武器。这…...

paligemma、Grounding-DINO-1.5简单无需标注无需训练直接可以使用的VLM图像到文本模型
1、paligemma 参考:https://github.com/google-research/big_vision/blob/main/big_vision/configs/proj/paligemma/README.md 模型架构: 文本与图像特征一起送入大模型 在线体验网址: https://huggingface.co/spaces/big-vision/paligemma 通过文字prompt既可与图片对话…...

FreeRTOS学习——FreeRTOS队列(下)之队列创建
本篇文章记录我学习FreeRTOS队列创建的知识。主要分享队列创建需要使用的初始化函数、队列复位函数。 需要进一步了解FreeRTOS队列的相关知识,读者可以参考以下文章: FreeRTOS学习——FreeRTOS队列(上)_freertos 单元素队列-CSDN博…...

js实现鼠标拖拽多选功能
实现功能 在PC端的H5页面中,客户拖动鼠标可以连选多个选项 效果展示 具体代码如下 <!DOCTYPE html> <html><head><title>鼠标拖拽多选功能</title><script src"https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js&quo…...

postgres_exporter 监控pg坑一:备库无延迟但是监控告警备库延迟
1.问题背景 监控告警某套pg的两个备库延迟大于300s 2.pg备库延迟告警策略 通过postgres_exporter(version 0.13.2)监控生产pg,延迟告警策略如下: pg_replication_lag_seconds{instance!~"host_ip_18801"} > 300 and ON(instance) pg_re…...

vue打包部署到springboot,通过tomcat运行
tomcat默认端口 8080springboot端口 9132vue 端口 9131 框架 项目是基于SpringBootVue前后端分离的仓库管理系统 后端:SpringBoot MybatisPlus前端:Node.js Vue element-ui数据库:mysql 一. 打包Vue项目 cmd中输入命令 npm run build 后…...

如何有效防止数据丢失
在数字时代,数据成为了个人和企业最宝贵的资产之一。不幸的是,数据丢失的威胁无时无刻不在潜伏着,无论是由于技术故障、人为错误还是恶意攻击,都可能对我们的数据造成不可逆转的损失。因此,采取有效的预防措施至关重要…...

linux命令中arj使用
arj 用于创建和管理.arj压缩包 补充说明 arj命令 是 .arj 格式的压缩文件的管理器,用于创建和管理 .arj 压缩包。 语法 arj(参数)参数 操作指令:对 .arj 压缩包执行的操作指令;压缩包名称:指定要操作的arj压缩包名称。 更多…...

UE5中搭建一个简单的海岛
本文将用UE的WaterSystem与地形搭建一个简单的海岛,通过WaterSystem的参数设置,可以更好的自定义海岸线等效果。 1.基础风貌 1.1.首先新建一个Basic基础场景,切换到地形编辑模式刷出一块高地,用于沙滩。 1.2.引入UE官方插件Wat…...

爬虫学习--12.MySQL数据库的基本操作(下)
MySQL查询数据 MySQL 数据库使用SQL SELECT语句来查询数据。 语法:在MySQL数据库中查询数据通用的 SELECT 语法 SELECT 字段1,字段2,……,字段n FROM table_name [WHERE 条件] [LIMIT N] 查询语句中你可以使用一个或者多个表&…...

js的算法-选择排序(简单选择排序)
选择排序 每一趟(如第i趟)在后面n-i1(i1,2,……n-1)个待排序元素中选取关键字最小的元素,作为有序子序列的第i 个元素,直到第i个元素,直到第n-1趟做完,待排序元素只剩下1个,就不用再选了。 快…...

Mac虚拟机工具 CrossOver 24.0.0 Beta3 Mac中文版
CrossOver是一款在Mac上运行Windows应用程序的软件,无需安装虚拟机或重启计算机,简化了操作过程,提高了工作效率,为用户带来便捷体验。前往Mac青桔下载,享受前所未有的便利和高效。摘要由作者通过智能技术生成 CrossOv…...

路由聚合和VRRP技术
实验拓扑图: 实验需求 1、内网IP地址使用172.16.0.0/16 2、SW1和SW2之间互为备份; 3、VRRP/stp/vlan/eth-trunk均使用; 4、所有pc均通过DHCP获取IP地址; 5、ISP只配置IP地址; 6、所有电脑可以正常访问ISP路由器环…...

【原创教程】三菱FX3U系列培训专题课教案
位置控制三要素 定位控制指令必须要有以下三个条件 1、位置移动速度(电机转速) 2、位置移动距离(电机的旋转圈数) 3、位置移动方向(电机的旋转方向) FX3U_PLC的高速脉冲输出端口 普通输出端口受程序控制,什么时间通,什么时间断,没有固定的通断周期 高速脉冲输出端口…...

清空了电脑回收站,之前的文件还能否恢复?
电脑已成为我们日常生活中不可或缺的一部分。我们在电脑上处理文档、保存图片、下载视频等,而电脑中的回收站则成为我们处理不再需要文件的一个便捷工具,当我们想要删除某些文档的话,它并不是立即从硬盘上消失,而是被系统移动到了…...

设计模式——职责链(责任链)模式
目录 职责链模式 小俱求实习 结构图 实例 职责链模式优点 职责链模式缺点 使用场景 1.springmvc流程 2.mybatis的执行流程 3.spring的过滤器和拦截器 职责链模式 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成…...