Table Of Contents

Previous topic

SOAPで呼び出す

Next topic

更新履歴

This Page

FAQs

複数のユーザプログラムをパイプで接続してRaSC上で動作させるには

パイプでの接続を記述したシェルスクリプトを用意するのが簡単です.例えば,構文解析システム KNP を動作させるには,以下のようなシェルスクリプトを用意します.

#!/bin/bash
juman | knp

このスクリプトを run_knp.sh として保存した上で,以下のようにサービス定義XMLを用意します.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean id="target"
    class="jp.go.nict.langrid.servicecontainer.handler.TargetServiceFactory">
    <property name="service">
      <bean class="jp.go.nict.wisdom.wrapper.StdIOCommandService">
        <property name="cmdLine" value="sh ___BASE_DIR___/run_knp.sh" />
        <property name="delimiterIn" value="\n" />
        <property name="delimiterOut" value="EOS\n" />
      </bean>
    </property>
  </bean>
</beans>

MessagePack RPCでユーザプログラムを呼び出す の手順に従い,このサービス定義XMLを指定してRaSCサービスを起動すると,パイプで接続された複数のユーザプログラムをRaSC上で稼働させることができます.

パイプでの入力を並列処理する

マルチコアCPUを生かして並列実行する に従って,並列実行の設定をしたRaSCサービスを起動します.

以下のようなJavaプログラムでRaSCサービスを呼び出すことで,標準入力からの読み取りと,並列処理を行えます.

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.util.Arrays;

import org.apache.commons.lang.StringUtils;

import jp.go.nict.langrid.client.msgpackrpc.MsgPackClientFactory;
import jp.go.nict.wisdom.wrapper.api.TextAnalysisService;

public class RaSCClient {
    public static void main(String[] args) throws Exception {
        try(MsgPackClientFactory factory = new MsgPackClientFactory()){
                TextAnalysisService client = factory.create(
                    TextAnalysisService.class,
                    new InetSocketAddress(args[0], Integer.parseInt(args[1])));

                try(InputStreamReader isr = new InputStreamReader(System.in, "UTF-8");
                    BufferedReader br = new BufferedReader(isr)) {

                        String[] list = new String[1000];
                        int count = 0;

                        while((list[count] = br.readLine()) != null) {
                            count++;
                            if (count == list.length){
                                String[] ret = client.analyzeArray(list);
                                System.out.println(StringUtils.join(ret, "\n"));
                                count = 0;
                            }
                        }
                        if(count > 0){
                            String[] ret = client.analyzeArray(Arrays.copyOf(list, count));
                            System.out.println(StringUtils.join(ret, "\n"));
                        }
                    }
            }
    }
}

コンパイルと実行には,RaSCコアパッケージ を使うのが簡単です(手順は MessagePack RPCでユーザプログラムを呼び出す を参照してください).以下のコマンドでコンパイルできます.

