}
’ http://router_server/ d b n a m e / db_name/ dbname/space_name
复制代码
请求中的field
与space的field
一一对应,如果field类型是向量,通过feature:[xxx, xxx]
写入,并且维数要与space中该field对应
对应调用handleUpdateDoc()
方法,这个方法通过传入的http请求初始化一个UpdateRequest
,这里需要注意的是为UpdateRequest
设置PKey这里
URLParamID = “_id”
args.Doc.PKey = params.ByName(URLParamID)
复制代码
如果传入参数有 _id
,那么PKey就等于传入的_id
,否则为""(空)。我们这里是插入,不需要传入_id
,所以_id
为空。而当调用查询之类接口时,会传入_id
。插入新物料时,后续的SetDocsField(docs)
方法中会为插入的物料自动生成一个id,方法大概就是自增,这里不深究,只要知道router会为新插入的物料生成唯一id就行了。
完成后,调用updateDoc()
函数处理初始化的UpdateRequest
。
updateDoc()
updateDoc
函数通过传入的 pb UpdateRequest
构建一个RouterRequest
并发送给partition
,步骤分为:
-
装填
RouterRequest
-
发送请求给partition
RouterRequest的结构
RouterRequest
结构如下
-
head
里是请求的基本信息:包括用户名,密码,目标dbname以及spacename -
md
是一个map,记录了请求的方法和id -
key为
HandlerType
时,value表示该请求对应的方法(增删改查) -
key为
MessageID
时,value表示本条请求的唯一id -
docs
是本条请求的物料信息 -
space
是本条请求对应的space的信息 -
sendMap
的key是partitionID,value是要发给这个partition的信息,其中items
包含了doc信息,其他别的借口时候再补充
updateDoc
关键的步骤代码如下:
request := client.NewRouterRequest(ctx, docService.client)
request.SetMsgID()
.SetMethod(client.ReplaceDocHandler)
.SetHead(args.Head)
.SetSpace()
.SetDocs(docs)
.SetDocsField()
.PartitionDocs()
items := request.Execute()
复制代码
装填
首先通过一串函数装填RouterRequest
-
SetMsgID()
为本条request生成唯一id,填入md[MessageID]
。 -
SetMethod()
填写md[HandlerType]
,表明本request是一条更新请求 -
SetSpace()
填写request的space
字段,获取方式是先从router本地缓存找,找不到就去etcd里拿 -
SetDocs()
填写request的docs
数组 -
SetDocsField()
为docs
数组里每一个doc
填写PKey
和Fields
字段, -
创建时,PKey为空,
generateUUID
为doc自动生成一个id -
这个函数在Fields里加了一个PKey
-
PartitionDocs()
填充sendMap
字段,就是把docs
字段里的doc都加进sendmap[id]
的items
数组里 -
id是根据doc的PKey做哈希算出来的,这决定了该doc存在哪个partition上
RouterRequest
装填完毕,下一步就是发送了
router发送给partition
发送是通过RouterRequest
的Execute()
函数
所有要给partition
发送请求的接口最后都会落在这个函数上,这个函数分为两步
-
构建
rpcClient
-
正式发送请求
先来看构建rpcClient
上一步中,我们已经确认了要发往每个partition
的数据,存在RouterRequest
的sendMap
成员里
首先通过partitionID
获取对应partition的信息,包括机器地址等信息。获取的方式就是先从本地缓存中取,如果没有就从ETCD
里拿,这里介绍一下router的本地缓存,router本地缓存如下图所示:
缓存相关都在router.client.masterclient.cliCache
下面
-
router.client.masterclient.cliCache
本身继承了sync.map
,存储了nodeID
对应的rpcClient
,避免多次创建 -
partitionCache
里存放了partitionID
对应的partition相关信息,包括机器节点ID(没有地址) -
serverCache
里存放了NodeID
对应的机器信息,包括IP、端口等 -
如果在缓存里没有找到,router会访问
etcd
获取相关数据,router.client.masterclient.store
就是etcd相关
继续回到构建rpcClient
,构建它的关键就是填写ip和端口。从缓存和etcd拿到nodeID
后,调用GetOrCreateRPCClient(ctx, nodeID)
,同样从缓存和etcd拿到nodeID对应的具体机器信息(地址、端口),并构造一个rpcClient
,完毕
构建好以后,发送就完事了,远程调用的方法是UnaryHandler
partition处理请求
方法入口是UnaryHandler.Execute
,根据请求是插入,调用update(ctx, store, req.Items)
这里贴一段代码,后面进入gamma引擎了,这里不做研究
func update(ctx context.Context, store PartitionStore, items []*vearchpb.Item) {
item := items[0]
docGamma := &gamma.Doc{Fields: item.Doc.Fields}
docBytes := docGamma.Serialize()
docCmd := &vearchpb.DocCmd{Type: vearchpb.OpType_REPLACE, Doc: docBytes}
if err := store.Write(ctx, docCmd, nil, nil); err != nil {
log.Error(“Add doc failed, err: [%s]”, err.Error())
item.Err = vearchpb.NewError(vearchpb.ErrorEnum_INTERNAL_ERROR, err).GetError()
} else {
item.Doc.Fields = nil
}
}
复制代码
批量插入
====
批量插入的请求如下,每一个插入的物料要两行
-
第一行固定 {“index”:{“_id”:“xxx”}} \n,_id可以为空,router会自动生成唯一id
-
第二行里是物料每一列的值
-
每一行用结尾需要‘\n’分开
curl -H “content-type: application/json” -XPOST -d’
{“index”: {“_id”: “v1”}}\n
{“field1”: “value”, “field2”: {“feature”: []}}\n
{“index”: {“_id”: “v2”}}\n
{“field1”: “value”, “field2”: {“feature”: []}}\n
’ http://router_server/ d b n a m e / db_name/ dbname/space_name/_bulk
复制代码
router中对应的处理方法是handleBulk()
,该方法通过http请求初始化一个BulkRequest
,主要就是解析请求中每一个doc
,把他们填入BulkRequest.docs
里,BulkRequest
结构如下:
message BulkRequest{
RequestHead head = 1;
repeated Document docs = 4;
}
复制代码
填充完后,调用bulk()
方法填充一个RouterRequest
并发送,步骤和单条插入里的updateDoc()
方法类似。
reply := handler.docService.bulk(ctx, args)
复制代码
与单条插入不同的是,批量插入的rpc请求中call的方法是BatchHandler
,ps接到router批量插入请求,调用对应的处理方法是bulk()
查询
==
查询接口示例如下
curl -H “content-type: application/json” -XPOST -d’
{
“query”: {
“sum”: [{
“field”: “field_name”,
“feature”: [0.1, 0.2, 0.3, 0.4, 0.5],
“min_score”: 0.9,
“boost”: 0.5
}],
“filter”: [{
“range”: {
“field_name”: {
“gte”: 160,
“lte”: 180
}
}
},
{
“term”: {
“field_name”: [“100”, “200”, “300”],
“operator”: “or”
}
}]
},
“retrieval_param”: {
“nprobe”: 20
总结
大型分布式系统犹如一个生命,系统中各个服务犹如骨骼,其中的数据犹如血液,而Kafka犹如经络,串联整个系统。这份Kafka源码笔记通过大量的设计图展示、代码分析、示例分享,把Kafka的实现脉络展示在读者面前,帮助读者更好地研读Kafka代码。
麻烦帮忙转发一下这篇文章+关注我
加入社区:https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0
“field_name”: [“100”, “200”, “300”],
“operator”: “or”
}
}]
},
“retrieval_param”: {
“nprobe”: 20
总结
大型分布式系统犹如一个生命,系统中各个服务犹如骨骼,其中的数据犹如血液,而Kafka犹如经络,串联整个系统。这份Kafka源码笔记通过大量的设计图展示、代码分析、示例分享,把Kafka的实现脉络展示在读者面前,帮助读者更好地研读Kafka代码。
麻烦帮忙转发一下这篇文章+关注我
[外链图片转存中…(img-6sy7tZtH-1725747032504)]
加入社区:https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0