问题描述
我正在使用 gRPC 开展一个项目,最近将其迁移到让每个服务都在 Docker 容器中运行(可能值得补充的是,我对容器化相当陌生)。今天,在将我的一个 Go 服务移动到容器中时,我遇到了这个错误,并且已经尝试解决它几个小时,但没有成功。我得到的错误是:
“rpc 错误:代码 = 未实现的描述 = 未知的服务 PowerEstimationServicePackage”
我在网上环顾四周,似乎该错误虽然没有很大的描述性,但通常意味着您的客户端正在进行的服务调用尚未被服务器在端口上注册。我现在已经花了几个小时查看我的代码,但终其一生都无法弄清楚为什么我会收到错误 - 据我所知,我已经正确注册了它!
另外,当我在本地运行 Go 服务时,它运行得很好!这让我相信这个问题与我在 Docker 中运行时的端口绑定有关。我觉得我对 Docker 如何处理网络有一个合理的理解,但很可能在这里忽略了一些东西。
相关客户端代码(本地运行)为:
const {
addrEstimationSP = "127.0.0.1:50101"
}
// Use this to implement the power estimation service routing
type estimationServer struct {
serverPB.UnimplementedPowerEstimationServicesServer
}
func main {
// Create a connection over the specified tcp port
callCounter := interceptors.ClientMetricStruct{}
connestimationSP := CreateInsecureServerConnection(addrEstimationSP,timeoutDuration,callCounter.ClientMetrics)
/* Create the client and pass the connection made above to it. After the client has been
created,we create the gRPC request */
InfoLogger.Println("Creating clients")
clientEstimationSP := estimationPB.NewPowerEstimationServicePackageClient(connestimationSP)
DebugLogger.Println("Succesfully created the clients")
requestMessageEstimationSP := estimationPB.ServicePackageRequestMessage{
InputFile: INPUTfilename,ModelType: estimationPB.ModelTypeEnum_OPENWATER,}
estimationContext,_ := context.WithTimeout(context.Background(),callTimeoutDuration)
responseEstimationSP,errEstimationSP := clientEstimationSP.PowerEstimatorService(estimationContext,&requestMessageEstimationSP)
if errEstimationSP != nil {
ErrorLogger.Println("Failed to make EstimationSP service call: ")
ErrorLogger.Println(errEstimationSP)
} else {
DebugLogger.Println("Succesfully made service call to GoLang EstimationSP.")
connestimationSP.Close()
}
responseMessage := serverPB.PowerEstimationResponse{
PowerEstimate: responseEstimationSP.PowerEstimate,}
}
,以及服务器端的相关代码(这是我试图迁移到 Docker 中的服务)是:
var (
// Addresses
addrMyself = os.Getenv("POWERESTIMATIONHOST") + ":50101"
)
// server is used to implement PowerEstimationServicePackage
type server struct {
serverPB.UnimplementedPowerEstimationServicePackageServer
}
func main() {
InfoLogger.Println("Started GoLang Aggregator")
// Create a listener on the specified tcp port
listener,err := net.Listen("tcp",addrMyself)
if err != nil {
ErrorLogger.Fatalf("Failed to listen on port %v: \n%v",addrMyself,err)
}
InfoLogger.Println("Listeneing on port: ",addrMyself)
// Create a gRPC server object
estimationServer := grpc.NewServer()
// Attach the power estimation service to the server
serverPB.RegisterPowerEstimationServicePackageServer(estimationServer,&server{})
// Start the server
if err := estimationServer.Serve(listener); err != nil {
ErrorLogger.Fatalf("Failed to expose service: \n%v",err)
}
}
func (s *server) PowerEstimatorService(ctx context.Context,request *serverPB.ServicePackageRequestMessage) (*serverPB.EstimateResponseMessage,error) {
...
}
客户端能够创建到服务器的连接,但在进行 clientEstimationPB.PowerEstimatorService(...) 调用时出错。该服务在 Docker 网络内运行,因此为其地址传递环境变量。 Dockerfile 和 Docker-compose 文件如下所示:
Dockerfile:
FROM golang:alpine
# Install git.
# Git is required for fetching the dependencies.
RUN apk add --no-cache git
workdir $GOPATH/src/github.com/nicholasbunn/masters/
# Create a program logs folder in the service directory
RUN mkdir ./program\ logs
RUN mkdir -p $GOPATH/src/github.com/nicholasbunn/masters/src/powerEstimationSP
copY /src/powerEstimationSP/go.mod ./src/powerEstimationSP
copY /src/powerEstimationSP/go.sum ./src/powerEstimationSP
# copy over contents into image
copY src/powerEstimationSP/interceptors/ ./src/powerEstimationSP/interceptors
copY src/powerEstimationSP/proto/ ./src/powerEstimationSP/proto
copY certification/ certification
copY src/powerEstimationSP/powerEstimationSP.go ./src/powerEstimationSP
copY src/powerEstimationSP/powerEstimationSP_test.go ./src/powerEstimationSP
copY src/fetchDataService/proto src/fetchDataService/proto
copY src/prepareDataService/proto src/prepareDataService/proto
copY src/estimateService/proto src/estimateService/proto
workdir $GOPATH/src/github.com/nicholasbunn/masters//src/powerEstimationSP/
# Fetch the dependecies
RUN go mod tidy
# Build the binary. for grpc gateway
RUN go build -o ./powerEstimationSP .
workdir $GOPATH/src/github.com/nicholasbunn/masters/
EXPOSE 50101
ENTRYPOINT ["./src/powerEstimationSP/powerEstimationSP"]
Docker-compose.yaml:
version: "3.8"
services:
...
powerestimationsp:
build:
context: .
dockerfile: src/powerEstimationSP/Dockerfile
environment:
POWERESTIMATIONHOST: powerestimationsp
FETCHHOST: fetchservice
PREPAREHOST: prepareservice
ESTIMATEHOST: estimateservice
PROMETHEUSHOST: prometheus
image: power_estimation_sp
networks:
- southernOcean
ports:
- 50101:50101
networks:
southernOcean:
我觉得解决这个问题真的很简单,可能只是我这边的一个小误会,我刚刚经历了一些阴天,所以希望有一双新的眼睛可以在这里提供帮助。
谢谢!
解决方法
我今天下午解决了问题,打算把它扔在这里,以防其他人无法用其他资源解决问题。
问题实际上来自我的 go 模块,而不是 Docker 或我的代码。我正在处理一个未发布的代码分支,在那里我更改了 proto 文件(我实际上只是将小写字母更改为大写字母以保持一致的语义),但是我的 go.mod 文件正在提取旧版本的proto 与较新的不兼容。客户端在本地机器上运行时使用较新的一个,而服务器在本地运行时使用较新的一个,这就是它在 Docker 之外工作的原因。在我的 Dockerfile(上面)中,我运行了“RUN go mod tidy”,它从我的主分支中提取了旧的 proto 文件并注册了一个不同名称的服务,这就是我收到这个错误的原因。
无论如何,很高兴现在一切都整理好了!希望这可以帮助其他人。