链式数据存储系统
目录
系统说明
服务端的模块设计
存储数据说明
服务端设计-程序入口(main)
数据库的连接-mysql包的编写
数据的加密-hash文件的编写
数据传递格式-proto文件的编写
具体实现方法-controller包的编写
日志的打印-logs包的编写
扩展服务端
系统说明
首先说明该系统所使用的开发语言以及其他工具
- 该系统的开发使用的是GO语言,数据传输使用的是protobuf,具体涉及的部分是grpc的初步运用、简单的系统架构设计。
- 内置了基础的系统日志模块。
- 数据库的查询、插入操作
- hash的单向加密
技术栈:
类别 | 使用技术 |
---|---|
开发语言 | Go(Golang) |
通信协议 | gRPC(Protocol Buffers) |
数据库 | MySQL + GORM |
加密算法 | SHA256 |
并发模型 | goroutine + sync.WaitGroup |
日志记录 | 自定义 logs 模块 |
项目结构 | 模块化设计,分包实现 |
系统的用处:建立三个(或者多个)服务端,形成环形链路,以:本节点存储上一节点的某些数据,上一节点查询数据需要经过本节点数据的验证 为核心。
系统暂时只有插入以及查询操作,后续可能会扩展其他操作
可以设置多个服务端
我们主要讲解怎么写一个单独的服务端,以及如何扩展服务端的数量
服务端的模块设计
主要包括服务端的入口,控制中心,日志、数据库、proto、hash加密模块连接六个模块
每一个服务端都有四个功能且两两一组
- 请求的发送:1.发出查询请求给下一节点 2.发出保存请求给下一节点
- 请求的接收:1.接收上一节点发送的查询请求 2.接收上一节点发送的保存请求
这个照片里面缺少了hash的模块,因为截图的时候没截上。嘻嘻
存储数据说明
该系统理论上讲是可以根据自身需要进行数据存储的设计,也就是MySQL表如何设计。
在本系统内,我们存储的数据是
- id
- 公司名
- 市值
- 总资产
可以根据自身需要灵活更改
服务端设计-程序入口(main)
作为程序的开头,main函数的设计是非常重要的
当程序开始运行的时候我们首先调用一个
logs.Logger.Println("系统启动")
来把程序运行的事件保存到日志文件中。这个包是我们自己编写的,具体的编写在下面的:日志的打印-logs包的编写部分。
同时,程序具有以下几个功能:
- 请求的发送:1.发出查询请求给下一节点 2.发出保存请求给下一节点
- 请求的接收:1.接收上一节点发送的查询请求 2.接收上一节点发送的保存请求
我们把这四个功能封装成四个函数,GetSend(接收查询请求)GetSave(接收保存请求)Ping(发出查询请求)Save(发出保存请求)
因为接收请求的部分需要用到监听功能,且监听功能会阻塞函数的继续运行。所以我们这里需要用到的是groutine并发,同时为了防止协程没结束,主函数提前结束的情况我们需要使用到“等待组”
首先我们创建一个等待组,然后添加两个“等待”
var wg = sync.WaitGroup{}
wg.Add(2)
使用go关键字创建两个协程
go controller.GetSend(&wg)
go controller.GetSave(&wg)
这两个函数都是controller包下的函数,因为我们为了防止main函数过于臃肿。就把所有的操作函数封装到另一个包内,而且协程也是要结束的,结束的时候需要调用wg.Done()方法,所以我们把wg这个变量的地址作为实参传递,函数形参应该设置一个相同类型的指针接收(wg * sync.WaitGroup)
再接下来就是另外两个功能(发送请求)了,由于我们需要手动触发这两个功能,所以我们设置了一个for循环,循环变量为key(bool)初始设置为true,如果我们需要结束循环的时候就设置为false。
num := 0
key := true
var allmoney string//总资产
var nowmoney string//市值
var company string//公司名for key {......}
循环体我们需要设置一个switch,通过用户输入的选项决定是发出哪一个请求,又或者是否退出循环。
fmt.Println("请输入1.查询 2.保存")
fmt.Scanln(&num)
switch num {case 1:fmt.Println("请输入要查询的公司")fmt.Scanln(&company)controller.Ping(company)case 2:fmt.Println("输入公司名,市值,总资产:")_, err := fmt.Scanln(&company, &allmoney, &nowmoney) //输入的值用空格隔开if err != nil {return}controller.Save(company, allmoney, nowmoney)default:key = false}
这里的num、company、allmoney、nowmoney这四个变量都是我们提前声明好的。声明的位置最好是在for循环之前声明,主要是为了循环体的简便。
这样,就会根据我们输入的不同跳转到不同的模块了。并且从这里我们可以看出
查询的ping()方法需要传递一个公司名作为参数,保存的save()方法则是需要公司名、市值、总资产三个参数。
如果输入了不是1和2的其他数字,就会让key变为false,循环结束。
数据库的连接-mysql包的编写
数据库的连接我们单独写一个包的作用是为了方便给本项目所有的文件提供一个统一的数据库处理变量。
我们使用两个全局变量(首字母大写以便其他包访问),它们的类型是 *gorm.DB,也就是一个指针。
// CompanyTab 连接所属user的存储自身公司数据的库
var CompanyTab *gorm.DB// HashTab 连接到所属user存储的下一个user的hash数据的库
var HashTab *gorm.DB
然后由于我们需要一个存储本地数据的表,一个存储下一节点hash值的表。所以我们需要生成两个结构体,结构体使用结构体标签进行描述。
MyCompany是存储本地数据的,NextHash是存储下一公司的hash数据的。它们的ID字段我们在调用gorm的Creat方法的时候尽量不要写,因为它会自动补充。
type MyCompany struct { //存储本公司的数据ID uint `gorm:"primarykey"` //设置主键、自增CompanyName string `gorm:"type:varchar(255);unique"` //设置公司名,不能重复AllMoney string //设置资金NowMoney string
}
type NextHash struct { //存储下一公司的hash数据ID uint `gorm:"primarykey"` //设置唯一的idCompanyName string `gorm:"type:varchar(255);unique"`HashDataAllMoney string `gorm:"type:varchar(255)"` //设置对应的hash值,最大为255字符HashDataNowMoney string `gorm:"type:varchar(255)"`
}
然后我们创建一个函数,函数的作用是连接数据库
存储数据的格式也有了,下面就是连接数据库了
dns := "root:123456@tcp(127.0.0.1:3306)/表的名字"
我们先声明一个数据库的参数的变量,存储数据库的参数(用户、密码、主机地址、端口号、数据库的名称)注:数据库要提前建好,数据库的表可以不用提前建好。
然后我们使用gorm包里面的Open方法,它的参数是一个mysql.Open(dns)和&gorm.Config{}
gorm.Open(mysql.Open(dns), &gorm.Config{})
它会返回一个*DB类型的指针,以及一个错误信息。我们把这个指针存放在我们刚刚定义的两个变量上
// CompanyTable 提供CompanyTab变量的连接
func CompanyTable() {dns := "root:123456@tcp(127.0.0.1:3306)/company1" //连接到company1公司CompanyTab, _ = gorm.Open(mysql.Open(dns), &gorm.Config{})CompanyTab.AutoMigrate(&MyCompany{})fmt.Println("create table is true")
}// NextCompanyHash 提供HashTab变量的连接
func NextCompanyHash() {dns := "root:123456@tcp(127.0.0.1:3306)/company1" //连接到company1公司HashTab, _ = gorm.Open(mysql.Open(dns), &gorm.Config{})HashTab.AutoMigrate(&NextHash{})fmt.Println("create table is true")
}
但是这又导致一个小问题,就是我们导入的时候,虽然可以调用这两个变量,但是由于无法运行这两个函数,就会导致变量里面啥都没有。对此,我们需要用到init函数,这个函数的作用就是在这个包被导入的时候自动调用init函数,我们把上面两个函数的调用放入init函数即可
func init() { //重点,被导包的时候默认允许,执行两个*gorm.DB的初始化CompanyTable()NextCompanyHash()
}
数据的加密-hash文件的编写
我们对数据的加密使用了hash加密,这部分博主理解的比较浅,就不过多讲解了
他主要是接收一个字符串,然后把字符串通过hash加密之后得到一串字符,把字符返回给调用该函数的一方即可
package hashimport ("crypto/sha256""fmt"
)/*
这里我们使用一个hash算法进行所有数据的处理
a 处理的 hash值 传递给b ,需要时从b里面获取hash值,再从新计算一次a的hash值,一样说明数据无误
不一样说明出现问题作用函数1:被调用,参数是一个数据,以及一个hash值,如果相同则返回true否则返回false
作用函数2:接受一个值,返回一个hash值
*/func HashData(input string) string {// 创建一个新的 SHA256 哈希实例hash := sha256.New()// 将输入字符串转换为字节并写入哈希计算器hash.Write([]byte(input))// 计算哈希值并返回结果的十六进制表示hashValue := hash.Sum(nil)return fmt.Sprintf("%x", hashValue)
}
数据传递格式-proto文件的编写
我们再写proto文件的时候也要注意,根据我们的需求来写。
首先指明proto的版本,以及生成目录和生成的文件所属包
syntax = "proto3";option go_package = ".;pb";
我们来讲解我们的需求:
首先是我们一个服务端仅仅只有两个请求:一个是查询请求、一个是保存请求。为了方便,我们给请求都需要添加一个head,说明自己来自于哪一个服务端
查询请求:string head、string company(公司名)
查询请求的回应:string head、string allmoney(返回总资产的hash值)、string nowmoney(返回市值的hash值)、bool foot(是否正确查询t or f)
message send {string head = 1;//请求头,表明自己属于哪一个userstring company = 2;//a-z哪一个公司
}message get {string head = 1;//请求头,表明自己属于哪一个userstring allmoney = 2;string nowmoney = 3;bool foot = 4;//请求的回应,成功true错误false
}
保存请求: string head、dataAllMoney、dataNowMoney
保存请求的回应:string head、bool retu(t or f 表示是否保存成功)
message send1{//发出数据保存请求,包含请求的头,数据本身string head = 1;string dataAllMoney = 2;string dataNowMoney = 3;
}
message get1{//返回结果,true or false.并返回数据的hash值string head = 1;bool retu = 2;
}
最后我们根据这两个请求设计一个service
service Func{rpc Read(send)returns(get);//这是读数据的请求,发出读取哪个公司,返回读取到的数据以及读取结果rpc Save(send1)returns(get1);//这是保存请求,如果更新数据,就把数据发送给下一user,然后通过hash算法返回hash值
}
设计完成之后生成对应的go文件即可,生成文件的命令:protoc -I . --go_out=. --go-grpc_out=. 文件名.proto
具体实现方法-controller包的编写
这个包里面有我们之前使用go协程运行的两个函数
它们主要的作用就是监听一个ip和端口,这里的ip没有写而是写成了:8081这种形式,它默认的是本机ip。我们把这个ip地址注册到我们使用grpc.NewServer()注册的服务器中,并且使用服务器.Serve()方法监听我们创建的连接之后,我们就可以使用grpc.Dial()连接到这个ip,并调用这个服务器的方法,至于服务器内都有哪些方法取决于pb.RegisterFuncServer(s, &Server{}),这个Server接口的所有方法都是可以的。
func GetSend(wg *sync.WaitGroup) { //接收查询请求并返回结果,把等待组作为参数传递给本函数执行done()方法defer wg.Done()dns := ":8081"lis, err := net.Listen("tcp", dns)if err != nil {logs.Logger.Fatal(err)}s := grpc.NewServer()pb.RegisterFuncServer(s, &Server{})s.Serve(lis) //创建grpc服务器并监听对应的ipreturn
}func GetSave(wg *sync.WaitGroup) { //接收保存请求并返回结果defer wg.Done()dns := ":8082"lis, err := net.Listen("tcp", dns)if err != nil {logs.Logger.Fatal(err)}s := grpc.NewServer()pb.RegisterFuncServer(s, &Server{})s.Serve(lis) //创建grpc服务器并监听对应的ipreturn
}
我们在main函数中,调用的四个函数都是属于controller包。由于GO语言的特性,我们如果想让本包的资源可以被外面访问就需要使用首字母大写的方式
接下来我们来看具体如何实现这四个方法吧
首先是监听的方法,它的实现是基于我们之前写的proto文件里面的service,它在go文件中是一个接口类型,接口内定义了两个方法,我们需要实现这两个方法。
实现方法的办法是定义一个结构体,让结构体实现接口内的方法,接口内的方法是两个监听的方法Read和Save所以我们先创建结构体,让结构体实现这两个方法
type Server struct { //实现接口pb.UnimplementedFuncServer
}
然后是两个方法的实现,这里有一部分涉及到了我们自己写的数据库的包。
简单讲一下这两个函数的逻辑,当这个函数被调用的时候他有两个默认的参数,以及两个返回值
参数:第一个是固定的ctx context.Context 第二个是我们之前在proto文件里面的参数
Read(send)returns(get)
这个send就是我们的参数之一,返回值也是,并且还要返回一个错误信息。
我们通过之前自己写的mysql包里面提供的两个变量来进行数据库的操作,然后根据返回值的类型、个数进行return。
/*read方法的作用是接收其他服务端发送的请求,根据请求头判断是否是正确的请求,然后把需要查询的数据库的hash值返回
user1的read方法只针对user3起效,因为user1存储的是user3的hash值*/
func (s *Server) Read(ctx context.Context, req *pb.Send) (*pb.Get, error) { //1v1//别的用户发出的请求都会被这里接受到,我们返回的请求是处理好的结果logs.Logger.Println("接收到hash查询请求,请求来自于", req.Company)data := &mysql.NextHash{}mysql.HashTab.Where("company_name = ?", req.Company).First(data)return &pb.Get{ //return anythingHead: "user1", //返回给请求发起者,说明自己是谁Foot: true, //标识查询成功Allmoney: data.HashDataAllMoney,Nowmoney: data.HashDataNowMoney,}, nil
}// Save
/*
这个方法的作用是存储user3放来的数据的hash值,然后返回结果给user3
*/
func (s *Server) Save(ctx context.Context, req *pb.Send1) (*pb.Get1, error) {//别的用户发送的保存请求,我们回应给它一个保存的结果logs.Logger.Println("接收到保存请求,user1存储user2的hash数据,所属公司:", req.Head)/*处理逻辑:接收到需要存储的数据,然后转变为hash值,把hash值保存到对应的数据*/dataAllHash := hash.HashData(req.DataAllMoney) //公司名+市值+总资产dataNowMoney := hash.HashData(req.DataNowMoney)conmpay := req.Headerr := mysql.HashTab.Create(&mysql.NextHash{CompanyName: conmpay,HashDataAllMoney: dataAllHash,HashDataNowMoney: dataNowMoney,})if err != nil {return &pb.Get1{ //存储失败,返回falseHead: "user1",Retu: false,}, nil //返回结果给请求发起方}/*连接数据库存储数据*/return &pb.Get1{ //one存储好之后返回给three一个true证明处理好了Head: "user1",Retu: true,}, nil //返回结果给请求发起方
}
接下来就是连接的发起方,也就是main函数中我们手动触发的那两个函数
这两个函数的作用就是,连接到对应的ip端口,然后调用接口内的方法(Read或Save)
c := pb.NewFuncClient(conn)这个语句的作用是创建一个客户端,里面的参数是一个连接,连接到我们想查询的系统的ip端口,这里它需要连接的是下一个服务端,所以ip和端口应该是下一个服务端中监听函数里面监听查询请求的函数的端口。如果是save那么就是连接下一个服务端中监听函数里面保存请求对应的IP端口。
同时在通过客户端服务器调用对应的函数的时候需要注意以下参数,参数要与客户端接收的参数对应上,比如我们上面的服务端接收一个context.Context类型的参数以及对应的proto文件生成的结构体,在传参的时候我们就需要context.Background(),和对应的结构体当作参数
func Ping(company string) { //测试函数logs.Logger.Println("系统执行查询,查询目标是user3", company)conn, err := grpc.Dial("localhost:8083", grpc.WithInsecure())if err != nil {logs.Logger.Fatal(err)}defer conn.Close()c := pb.NewFuncClient(conn)res, err := c.Read(context.Background(), &pb.Send{Head: "user1", //作为user1向user2发送请求,请求的作用是查询,查询user2里面存储的user1的数据的hash值Company: company, //表示查询哪一个公司})if err != nil {logs.Logger.Fatal(err)}fmt.Println("head is:", res.Head, "foot is:", res.Foot, "allmoney hash is :", res.Allmoney, "nowmoney hash is:", res.Nowmoney) //获取到hash数据dataOfMysql := mysql.MyCompany{}mysql.CompanyTab.Where("company_name = ?", company).Find(&dataOfMysql)a := hash.HashData(dataOfMysql.AllMoney)b := hash.HashData(dataOfMysql.NowMoney)if a == res.Allmoney && b == res.Nowmoney {fmt.Println("data is true ^_^")}
}func Save(company, allmoney, nowmoney string) { //接收参数,第一个是新加入的公司名logs.Logger.Println("系统发出保存请求,保存公司为:", company, "发送给user3")conn, err := grpc.Dial("localhost:8084", grpc.WithInsecure())if err != nil {logs.Logger.Fatal(err)}defer conn.Close()c := pb.NewFuncClient(conn)c.Save(context.Background(), &pb.Send1{Head: company,DataAllMoney: allmoney,DataNowMoney: nowmoney,})mysql.CompanyTab.Create(&mysql.MyCompany{CompanyName: company,AllMoney: allmoney,NowMoney: nowmoney,})logs.Logger.Println("新公司添加成功,hash值存放在user3中")
}
这里面还有日志的打印,这部分就比较简单了。接下啦我们会介绍
日志的打印-logs包的编写
我们日志的打印就是创建一个文件,然后使用特定的格式进行对文件的写入
package logsimport ("log""os"
)var Logger *log.Loggerfunc init() {file, err := os.OpenFile("system_user1.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)if err != nil {log.Fatalln("打开日志文件失败:", err)}Logger = log.New(file, "", log.LstdFlags) // 例如输出格式:[时间] 内容
}
我们只需要导入logs包,然后调用包里面的Logger即可
扩展服务端
扩展服务端也是比较简单的,我们首先明确的是原有的服务端们是形成一个环路的,如果要扩展,就需要添加新的ip端口,让上一user的服务端的ip指向我们插入的user的监听方,让我们插入的请求方的user指向下一user的监听方。
还有就是需要清除一些数据,假如在auser和buser中间插入一个cuser,我们知道,a作为环路的头存放的是环路的尾的hash数据,所以从如果要插入到a和b中间,c就需要存放a的hash数据,并且b要清除a的hash数据存放c的hash数据
ai总结:(比我讲的清晰)
🧠 环状服务端结构的扩展逻辑(清晰版)
✅ 当前结构假设:
你有一个由多个服务端(称为 user 节点)组成的环状结构,每个节点:
监听一个特定 IP+端口;
保存一个“前驱节点”的 Hash 数据(可能用于验证完整性、链路溯源等);
指向下一个节点的地址(用作请求的下一个跳转目标);
✅ 扩展的目标:
将一个新的节点
cuser
插入到auser
和buser
之间。🧩 插入步骤:
1. 修改路由关系(即 IP/端口指向关系):
让
auser
的 下一个跳转地址 改为cuser
的监听地址;让
cuser
的 下一个跳转地址 指向原本的buser
;
buser
的下一个保持不变。2. 更新 Hash 数据:
原本
auser
存的是buser
的 hash 数据(因为它作为头,存的是尾);插入
cuser
后,cuser
应该保存auser
的 hash 数据;
buser
原本保存的是auser
的 hash 数据,现在应该清除这一条,改为存储cuser
的 hash 数据。✍️ 举个表格说明(前 → 后):
节点
原指向
原存 hash
修改后指向
修改后存 hash
auser
buser
buser
的 hash
cuser
buser
的 hash(可选,或删)
cuser
N/A
N/A
buser
auser
的 hash ✅
buser
-
auser
的 hash-
cuser
的 hash ✅🎯 最终效果:
环路结构维持稳定;
插入节点后,Hash 链也保持一致性;
每个节点仍然拥有自己应当保存的上一个节点的 Hash 信息;
并且数据传递或验证顺序没有断裂。
相关文章:
链式数据存储系统
目录 系统说明 服务端的模块设计 存储数据说明 服务端设计-程序入口(main) 数据库的连接-mysql包的编写 数据的加密-hash文件的编写 数据传递格式-proto文件的编写 具体实现方法-controller包的编写 日志的打印-logs包的编写 扩展服务端 系统说…...
《理解 Java 泛型中的通配符:extends 与 super 的使用场景》
大家好呀!👋 今天我们要聊一个让很多Java初学者头疼的话题——泛型通配符。别担心,我会用最通俗易懂的方式,带你彻底搞懂这个看似复杂的概念。准备好了吗?Let’s go! 🚀 一、为什么我们需要泛型通配符&…...
Scala 入门指南
Scala 入门指南 目录 简介环境搭建基础语法面向对象编程函数式编程集合模式匹配特质隐式转换并发编程与 Java 互操作最佳实践常见问题 简介 Scala 是一种多范式编程语言,结合了面向对象编程和函数式编程的特性。它运行在 JVM 上,与 Java 完全兼容&am…...
GESP2025年3月认证C++八级( 第一部分选择题(11-15))
杨辉三角形: #include <iostream> using namespace std;#define N 35 // 最多支持输出 35 行 int a[N]; // 一维数组,用于存储当前行的杨辉三角数int main() {int n;cin >> n; // 输入要输出的行数for (int i 0; i < n; i) {a[i] …...
Dynamics 365 Business Central Master Data Managerment Setup 主数据管理
#Dynamics 365 BC ERP# #Navision# 引言 在BC中除了之前有一个章节提到的用Code 同步资料, 也可以用内置主数据管理功能来同步资料。 Master Data Management Setup 设置Source Company 为 主要管理主数据的公司 Synchronization Tables 设置需要同步的Table 这…...
深入理解 requestIdleCallback 与大数据加载优化
使用 requestIdleCallback 优化大批量 DOM 操作 —— 以加载 100 万条数据为例 在前端开发中,如果你尝试在短时间内往 DOM 中添加大量元素,比如一次性插入 100 万条数据,页面极有可能卡顿甚至直接崩溃。为了解决这一性能问题,我们…...
【MySQL】索引运算与NULL值问题详解:索引字段应尽量 NOT NULL ,NULL值不能参与部分索引运算
索引运算与NULL值问题详解 不能参与的"部分索引运算"指什么? 这里的"部分索引运算"指的是索引列在某些特定操作或条件下无法被MySQL优化器有效利用的情况,特别是当字段包含NULL值时。主要包括以下几种情况: 1. 比较运…...
STM32 F103 标准库CH452A 4线 数码管驱动芯片显示数码管
公司生产测试需要统一去检查这版CH452A的好坏,网上找了一下没有现成可以用的4线CH452A的驱动程序,所以直接就肝了移植官方的51程序到stm32上面去,亲测可以使用!! 文末有代码 测试图片: 如你所见我测了一堆…...
Vue 和 Spring boot 和 Bean 不同生命周期
一、Vue 组件生命周期 父子组件生命周期顺序: 创建时: 父 beforeCreate → 父 created → 父 beforeMount → 子组件生命周期 → 父 mounted 更新时: 父 beforeUpdate → 子组件更新 → 父 updated。 销毁时: 父 beforeDestroy…...
期货数据API对接实战指南
一、期货数据接口概述 StockTV提供全球主要期货市场的实时行情与历史数据接口,覆盖以下品种: 商品期货:原油、黄金、白银、铜、天然气、农产品等金融期货:股指期货、国债期货特色品种:马棕油、铁矿石等区域特色期货 …...
Flask(2): 在windows系统上部署项目2
4 创建并激活虚拟环境 虚拟环境非常有用,可以将多个项目隔离开来。根据我看的教程,貌似以前有多种创建方式,后来官方自带了。目前我就用官方的方式。 4.1 创建虚拟环境 创建部署文件夹(假如目录为d:\project01),在命令提示…...
Java 中 Synchronized如何保证可见性
在 Java 多线程编程中,可见性问题是指一个线程对共享变量的修改,其他线程能够立即看到。如果没有适当的同步机制,可能会出现线程 A 修改了共享变量的值,但线程 B 仍然使用的是修改前的值,导致程序出现错误。synchroniz…...
33、Python单元测试与pytest框架从入门到精通
Python单元测试与pytest框架从入门到精通 引言 在软件开发领域,完善的测试体系是保证代码质量的生命线。本文将深入探讨Python单元测试的核心技术,从标准库unittest到功能强大的pytest框架,通过20个代码示例展示测试驱动开发(TD…...
mvccc
. MVCC (多版本并发控制) 概念: MVCC 是一种并发控制技术,用于在数据库中实现并发事务的读写操作,同时保证事务的隔离性。MVCC 的核心思想是,在数据库中维护数据的多个版本,每个事务在读取数据时,读取的是…...
ONLYOFFICE深度解锁系列.2-Excel 跨文件数据整合实战指南-可道云的另一个严重bug
一、为什么需要跨文件数据整合? 在企业办公和团队协作中,数据往往分散在不同文件中。传统复制粘贴方式存在三大痛点: 版本混乱:源数据更新后需反复同步 错误风险:手动操作易造成数据偏差 效率低下:多文件…...
如何对Flutter应用程序进行单元测试
Flutter单元测试完全指南:从基础到高级实践 面试求职资源 面试试题小程序:涵盖测试基础、Linux操作系统、MySQL数据库、Web功能测试、接口测试、APPium移动端测试、Python知识、Selenium自动化测试相关、性能测试、计算机网络知识、Jmeter、HR面试等内…...
多模态大语言模型arxiv论文略读(二十四)
VCoder: Versatile Vision Encoders for Multimodal Large Language Models ➡️ 论文标题:VCoder: Versatile Vision Encoders for Multimodal Large Language Models ➡️ 论文作者:Jitesh Jain, Jianwei Yang, Humphrey Shi ➡️ 研究机构: SHI Labs…...
前端根据后端返回的excel二进制文件流进行导出下载
需求 在vue2中,后端接口返回一个文件流,前端实现excel文件流导出下载功能。 解决方案 利用axios请求后端接口,把后端返回的blob文件流转为一个临时在线url,然后利用a标签实现导出下载功能。 具体实现步骤 1、封装axios请求拦…...
代码随想录刷题|Day20(组合总数,组合总数2、分割回文串)
回溯算法 Part02 组合总数 力扣题目链接 代码随想录链接 视频讲解 题目描述: 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你…...
【NLP 64、基于LLM的垂直领域【特定领域】问答方案】
找不到生活的答案,就先找自己 —— 25.4.17 一、垂直领域问答 1.特点 ① 问答内容通常不存在于通用语料 ② 回答内容对准确性要求较高,召回要求较低(可以转人工处理) ③ 拓展性和可控性(可以根据需求,增、…...
pytest自动化中关于使用fixture是否影响用例的独立性
第一个问题:难道使用fixture 会不会影响用例独立 ✅ 简单回答: 使用 fixture ≠ 不独立。 只要你的 fixture 是每次测试都能自己运行、自己产生数据的,那么测试用例依然是“逻辑独立”的。 ✅ 怎么判断 fixture 是否影响独立性?…...
嵌入式物联网开发(二)如何创建N32G45的Keil工程并实现串口打印
如何创建N32G45的Keil工程并实现串口打印 打开Keil IDE, 点击菜单栏Project按钮,选择Create New Project,并在弹出的对话框中选择工程目录,并取一个名字,这里取名bootloader:在弹出的对话框中选择我们的对应的芯片型号: N32G452R…...
基于Canal+Spring Boot+Kafka的MySQL数据变更实时监听实战指南
前期知识背景 binlog 什么是binlog 它记录了所有的DDL和DML(除 了数据查询语句)语句,以事件形式记录,还包含语句所执行的消耗的时间,MySQL 的二进制日志是事务安全型的。一般来说开启二进制日志大概会有1%的性能损耗。 binlog分类 MySQL Bi…...
Unity入门笔记(缘更)
内容来源SiKi学院的Luna’s Fantasy 文章目录 一、基础知识1.准备2.基础知识1.层级(Layer)2.轴心点3.预制体(Prefab)4.刚体组件(Rigidbody)5.碰撞器组件(BoxCollider) 二、代码1.移动 一、基础知识 1.准备 Unity安装: https://unity.cn 2.基础知识 1.层级(Layer…...
SpringAI+DeepSeek大模型应用开发——2 大模型应用开发架构
目录 2.大模型开发 2.1 模型部署 2.1.1 云服务-开放大模型API 2.1.2 本地部署 搜索模型 运行大模型 2.2 调用大模型 接口说明 提示词角色 编辑 会话记忆问题 2.3 大模型应用开发架构 2.3.1 技术架构 纯Prompt模式 FunctionCalling RAG检索增强 Fine-tuning …...
Prometheus thanos架构
Thanos 是一个用于扩展 Prometheus 的高可用性和长期存储的解决方案。它通过整合多个 Prometheus 实例,提供了全局查询、长期存储、以及高可用性的能力。Thanos 的架构主要由以下几个核心组件组成: 1. Sidecar 功能: Sidecar 是与每个 Prom…...
嵌入式软件--stm32 DAY 1
一、STM32概述 1.ARM内核 ARM是一家英国公司。后被日本软银收购。 RISC(精简指令集计算机) 产品:ARM架构处理器,相关外围组件的电路设计方案。 怎么卖 :知识产权授权 只卖方案不卖具体产品 买了如何用 拿到ARM的方案 设计产…...
【家政平台开发(53)】解锁家政平台高可用秘籍:负载均衡与架构部署
本【家政平台开发】专栏聚焦家政平台从 0 到 1 的全流程打造。从前期需求分析,剖析家政行业现状、挖掘用户需求与梳理功能要点,到系统设计阶段的架构选型、数据库构建,再到开发阶段各模块逐一实现。涵盖移动与 PC 端设计、接口开发及性能优化,测试阶段多维度保障平台质量,…...
Kotlin整数相除精度损失roundToInt
Kotlin整数相除精度损失roundToInt import kotlin.math.roundToIntfun main() {val a 0.0fval delta 0.1ffor (i in 0..10) {val r a i * deltaprintln("float${r} toInt${r.toInt()} (0.5 toInt)${(r 0.5).toInt()} round${Math.round(r)} roundToInt${r.roundToInt…...
RPCRT4!OSF_CCONNECTION::OSF_CCONNECTION函数分析之初始化中的u.ConnSendContext----RPC源代码分析
RPCRT4!OSF_CCONNECTION::OSF_CCONNECTION函数分析之初始化中的u.ConnSendContext 第一部分: 1: kd> kc # 00 RPCRT4!OSF_CCONNECTION::OSF_CCONNECTION 01 RPCRT4!OSF_CASSOCIATION::AllocateCCall 02 RPCRT4!OSF_BINDING_HANDLE::AllocateCCall 03 RPCRT4!OSF…...
visual studio 2022更改项目名称,灾难性故障(异常来自HRESULT)
系列文章目录 文章目录 系列文章目录前言一、具体步骤二、遇到的问题 前言 在visual studio 2022中,有时候遇到一个很大的工程,我们只是想改写工程名称,而又不想重建项目,如何实现呢? 比如将 Visual Studio 中的 Qt 工…...
用 Deepseek 写的uniapp油耗计算器
下面是一个基于 Uniapp 的油耗计算器实现,包含 Vue 组件和页面代码。 1. 创建页面文件 在 pages 目录下创建 fuel-calculator 页面: <!-- pages/fuel-calculator/fuel-calculator.vue --> <template><view class"container"…...
【KWDB创作者计划】_KwDB2.2.0深度实践:从存储引擎到物联网场景的多模数据库实战
简介 本文基于KwDB2.2.0最新版本,通过存储引擎原理、跨模计算实战和物联网场景落地三个维度,结合代码示例与实操案例,系统解析KwDB的分布式多模能力。从零搭建物联网数据平台,探索多模数据融合的创新价值,助你掌握新一…...
linux 学习 2.vim学习指南
vim vim是一款功能及其强大的编辑器,我们需要掌握其基本的操作才能数量的使用他 如果你想要功能更加丰富的vim获得代码补全之类的复杂功能,强烈建议你安装一下vimplus,可以参考这里vimplus 官方教程 建议学习的时候直接跟着教程一步步操作…...
深度学习在自动驾驶车辆车道检测中的应用
引言 自动驾驶技术是人工智能领域的一个前沿方向,而车道检测是实现自动驾驶的关键技术之一。通过识别和跟踪车道线,自动驾驶车辆能够保持在车道内行驶,提高行车安全。本文将详细介绍如何使用深度学习技术进行车道检测,并提供一个…...
深度学习-Torch框架-2
八、自动微分 自动微分模块torch.autograd负责自动计算张量操作的梯度,具有自动求导功能。自动微分模块是构成神经网络训练的必要模块,可以实现网络权重参数的更新,使得反向传播算法的实现变得简单而高效。 1. 基础概念 张量 Torch中一切皆…...
FlinkCDC初始化时报错“IllegalArgumentException: Unexpected input: “异常定位与原理分析
本篇是纯技术文章,是排查线上问题的真实记录。这个异常我在网上没搜到相同案例,所以特此记录下,方便后期回顾。 一、背景 利用FlinkCDC3.0动态监听数据库Schema变更的能力开发了一个生产数据库DDL语句变更审计告警的服务,这两天突然发现服务一直报错,经过4个小时的排查,…...
从代码学习深度学习 - Transformer PyTorch 版
文章目录 前言1. 位置编码(Positional Encoding)2. 多头注意力机制(Multi-Head Attention)3. 前馈网络与残差连接(Position-Wise FFN & AddNorm)3.1 基于位置的前馈网络(PositionWiseFFN)3.2 残差连接和层规范化(AddNorm)4. 编码器(Encoder)4.1 编码器块(Enco…...
多模态大语言模型arxiv论文略读(二十五)
ManipLLM: Embodied Multimodal Large Language Model for Object-Centric Robotic Manipulation ➡️ 论文标题:ManipLLM: Embodied Multimodal Large Language Model for Object-Centric Robotic Manipulation ➡️ 论文作者:Xiaoqi Li, Mingxu Zhang…...
LVS+Keepalived+dns高可用项目架构
一、搭建DNS服务 配置主服务器 1.修改核心配置文件 [rootDNS-master ~]# vim /etc/named.conf options { listen-on port 53 { 192.168.111.107;192.168.111.100; }; directory "/var/named"; }; zone "haha.com" IN { ty…...
C#日志辅助类(Log4Net)实现
一、Log4Net类库安装 在解决方案中项目上右键单击,选择“管理NuGet程序包”,在浏览窗口的搜索框输入log4net进行搜索,安装搜索出的第一项,如下图。 二、辅助类实现(Log4NetHelper) using log4net.Appender; using log4net.Config; using log4net.Layout; using log4net…...
【FFmpeg从入门到精通】第二章-FFmpeg工具使用基础
1 ffmpeg常用命令 ffmpeg在做音视频编解码时非常方便,所以在很多场景下转码使用的是ffmpeg,通过 ffmpeg --help可以看到 ffmpeg 常见的命令大概分为6个部分,具体如下。 ffmpeg信息查询部分公共操作参数部分文件主要操作参数部分视频操作参数…...
论文阅读:2022 ACL TruthfulQA: Measuring How Models Mimic Human Falsehoods
总目录 大模型安全相关研究:https://blog.csdn.net/WhiffeYF/article/details/142132328 TruthfulQA: Measuring How Models Mimic Human Falsehoods https://arxiv.org/pdf/2109.07958 https://www.doubao.com/chat/3130551217163266 https://github.com/sylin…...
基于C++(MFC)实现的文件管理系统
基于 MFC 的文件管理系统 第一章 题目解读与要求分析 1 实习题目 实现一个文件系统。 2 功能要求 界面上显示树形目录结构 a)根节点是“我的电脑” b)“我的电脑”下有几个盘符(C、D、E 等)就有几个子节点,递归…...
selenium 实现模拟登录中的滑块验证功能
用python在做数据采集过程中,经常需要用到模拟登录,经常遇到各种图片、文字甚至短信等验证,如果能通过脚本的方便实现验证,就可以自动帮我更高效地收集数据。Selenium 是一个开源的 Web 自动化测试工具,最初是为网站自…...
Oracle 19c部署之数据库软件安装(二)
在完成了Oracle Linux 9的初始化配置之后,我们准备安装Oracle 19c数据库软件。 Oracle数据库支持两种主要的安装方式:图形化安装和静默安装。这两种方法各有优缺点,选择哪种取决于你的具体需求、环境配置以及个人偏好。 图形化安装 图形化安…...
Paramiko 使用教程
目录 简介安装 Paramiko连接到远程服务器执行远程命令文件传输示例 简介 Paramiko 是一个基于 Python 的 SSH 客户端库,它提供了在网络上安全传输文件和执行远程命令的功能。本教程将介绍 Paramiko 的基本用法,包括连接到远程服务器、执行命令、文件传输…...
从EOF到REOF:如何用旋转经验正交函数提升时空数据分析精度?
目录 1. 基本概念与原理2. 应用场景3. 与传统EOF的区别4. 技术实现5. 其他领域中的“REOF”参考资料 REOF 的输入是多个地区在不同时间的气候数据(如温度或降雨量),它的作用是通过旋转计算找出这些数据中最主要的变化规律,输出则是…...
VS-Code创建Vue3项目
1 创建工程文件 创建一个做工程项目的文件夹 如:h5vue 2 cmd 进入文件 h5vue 3 输入如下命令 npm create vuelatest 也可以输入 npm create vitelatest 4 输入项目名称 项目名称:自已输入 回车 可以按键盘 a (全选) 回车: Playwright…...
JESD204B接收器核心实现和系统级关键细节
目录 1.通道偏移 2.弹性缓冲器的实现 3.接受延迟 4.RX端到端延迟 5.计算端到端延迟 6.实现可重复的延迟 1.通道偏移 JESD204B接收器核心已经过验证,其功能具有高达8个字节的通道到通道偏斜。 2.弹性缓冲器的实现 在JESD204B设备中,接收通道对齐弹性缓冲区是在分布式…...