javac -cp lib/*: RaSCClient.java

ホスト名とポートを指定し,以下のように利用できます.ここでは,ホストは localhost, ポートは 19999 でRaSCサービスが起動しているものとします.

cat INPUT_TXT | java -cp ./lib/*: RaSCClient localhost 19999

空白を含むディレクトリやファイル名をコマンドラインに指定するには

サービス定義XMLに指定するコマンドラインで,ディレクトリ名やファイル名に空白が含まれると,MessagePack RPCでユーザプログラムを呼び出す に示した記述では正しく動作しません.

プロパティ cmdLine の代わりに,cmdArray を使って指定してください.以下は,Windows環境におけるmecabのデフォルトパスを指定する例です.

<property name="cmdArray">
  <list>
    <value>C:\Program Files\Mecab\bin\mecab</value>
    <value>-O</value>
    <value>wakati</value>
  </list>
</property>

cmdArrayプロパティとcmdLineプロパティが同時に設定されるとエラーになります.

“EOS”などの終端記号を結果に含めたい

サービス定義XMLincludeDelimtrue にしてください.

RaSCサービスの死活を確認する

String getStatus() というメソッドを呼ぶと,設定されたコマンドラインと,プールされたプロセス数が返されます. 以下に呼出例を示します. SERVICE_HOST, SERVICE_PORT はそれぞれ,RaSCサービスが起動しているホストとポートであるとします.

呼出例: Java

TextAnalysisService client = factory.create(TextAnalysisService.class,
          new InetSocketAddress(SERVICE_HOST, SERVICE_PORT));
String ret = client.getStatus();

呼出例: Perl

my $client = AnyEvent::MPRPC::Client->new(
  host => "SERVICE_HOST", port => "SERVICE_PORT"
 );
my $ret = $client->call('getStatus')->recv;

呼出例: Python

client = msgpackrpc.Client(msgpackrpc.Address("SERVICE_HOST", SERVICE_PORT))
ret = client.call('getStatus')

MessagePack RPCでユーザプログラムを呼び出す で起動したMeCabサービスの場合の,結果の例を示します.サービス定義XMLに設定されたコマンドラインと,実行中のプロセス数・上限のプロセス数が返されます.

Command line: /usr/local/bin/mecab
Pooled processes: 1 / 20

RaSCサービスが応答しない

クライアントから呼び出しを行っても結果が得られず,サービス定義XMLに設定したタイムアウト時間が経過してからエラーが返ることがあります. ユーザプログラムが実際には短時間で終了しているとすると,以下のような問題が考えられます.

  • ユーザプログラムに入力デリミタが正しく送信できていない
  • ユーザプログラムが出力する,出力デリミタを認識できていない

入力デリミタ,出力デリミタは, サービス定義XML に定義された,入力や出力の単位を認識するための区切りです.

例えば,ユーザプログラムが入力デリミタとして [END_OF_INPUT]\n (末尾に改行あり)という文字列を認識するとします. このとき,RaSCの設定で [END_OF_INPUT] (末尾に改行無し) を設定してしまうと,ユーザプログラムは改行コードを待ち続け,応答がなくなったように見えます.RaSCは設定した時間以上応答がない場合,プログラムを終了させ,再起動します(再起動が行われたことは,RaSCサービスのログに出力されます).

同様の問題は,出力についても発生します.例えば,ユーザプログラムが出力デリミタとして [END_OF_OUTPUT] (末尾に改行無し)という文字列を出力するとします. 一方,RaSCの設定で [END_OF_INPUT]\n (末尾に改行あり) を設定してしまうと,RaSCは改行コードを待ち続けます.

RaSCサービスからの応答がない場合には,入出力デリミタ(改行コードの有り無し,改行コードの種類),クライアントからの呼び出しにおける入力をチェックしてみてください.

同時に複数のリクエストを受けると何がおきるか

RaSCサービスの構成 に説明があります.

起動するプログラムのプロセス数の上限,タイムアウト時間の設定は サービス定義XML を参照してください.

既存ツールをRaSCで動かすように改修するには

ユーザプログラムをRaSCサービス上で稼働させるには,以下が必要です.

  • 入出力に標準入出力を使用する
  • 入出力のデリミタを決める
  • 1単位の入力を受けても終了しないようにする.

以下は,これを実現するためのCRF++の改修例です. CRF++には標準入力を使用するオプションがあるため,入出力のデリミタを決めます.ここでは,入力のデリミタに [END_OF_INPUT], 出力のデリミタに EOS を用いるとします. そのため,下記のソースコードのハイライトされた箇所を追加し,1行読み込んだ内容が [END_OF_INPUT] だった場合に EOS\n を標準出力に書き込む処理を加えます.

CRF++-0.58/tagger.cpp

bool TaggerImpl::read(std::istream *is) {
  scoped_fixed_array<char, 8192> line;
  clear();

  for (;;) {
    if (!is->getline(line.get(), line.size())) {
      is->clear(std::ios::eofbit|std::ios::badbit);
      return true;
    }
    if(std::strcmp(line.get(), "[END_OF_INPUT]") == 0){
      std::cout << "EOS\n";
      return true;
    }
    if (line[0] == '\0' || line[0] == ' ' || line[0] == '\t') {
      break;
    }
    if (!add(line.get())) {
      return false;
    }
  }

  return true;
}

大量のデータをRaSCサービスで処理するには

サイズの大きいデータを一度に処理しようとすると,バッファが溢れ,エラーになることがあります. サービス定義XML の設定で,バッファサイズを変更することができます.

文字化けする

利用するユーザプログラムによっては,環境変数 LANG が ja_JP.UTF-8 でないと正しく動作しないことがあります.

環境変数を設定するには

サービス定義XMLにパラメータ environment を定義することで,実行されるプログラムの環境変数を設定できます,(バージョン 1.0.2 以降).

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean id="target" class="jp.go.nict.langrid.servicecontainer.handler.TargetServiceFactory">
    <property name="service">
      <bean class="jp.go.nict.wisdom.wrapper.StdIOCommandService">
        <property name="cmdLine" value="___PATH_TO_PROGRAM___" />
        <property name="delimiterIn" value="___INPUT_DELIMITER___" />
        <property name="delimiterOut" value="___OUTPUT_DELIMITER___" />
        <property name="environment">
          <map>
            <entry key="VAR1">
              <value>VALUE1</value>
            </entry>
            <entry key="VAR2">
              <value>VALUE2/value>
            </entry>
       </map>
    </property>
  </bean>
</beans>