No.5 Corticonの呼び出し方と処理速度について (大量データ処理インプロセス編) 【前編】
2016.09.14 Progress Corticon
本エントリーは株式会社アシスト様が寄稿したエントリー(https://www.ashisuto.co.jp/product/category/brms/progress_corticon/column/detail/brmstech05.html)を転載したものとなります。
|
Corticonは、業務オペレーションを伴うオンライン処理はもちろん、バッチ処理による業務データの判定にも用いられる場合があります。しかし、大量なデータ処理を行うアプリケーションをWebサービス(SOAPもしくはREST)で実装した場合、Session Time OutやOut Of Memoryなどの致命的なエラーが発生する可能性があることは明らかです。今回は、そのような大量データの判定処理にエラーなく許容される時間内でCorticon Serverのデシジョンサービスを利用する方法について解説します。 |
|
大量データ処理の想定と検証
![]() |
---|
例えば、「ある値が全データの上位10%の場合なんらかの処理を行う」というルールであれば、全データをルールに一括投入し上位10%に入っているかどうか判定する必要があります。そのデータがDBなどで管理されていれば、数十万件を超えることもあるでしょう。もちろんクライアントサイドでSQL文などを使用して対象データを絞り込んでおけば(上記の例ではあらかじめ上位10%のデータ)、対象データだけをルールに入力すれば良いので、そのような問題は回避できるでしょう。しかし、それでは本来BRMSで管理するはずだった「上位10%」といったルールが、クライアントサイドのSQLで実装されることになり、BRMSを利用するメリットが減少してしまいます。このような課題に対してCorticonではどのようなアプローチで解決するか、解決方法と処理速度を検証しました。
Corticonならではの手法
考えられる解決方法は、Webサービスを使用しない「インプロセス」という手法とさらにルールの中からデータベースを直接参照する「EDC」オプションを使用する方法があります。
![]() |
---|
※ インプロセスについて
インプロセスとはCorticon Serverの利用方法の一種で、ユーザーが実装したJavaプログラムのプロセス内でCorticon Serverを実行する方法になります。Corticon Serverの最も一般的な利用方法はWebサービスでの利用ですが、インプロセスを利用すると、Webサービスで発生する様々なオーバヘッド(データの転送処理やパース処理)が無いため、高速に動作します。
※ EDCについて |
|
まずは、「インプロセス」という手法での処理速度を検証します。
検証環境
- CPU: 2.20 GHz * 2コア
- OS: Windows 7 Professional 64bit
- Corticon Server: 5.5.2.7
- Microsoft SQL Server 2012 Express
検証用のデータ
今回の検証では大量データのサンプルとして、日本郵便のサイトで配布されている郵便番号マスタ(約12万件)を使用します。このデータを、Microsoft SQL Server内のテーブル「m_post」に、インポートしました。つぎのイメージです。
(▼テーブル「m_post」のCREATE TABLE文)
CREATE TABLE [dbo].[m_post]( [row_id] [bigint] NOT NULL, [post_no_old] [varchar](255) NULL, [post_no] [varchar](255) NULL, [address_1] [varchar](255) NULL, [address_2] [varchar](255) NULL, [address_3] [varchar](255) NULL, CONSTRAINT [PK_m_post] PRIMARY KEY CLUSTERED ( [row_id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
(▼テーブル「m_post」の内容: 40001行-40010行を表示)
|
検証用の判定ルール
- ルールへの入力値は、郵便番号が含まれたユーザーデータと、日本全国の郵便番号情報12万件が入ったマスタデータ
- ユーザーデータの郵便番号とマスタデータの郵便番号を照会して、郵便番号として正しいかどうか、を判定する
- 郵便番号として正しかった場合、その住所が首都圏(千葉、東京、神奈川、埼玉)かどうか、を判定する
このルールで使用するシソーラス(階層語彙、データ構造体、Corticonにおける語彙)は、つぎのとおりです。
|
エンティティ「m_post」はSQL Server内のテーブル「m_post」と全く同じ構造で、テーブル「m_post」から取得したデータ12万件が入ります。
エンティティ「Person」にはユーザーデータが入り、「Person」内の属性「PostNo」に入っている郵便番号をルールで判定します。属性「checkPostNoExit」, 「checkTokyoArea」には判定結果が入ります。
検証用のシーソラス(語彙)とデシジョンテーブル(ルールシート)
今回の検証で使用するルールはつぎのとおりです。
(▼スコープ)
|
(▼デシジョンテーブル)
|
(▼ルールステートメント)
|
ルールのポイントは、スコープ画面のフィルタ部分です。Corticonでは、フィルタ機能を利用してデシジョンテーブルに入る前にデータ量を絞ると、処理速度が向上します。ここでは、「m_post」には郵便番号データ約12万件が格納されますが、フィルタで処理された結果、「Person」内の「PostNo」に一致した「m_post」のデータだけがデシジョンテーブルの処理に入ります。
インプロセスプログラムの作成
次にCorticon Serverを実行するJavaプログラムを作成します。
★メイン
package CorticonExecuteInProcess; import java.util.ArrayList; import com.corticon.eclipse.server.core.CcServerFactory; import com.corticon.eclipse.server.core.ICcServer; import com.corticon.service.ccserver.ICcRuleMessage; import com.corticon.service.ccserver.ICcRuleMessages; import java.sql.Connection; import java.sql.Driver; import java.sql.ResultSet; import java.sql.Statement; import java.util.Properties; public class CorticonExecuteInProcess { public static void main(String[] args) { try { // Corticon Server 開始 ICcServer iCcServer = CcServerFactory.getCcServer(); System.out.println(iCcServer.getDecisionServiceNames().toString()); // デシジョンサービスへのインプットデータ(Javaオブジェクト) ArrayList<Object> colWorkObjs = getBusinessObjects(); // デシジョンサービス「Sample」を実行 long start = System.currentTimeMillis(); ICcRuleMessages Messages = iCcServer.execute("Sample", colWorkObjs); long end = System.currentTimeMillis(); // 結果の表示 double millisec = end - start; double sec = millisec / 1000; System.out.println("処理時間: " + millisec + " millisecond, " + sec + " second"); for (int i = 0; i < 2; i++) { Person p1 = (Person) colWorkObjs.get(i); System.out.println("postNo: " + p1.getPostNo()); System.out.println("personName: " + p1.getPersonName()); System.out.println("checkPostNoExist: " + p1.getCheckPostNoExist()); System.out.println("checkTokyoArea: " + p1.getCheckTokyoArea()); System.out.println(""); } for (int i = 0; i < Messages.getMessages().size(); i++) { System.out.println(((ICcRuleMessage) Messages.getMessages().get(i)).getText()); } }catch (Exception e) { System.out.println(e.getMessage()); } System.exit(0); } private static void insertPerson(ArrayList<Object>alBOs) { Person p1 = new Person(); p1.setPostno("1020073"); p1.setPersonName("大塚辰男"); Person p2 = new Person(); p2.setPostno("1111111"); p2.setPersonName("ビルトッテン"); alBOs.add(p1); alBOs.add(p2); } private static void insertPostMaster(ArrayList<Object>alBOs) { try { // SQL Server の「m_post」から全データを取得し、alBOsに格納 Driver d = (Driver) Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver").newInstance(); String connUrl = "jdbc:sqlserver://localhost:1433;database=test;integratedSecurity=false;user=sa;password=Progress@win7"; Connection con = d.connect(connUrl, new Properties()); //String SQL = "SELECT * FROM m_post ORDER BY row_id OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY;"; String SQL = "SELECT * FROM m_post ORDER BY row_id;"; Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(SQL); while (rs.next()) { m_post oPost = new m_post(); oPost.setRow_Id(Integer.parseInt(rs.getString("row_id"))); oPost.setPost_No_Old(rs.getString("post_no_old")); oPost.setPost_No(rs.getString("post_no")); oPost.setAddress_1(rs.getString("address_1")); oPost.setAddress_2(rs.getString("address_2")); oPost.setAddress_3(rs.getString("address_3")); alBOs.add(oPost); } rs.close(); stmt.close(); } catch (Exception e) { e.printStackTrace(); } } private static ArrayList<Object> getBusinessObjects() { try { ArrayList<Object> alBOs = new ArrayList<Object>(); insertPerson(alBOs); insertPostMaster(alBOs); return alBOs; } catch (Exception e) { System.out.println(e.getMessage()); return null; } } }
★語彙クラス
package CorticonExecuteInProcess; public class m_post { private Integer row_id = null; private String post_no_old = null; private String post_no = null; private String address_1 = null; private String address_2 = null; private String address_3 = null; public long getRow_Id() { return row_id; } public void setRow_Id(Integer al_row_id) { row_id = al_row_id; } public String getPost_No_Old() { return post_no_old; } public void setPost_No_Old(String as_post_no_old) { post_no_old = as_post_no_old; } public String getPost_No() { return post_no; } public void setPost_No(String as_post_no) { post_no = as_post_no; } public String getAddress_1() { return address_1; } public void setAddress_1(String as_address_1) { address_1 = as_address_1; } public String getAddress_2() { return address_2; } public void setAddress_2(String as_address_2) { address_2 = as_address_2; } public String getAddress_3() { return address_3; } public void setAddress_3(String as_address_3) { address_3 = as_address_3; } }
このプログラムは、Corticon ServerのコアライブラリとSQL ServerのJDBCドライバを参照設定しています。また、インプロセスのプログラムの場合、語彙クラスを自作する必要がある点に注意してください。メインプログラムの内容は、プログラム内のコメントでも簡単に記述していますが、全体の流れとしてはm_postオブジェクトにDB内の全件データを格納し、デシジョンサービスを実行しています。
このプログラムをエクスポート(コンパイル)したJARファイルを作成します。次に、作成したJARファイル内の語彙クラスとルールの語彙ファイル(ecore)をマッピングする設定を行います。Corticon Studioの語彙編集画面で、メニューの「語彙」-「Javaオブジェクトメッセージング」-「Javaクラスメタデータのインポート」で、作成したJARを設定すると、自動的に作成した語彙クラスと各属性のget/setメソッドが語彙に設定されます。
(設定後の語彙編集画面)
|
|
語彙のマッピング設定のあと、ルールフローのコンパイルとデプロイを行います。コンパイルにはCorticon Server付属の「Deployment Console」を使用しますが、「Deployment Console」実行時のCLASSPATHに、作成したJARファイルを追加してください。例えばコンパイルしたJARをSample.jarとしてデスクトップに保存しているときはつぎのようになります。
(<Deployment Consoleフォルダ>\bin\deployConsole.bat に以下を追加)
set CORTICON_CLASSPATH=%CORTICON_CLASSPATH%;C:\Users\progress\Desktop\Sample.jar
Deployment ConsoleでコンパイルしたEDSファイルを作成したら、Corticon Serverにデプロイします。デプロイ方法はWebサービス経由、CDDファイル、API経由など、いづれの方法でも問題ありません。
インプロセスプログラムの実行
ルールをデプロイ後、メインプログラムを実行します。実行時にデプロイしたルールを読み込むためにJavaの起動オプションとして「-DCORTICON_SETTING=SER -DCORTICON_WORK_DIR=<cortcon_work_path>」を指定します。
インプロセスプログラムの実行結果
実行結果はつぎのようになります。
Starting Progress Corticonサーバ : 5.5.2.7 -b7551 Progress Corticonサーバ CcServerSandbox location : C:/CcServerWork/SER/CcServerSandbox Loglevel : INFO Logpath : C:/CcServerWork/logs [Sample, test, test2] 処理時間: 4859.0 millisecond, 4.859 second postNo: 1020073 personName: 大塚辰男 checkPostNoExist: true checkTokyoArea: true postNo: 1111111 personName: ビルトッテン checkPostNoExist: false checkTokyoArea: false ビルトッテンさんの郵便番号は不正です 大塚辰男さんは東京都, 千代田区, 九段北にお住まいです 首都圏です
処理時間には誤差があるものの、おおよそ「5秒」前後です。
同様のプログラムをWebサービス(SOAP/REST)として作成し12万件のリクエストを実行してもSession Time Outして結果が返ってきません。それと比較すると、かなり高速に処理されていることがわかります。
さいごに
次回は、ルール内から直接外部のデータベースを参照することが可能な「EDC」オプションを検証します。
著者紹介
|
情報基盤事業部 製品統括部プログレス推進部 |