gRPCの飼育係ベースのロードバランシング

REVIEW

gRPCは、主に、Googleによって開発されたプロトコルの開発のベースいるProtobuf(プロトコルバッファ)配列を設計したHTTP / 2標準プロトコルに基づいて、モバイルアプリケーションのために、高性能、汎用オープンRPCフレームワークであり、言語の数の開発をサポートします。

そのプラグイン機構を介してgRPCは、この資料はgRPCロードバランシングによって定義する方法について説明インターフェイス、非常に柔軟な負荷分散、コールチェーン、ヘルスチェック、認証局と他のモジュールとすることができます。

負荷分散スキーム

加えて、RPCサービスは、モジュールをデカップリング、クロスランゲージ・コールの問題を解決するために、重要な点は、サービスのマイクロモジュールを介してである、あなたは、アプリケーション層の待ち時間を短縮、非同期を通じて、アプリケーション層サービスのRPC呼び出し複数のサービスをノード・レベルを拡張することができます。

それはRPCサービスの間のステートレスなので、あなたはそのサービス機能を拡張するために、マシンのレベルを上げることができます。しかし、どのノードそれを複数使用するには?

一般的には、フロントロードバランスのRPC(LB)に添加してもよく、LBは、対応するサービスノードの後ろハング。アプリケーション層LBへの直接アクセスは、RPCノードは、アプリケーション層には見えない、LBの後ろに隠れて。この利点は、LBアドレスとポートが大幅にアプリケーション呼び出しの複雑さを軽減し、LBアルゴリズムを気にしない、アプリケーション層、アプリケーション層に提供することができる限り、より明白です。接続-LBの再とき、あなたはバックエンドサービスを再選択することができますので、このようにして、ウェブや他の短接続されたアプリケーションは、より優れたソリューションです。

しかし、大きな問題を抱えているため、長い連動サービスのために。例えば、長いアプリケーションはリンクがRPCサービスBのバックエンドに転送LB、LBランダムに接続され、Aは長いリンクに起因して、後続のすべての要求は、あなたは負荷分散の役割を果たしていないことができ、Bに行きます。あなたは、接続が解放され、各電話を接続していない、長いリンクを維持するために、RPCと最高のサービスを考えるかもしれません。gRPCようなアセンブリは、負荷分散、負荷分散を提供していませんが、公開されたインターフェースは限り拡張するNameResolverProvider方法は、あなたが簡単にロード・バランシングモジュールを実現することができますインターフェイスを実装するクラス。

導入に基づいてバランスをとるGRPC荷重は、をご参照くださいjuejin.im/post/5cd6e6 ...

ここでNameResolver飼育係による負荷分散を実現する方法

ZkNameResolverProvider達成

public class ZkNameResolverProvider extends NameResolverProvider {
    @Override
    protected boolean isAvailable() {
        return true;
    }

    @Override
    protected int priority() {
        return 5;
    }

    @Nullable
    @Override
    public NameResolver newNameResolver(URI targetUri, Attributes params) {
        return new ZkNameResolver(targetUri);
    }

    @Override
    public String getDefaultScheme() {
        return "zk";
    }
}
复制代码

ZkNameResolver達成

public class ZkNameResolver extends NameResolver implements Watcher {
    private URI zkUri;
    private ZooKeeper zoo;
    private Listener listener;
    private final int ZK_CONN_TIMEOUT = 3000;
    private final String ZK_PATH = "/grpc_server_list";

    ZkNameResolver(URI zkUri) {
        this.zkUri = zkUri;
    }

    @Override
    public String getServiceAuthority() {
        return zkUri.getAuthority();
    }

