准备

  1. 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())
}
  1. 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

通过服务发现,