Continuationクラスでの非同期通信

今回はContinuationクラスについて取り上げます。

スポンサーリンク

Continuationクラスとは

Continuationは日本語に訳すと継続や存続などを意味します。
よくゲームとかで使うコンティニューの名詞形ですね。
これをApexクラスの中で使用できるのですが、使い方についてはまだ浸透されていません。

ContinuationクラスはVisualforceページから非同期コールアウトを使用する時に、コールアウトの時間が長くなる時に使用されます。
Spring15からの機能で、こちらに簡単な処理の流れなも載っています。

従来のHTTPコールアウトに比べると、

  • 一度に3つのHTTPコールアウトが実施できる
  • 非同期を実行して終わりでなく、コールアウト終了後にApexのアクションを実施できる
  • 長時間要求の同時実行数(同時実行10まで)の制限の対象とならない

など、イイことずくめの機能であり、覚えておいて損はない機能だと思います。

使用例

では、使い方について、今回はVisualForceページとボタンを押した時に、外部サーバへHTTPコールアウトする処理を作成します。

まずは、VisualForceページから
ContinuationTest.page

<apex:page controller="ContinuationController" showChat="false" showHeader="false">
    <apex:form >
        <!-- ボタン押下でコールアウトを実行し、結果を出力 -->
        <apex:commandButton action="{!startRequest}" value="Start Request" reRender="result"/>
    </apex:form>

    <!-- コールアウト結果を出力 -->
    <apex:outputText id="result" value="{!result}" />
</apex:page>

次はApexクラス。こちらに、Continuationクラスの仕組みを作ります。
ContinuationController.cls

public with sharing class ContinuationController {

    // リクエストラベル
    public String requestLabel;

    // コールアウト結果のプロパティ
    public String result {get;set;}

    // エンドポイント
    private static final String LONG_RUNNING_SERVICE_URL =
    'https://th-apex-http-callout.herokuapp.com/animals';

    // アクションメソッド
    public Object startRequest() {
        // Continuationクラスを作成し、タイムアウトを設定
        Continuation con = new Continuation(40);

        // コールバックメソッド名を設定
        con.continuationMethod='processResponse';

        // HTTPリクエストを作成
        HttpRequest req = new HttpRequest();
        req.setMethod('GET');
        req.setEndpoint(LONG_RUNNING_SERVICE_URL);

        // ContinuationクラスにHTTPリクエストを追加
        this.requestLabel = con.addHttpRequest(req);

        // PageReferenceの代わりにContinuationクラスを返却
        return con;
    }

    // コールバックメソッド
    public Object processResponse() {
        // レスポンスラベルをキーとして、レスポンスを取得する
        HttpResponse response = Continuation.getResponse(this.requestLabel);

        // Set the result variable that is displayed on the Visualforce page
        this.result = response.getBody(); 
        // VisualForceページの画面遷移、re-renderであればnullを返却
        return null;
    }
}

Apexクラスのアクションの流れを簡単に説明しますと、

Continuationクラスを使用しない、従来のHTTPコールアウトを使用した処理は、

  1. VisualForce上からボタン押下
  2. ApexクラスコントローラアクションのトランザクションでHTTPリクエストを作成
  3. トランザクション内でコールアウトを実行し、レスポンスを取得
  4. レスポンスに応じてメッセージ出力や画面遷移を行う

といった感じです。
この時、3のコールアウトの際に、サーバの応答が遅い状態だと、トランザクション内での処理がストップしてしまい、画面はフリーズし、同時に実行されるHTTPコール数が消費されサーバ側の制限に引っかかりやすくなり、設計上よろしくない作りとなってしまいます。

次にContinuationクラスを使用した場合、コールアウト側は非同期となり、タイミングによって様々なプロセスが動きます。

  1. VisualForce上からボタン押下
  2. ApexクラスコントローラアクションのトランザクションでHTTPリクエストを作成
  3. ContinuationクラスにHTTPリクエストを追加(この時、リクエストラベルが生成)
  4. Continuationクラスを返却してアクションメソッド終了

ボタン押下時のアクションは一旦ここで切れます。

この時に、非同期でContinuationによるHTTPコールアウトが裏で実行されます。

その時のContinuation側の処理はこのようになります。

  1. リクエストラベルを使用して、指定したHTTPリクエストのレスポンスを取得(複数あれば複数回実施)
  2. レスポンスに応じてメッセージ出力や画面遷移を行う

このように、従来のHTTPコールアウト処理を分断した形になります。

複数HTTPコールアウトで確認

今後はApexクラスを少し修正してみます。

ContinuationController.cls

public with sharing class ContinuationController {

    // リクエストラベル
    public String requestLabel;

    // コールアウト結果のプロパティ
    public String result {get;set;}

    // エンドポイント
    private static final String LONG_RUNNING_SERVICE_URL =
    'https://th-apex-http-callout.herokuapp.com/animals';

    // アクションメソッド
    public Object startRequest() {
        // Continuationクラスを作成し、タイムアウトを設定
        Continuation con = new Continuation(40);

        // コールバックメソッド名を設定
        con.continuationMethod='processResponse';

        // HTTPリクエストを作成
        HttpRequest req = new HttpRequest();
        req.setMethod('GET');
        req.setEndpoint(LONG_RUNNING_SERVICE_URL);
      
        // ContinuationクラスにHTTPリクエストを追加
        this.requestLabel = con.addHttpRequest(req);
        
        // 2つめのHTTPリクエストを作成
        HttpRequest req2 = new HttpRequest();
        req2.setMethod('POST');
        req2.setEndpoint(LONG_RUNNING_SERVICE_URL);
        req2.setHeader('Content-Type', 'application/json;charset=UTF-8');
        req2.setBody('{"name":"mighty moose"}');

         // ContinuationクラスにHTTPリクエストを追加
         this.requestLabel2 = con.addHttpRequest(req2);

        // PageReferenceの代わりにContinuationクラスを返却
        return con;
    }

    // コールバックメソッド
    public Object processResponse() {
        // 1つめのHTTPコールアウトのレスポンスを取得
        HttpResponse response = Continuation.getResponse(this.requestLabel);
        String ret1 =  response.getBody();

        // 2つめのHTTPコールアウトのレスポンスを取得
        response = Continuation.getResponse(this.requestLabel2);
        String ret2 =  response.getBody();
        
        this.result = ret1 + ret2; 
        // VisualForceページの画面遷移、re-renderであればnullを返却
        return null;
    }
}

このように、複数のHTTPリクエストを使用して実行してみます(最大3つまで追加できます)。
リクエストラベルを使ってのレスポンス処理が必要になりますが、複数コールアウトでGET・POSTが混じったものも問題なく実行できました。

まとめ

  • ContinuationクラスはHTTPコールアウトを使用するVisualForceページで使用する
  • Continuationクラスには最大3つのHTTPリクエストが登録でき、非同期実行のため同時実行数にカウントされない
  • アクションメソッド側でContinuationクラスを返却し、コールバックメソッドで画面遷移やメッセージ出力を行う

コメント