    @Override
    public void start(Listener listener) {
        this.listener = listener;
        final CountDownLatch latch = new CountDownLatch(1);
        String zkAddr = zkUri.getHost() + ":" + zkUri.getPort();
        System.out.printf("connect to zookeeper server %s", zkAddr);
        try {
            this.zoo = new ZooKeeper(zkAddr, ZK_CONN_TIMEOUT, new Watcher() {
                @Override
                public void process(WatchedEvent event) {
                    if (event.getState() == Event.KeeperState.SyncConnected) {
                        latch.countDown();
                    }
                }
            });
        } catch (IOException e) {
            System.out.printf(e);
            System.out.printf("connect to zookeeper failed, JVM exited [%s]", e.getMessage());
            System.exit(1);
        }
        try {
            latch.await();
            System.out.printf("connect to zookeeper succeed");
        } catch (InterruptedException e) {
            System.out.printf(e);
            System.out.printf("CountDownLatch interrupted, JVM exited [%s]", e.getMessage());
            System.exit(1);
        }
        try {
            Stat stat = zoo.exists(ZK_PATH, true);
            if (stat == null) {
                System.out.printf("%s not exists", ZK_PATH);
            } else {
                System.out.printf("%s exists", ZK_PATH);
            }
        } catch (KeeperException | InterruptedException e) {
            System.out.printf(e);
        }

        try {
            List<String> children = zoo.getChildren(ZK_PATH, this);
            addServersToListener(children);
        } catch (KeeperException | InterruptedException e) {
            System.out.printf(e);
            System.out.printf("get children of %s failed [%s], JVM exited", ZK_PATH, e.getMessage());
            System.exit(1);
        }
    }
    // 把zookeeper ZK_PATH的子节点作为rpc的节点地址,注册到gRPC负载均衡服务中
    private void addServersToListener(List<String> servers) {
        System.out.printf("rpc servers:%s", servers);
        ArrayList<EquivalentAddressGroup> addressGroups = new ArrayList<EquivalentAddressGroup>();
        for (String server : servers) {
            List<SocketAddress> socketAddresses = new ArrayList<SocketAddress>();
            String[] address = server.split(":");
            socketAddresses.add(new InetSocketAddress(address[0], Integer.parseInt(address[1])));
            addressGroups.add(new EquivalentAddressGroup(socketAddresses));
        }
        if (addressGroups.size() > 0) {
            listener.onAddresses(addressGroups, Attributes.EMPTY);
        } else {
            System.out.printf("No servers find, keep looking");
        }
    }

    @Override
    public void shutdown() {
        try {
            zoo.close();
        } catch (InterruptedException e) {
            System.out.printf(e);
        }
    }

    @Override
    public void process(WatchedEvent event) {
        if (event.getType() == Event.EventType.None) {
            System.out.printf("Zookeeper connection expired");
        } else {
            try {
                List<String> children = zoo.getChildren(ZK_PATH, false);
                addServersToListener(children);
                zoo.getChildren(ZK_PATH, true);
            } catch (Exception e) {
                System.out.printf(e);
            }
        }
    }
}
复制代码
  • ホストRPCサービス:飼育係のパスとしてポート(/grpc_server_list/rpc_host:50010)、他のイベントの削除飼育係リスニングパスを作成
  • ノードは、RPCオフラインまたはオンライン、飼育係から追加または削除されたノードの動的な情報を有する場合
  • listener.onAddressesgRPCに負荷分散するために登録されたRPCサービスのアドレス

チャンネルを作成します。

this.channel = ManagedChannelBuilder
// 配置zk地址
.forTarget("zk://zkhost:2181")
// 配置NameResolverProvider实现类
.nameResolverFactory(new ZkNameResolverProvider())
.enableRetry()
.maxRetryAttempts(5)
.keepAliveTime(5, TimeUnit.MINUTES)
.keepAliveWithoutCalls(true)
.keepAliveTimeout(10, TimeUnit.MINUTES)
.idleTimeout(24, TimeUnit.HOURS)
// 轮询策略
.loadBalancerFactory(RoundRobinLoadBalancerFactory.getInstance())
.usePlaintext()
.build();
复制代码
  • forTarget("zk://zkhost:2181")飼育係リンクアドレスを設定します
  • nameResolverFactory(new ZkNameResolverProvider())コンフィギュレーションのNameResolverProvider実装クラスは、によってgRPCを聞かせてZkNameResolverProvider使用可能なノードのアドレス検索サービス
  • 構成に基づいてRPCコールサービス、loadBalancerFactory(RoundRobinLoadBalancerFactory.getInstance())コール対応するポーリングバックエンドサービス戦略

概要

gRPCインターフェイスを実装することにより、非常に柔軟なロード・バランシング・インターフェースを提供し、それが簡単にロードバランシングを実現することができます。カスタム負荷分散メカニズムによって、大幅にRPCネットワークのオーバーヘッドを改善し、同時に各RPCサービスへのポーリング、拡張されたRPC応答を各RPCとの長いリンクを維持するために、発信者を確保します。飼育係によって(特定のパスを監視するためのメカニズムを見ることができる/grpc_server_list非常に利便性のバックエンドサービスレベルの拡張を改善する、組立ラインオフに登録動的実装サービスを子ノードを追加または削除します)。

ます。https://juejin.im/post/5d0a41a65188252b8d1d45b6で再現

おすすめ

転載: blog.csdn.net/weixin_33890526/article/details/93164591