すべてのプロダクト
Search
ドキュメントセンター

HTTPDNS:Android での HTTPDNS と gRPC のベストプラクティス

最終更新日:Dec 04, 2025

Android SDK の統合プロセスのドキュメントでは、Android SDK のインポートと設定、IP アドレスの解決、ネットワークライブラリへの SDK の適用、および統合の検証までの完全なプロセスについて説明しています。このトピックでは、HTTPDNS を gRPC で使用するための具体的な手順について説明します。

1. 背景情報

gRPC は、Google 社の高性能なオープンソースのリモートプロシージャコール (RPC) フレームワークです。HTTP/2 プロトコルに基づいており、マイクロサービスモデルで広く使用されています。デフォルトでは、gRPC は名前解決にシステム DNS を使用します。ただし、開発者が名前解決ロジックをカスタマイズできる NameResolver 拡張メカニズムも提供しています。
Android では、アプリケーションで gRPC を使用している場合、カスタムの NameResolver を実装して gRPC に HTTPDNS を統合できます。これにより、名前解決のためのシステム DNS が置き換えられます。

: このソリューションは、トランスポートレイヤーとして、Android で推奨される方法である grpc-okhttp を使用する gRPC クライアントに適用されます。

2. 仕組み

gRPC の名前解決フローは次のとおりです:

ManagedChannel
    ↓
NameResolver (カスタムエンドポイント)
    ↓
LoadBalancer
    ↓
Transport (grpc-okhttp)
    ↓
TCP 接続

カスタム NameResolver を作成することで、名前解決の段階で HTTPDNS を統合できます。カスタム NameResolver は解決された IP アドレスのリストを gRPC に返し、gRPC の `LoadBalancer` がロードバランシングとフェイルオーバーを処理します。

3. 統合手順

3.1. 依存関係の追加

gRPC 依存関係を build.gradle ファイルに追加します:

dependencies {
    // EMAS HTTPDNS
    implementation 'com.aliyun.ams:alicloud-android-httpdns:x.x.x'
    // gRPC Android
    implementation 'io.grpc:grpc-okhttp:x.x.x'
    implementation 'io.grpc:grpc-stub:x.x.x'
    implementation 'io.grpc:grpc-protobuf-lite:x.x.x'
}

3.2. HTTPDNS の初期化

`Application` クラスで HTTPDNS サービスを初期化します:

public class MyApplication extends Application {
    @Overridepublic void onCreate() {
        super.onCreate();
        
        // HTTPDNS を初期化
        InitConfig config = new InitConfig.Builder()
                .setContext(this)
                .setSecretKey("your_secret_key")  // オプション
                .setEnableCacheIp(true, 24 * 60 * 60 * 1000)  // キャッシュを有効化
                .setEnableExpiredIp(true)  // 期限切れ IP の使用を許可
                .setTimeoutMillis(2000)
                .build();
        
        HttpDns.init("your_account_id", config);
    }
}

3.3. カスタム NameResolver の実装

EmasHttpDnsNameResolver クラスを作成します:

import io.grpc.EquivalentAddressGroup;
import io.grpc.NameResolver;
import io.grpc.Status;
import com.alibaba.sdk.android.httpdns.HttpDns;
import com.alibaba.sdk.android.httpdns.HttpDnsService;
import com.alibaba.sdk.android.httpdns.HTTPDNSResult;
import com.alibaba.sdk.android.httpdns.RequestIpType;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;

public class EmasHttpDnsNameResolver extends NameResolver {

    private final String host;
    private final int port;
    private Listener2 listener;

    public EmasHttpDnsNameResolver(String host, int port) {
        this.host = host;
        this.port = port;
    }

    @Overridepublic String getServiceAuthority() {
        return host;
    }

    @Overridepublic void start(Listener2 listener) {
        this.listener = listener;
        resolve();
    }

    @Overridepublic void refresh() {
        resolve();
    }

