<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Golang微服務(一)

      Posted on 2023-02-20 12:57  呱呱呱呱嘰里呱啦  閱讀(109)  評論(0)    收藏  舉報

      Golang微服務(一)

      一、protobuf常規使用及踩坑記錄

      1.類型映射關系及零值

      關于protobuf的文檔見:Protocol Buffers Documentation (protobuf.dev),文檔中可見protobuf與go語言的類型映射關系,以及各種類型的零值。當message中的沒有提供指定字段,接收端并不會拋出異常,而是會使用該字段的零值。見以下測試:

      syntax = "proto3";
      
      option go_package = ".;proto";
      
      service Greeter {
        rpc SayHello(HelloRequest) returns (HelloResponse);
      }
      
      message HelloRequest {
        string name = 1; // 此處定義請求字段name
      }
      
      message HelloResponse {
        string greeting = 1;
      }
      
      // server端
      
      package main
      
      import (
      	"context"
      	"google.golang.org/grpc"
      	"learn/grpc_things/proto"
      	"net"
      )
      
      type Server struct {
      	proto.UnimplementedGreeterServer
      }
      
      func (s *Server) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloResponse, error) {
      	return &proto.HelloResponse{
      		Greeting: "Hello, " + request.Name,
      	}, nil
      }
      
      func main() {
      	s := grpc.NewServer()
      	proto.RegisterGreeterServer(s, &Server{})
      	lis, err := net.Listen("tcp", ":6666")
      	if err != nil {
      		panic(err)
      	}
      	err = s.Serve(lis)
      	if err != nil {
      		panic(err)
      	}
      }
      
      // 客戶端
      package main
      
      import (
      	"context"
      	"fmt"
      	"google.golang.org/grpc"
      	"learn/grpc_things/proto"
      )
      
      func main() {
      	conn, err := grpc.Dial("127.0.0.1:6666", grpc.WithInsecure())
      	defer conn.Close()
      	if err != nil {
      		panic(err)
      	}
      	c := proto.NewGreeterClient(conn)
      	name := proto.HelloRequest{} // 不提供proto文件中定義的Name字段
      	resp, errResp := c.SayHello(context.Background(), &name)
      	if errResp != nil {
      		panic(errResp)
      	}
      	fmt.Println(resp.Greeting) // 打印服務端的響應:Hello,
      }
      

      2.go_package設置

      option go_package設置,格式是"生成結果文件存放路徑;go項目中的package"。

      “生成結果文件存放路徑”可以使用".","..","../"之類的標識,當指定路徑不存在時則會創建。go_package或者proto中的package用于分別指定當前文件在其同語言中的(類似)作用域,目的應該是確保標識符的唯一性。

      3.protobuf的字段編號

      在開發中需要保持服務端和客戶端proto文件的完全一致,否則可能出現不容易發現的BUG。例如在proto文件中“string name = 1;”中的1指此字段在某個結構中的編號,protobuf通過自動生成其他語言對應的代碼完成了一種在具體結構中字段--編號的對應,這樣編碼時可以通過“編號+值長度+值”代替“鍵+值”,可以減少用于實際傳輸的數據量,在另一端解析時則可以通過編號和“字段--編號”的映射順序取出值并賦給對應的字段。見以下示例說明:

      syntax = "proto3";
      
      option go_package = ".;proto";
      
      service Greeter {
        rpc SayHello(HelloRequest) returns (HelloResponse);
      }
      
      message HelloRequest {
        string name = 1;
        string url = 2;
        // 如果另一端的proto文件設置為
        // string url = 1;
        // string name = 2;
        // 此時代碼可能可以繼續運行并干擾調試
      }
      
      message HelloResponse {
        string greeting = 1;
      }
      

      4.proto文件的import

      可以自定義proto文件以供引用或者引用公共proto(google/protobuf/something),在go中引用公共支持的proto結構時引用proto對應的公共包路徑即可,例如(import "github.com/golang/protobuf/ptypes/empty")

      // base.proto
      // 注意,如果想要在go代碼中引用base.proto中的結構,該proto文件也需要通過protoc生成go文件
      syntax = "proto3";
      
      option go_package = ".;proto";
      
      message Empty {
      }
      
      message Pong {
        string res = 1;
      }
      
      // hello.proto
      syntax = "proto3";
      
      import "base.proto"
      // 公共支持:import "google/protobuf/empty.proto";
      
      option go_package = ".;proto";
      
      service Greeter {
        rpc SayHello(HelloRequest) returns (HelloResponse);
        rpc Ping(Empty) returns (Pong);
        // 調用公共支持:rpc Ping(google.protobuf.Empty) returns (Pong);
      }
      
      message HelloRequest {
        string name = 1;
      }
      
      message HelloResponse {
        string greeting = 1;
      }
      

      5.protobuf的message嵌套

      當一個message復雜到需要由其他message構成,而不需要將所有message提取至公共作用域時,可以直接在內部定義。

      message HelloResponse {
        string greeting = 1;
        message Detail {
          string info = 1;
          string id = 2;
        }
        repeated Detail data = 2;
      }
      

      6.protobuf的枚舉、map、timestamp

      syntax = "proto3";
      option go_package = ".;proto";
      
      service Greeter {
        rpc SayHello(HelloRequest) returns (HelloResponse);
      }
      
      // 枚舉
      enum Gender {
        FEMALE = 0;
        MALE = 1;
      }
      
      message HelloRequest {
        string name = 1;
        Gender g = 2;
        google.protobuf.Timestamp ts = 3; 
        // timestamp go代碼中可以用timestamppb.New()生成時間戳數據
      }
      
      message HelloResponse {
        string greeting = 1;
        map<string, int32> rank = 2; // map
      }
      

      二、gRPC常規使用及踩坑記錄

      1.metadata

      類似http中的header,為當次調用提供信息。

      type MD map[string][]string
      

      使用

      // 實例化metadata
      md := metadata.New(map[string]string{"key": "value", })
      md1 := metadata.Pairs("key1", "value1", "key2", "value2", )
      // 發送metadata
      ctx := metadata.NewOutgoingContext(context.Background(), md)
      response, err := client.RPCThings(ctx, requestThings)
      // 接收metadata
      md, ok := metadata.FromIncomingContext(ctx)
      

      2.gRPC攔截器

      一元攔截器

      server端

      	myInterceptor := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
      		fmt.Println("新請求")
              res, err := handler(ctx, req)
              fmt.Println("新響應")
              return res, err
      	} // 實現攔截器邏輯函數
      
      	itc := grpc.UnaryInterceptor(myInterceptor) // 實例化攔截器
      	s := grpc.NewServer(itc) // 初始化server時傳入攔截器
      

      client端

      	ci := 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)
      		fmt.Println("處理用時:", time.Since(start))
      		return err
      	} // 實現攔截器邏輯函數
      
      	conn, err := grpc.Dial("127.0.0.1:6666", grpc.WithInsecure(), grpc.WithUnaryInterceptor(ci)) // 實例化攔截器并傳入撥號函數
      

      3.gRPC Auth認證

      • 手動實現

      // 利用metadata和攔截器實現認證
      // client端,在攔截器中修改ctx,為每次請求增加驗證信息
      	ci := func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
      		start := time.Now()
      		md := metadata.New(map[string]string{"token": "right"})
      		ctx = metadata.NewOutgoingContext(context.Background(), md)
      		err := invoker(ctx, method, req, reply, cc)
      		fmt.Println("處理用時:", time.Since(start))
      		return err
      	}
      
      	conn, err := grpc.Dial("127.0.0.1:6666", grpc.WithInsecure(), grpc.WithUnaryInterceptor(ci))
      
      // server端,在攔截器中獲取metadata并校驗
      	myInterceptor := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
      		fmt.Println("新請求")
      		md, ok := metadata.FromIncomingContext(ctx)
      		if !ok {
      			return resp, status.Errorf(codes.Unauthenticated, "認證失敗")
      		}
      		if authInfo, got := md["token"]; got && strings.Join(authInfo, "") == "right" {
      			res, err := handler(ctx, req)
      			return res, err
      		} else {
      			return resp, status.Errorf(codes.Unauthenticated, "認證失敗")
      		}
      
      	}
      
      	itc := grpc.UnaryInterceptor(myInterceptor)
      	s := grpc.NewServer(itc)
      
      • 使用PerRPCCredentials接口實現

      // client端
      // 實現PerRPCCredentials接口
      type myCdt struct {
      }
      
      func (mc myCdt) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
      	return map[string]string{"token": "right"}, nil
      }
      
      func (mc myCdt) RequireTransportSecurity() bool {
      	return false
      }
      
      // 撥號
      	conn, err := grpc.Dial("127.0.0.1:6666", grpc.WithInsecure(), grpc.WithPerRPCCredentials(myCdt{}))
      	
      

      4.gRPC err處理

      狀態碼:grpc/statuscodes.md at master · grpc/grpc · GitHub

      // server端返回status err
      func (s *Server) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloResponse, error) {
      	fmt.Println(request.G)
      	return nil, status.Errorf(codes.InvalidArgument, "參數錯誤:%s", request.Name)
      }
      // status.Errorf同樣基于status.New()方法,只不過在New()的結果上調用了.Err()方法
      
      // client端解析status err
      resp, errResp := c.SayHello(ctx, &name)
      	if errResp != nil {
      		statusErr, ok := status.FromError(errResp)
      		if !ok {
      			fmt.Println("解析status err失敗")
      		} else {
      			fmt.Println(statusErr.Message(), statusErr.Code())
      		}
      		return
      	}
      

      5.gRPC超時

      重在設置超時的原因:防止服務鏈中的節點單次任務占用資源過多以及負面作用疊加

      常規簡單處理:

      ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
      
      主站蜘蛛池模板: 国内精品自产拍在线播放| 99久久99久久精品国产片| 国产精品视频亚洲二区| 国产一级片内射在线视频| 青青青青国产免费线在线观看| 东辽县| 99久久国产福利自产拍| 97免费在线观看视频| 国产自在自线午夜精品| 四虎成人精品国产永久免费| 女人喷水高潮时的视频网站| 日韩高清亚洲日韩精品一区二区 | 人人玩人人添人人澡超碰| 动漫AV纯肉无码AV电影网| 亚洲欧美高清在线精品一区二区| 成人免费视频一区二区三区| 疯狂做受xxxx高潮欧美日本| 欧美成人午夜性视频| 亚洲欧美日产综合在线网| 国产午夜影视大全免费观看| 亚洲一区中文字幕第十页| 亚洲av无码牛牛影视在线二区| 国产精品一区二区三区激情| 国产高潮刺激叫喊视频| 无码人妻斩一区二区三区 | 亚洲av第三区国产精品| b站永久免费看片大全| 国产精品色三级在线观看| 人妻精品久久无码专区精东影业| 亚洲精品久久| 亚洲欧美另类激情综合区蜜芽| 国内精品视频一区二区三区八戒| 安图县| 亚洲精品区二区三区蜜桃| 久久亚洲精品中文字幕波多野结衣| 被灌满精子的少妇视频| 国产精品日韩中文字幕熟女| 国产精品亚洲а∨天堂2021| 久久99精品久久久大学生| 无码专区 人妻系列 在线 | 亚洲乱色一区二区三区丝袜|