If you just want to play with go-Web standalone development you can refer to the Gin project framework

I. Data transfer between microservices

  • 1. In microservices, data can be exchanged not only through input and return parameters, but also through metadata

  • 2. Define a simple proto file

    syntax = "proto3";
    
    option go_package = ".; proto";
    
    service HelloWorld {
      rpc SayHello(HelloRequest) returns(HelloResponse);
    }
    
    message HelloRequest {
      string name = 1;
    }
    
    message HelloResponse {
      string message = 1;
    }
    Copy the code
  • 3, the server side defines the interface client to pass the metadata data

    type server struct{}func (s *server) SayHello(ctx context.Context, in *proto.HelloRequest) (*proto.HelloResponse, error) {
    	md, ok := metadata.FromIncomingContext(ctx)
    	if! ok { log.Fatalf("Failed to obtain metadata data")}if nameSlice, ok := md["name"]; ok {
    		fmt.Println("Acquired data", nameSlice[0])}return &proto.HelloResponse{
    		Message: "hello" + in.Name,
    	}, nil
    }
    func main(a) {
    	listen, err := net.Listen("tcp".": 9000")
    	iferr ! =nil {
    		log.Fatalf("Listening port error :" + err.Error())
    	}
    	service := grpc.NewServer()
    	proto.RegisterHelloWorldServer(service, &server{})
    	err = service.Serve(listen)
    	iferr ! =nil {
    		fmt.Println(err.Error())
    	}
    }
    Copy the code
  • 4, the client defines the metadata to pass parameters to the server

    func main(a) {
    	conn, err := grpc.Dial("localhost:9000", grpc.WithInsecure())
    	iferr ! =nil {
    		fmt.Println(err.Error())
    		return
    	}
    	defer conn.Close()
    
    	c := proto.NewHelloWorldClient(conn)
    	// Customize the metadata parameters to pass
    	md := metadata.New(map[string]string{
    		"name":     "admin"."password": "123456",
    	})
    	ctx := metadata.NewOutgoingContext(context.Background(), md)
    	response, err := c.SayHello(ctx, &proto.HelloRequest{
    		Name: "admin",})iferr ! =nil {
    		fmt.Println(err.Error())
    		return
    	}
    	fmt.Println("Data returned by the server" + response.Message)
    }
    Copy the code

