У меня возникла проблема связи между клиентом и сервером GRPC после переключения пространства имен сети в моем проекте Golang. Чтобы увеличить эту проблему, я изменил пример программы GRPC Hello World соответственно с той же самой проблемой в результате. Существуют ли какие-либо известные ограничения при использовании GRPC после переключения пространства имен в приложении golang?
Я читал о проблемах, связанных с переключением пространства имен в Golang, но я думаю, это зависит от поведения GRPC, может ли это привести к проблеме или нет.
Создает ли клиент GRPC дополнительные программы? Такая процедура не обязательно будет выполняться в том же пространстве имен, что и исходная программа, заблокировавшая поток из-за переключателя пространства имен.
Я предполагаю, что сервер GRPC порождает процедуры для каждого клиента, но создает ли он сокет в исходной программе перед созданием нового? Этот вопрос также связан с тем, что порожденные программы не должны выполняться в том же пространстве имен, где был вызван grpc.Serve () (для переключения пространства имен требуется runtime.LockOSThread ()).
Связь работает, если сервер и клиент запущены в соответствующих пространствах имен (ip netns exec ...), но не работает, если переключение пространства имен выполняется внутри клиента. Связь также работает, когда переключение пространства имен выполняется внутри сервера, поэтому проблема должна быть в клиенте.
greeter_client / main.go:
package main
import (
"fmt"
"log"
"os"
"runtime"
"syscall"
"time"
pb "grpctest/helloworld/helloworld"
"github.com/vishvananda/netns"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
const (
defaultName = "world"
defaultAddress = "localhost:50051"
nsEnv = "NAMESPACE"
addressEnv = "ADDRESS"
blockEnv = "DIALBLOCK"
)
func main() {
fmt.Printf("* Client thread id before runtime.LockOSThread(): %d\n", syscall.Gettid())
runtime.LockOSThread()
defer runtime.UnlockOSThread()
fmt.Printf("* Client thread id after runtime.LockOSThread(): %d\n", syscall.Gettid())
var dialOpts []grpc.DialOption
dialOpts = append(dialOpts, grpc.WithInsecure())
_, ok := os.LookupEnv(blockEnv)
if ok == true {
dialOpts = append(dialOpts, grpc.WithBlock())
fmt.Printf("* Dial in blocked mode\n")
} else {
fmt.Printf("* Dial in unblocked mode\n")
}
address, ok := os.LookupEnv(addressEnv)
if ok == false {
address = defaultAddress
}
fmt.Printf("* Talk to server at %s\n", address)
var origns netns.NsHandle
namespace, ok := os.LookupEnv(nsEnv)
if ok {
fmt.Printf("* Switch namespace to %s\n", namespace)
origns, err := netns.Get()
if err != nil {
log.Fatal("failed to get current namespace")
}
defer origns.Close()
newns, err := netns.GetFromName(namespace)
if err != nil {
log.Fatalf("failed to get new namespace: %s", namespace)
}
err = netns.Set(newns)
if err != nil {
log.Fatalf("failed to set new namespace: %s", namespace)
}
defer newns.Close()
}
fmt.Printf("* Client thread id before grpc.Dial(): %d\n", syscall.Gettid())
// Set up a connection to the server.
conn, err := grpc.Dial(address, dialOpts...)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
fmt.Printf("* Client thread id before pb.NewGreeterClient(): %d\n", syscall.Gettid())
c := pb.NewGreeterClient(conn)
fmt.Printf("* Client thread id after pb.NewGreeterClient(): %d\n", syscall.Gettid())
// Contact the server and print out its response.
name := defaultName
if len(os.Args) > 1 {
name = os.Args[1]
}
r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
if err != nil {
fmt.Printf("could not greet: %v", err)
select {}
log.Fatalf("could not greet: %v", err)
}
fmt.Printf("* Client thread id after c.SayHello(): %d\n", syscall.Gettid())
log.Printf("Greeting: %s", r.Message)
time.Sleep(5 * time.Second)
if namespace != "" {
netns.Set(origns)
}
fmt.Printf("* Client thread id at exit: %d\n", syscall.Gettid())
}
greeter_server / main.go:
package main
import (
"fmt"
"log"
"net"
"os"
"runtime"
"syscall"
pb "grpctest/helloworld/helloworld"
"github.com/vishvananda/netns"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
const (
port = ":50051"
nsEnv = "NAMESPACE"
)
// server is used to implement helloworld.GreeterServer.
type server struct{}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
fmt.Printf("* RPC call server thread id: %d\n", syscall.Gettid())
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
func main() {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var origns netns.NsHandle
namespace := os.Getenv(nsEnv)
if namespace != "" {
fmt.Printf("* Switch namespace to %s\n", namespace)
origns, err := netns.Get()
if err != nil {
log.Fatal("failed to get current namespace")
}
defer origns.Close()
newns, err := netns.GetFromName(namespace)
if err != nil {
log.Fatalf("failed to get new namespace: %s", namespace)
}
err = netns.Set(newns)
if err != nil {
log.Fatalf("failed to set new namespace: %s", namespace)
}
defer newns.Close()
}
fmt.Printf("* Main server thread id: %d\n", syscall.Gettid())
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
// Register reflection service on gRPC server.
reflection.Register(s)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
if namespace != "" {
netns.Set(origns)
}
fmt.Printf("* Main server exits (thread id: %d)\n", syscall.Gettid())
}