gRPC 소스 상세 설명 (1) 구성화된 구조체

grpc 원본 코드 구조 설명
DialOptions
DialOptions는 가장 중요한 일환으로 매번 rpc 요청을 설정할 때의 선택을 책임진다.
구조
이거의 구조 링크를 먼저 볼게요.
// dialOptions configure a Dial call. dialOptions are set by the DialOption
// values passed to Dial.
type dialOptions struct {
    unaryInt  UnaryClientInterceptor
    streamInt StreamClientInterceptor

    chainUnaryInts  []UnaryClientInterceptor
    chainStreamInts []StreamClientInterceptor

    cp          Compressor
    dc          Decompressor
    bs          backoff.Strategy
    block       bool
    insecure    bool
    timeout     time.Duration
    scChan      

명명이 매우 규범적이기 때문에, 주석을 더하면 각field가 설정한 어떤 속성을 쉽게 이해할 수 있다.스쳐 지나가면 압축 해압기, 시간 초과 막힘 설정, 인증 안전 전송, 부하 균형, 서비스 영구화 정보 저장, 설정, 심장 박동 검사 등이 있을 것이다.
그 함수 방법은 모두 그 중의 필드를 설정하는 것이다.
설정 방법
여기는grpc디자인이 비교적 좋은 곳으로 함수 설정을 통해 함수를 생성하는 함수를 동시에 설치한다.무슨 뜻이죠?먼저 그림과 결합하여 이해하자면 이것도 전체grpc설정의 정수 부분이다
이 뜻은DialOptions는 내보내기 인터페이스로, 실현 함수는 apply가 매개 변수인 dialOptions를 동시에 받아들여 수정하는 것이다.실제로 new FuncDialOptions 함수를 사용하여 dialOptions를 수정하는 방법으로 funcDialOptions 구조체에 포장하고 실제 Dial을 호출할 때 패키지를 닫아서 funcDialOptions 구조체를 호출하는 apply 방법을 사용한다.여기서 Dial 방법의 원본 코드를 볼 수 있습니다. (Dial은 DialContext가 작용하는opt.apply ()
func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) {
    cc := &ClientConn{
        target:            target,
        csMgr:             &connectivityStateManager{},
        conns:             make(map[*addrConn]struct{}),
        dopts:             defaultDialOptions(),
        blockingpicker:    newPickerWrapper(),
        czData:            new(channelzData),
        firstResolveEvent: grpcsync.NewEvent(),
    }
    ···
    for _, opt := range opts {
        opt.apply(&cc.dopts)
    }
    ···
}

이곳의 옵션은 client가 rpc 요청을 하는 핵심 중계소라고 할 수 있다.또 다른 중요한 인터페이스는 다이얼 옵션 구조체에 집중되어 초기화 처리된 것이다callOptions []CallOptionCallOption
CallOption은 rpc 에 정의된 인터페이스입니다.util 패키지 내
구조
// CallOption configures a Call before it starts or extracts information from
// a Call after it completes.
type CallOption interface {
    // before is called before the call is sent to any server.  If before
    // returns a non-nil error, the RPC fails with that error.
    before(*callInfo) error

    // after is called after the call has completed.  after cannot return an
    // error, so any failures should be reported via output parameters.
    after(*callInfo)
}

조작된 것은callInfo구조의 데이터로 dialOptions구조체에 포함된다. 즉, 다이얼이 매번 호출될 때마다.
callInfo
동시에 그 자체의 정의는 매우 재미있다. 조작된 것은 callInfo 구조체이다.
// callInfo contains all related configuration and information about an RPC.
type callInfo struct {
    compressorType        string
    failFast              bool
    stream                ClientStream
    maxReceiveMessageSize *int
    maxSendMessageSize    *int
    creds                 credentials.PerRPCCredentials
    contentSubtype        string
    codec                 baseCodec
    maxRetryRPCBufferSize int
}

압축, 흐름 제어, 인증, 디코더 등 단일 호출에 사용되는 사용자 정의 옵션을 볼 수 있습니다.
하나의 실현
Call Option 인터페이스의 구현을 간단히 살펴보십시오.
// Header returns a CallOptions that retrieves the header metadata
// for a unary RPC.
func Header(md *metadata.MD) CallOption {
    return HeaderCallOption{HeaderAddr: md}
}

// HeaderCallOption is a CallOption for collecting response header metadata.
// The metadata field will be populated *after* the RPC completes.
// This is an EXPERIMENTAL API.
type HeaderCallOption struct {
    HeaderAddr *metadata.MD
}

func (o HeaderCallOption) before(c *callInfo) error { return nil }
func (o HeaderCallOption) after(c *callInfo) {
    if c.stream != nil {
        *o.HeaderAddr, _ = c.stream.Header()
    }
}

실제 작업은 before와 애프터 방법에서 실행되며 클라이언트가 요청을 할 때 자동으로 실행됩니다. 말 그대로 하나는 호출하기 전에 실행하고 하나는 호출 후에 실행합니다.
주의 실현
여기서 알 수 있듯이 여기도 함수를 통해 이 두 가지 방법을 가진 구조체를 되돌려준다. 이 디자인에 주의하면 너 자신의 Option이 디자인할 때 참고할 수 있다.
두 가지 방법
Client가 당신의 Call Option 설정을 받아들일 수 있는 두 가지 방법이 있습니다.
  • 클라이언트 사용 방법을 사용할 때 직접 매개 변수로 전달하고 방금 말한 함수 - Call Option 인터페이스를 실현한 구조체로 되돌려준다.
  • 클라이언트를 생성할 때 설정을 전달합니다.구체적으로 다음과 같다.
  • dialOptions를 통해go의 함수grpc.WithDefaultCallOptions() 
  • 이 함수는dialOptions의 필드 []CallOption에 CallOption을 설정합니다.

  • // WithDefaultCallOptions returns a DialOption which sets the default
    // CallOptions for calls over the connection.
    func WithDefaultCallOptions(cos ...CallOption) DialOption {
        return newFuncDialOption(func(o *dialOptions) {
            o.callOptions = append(o.callOptions, cos...)
        })
    }

    좀 이해가 안 가죠?실례를 하나 드릴게요.
  • 사용하는 첫 번째 방법
  • response, err := myclient.MyCall(ctx, request, grpc.CallContentSubtype("mycodec"))
  • 두 번째 방법 사용
  • myclient := grpc.Dial(ctx, target, grpc.WithDefaultCallOptions(grpc.CallContentSubtype("mycodec")))

    여기에 mycodec 디코더를 설치했다고 가정하십시오.바로 아래에 그것의 설계를 설명하시오.
    달리
    주의해야 할 것은 내가 단지 클라이언트 호출 시 설정만 언급한 것 같은데,callOption이 클라이언트 설정에만 있는 상황이 모두를 곤혹스럽게 하는 것 아니냐는 것이다.실제로 gRPC 서버 측은 자동으로callOption의 설정을 검사하고 이 선택을 지원하는지 확인합니다. 지원하지 않으면 실패로 돌아갑니다.서버에서 등록된 모든 코덱 디코더를 설치한 후 클라이언트가 해당 설정을 직접 사용하면 된다는 것이다.
    Codec
    gRPC에서 코드는 두 개의 인터페이스 정의가 있는데 하나는 베이스 코드가 정상적인 Marshal과 Unmarshal 방법을 포함하고 다른 하나는 이름을 가진 코드가 encoding 패키지에 정의된 것이다. 이것은registry를 등록할 때 이 방법을 사용하기 때문이다.
    이음매
    type Codec interface {
        // Marshal returns the wire format of v.
        Marshal(v interface{}) ([]byte, error)
        // Unmarshal parses the wire format into v.
        Unmarshal(data []byte, v interface{}) error
        // String returns the name of the Codec implementation.  This is unused by
        // gRPC.
        String() string
    }

    이 방법입니다.
    // RegisterCodec registers the provided Codec for use with all gRPC clients and
    // servers.
    //
    // The Codec will be stored and looked up by result of its Name() method, which
    // should match the content-subtype of the encoding handled by the Codec.  This
    // is case-insensitive, and is stored and looked up as lowercase.  If the
    // result of calling Name() is an empty string, RegisterCodec will panic. See
    // Content-Type on
    // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
    // more details.
    //
    // NOTE: this function must only be called during initialization time (i.e. in
    // an init() function), and is not thread-safe.  If multiple Compressors are
    // registered with the same name, the one registered last will take effect.
    func RegisterCodec(codec Codec) {
        if codec == nil {
            panic("cannot register a nil Codec")
        }
        if codec.Name() == "" {
            panic("cannot register Codec with empty string result for Name()")
        }
        contentSubtype := strings.ToLower(codec.Name())
        registeredCodecs[contentSubtype] = codec
    }

    Compressor
    또한 encoding 패키지에Compressor 인터페이스가 정의되어 있으므로 Codec를 참조하여 이해하면 됩니다.
    // Compressor is used for compressing and decompressing when sending or
    // receiving messages.
    type Compressor interface {
        // Compress writes the data written to wc to w after compressing it.  If an
        // error occurs while initializing the compressor, that error is returned
        // instead.
        Compress(w io.Writer) (io.WriteCloser, error)
        // Decompress reads data from r, decompresses it, and provides the
        // uncompressed data via the returned io.Reader.  If an error occurs while
        // initializing the decompressor, that error is returned instead.
        Decompress(r io.Reader) (io.Reader, error)
        // Name is the name of the compression codec and is used to set the content
        // coding header.  The result must be static; the result cannot change
        // between calls.
        Name() string
    }
    

    MetaData 
    이 패키지는 context의Value field, 즉 키-value 형식의 저장소에 대응합니다
    다른 가방에 MD라고 간략하게 적었어요.
    구조
    type MD map[string][]string

    함수.
    완벽한 저장 기능을 실현하여 단일 읽기에서 대량 읽기(pair 모드를 사용하고...string을 매개 변수로 하고len(string)%2==1일 때 오류가 발생합니다. 짝이 없는 원 정보가 고립되기 때문입니다.
    다른 몇 가지 함수는 context에서 읽기와 쓰기를 실현했습니다. (여기 쓰기는 context.WithValue 방법을 사용합니다.parent context를 생성하는copy입니다.
    주의⚠️
  • 주의해야 할 것은 메타데이터 구조체 중value의 구조는[]string이다.
  • 동시에 키는'grpc-'로 시작할 수 없습니다. 이것은 grpc의 인터넷 가방에 이미 보존되어 있기 때문입니다.
  • 더욱 중요한 것은 context에서 읽는 방식이다. 사실 MetaData 구조는 context Value의value값에 대응하고 키 값은 빈 구조체로 설정하여 입력과 입력을 동시에 구분한다.
  • type mdIncomingKey struct{}
  • type mdOutgoingKey struct{}

  • 좋은 웹페이지 즐겨찾기