    private void resolve() {
        List<EquivalentAddressGroup> addressGroups = new ArrayList<>();

        try {
            // HTTPDNS を呼び出してドメイン名を解決
            HttpDnsService httpDnsService = HttpDns.getService("your_account_id");
            HTTPDNSResult result = httpDnsService.getHttpDnsResultForHostSyncNonBlocking(
                host, 
                RequestIpType.auto
            );

            // HTTPDNS が null を返す場合、システム DNS にフォールバック
            if (result == null || 
                (result.getIps() == null || result.getIps().length == 0) &&
                (result.getIpv6s() == null || result.getIpv6s().length == 0)) {
                
                InetAddress[] systemAddrs = InetAddress.getAllByName(host);
                for (InetAddress addr : systemAddrs) {
                    addressGroups.add(new EquivalentAddressGroup(
                        new InetSocketAddress(addr, port)
                    ));
                }
            } else {
                // IPv4 アドレスを追加
                if (result.getIps() != null) {
                    for (String ip : result.getIps()) {
                        addressGroups.add(new EquivalentAddressGroup(
                            new InetSocketAddress(ip, port)
                        ));
                    }
                }
                
                // IPv6 アドレスを追加
                if (result.getIpv6s() != null) {
                    for (String ip : result.getIpv6s()) {
                        addressGroups.add(new EquivalentAddressGroup(
                            new InetSocketAddress(ip, port)
                        ));
                    }
                }
            }

            // 解決結果を返す
            ResolutionResult resolutionResult = ResolutionResult.newBuilder()
                    .setAddresses(addressGroups)
                    .build();

            listener.onResult(resolutionResult);

        } catch (Exception e) {
            listener.onError(Status.UNAVAILABLE.withDescription(
                "HTTPDNS resolve failed: " + e.getMessage()
            ));
        }
    }

    @Overridepublic void shutdown() {
        // リソースをクリーンアップ
    }
}

3.4. NameResolverProvider の実装

EmasHttpDnsResolverProvider クラスを作成します:

import io.grpc.NameResolver;
import io.grpc.NameResolverProvider;
import java.net.URI;

public class EmasHttpDnsResolverProvider extends NameResolverProvider {

    private static final String SCHEME = "emashttpdns";

    @Overrideprotected boolean isAvailable() {
        return true;
    }

    @Overrideprotected int priority() {
        return 5;
    }

    @Overridepublic NameResolver newNameResolver(URI targetUri, NameResolver.Args args) {
        if (!SCHEME.equals(targetUri.getScheme())) {
            return null;
        }

        String host = targetUri.getHost();
        int port = targetUri.getPort() == -1 ? 443 : targetUri.getPort();

        return new EmasHttpDnsNameResolver(host, port);
    }

    @Overridepublic String getDefaultScheme() {
        return SCHEME;
    }
}

3.5. NameResolver の登録

`Application` クラスでカスタム NameResolver を登録します:

public class MyApplication extends Application {
    @Overridepublic void onCreate() {
        super.onCreate();
        
        // HTTPDNS の初期化 (ステップ 3.2 を参照)
        // ...
        
        // カスタム NameResolver を登録
        NameResolverRegistry.getDefaultRegistry()
                .register(new EmasHttpDnsResolverProvider());
    }
}

3.6. gRPC クライアントの使用

gRPC チャンネルを作成する際に、カスタムスキームを使用します:

// チャンネルを作成
ManagedChannel channel = OkHttpChannelBuilder
        .forTarget("emashttpdns://your-grpc-server.com:443")
        .overrideAuthority("your-grpc-server.com")  // 重要:これにより、TLS SNI が正しく保証されます
        .useTransportSecurity()  // TLS を有効化
        .build();

// スタブを作成して呼び出しを実行
YourServiceGrpc.YourServiceBlockingStub stub = 
    YourServiceGrpc.newBlockingStub(channel);

YourResponse response = stub.yourMethod(request);

4. 統合の検証

統合が完了したら、ハイジャック偽装や障害インジェクションテストなどのメソッドを使用して、統合が成功したことを検証できます。詳細については、「ネットワークライブラリ統合の成功検証」ドキュメントをご参照ください。

5. まとめ

gRPC の NameResolver インターフェイスを実装することで、HTTPDNS を gRPC クライアントに統合できます。このアプローチには、次の利点があります:

  • シンプルな実装:NameResolver インターフェイスを実装するだけで、HTTPDNS サービスを統合できます。

  • 高い汎用性:このメソッドはさまざまな gRPC シナリオで機能し、証明書の検証やサーバ名表示 (SNI) などのセキュリティメカニズムと互換性があります。

  • 高い可用性:複数の IP アドレス間での自動ロードバランシングとフェイルオーバーをサポートします。HTTPDNS の名前解決が失敗した場合、自動的にシステム DNS にフォールバックします。

  • パフォーマンスの最適化:DNS ハイジャックを防ぎ、名前解決の速度と成功率を向上させます。