Use of interceptorsinterceptor

  • 1, the so-called interceptor is in the process of network request, interception request and response, the following first configure a simple protobuf network request, and then add interceptor on this basis, to intercept the data sent by the client to the server, can also be used to count the interface access duration

    • 1.protofile
    syntax = "proto3";
    
    option go_package = ".; proto";
    
    service HelloWorld {
      rpc SayHello(HelloRequest) returns(HelloResponse);
    }
    
    message HelloRequest {
      string name = 1;
    }
    
    message HelloResponse {
      string message = 1;
    }
    Copy the code
    • 2. Use command generationgofile
    protoc -I=. --go_out=plugins=grpc,paths=source_relative:. helloWorld.proto
    Copy the code
    • 3. Server-side code

      type server struct{}func (s *server) SayHello(ctx context.Context, in *proto.HelloRequest) (*proto.HelloResponse, error) {
      	return &proto.HelloResponse{
      		Message: "hello" + in.Name,
      	}, nil
      }
      
      func main(a) {
      	listen, err := net.Listen("tcp".": 9000")
      	iferr ! =nil {
      		log.Fatalf("Listening port error :" + err.Error())
      	}
      	service := grpc.NewServer()
      	proto.RegisterHelloWorldServer(service, &server{})
      	err = service.Serve(listen)
      	iferr ! =nil {
      		fmt.Println(err.Error())
      	}
      }
      Copy the code
    • 4. Client

      func main(a) {
      	conn, err := grpc.Dial("localhost:9000", grpc.WithInsecure())
      	iferr ! =nil {
      		fmt.Println(err.Error())
      		return
      	}
      	defer conn.Close()
      
      	c := proto.NewHelloWorldClient(conn)
      	response, err := c.SayHello(context.Background(), &proto.HelloRequest{
      		Name: "admin",})iferr ! =nil {
      		fmt.Println(err.Error())
      		return
      	}
      	fmt.Println("Data returned by the server" + response.Message)
      }
      
      Copy the code
  • 2, on the server side to define interceptors to intercept requests, the client side remains unchanged, start the client and server side, observe the server side

    func main(a) {
    	listen, err := net.Listen("tcp".": 9000")
    	iferr ! =nil {
    		log.Fatalf("Listening port error :" + err.Error())
    	}
    	// Define interceptors
    	interceptor := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
    		fmt.Println("A new request was received")
    		return handler(ctx, req)
    	}
    	// Use interceptor
    	opt := grpc.UnaryInterceptor(interceptor)
    	service := grpc.NewServer(opt)
    	
    	proto.RegisterHelloWorldServer(service, &server{})
    	err = service.Serve(listen)
    	iferr ! =nil {
    		fmt.Println(err.Error())
    	}
    }
    Copy the code
  • Interception prints information before and after a request

    .// Define interceptors
    interceptor := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
      fmt.Println("Print before request")
      res, err := handler(ctx, req)
      fmt.Println("Request complete print")
      return res, err
    }
    // Use interceptor
    opt := grpc.UnaryInterceptor(interceptor)
    service := grpc.NewServer(opt)
    ...
    Copy the code
  • 4. Use of client interceptors

    func main(a) {
    	// Define interceptors
    	interceptor := func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ... grpc.CallOption) error {
    		start := time.Now()
    		err := invoker(ctx, method, req, reply, cc, opts...)
    		fmt.Printf("Time consuming: % s \ n", time.Since(start))
    		return err
    	}
    	// Use interceptor
    	opt := grpc.WithUnaryInterceptor(interceptor)
    	conn, err := grpc.Dial("localhost:9000", grpc.WithInsecure(), opt)
    	iferr ! =nil {
    		fmt.Println(err.Error())
    		return
    	}
    	defer conn.Close()
    
    	c := proto.NewHelloWorldClient(conn)
    	response, err := c.SayHello(context.Background(), &proto.HelloRequest{
    		Name: "admin",})iferr ! =nil {
    		fmt.Println(err.Error())
    		return
    	}
    	fmt.Println("Data returned by the server" + response.Message)
    }
    Copy the code

