一、手写动态链接
Milvus 代码库分为了 C 和 Go 两个部分,Go 部分负责系统主体架构、分布式系统、存储/查询链路等,C 部分负责查询、索引引擎专注于单机场景下的高性能,两者之间通过 cgo 接口调用。
为了维护两种语言的代码,就需要加入两种语言的生态。Go 作为一个年轻、现代的语言,开箱自带包管理、自动化测试框架和丰富的标准库;而 C 就走向了另一个极端,虽然有极致的性能和可控的内存管理,但生态过于碎片化。幸好在 build system 领域,CMake 有成为事实标准的趋势。
Milvus 很自然地选择 CMake 作为 C 构建系统,通过编写 CMakeLists.txt 描述要生成的 library 和 headers,而 Go 则通过 cgo 接口链接到相应的 library,在早期版本里是这样写的:
代码语言:javascript复制/*
#cgo CFLAGS: -I${SRCDIR}/../core/output/include
#cgo darwin LDFLAGS: -L${SRCDIR}/../core/output/lib -lmilvus_segcore -Wl,-rpath,"${SRCDIR}/../core/output/lib"
#cgo linux LDFLAGS: -L${SRCDIR}/../core/output/lib -lmilvus_segcore -Wl,-rpath=${SRCDIR}/../core/output/lib
#include "segcore/collection_c.h"
#include "common/type_c.h"
#include "segcore/segment_c.h"
*/
import "C"
import (
"errors"
"fmt"
"unsafe"
"github.com/milvus-io/milvus/internal/util/cgoconverter"
)
不难发现这样写有几个问题:
1. 不同操作系统需要指定不同的编译参数
2. hard code 库文件路径耦合严重,不利于维护
以上两个问题相对容易解决,在使用第三方 go library 时,问题会更难解决,例如 Milvus 使用了 https://github.com/tecbot/gorocksdb[1] 作为 Go 的 rocksdb 接口。
gorocksdb 需要修改 CGO 的一系列 go env 才能编译成功,究其原因也是因为 gorocksdb 在使用 rocksdb library 时没有指定 library 和 header 的路径,必须在系统路径中才能找到 librocksdb 。
代码语言:javascript复制package gorocksdb
// #include "stdlib.h"
// #include "rocksdb/c.h"
import "C"
import (
"reflect"
"unsafe"
)
这就导致了 Milvus 的编译脚本中需要 hack go env 才能顺利编译:
代码语言:javascript复制go env -w CGO_CFLAGS="-I${OUTPUT_LIB}/include"
ldflags=""
if [ -f "${OUTPUT_LIB}/lib/librocksdb.a" ]; then
case "${unameOut}" in
Linux*) ldflags="-L${OUTPUT_LIB}/lib -l:librocksdb.a -lstdc -lm -lz";;
Darwin*) ldflags="-L${OUTPUT_LIB}/lib -lrocksdb -stdlib=libc -lm -lz -lbz2 -ldl";;
*) echo "UNKNOWN:${unameOut}"; exit 0;
esac
else
case "${unameOut}" in
Linux*) ldflags="-L${OUTPUT_LIB}/lib64 -l:librocksdb.a -lstdc -lm -lz";;
Darwin*) ldflags="-L${OUTPUT_LIB}/lib64 -lrocksdb -stdlib=libc -lm -lz -lbz2 -ldl";;
*) echo "UNKNOWN:${unameOut}" ; exit 0;
esac
fi
if [ "$MSYSTEM" == "MINGW64" ] ; then
ldflags="-L${OUTPUT_LIB}/lib -lrocksdb -lstdc -lm -lz -lshlwapi -lrpcrt4"
fi
if [[ $(arch) == 'arm64' ]]; then
go env -w GOARCH=arm64
fi
go env -w CGO_LDFLAGS="$ldflags" && GO111MODULE=on
go get github.com/tecbot/gorocksdb
二、pkg-config 链接管理
早期的做法也不是不能用