3 minutes
Network_grpc
准备
- client
package main
import (
	"context"
	"log"
	"time"
	"google.golang.org/grpc"
	pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
const (
	address     = "localhost:50051"
	defaultName = "world"
)
func main() {
	// Set up a connection to the server.
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
	defer cancel()
	conn, err := grpc.DialContext(ctx, address, grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(conn)
	// Contact the server and print out its response.
	success(c)
}
func success(c pb.GreeterClient) {
	name := defaultName
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	log.Printf("Greeting: %s", r.GetMessage())
}
- server
package main
import (
	"context"
	"log"
	"net"
	"google.golang.org/grpc"
	pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
const (
	port = ":50051"
)
// server is used to implement helloworld.GreeterServer.
type server struct {
	pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	log.Printf("Received: %v", in.GetName())
	return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func main() {
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, &server{})
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}
成功的响应
先启动server, 在启动client, 正常收发没问题
- client:
2020/09/19 13:14:19 Greeting: Hello world
- server
2020/09/19 13:14:19 Received: world
直连场景下, DialContext连接 ip不存在
只启动client, 不启动server
- client
2020/09/19 13:17:52 did not connect: context deadline exceeded
很明显, 通过context 能够及时得到结果, 连接层抽象直接失败
直连场景下, DialContext 成功, 但是 方法调用过程ip不存在
修改client端 success 代码替换成调用 notFound(), 如下:
func notFound(c pb.GreeterClient) {
	log.Println("begin sleep")
	time.Sleep(time.Second * 5)  // 等待一段时间来保证server关闭
	log.Println("after sleep")
	name := defaultName
	ctx, cancel := context.WithTimeout(context.Background(), time.Second) 
	defer cancel()
	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	log.Printf("Greeting: %s", r.GetMessage())
}
先启动server, 再启动client, 看到 begin sleep, 立即关闭server. 得到:
- client:
2020/09/19 13:25:48 begin sleep
2020/09/19 13:25:53 after sleep
2020/09/19 13:25:53 could not greet: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial tcp 127.0.0.1:50051: connect: connection refused"
可以发现, client 也理解失败了
直连场景下, DialContext 成功, 但是 方法调用过程ip 不是 http 协议: tcp
添加 tcp server:
package main
import (
	"log"
	"net"
	"time"
)
func main() {
	var l net.Listener
	var err error
	l, err = net.Listen("tcp", ":50051")
	if err != nil {
		log.Fatal("listen tcp failed")
	}
	defer l.Close()
	log.Println("accept connection iterative")
	for {
		conn, err := l.Accept()
		if err != nil {
			log.Println("accept failed")
			time.Sleep(5 * time.Second)
			continue
		}
		go handle(conn)
	}
}
func handle(conn net.Conn) {
	defer conn.Close()
	for {
		//io.Copy(conn, conn)  
		log.Println("handler before")
		time.Sleep(10 * time.Second)
		log.Println("handler after")
		return
	}
}
这里用的是tcp协议, server 可以返回响应, 也可以进行连接数据的copy.
- 超时的case
client直接超时, client 日志如下:
2020/09/19 21:12:16 did not connect: context deadline exceeded
- 数据拷贝, 无效数据的场景
client 会重试 请求 server 三次, 最终超时; server端打印
2020/09/19 21:15:16 accept
2020/09/19 21:15:16 handler before
2020/09/19 21:15:16 handler after
2020/09/19 21:15:17 accept
2020/09/19 21:15:17 handler before
2020/09/19 21:15:17 handler after
2020/09/19 21:15:18 accept
2020/09/19 21:15:18 handler before
2020/09/19 21:15:18 handler after
直连场景下, DialContext 成功, 但是 方法调用过程ip 是 http 协议, 不是 http2
编辑http server:
package main
import (
	"net/http"
	"time"
)
func ServerHttp(w http.ResponseWriter, r *http.Request) {
	time.Sleep(time.Second * 10)
}
func main() {
	http.HandleFunc("/", ServerHttp)
	http.ListenAndServe(":50051", nil)
}
直连场景下, DialContext 成功, 但是 方法调用过程ip 是 http2 协议, 不是 grpc
添加 http2 server,
package main
import (
	"net/http"
	"golang.org/x/net/http2"
)
func main() {
	var srv *http.Server
	srv.Addr = ":50051"
	http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
		writer.Write([]byte("hello func"))
	})
	http2.ConfigureServer(srv, &http2.Server{})
	srv.ListenAndServe()
}
client 依旧 deadline
2020/09/19 21:42:31 did not connect: context deadline exceeded