この記事は、クライアント grpc ロード バランシングのソース コードを自分で表示するプロセスを記録するものです. あまり詳細には説明しません. 参考価値はほとんどないので、直接スキップできます. 主に自分用です.
1.メインインターフェース:バランサーリゾルバー
1.バランサーの定義
リゾルバ定義
具体的な場所は
1. grpc ソース コードは、dns、passthrough、および unix の 3 つの構造を持つリゾルバー (リゾルバー) Builder インターフェースを実装します。clientconn.goファイル内のパッケージを参照して該当パッケージの初期化メソッドに登録
2. internal/resolver/passthrough/passthrough.go ファイルに init 初期化関数を入力し、passthroughBuilder が登録されていることを確認します。リゾルバーパッケージの Register 関数が呼び出されます
func init() {
resolver.Register(&passthroughBuilder{})
}
3. Register は、登録された構造をマップであるグローバル変数 m に保存します。キーは Scheme で、値は Builder インターフェースです。grpc のデフォルト スキームは passthrough であることがわかります。つまり、passthrough は何もしません。
package resolver
import (
"context"
"net"
"net/url"
"strings"
"google.golang.org/grpc/attributes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/internal/pretty"
"google.golang.org/grpc/serviceconfig"
)
var (
// m is a map from scheme to resolver builder.
m = make(map[string]Builder)
// defaultScheme is the default scheme to use.
defaultScheme = "passthrough"
)
// TODO(bar) install dns resolver in init(){}.
// Register registers the resolver builder to the resolver map. b.Scheme will be
// used as the scheme registered with this builder.
//
// NOTE: this function must only be called during initialization time (i.e. in
// an init() function), and is not thread-safe. If multiple Resolvers are
// registered with the same name, the one registered last will take effect.
func Register(b Builder) {
m[b.Scheme()] = b
}
// Get returns the resolver builder registered with the given scheme.
//
// If no builder is register with the scheme, nil will be returned.
func Get(scheme string) Builder {
if b, ok := m[scheme]; ok {
return b
}
return nil
}
4. passthroughBuilder をもう一度見て、Buider インターフェイスを実装していることを確認します。Build メソッドは、Resolver インターフェイスを実装する構造体 passthroughResolver を返します。その中で、 passthroughBuilder はパーサーを作成するために使用され、 passthroughResolver は作成されたパーサーです
package passthrough
import (
"errors"
"google.golang.org/grpc/resolver"
)
const scheme = "passthrough"
type passthroughBuilder struct{}
func (*passthroughBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
if target.Endpoint() == "" && opts.Dialer == nil {
return nil, errors.New("passthrough: received empty target in Build()")
}
r := &passthroughResolver{
target: target,
cc: cc,
}
r.start()
return r, nil
}
func (*passthroughBuilder) Scheme() string {
return scheme
}
type passthroughResolver struct {
target resolver.Target
cc resolver.ClientConn
}
func (r *passthroughResolver) start() {
r.cc.UpdateState(resolver.State{Addresses: []resolver.Address{
{Addr: r.target.Endpoint()}}})
}
func (*passthroughResolver) ResolveNow(o resolver.ResolveNowOptions) {}
func (*passthroughResolver) Close() {}
func init() {
resolver.Register(&passthroughBuilder{})
}
5. このコードで返される resolverBuilder は、上記で見つかった登録済みビルダーであり、resolver.Get は 1.3 の対応するコードです。
7. パーサーのパッケージ newCCResolverWrapper を生成し、build を呼び出してパーサーを作成する. このプロセスでは、パーサーの start メソッドが呼び出されます. このメソッドでさらに重要なことは、2 つのことを行うことです. 1. バランサー信号を選択するためのトリガー。2. トリガー状態の変更 (ロード バランシング、つまり接続の作成を含む)
上記は、switchToUpdate と ccStateUpdate をそれぞれ ccBalancerWrapper のチャネルに送信しました
バランサーの選択ロジック
baseBalancer会将所有地址都创建一个连接
invoke的时候,会调用picker选择连接
以上路径最终调用这个方法,可以看到是轮询使用连接