Use interceptors andmetadataTo do licensing between microservices

  • 1. Here we are authorized to make a simple function similar to the user login can access, imagine the client as a browser, imagine the server side as we are familiar with gIN-Web development

  • 2. Simple demo is the same as above, just add interceptor and metadata operations on top of it

  • 3. Pass the user name and password to the server in the interceptor on the client side

    func main(a) {
    	// Define interceptors
    	interceptor := func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ... grpc.CallOption) error {
    		start := time.Now()
    		fmt.Printf("Time consuming: % s \ n", time.Since(start))
    		// Use metadata to pass data
    		md := metadata.New(map[string]string{
    			"username": "admin"."password": "123456",
    		})
    		ctx = metadata.NewOutgoingContext(context.Background(), md)
    		err := invoker(ctx, method, req, reply, cc, opts...)
    		return err
    	}
    	// Use interceptor
    	opt := grpc.WithUnaryInterceptor(interceptor)
    	conn, err := grpc.Dial("localhost:9000", grpc.WithInsecure(), opt)
    	iferr ! =nil {
    		fmt.Println(err.Error())
    		return
    	}
    	defer conn.Close()
    
    	c := proto.NewHelloWorldClient(conn)
    	response, err := c.SayHello(context.Background(), &proto.HelloRequest{
    		Name: "admin",})iferr ! =nil {
    		fmt.Println(err.Error())
    		return
    	}
    	fmt.Println("Data returned by the server" + response.Message)
    }
    Copy the code
  • 4. The server obtains metadata data in the interception and determines the user name and password

    func main(a) {
    	listen, err := net.Listen("tcp".": 9000")
    	iferr ! =nil {
    		log.Fatalf("Listening port error :" + err.Error())
    	}
    	// Define interceptors
    	interceptor := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
    		fmt.Println("Print before request")
    		// Parse data validation in metadata
    		md, ok := metadata.FromIncomingContext(ctx)
    		if! ok {return resp, status.Error(codes.Unauthenticated, "Invalid parameter")}var (
    			username string
    			password string
    		)
    		if val, ok := md["username"]; ok {
    			username = val[0]}if val, ok := md["password"]; ok {
    			password = val[0]
    		}
    		fmt.Println(username, password, "Received parameters")
    		ifusername ! ="admin"|| password ! ="123456" {
    			return resp, status.Error(codes.Unauthenticated, "Wrong username and password")
    		}
    		res, err := handler(ctx, req)
    		fmt.Println("Request complete print")
    		return res, err
    	}
    	// Use interceptor
    	opt := grpc.UnaryInterceptor(interceptor)
    	service := grpc.NewServer(opt)
    
    	proto.RegisterHelloWorldServer(service, &server{})
    	err = service.Serve(listen)
    	iferr ! =nil {
    		fmt.Println(err.Error())
    	}
    }
    Copy the code

4. Parameter verification

  • Github Reference Documentation
  • 2, this document is not very stable, if necessary, you can study by yourself

Five, return state

  • 1, we have the status in the HTTP request, naturally in the GRPC also have the status code, to mark the current request success and failure

  • Github Reference Documentation

  • 3. The server returns the status code to the client

    import(..."google.golang.org/grpc"
    	"google.golang.org/grpc/codes"
    	"google.golang.org/grpc/status"
    	"log"
    	"net"
    )
    
    type server struct{}func (s *server) SayHello(ctx context.Context, in *proto.HelloRequest) (*proto.HelloResponse, error) {
    	//return &proto.HelloResponse{
    	//	Message: "hello" + in.Name,
    	//}, nil
    	// Return error
    	return nil, status.Error(codes.NotFound, "Records could not be found.")}func main(a) {
    	listen, err := net.Listen("tcp".": 9000")
    	iferr ! =nil {
    		log.Fatalf("Listening port error :" + err.Error())
    	}
    
    	service := grpc.NewServer()
    
    	proto.RegisterHelloWorldServer(service, &server{})
    	err = service.Serve(listen)
    	iferr ! =nil {
    		fmt.Println(err.Error())
    	}
    }
    Copy the code
  • 4. The client receives an error and prompts the status code in the error

    func main(a) {
    
    	conn, err := grpc.Dial("localhost:9000", grpc.WithInsecure())
    	iferr ! =nil {
    		fmt.Println(err.Error())
    		return
    	}
    	defer conn.Close()
    
    	c := proto.NewHelloWorldClient(conn)
    	response, err := c.SayHello(context.Background(), &proto.HelloRequest{
    		Name: "admin",})iferr ! =nil {
    		str, ok := status.FromError(err)
    		if! ok {panic("Failed to parse error message")
    		}
    		fmt.Println(str.Message(), "Error message received")
    		fmt.Println(str.Code(), "Error code obtained")
    		fmt.Println(err.Error())
    		return
    	}
    	fmt.Println("Data returned by the server" + response.Message)
    }
    Copy the code

Six, timeout processing

  • 1. Set a maximum time limit on the client

    // Set the time
    ctx, _ := context.WithTimeout(context.Background(), time.Second*3)
    response, err := c.SayHello(ctx, &proto.HelloRequest{
      Name: "admin",})Copy the code
  • 2. Data is returned when the server is hibernated